about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--COPYING17
-rw-r--r--LICENSE674
-rw-r--r--README.md39
-rw-r--r--default.nix13
-rw-r--r--devhell/README.md34
-rw-r--r--devhell/entry-eris.nix (renamed from entry-eris.nix)0
-rw-r--r--devhell/entry-skunkworks.nix (renamed from entry-skunkworks.nix)0
-rw-r--r--devhell/entry-titan.nix (renamed from entry-titan.nix)0
-rw-r--r--devhell/machines/machine_common.nix (renamed from machines/machine_common.nix)0
-rw-r--r--devhell/machines/machine_eris.nix (renamed from machines/machine_eris.nix)0
-rw-r--r--devhell/machines/machine_skunkworks.nix (renamed from machines/machine_skunkworks.nix)0
-rw-r--r--devhell/machines/machine_titan.nix (renamed from machines/machine_titan.nix)0
-rw-r--r--devhell/network/network_common.nix (renamed from network/network_common.nix)0
-rw-r--r--devhell/network/network_eris.nix (renamed from network/network_eris.nix)0
-rw-r--r--devhell/network/network_skunkworks.nix (renamed from network/network_skunkworks.nix)0
-rw-r--r--devhell/network/network_titan.nix (renamed from network/network_titan.nix)0
-rw-r--r--devhell/overrides/overrides.nix (renamed from overrides/overrides.nix)0
-rw-r--r--devhell/pkgs/pkgs_common.nix (renamed from pkgs/pkgs_common.nix)0
-rw-r--r--devhell/pkgs/pkgs_eris.nix (renamed from pkgs/pkgs_eris.nix)0
-rw-r--r--devhell/pkgs/pkgs_skunkworks.nix (renamed from pkgs/pkgs_skunkworks.nix)0
-rw-r--r--devhell/pkgs/pkgs_titan.nix (renamed from pkgs/pkgs_titan.nix)0
-rw-r--r--devhell/release.nix13
-rw-r--r--devhell/services/services_common.nix (renamed from services/services_common.nix)0
-rw-r--r--devhell/services/services_eris.nix (renamed from services/services_eris.nix)0
-rw-r--r--devhell/services/services_skunkworks.nix (renamed from services/services_skunkworks.nix)0
-rw-r--r--devhell/services/services_titan.nix (renamed from services/services_titan.nix)0
-rw-r--r--devhell/users/users_dev.nix (renamed from users/users_dev.nix)0
-rw-r--r--doc/entities.ent22
-rw-r--r--doc/index.xml37
-rw-r--r--doc/install.xml81
-rw-r--r--doc/options.xml9
-rw-r--r--lib/call-machine.nix66
-rw-r--r--lib/call-network.nix19
-rw-r--r--lib/default.nix5
-rw-r--r--lib/get-tests.nix23
-rw-r--r--machines/README.md4
-rw-r--r--machines/aszlig/arilou.nix44
-rw-r--r--machines/aszlig/dnyarri.nix117
-rw-r--r--machines/aszlig/kzerza.nix69
-rw-r--r--machines/aszlig/managed/brawndo.nix116
-rw-r--r--machines/aszlig/managed/tyree.nix86
-rw-r--r--machines/aszlig/meshuggah.nix56
-rw-r--r--machines/aszlig/tishtushi.nix51
-rw-r--r--machines/default.nix30
-rw-r--r--machines/labnet/buffer.patch13
-rw-r--r--machines/labnet/labtops.nix61
-rw-r--r--machines/labnet/manual-setup.md24
-rw-r--r--machines/misc/mailserver.nix118
-rw-r--r--machines/profpatsch/base-server.nix31
-rw-r--r--machines/profpatsch/base-workstation.nix104
-rw-r--r--machines/profpatsch/base.nix41
-rw-r--r--machines/profpatsch/haku.nix84
-rw-r--r--machines/profpatsch/katara.nix401
-rw-r--r--machines/profpatsch/lib.nix17
-rw-r--r--machines/profpatsch/patches/searx-rm-soundcloud.patch15
-rw-r--r--machines/profpatsch/patches/searx-secret-key.patch251
-rw-r--r--machines/profpatsch/patches/sent-bg.patch18
-rw-r--r--machines/profpatsch/patches/taffybar-color.patch40
-rw-r--r--machines/profpatsch/patches/taffybar.patch71
-rw-r--r--machines/profpatsch/pkgs.nix90
-rw-r--r--machines/profpatsch/xmpp-client.nix31
-rw-r--r--machines/sternenseemann/fliewatuet.nix277
-rw-r--r--machines/sternenseemann/patches/2bwm-config.patch131
-rw-r--r--machines/sternenseemann/pkgs.nix11
-rw-r--r--machines/sternenseemann/schaf.nix105
-rw-r--r--machines/sternenseemann/schnurrkadse.nix179
-rw-r--r--modules/README.md49
-rw-r--r--modules/core/common.nix83
-rw-r--r--modules/core/lazy-packages.nix67
-rw-r--r--modules/core/licensing.nix19
-rw-r--r--modules/core/tests.nix415
-rw-r--r--modules/hardware/gamecontroller.nix109
-rw-r--r--modules/hardware/rtl8192cu/default.nix50
-rw-r--r--modules/hardware/t100ha/brcmfmac43340-sdio.txtbin0 -> 3239 bytes
-rw-r--r--modules/hardware/t100ha/default.nix107
-rw-r--r--modules/hardware/t100ha/drm.patch733
-rw-r--r--modules/hardware/t100ha/meta-keys.patch44
-rw-r--r--modules/hardware/t100ha/sdio.patch52
-rw-r--r--modules/hardware/t100ha/sound.patch5620
-rw-r--r--modules/hardware/thinkpad.nix31
-rw-r--r--modules/module-list.nix36
-rw-r--r--modules/programs/fish/fasd.nix30
-rw-r--r--modules/programs/gnupg/agent-wrapper.c313
-rw-r--r--modules/programs/gnupg/default.nix191
-rw-r--r--modules/programs/gnupg/pinentry-wrapper.c281
-rw-r--r--modules/services/multipath-vpn.nix246
-rw-r--r--modules/services/postfix/default.nix65
-rw-r--r--modules/services/starbound.nix351
-rw-r--r--modules/system/iso.nix12
-rw-r--r--modules/user/aszlig/profiles/base.nix111
-rw-r--r--modules/user/aszlig/profiles/managed.nix41
-rw-r--r--modules/user/aszlig/profiles/workstation/default.nix182
-rw-r--r--modules/user/aszlig/profiles/workstation/lazy-packages.nix28
-rw-r--r--modules/user/aszlig/profiles/workstation/packages.nix78
-rw-r--r--modules/user/aszlig/programs/gajim/config.nix731
-rw-r--r--modules/user/aszlig/programs/gajim/config.patch80
-rw-r--r--modules/user/aszlig/programs/gajim/default.nix70
-rw-r--r--modules/user/aszlig/programs/git/default.nix70
-rw-r--r--modules/user/aszlig/programs/mpv/default.nix25
-rw-r--r--modules/user/aszlig/programs/taalo-build/default.nix93
-rw-r--r--modules/user/aszlig/programs/taskwarrior/config.patch48
-rw-r--r--modules/user/aszlig/programs/taskwarrior/default.nix36
-rw-r--r--modules/user/aszlig/programs/vim/default.nix386
-rw-r--r--modules/user/aszlig/programs/xpdf/default.nix20
-rw-r--r--modules/user/aszlig/programs/zsh/default.nix129
-rw-r--r--modules/user/aszlig/services/i3/conky.nix122
-rw-r--r--modules/user/aszlig/services/i3/default.nix134
-rw-r--r--modules/user/aszlig/services/i3/i3.conf131
-rw-r--r--modules/user/aszlig/services/i3/workspace.nix107
-rw-r--r--modules/user/aszlig/services/slim/default.nix45
-rw-r--r--modules/user/aszlig/services/vlock/default.nix57
-rw-r--r--modules/user/aszlig/services/vlock/message.cat18
-rw-r--r--modules/user/aszlig/services/vlock/message.colmap18
-rw-r--r--modules/user/aszlig/system/bfq.patch10167
-rw-r--r--modules/user/aszlig/system/kernel.nix31
-rw-r--r--modules/user/openlab/base.nix79
-rw-r--r--modules/user/openlab/labtops.nix102
-rw-r--r--modules/user/openlab/stackenblocken.nix38
-rw-r--r--modules/user/profpatsch/programs/scanning.nix13
-rw-r--r--nixpkgs-path.nix2
-rwxr-xr-xpkgs/aszlig/aacolorize/aacolorize.py182
-rw-r--r--pkgs/aszlig/aacolorize/default.nix13
-rw-r--r--pkgs/aszlig/axbo/default.nix53
-rw-r--r--pkgs/aszlig/default.nix14
-rw-r--r--pkgs/aszlig/gajim/default.nix33
-rw-r--r--pkgs/aszlig/git-detach/default.nix33
-rw-r--r--pkgs/aszlig/grandpa/default.nix18
-rw-r--r--pkgs/aszlig/librxtx-java/default.nix47
-rw-r--r--pkgs/aszlig/lockdev/default.nix23
-rw-r--r--pkgs/aszlig/nixops/default.nix37
-rw-r--r--pkgs/aszlig/pvolctrl/default.nix35
-rw-r--r--pkgs/aszlig/santander/default.nix89
-rw-r--r--pkgs/aszlig/santander/pipelight.patch13
-rw-r--r--pkgs/aszlig/santander/wine-no-unixfs.patch292
-rw-r--r--pkgs/aszlig/santander/winscard.patch11
-rw-r--r--pkgs/build-support/channel.nix32
-rw-r--r--pkgs/default.nix28
-rw-r--r--pkgs/games/base-module.nix13
-rw-r--r--pkgs/games/default.nix26
-rw-r--r--pkgs/games/humblebundle/antichamber.nix58
-rw-r--r--pkgs/games/humblebundle/bastion.nix62
-rw-r--r--pkgs/games/humblebundle/brigador.nix103
-rw-r--r--pkgs/games/humblebundle/cavestoryplus.nix39
-rw-r--r--pkgs/games/humblebundle/default.nix54
-rw-r--r--pkgs/games/humblebundle/fetch-humble-bundle/default.nix182
-rw-r--r--pkgs/games/humblebundle/fez.nix35
-rw-r--r--pkgs/games/humblebundle/ftl.nix38
-rw-r--r--pkgs/games/humblebundle/guacamelee.nix61
-rw-r--r--pkgs/games/humblebundle/hammerwatch.nix40
-rw-r--r--pkgs/games/humblebundle/jamestown.nix55
-rw-r--r--pkgs/games/humblebundle/liads.nix43
-rw-r--r--pkgs/games/humblebundle/megabytepunch.nix18
-rw-r--r--pkgs/games/humblebundle/pico-8.nix51
-rw-r--r--pkgs/games/humblebundle/rocketbirds.nix11
-rw-r--r--pkgs/games/humblebundle/spaz.nix39
-rw-r--r--pkgs/games/humblebundle/starbound.nix329
-rw-r--r--pkgs/games/humblebundle/swordsandsoldiers.nix43
-rw-r--r--pkgs/games/humblebundle/unepic.nix44
-rw-r--r--pkgs/games/steam/default.nix38
-rw-r--r--pkgs/games/steam/fetchsteam/default.nix92
-rw-r--r--pkgs/games/steam/fetchsteam/downloader.patch34
-rw-r--r--pkgs/games/steam/starbound.nix207
-rw-r--r--pkgs/lib/call-package-scope.nix25
-rw-r--r--pkgs/list-gamecontrollers/default.nix10
-rw-r--r--pkgs/list-gamecontrollers/list-gc.c31
-rw-r--r--pkgs/openlab/default.nix6
-rw-r--r--pkgs/openlab/gitit/default.nix18
-rw-r--r--pkgs/openlab/stackenblocken/default.nix86
-rwxr-xr-xpkgs/profpatsch/backlight/backlight.py38
-rw-r--r--pkgs/profpatsch/backlight/default.nix17
-rw-r--r--pkgs/profpatsch/default.nix38
-rw-r--r--pkgs/profpatsch/display-infos/default.nix38
-rw-r--r--pkgs/profpatsch/nman/default.nix12
-rw-r--r--pkgs/profpatsch/nman/nman.c80
-rw-r--r--pkgs/profpatsch/patches/mtp-jolla.patch34
-rw-r--r--pkgs/profpatsch/show-qr-code/default.nix28
-rw-r--r--pkgs/profpatsch/warpspeed/default.nix34
-rw-r--r--pkgs/sternenseemann/default.nix6
-rw-r--r--pkgs/sternenseemann/logbook/default.nix25
-rw-r--r--pkgs/sternenseemann/spacecookie/default.nix25
-rw-r--r--release.nix199
-rw-r--r--tests/default.nix19
-rw-r--r--tests/games/starbound.nix103
-rw-r--r--tests/make-test.nix30
-rw-r--r--tests/programs/gnupg/default.nix136
-rw-r--r--tests/programs/gnupg/snakeoil.asc59
-rw-r--r--tests/richi235/multipath-vpn.nix189
187 files changed, 29751 insertions, 37 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..8377ade4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,17 @@
+All Nix expressions in this repository are free software: you can
+redistribute them and/or modify them under the terms of the GNU General
+Public License as published by the Free Software Foundation, either
+version 3 of the License, or (at your option) any later version.
+
+The expression files are distributed in the hope that they will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+Public License for more details.
+
+For files other than the Nix expression files the same terms apply,
+unless explicitly stated otherwise here or in the file header.
+
+You should have received a copy of the GNU General Public License
+along with this program (see LICENSE file).
+
+If not, see <http://www.gnu.org/licenses/>.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README.md b/README.md
index d8d987c7..37d71f5b 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,13 @@
-# nixosconf
-_NixOS configuration for my current machines._
+About Vuizvui
+=============
 
-Feel free to use and adapt as you wish.
+This contains a set of NixOS modules/configurations and various other Nix
+expressions used by [OpenLab Augsburg](https://openlab-augsburg.de) and its
+members.
 
-In case you've wondered where to start, normally it's enough to simply import
-the `entry-*.nix` file, respective of the machine you want to build (naturally
-you can also just rename the files to reflect your own configuration/machine
-names), into your `/etc/nixos/configuration.nix`, for example:
-```
-{ config, pkgs, lib, ... }:
+Documentation
+=============
 
-{
-  imports =
-    [
-      /home/<username>/nixosconf/entry-<machinename>.nix
-    ];
-}
-```
+You can find the latest build of the documentation here:
 
-**NOTE:** This NixOS configuration is geared towards people running NixOS
-_unstable_ or `nixpkgs` [master branch](https://github.com/NixOS/nixpkgs). This
-also means that if `nixos-rebuild switch` encounters an error, then it is likely
-that whatever caused the error has not yet been included in the latest Hydra
-channel update (this is assuming that you are using the _unstable_ channel).
-Therefore your best bet is to either disable the culprit in the configuration
-or just wait until the [unstable channel](https://hydra.nixos.org/job/nixos/trunk-combined/tested#tabs-constituents)
-has been updated. Or, of course, run directly from `nixpkgs` master branch.
-
-**NOTE 2:** Unless you have access to a Hydra build farm/machine you will most
-likely want to remove the `binaryCaches` option in the `machine_common.nix`
-file. This also means that you can ignore the `release.nix` file. Unless, of
-course, you have access to a Hydra, in which case you'll still want to adapt
-the `binaryCache` option.
+https://headcounter.org/hydra/job/openlab/vuizvui/manual/latest/download
diff --git a/default.nix b/default.nix
new file mode 100644
index 00000000..86e58376
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,13 @@
+{ system ? builtins.currentSystem, ... }@args:
+
+{
+  machines = import ./machines;
+
+  pkgs = import ./pkgs {
+    pkgs = import (import ./nixpkgs-path.nix) args;
+  };
+
+  lib = import "${import ./nixpkgs-path.nix}/lib" // {
+    vuizvui = import ./lib;
+  };
+}
diff --git a/devhell/README.md b/devhell/README.md
new file mode 100644
index 00000000..d8d987c7
--- /dev/null
+++ b/devhell/README.md
@@ -0,0 +1,34 @@
+# nixosconf
+_NixOS configuration for my current machines._
+
+Feel free to use and adapt as you wish.
+
+In case you've wondered where to start, normally it's enough to simply import
+the `entry-*.nix` file, respective of the machine you want to build (naturally
+you can also just rename the files to reflect your own configuration/machine
+names), into your `/etc/nixos/configuration.nix`, for example:
+```
+{ config, pkgs, lib, ... }:
+
+{
+  imports =
+    [
+      /home/<username>/nixosconf/entry-<machinename>.nix
+    ];
+}
+```
+
+**NOTE:** This NixOS configuration is geared towards people running NixOS
+_unstable_ or `nixpkgs` [master branch](https://github.com/NixOS/nixpkgs). This
+also means that if `nixos-rebuild switch` encounters an error, then it is likely
+that whatever caused the error has not yet been included in the latest Hydra
+channel update (this is assuming that you are using the _unstable_ channel).
+Therefore your best bet is to either disable the culprit in the configuration
+or just wait until the [unstable channel](https://hydra.nixos.org/job/nixos/trunk-combined/tested#tabs-constituents)
+has been updated. Or, of course, run directly from `nixpkgs` master branch.
+
+**NOTE 2:** Unless you have access to a Hydra build farm/machine you will most
+likely want to remove the `binaryCaches` option in the `machine_common.nix`
+file. This also means that you can ignore the `release.nix` file. Unless, of
+course, you have access to a Hydra, in which case you'll still want to adapt
+the `binaryCache` option.
diff --git a/entry-eris.nix b/devhell/entry-eris.nix
index 96c4cc15..96c4cc15 100644
--- a/entry-eris.nix
+++ b/devhell/entry-eris.nix
diff --git a/entry-skunkworks.nix b/devhell/entry-skunkworks.nix
index c53cb751..c53cb751 100644
--- a/entry-skunkworks.nix
+++ b/devhell/entry-skunkworks.nix
diff --git a/entry-titan.nix b/devhell/entry-titan.nix
index 2b178190..2b178190 100644
--- a/entry-titan.nix
+++ b/devhell/entry-titan.nix
diff --git a/machines/machine_common.nix b/devhell/machines/machine_common.nix
index b7fb5215..b7fb5215 100644
--- a/machines/machine_common.nix
+++ b/devhell/machines/machine_common.nix
diff --git a/machines/machine_eris.nix b/devhell/machines/machine_eris.nix
index 60b6944c..60b6944c 100644
--- a/machines/machine_eris.nix
+++ b/devhell/machines/machine_eris.nix
diff --git a/machines/machine_skunkworks.nix b/devhell/machines/machine_skunkworks.nix
index 12ad9687..12ad9687 100644
--- a/machines/machine_skunkworks.nix
+++ b/devhell/machines/machine_skunkworks.nix
diff --git a/machines/machine_titan.nix b/devhell/machines/machine_titan.nix
index 5a0018fb..5a0018fb 100644
--- a/machines/machine_titan.nix
+++ b/devhell/machines/machine_titan.nix
diff --git a/network/network_common.nix b/devhell/network/network_common.nix
index e54c78fc..e54c78fc 100644
--- a/network/network_common.nix
+++ b/devhell/network/network_common.nix
diff --git a/network/network_eris.nix b/devhell/network/network_eris.nix
index e55f319f..e55f319f 100644
--- a/network/network_eris.nix
+++ b/devhell/network/network_eris.nix
diff --git a/network/network_skunkworks.nix b/devhell/network/network_skunkworks.nix
index fe943e1d..fe943e1d 100644
--- a/network/network_skunkworks.nix
+++ b/devhell/network/network_skunkworks.nix
diff --git a/network/network_titan.nix b/devhell/network/network_titan.nix
index 06b34dcd..06b34dcd 100644
--- a/network/network_titan.nix
+++ b/devhell/network/network_titan.nix
diff --git a/overrides/overrides.nix b/devhell/overrides/overrides.nix
index 3e07a75c..3e07a75c 100644
--- a/overrides/overrides.nix
+++ b/devhell/overrides/overrides.nix
diff --git a/pkgs/pkgs_common.nix b/devhell/pkgs/pkgs_common.nix
index e723fb5e..e723fb5e 100644
--- a/pkgs/pkgs_common.nix
+++ b/devhell/pkgs/pkgs_common.nix
diff --git a/pkgs/pkgs_eris.nix b/devhell/pkgs/pkgs_eris.nix
index fa834b0e..fa834b0e 100644
--- a/pkgs/pkgs_eris.nix
+++ b/devhell/pkgs/pkgs_eris.nix
diff --git a/pkgs/pkgs_skunkworks.nix b/devhell/pkgs/pkgs_skunkworks.nix
index 296dbca1..296dbca1 100644
--- a/pkgs/pkgs_skunkworks.nix
+++ b/devhell/pkgs/pkgs_skunkworks.nix
diff --git a/pkgs/pkgs_titan.nix b/devhell/pkgs/pkgs_titan.nix
index 3efab6c1..3efab6c1 100644
--- a/pkgs/pkgs_titan.nix
+++ b/devhell/pkgs/pkgs_titan.nix
diff --git a/devhell/release.nix b/devhell/release.nix
new file mode 100644
index 00000000..10c2a790
--- /dev/null
+++ b/devhell/release.nix
@@ -0,0 +1,13 @@
+### THIS FILE IS FOR HYDRA BUILD ONLY! YOU CAN IGNORE THIS FILE IF YOU ARE NOT USING A HYDRA!
+
+let
+  supportedSystems = [ "x86_64-linux" ];
+  system = "x86_64-linux";
+
+in
+
+{
+  skunkworks = (import <nixpkgs/nixos> { configuration = ./entry-skunkworks.nix; }).system;
+  titan = (import <nixpkgs/nixos> { configuration = ./entry-titan.nix; }).system;
+  eris = (import <nixpkgs/nixos> { configuration = ./entry-eris.nix; }).system;
+}
diff --git a/services/services_common.nix b/devhell/services/services_common.nix
index 42ba2525..42ba2525 100644
--- a/services/services_common.nix
+++ b/devhell/services/services_common.nix
diff --git a/services/services_eris.nix b/devhell/services/services_eris.nix
index cfec789f..cfec789f 100644
--- a/services/services_eris.nix
+++ b/devhell/services/services_eris.nix
diff --git a/services/services_skunkworks.nix b/devhell/services/services_skunkworks.nix
index f0fa4243..f0fa4243 100644
--- a/services/services_skunkworks.nix
+++ b/devhell/services/services_skunkworks.nix
diff --git a/services/services_titan.nix b/devhell/services/services_titan.nix
index a4ed892c..a4ed892c 100644
--- a/services/services_titan.nix
+++ b/devhell/services/services_titan.nix
diff --git a/users/users_dev.nix b/devhell/users/users_dev.nix
index 41649ae0..41649ae0 100644
--- a/users/users_dev.nix
+++ b/devhell/users/users_dev.nix
diff --git a/doc/entities.ent b/doc/entities.ent
new file mode 100644
index 00000000..cee77e2d
--- /dev/null
+++ b/doc/entities.ent
@@ -0,0 +1,22 @@
+<!ENTITY nixos.url "https://nixos.org/">
+<!ENTITY nixpkgs.url "https://nixos.org/nixpkgs/">
+<!ENTITY openlab.url "https://openlab-augsburg.de/">
+
+<!ENTITY nixos '
+<link xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xlink:href="&nixos.url;">NixOS</link>
+'>
+<!ENTITY nixpkgs '
+<link xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xlink:href="&nixpkgs.url;">&lt;nixpkgs&gt;</link>
+'>
+<!ENTITY openlab '
+<link xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xlink:href="&openlab.url;">OpenLab Augsburg</link>
+'>
+
+<!ENTITY hydra.base "https://headcounter.org/hydra">
+<!ENTITY hydra.channelbase "&hydra.base;/channel/custom/openlab/vuizvui">
diff --git a/doc/index.xml b/doc/index.xml
new file mode 100644
index 00000000..efb0bf2f
--- /dev/null
+++ b/doc/index.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE book [
+<!ENTITY % entities SYSTEM "entities.ent">
+%entities;
+]>
+<book xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+  <title>Vuizvui</title>
+  <preface>
+    <title>What is Vuizvui?</title>
+    <para>
+      Vuizvui is a set of NixOS modules and machine configurations that aim to
+      extend &nixos; in a way that fits the needs of the members of the
+      &openlab; and also serves as a playground for experimental features that
+      are yet to be included in the official NixOS project once they're well
+      tested and matured enough.
+    </para>
+
+    <para>
+      This means that module options in Vuizvui are subject to change without
+      retaining backwards-compatibility for configurations outside of the
+      defined machines.
+    </para>
+
+    <para>
+      The name <literal>Vuizvui</literal> is of Bavarian origins and means
+      something like <literal>too much</literal> while on the other side
+      <literal>nix</literal> means nothing. Which fits quite well because this
+      repository is for everything either too complex or not polished/generic
+      enough to be pushed into &nixpkgs;.
+    </para>
+  </preface>
+
+  <xi:include href="install.xml" />
+  <xi:include href="options.xml" />
+</book>
diff --git a/doc/install.xml b/doc/install.xml
new file mode 100644
index 00000000..b181dac7
--- /dev/null
+++ b/doc/install.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!DOCTYPE book [
+<!ENTITY % entities SYSTEM "entities.ent">
+%entities;
+]>
+<part xmlns="http://docbook.org/ns/docbook"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+  <title>Installing a machine in Vuizvui</title>
+
+  <para>
+    The easiest way to get started is if the machine is already in Vuizvui so
+    there is a channel available.
+  </para>
+
+  <para>
+    You can have a look at
+    <link xlink:href="&hydra.base;/jobset/openlab/vuizvui#tabs-channels">the
+      list of channels</link>
+    to check whether a channel exists for the machine you want to install.
+  </para>
+
+  <para>
+    So let's say you want to install the machine <literal>labtop</literal> which
+    has the channel attribute <literal>channels.machines.labnet.labtops.labtop</literal>
+  </para>
+
+  <para>
+    First you need to add the channel for the
+    <systemitem class="username">root</systemitem> user of your current system
+    using the following commands:
+
+    <command>
+<screen>
+nix-channel --add <link
+  xlink:href="&hydra.channelbase;/channels.machines.labnet.labtop"/> vuizvui
+nix-channel --remove nixos  # otherwise it will interfere with the rebuild
+nix-channel --update
+</screen>
+    </command>
+
+    Notice the <literal>vuizvui</literal> argument at the end of the first
+    command. This makes the channel available as
+    <literal>&lt;vuizvui&gt;</literal> in the search path of the current system.
+  </para>
+
+  <para>
+    For the first installation the <envar>NIX_PATH</envar> isn't correctly set
+    and will be set to include the <literal>vuizvui</literal> channel after
+    you've switched to the configuration for the first time.
+  </para>
+
+  <para>
+    Next put the following in your
+    <filename>/etc/nixos/configuration.nix</filename>:
+  </para>
+
+  <screen><code language="nix">(import &lt;vuizvui/machines&gt;).labnet.labtop.config</code></screen>
+
+  <para>
+    Of course you need to replace <literal>labnet.labtop</literal> with the
+    attribute of your machine.
+  </para>
+
+  <para>
+    Now in order to do the first build and activation of the configuration, you
+    need to issue the following command as root:
+  </para>
+
+  <!-- FIXME: This WON'T work because of wrong NIX_PATH and missicg binary
+              cache public key! -->
+  <!-- TODO: create a bootsrap script that does this automatically -->
+  <screen><command>nixos-rebuild \
+  -I nixpkgs=/nix/var/nix/profiles/per-user/root/channels/vuizvui/nixpkgs \
+  --option binary-cache-public-keys "headcounter.org:/7YANMvnQnyvcVB6rgFTdb8p5LG1OTXaO+21CaOSBzg=" \
+      switch</command></screen>
+
+  <para>
+    We redefine <literal>nixpkgs</literal> here, because vuizvui brings its own nixpkgs that gets build on the hydra, using it we get to download from the binary cache. Additionally, we need to manually specify the public key for the <literal>headcounter.org</literal> hydra.
+  </para>
+</part>
diff --git a/doc/options.xml b/doc/options.xml
new file mode 100644
index 00000000..da30e6c8
--- /dev/null
+++ b/doc/options.xml
@@ -0,0 +1,9 @@
+<part xmlns="http://docbook.org/ns/docbook"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>Vuizvui-specific NixOS options</title>
+    <para>
+        The following NixOS options are specific to Vuizvui:
+    </para>
+    <xi:include href="options-db.xml" />
+</part>
diff --git a/lib/call-machine.nix b/lib/call-machine.nix
new file mode 100644
index 00000000..ba38b40c
--- /dev/null
+++ b/lib/call-machine.nix
@@ -0,0 +1,66 @@
+path: cfg:
+
+let
+  nixpkgs = import ../nixpkgs-path.nix;
+
+  eval = import "${nixpkgs}/nixos/lib/eval-config.nix" {
+    modules = [ path cfg ] ++ import ../modules/module-list.nix;
+  };
+
+  iso = mkIso "installer/cd-dvd/iso-image.nix" (
+    { lib, ... }: let
+      name = eval.config.networking.hostName;
+      upperName = lib.toUpper name;
+    in rec {
+      isoImage.isoName = "${name}.iso";
+      isoImage.volumeID = builtins.substring 0 11 "${upperName}_LIVE";
+      isoImage.makeEfiBootable = true;
+      isoImage.makeUsbBootable = true;
+      isoImage.appendToMenuLabel = " \"${name}\" Live System";
+    }
+  );
+
+  installerIso = mkIso "installer/cd-dvd/installation-cd-minimal.nix" {
+    environment.sessionVariables = {
+      NIX_PATH = [ "vuizvui=${../.}" ];
+    };
+  };
+
+  mkIso = isoModule: extraConfig: let
+    wrapIso = { config, pkgs, lib, ... }@attrs: let
+      isoEval = import "${nixpkgs}/nixos/modules/${isoModule}" attrs;
+      isoEvalcfg = isoEval.config or {};
+      bootcfg = isoEvalcfg.boot or {};
+      fscfg = isoEvalcfg.fileSystems or {};
+    in {
+      options = isoEval.options or {};
+      imports = (isoEval.imports or []) ++ [ extraConfig ];
+      config = isoEvalcfg // {
+        boot = bootcfg // lib.optionalAttrs (bootcfg ? loader) {
+          loader = lib.mkForce bootcfg.loader;
+        };
+        fileSystems = lib.mapAttrs (lib.const lib.mkForce) fscfg // {
+          "/boot" = lib.mkForce (fscfg."/boot" or {
+            device = "none";
+            fsType = "none";
+            options = [ "noauto" ];
+          });
+        };
+      };
+    };
+  in import "${nixpkgs}/nixos/lib/eval-config.nix" {
+    modules = [ config wrapIso ];
+  };
+
+  config = {
+    imports = [ path cfg ] ++ import ../modules/module-list.nix;
+  };
+
+  vm = (import "${nixpkgs}/nixos" {
+    configuration = config;
+  }).vm;
+
+in {
+  build = eval.config.system.build.toplevel;
+  inherit config eval iso installerIso vm;
+}
diff --git a/lib/call-network.nix b/lib/call-network.nix
new file mode 100644
index 00000000..6b621357
--- /dev/null
+++ b/lib/call-network.nix
@@ -0,0 +1,19 @@
+path: args:
+
+with import "${import ../nixpkgs-path.nix}/lib";
+
+let
+  machineAttrs = import path;
+
+  mkMachine = name: {
+    inherit name;
+    value = import ./call-machine.nix machineAttrs.${name} ({ lib, ... }: {
+      imports = lib.singleton (args.extraConfig or {});
+      networking.hostName = lib.mkOverride 900 name;
+      _module.args.nodes = mapAttrs (const (m: m ? eval)) machines;
+    } // removeAttrs args [ "extraConfig" ]);
+  };
+
+  machines = listToAttrs (map mkMachine (attrNames machineAttrs));
+
+in machines
diff --git a/lib/default.nix b/lib/default.nix
new file mode 100644
index 00000000..05118275
--- /dev/null
+++ b/lib/default.nix
@@ -0,0 +1,5 @@
+rec {
+  callMachine = import ./call-machine.nix;
+  callNetwork = import ./call-network.nix;
+  getVuizvuiTests = import ./get-tests.nix;
+}
diff --git a/lib/get-tests.nix b/lib/get-tests.nix
new file mode 100644
index 00000000..2f78f58c
--- /dev/null
+++ b/lib/get-tests.nix
@@ -0,0 +1,23 @@
+{ system ? builtins.currentSystem
+, nixpkgs ? import ../nixpkgs-path.nix
+, vuizvuiTests ? ../tests
+, excludeVuizvuiGames ? false
+}:
+
+with import "${nixpkgs}/lib";
+
+{
+  nixos = let
+    upstreamTests = (import "${nixpkgs}/nixos/release.nix" {
+      inherit nixpkgs;
+    }).tests;
+    isTestOrJob = attr: (attr.type or null) == "derivation" || attr ? test;
+    isTestOrSystems = attr: isTestOrJob attr || attr ? ${system};
+    cond = attr: !isTestOrSystems attr;
+    reduce = attr: if isTestOrJob attr then attr else attr.${system};
+  in mapAttrsRecursiveCond cond (path: reduce) upstreamTests;
+
+  vuizvui = removeAttrs (import vuizvuiTests {
+    inherit system;
+  }) (optional excludeVuizvuiGames "games");
+}
diff --git a/machines/README.md b/machines/README.md
new file mode 100644
index 00000000..9d3fe3af
--- /dev/null
+++ b/machines/README.md
@@ -0,0 +1,4 @@
+This directory contains NixOS machine configurations.
+
+Feel free to add your own configuration into a subdirectory named after your
+nickname or handle and update default.nix accordingly.
diff --git a/machines/aszlig/arilou.nix b/machines/aszlig/arilou.nix
new file mode 100644
index 00000000..8fbc4353
--- /dev/null
+++ b/machines/aszlig/arilou.nix
@@ -0,0 +1,44 @@
+{ config, pkgs, lib, ... }:
+
+let
+  rootUUID = "e9c95c74-e4cf-41f6-bb45-baf8dd579217";
+  swapUUID = "4d172959-5cfd-4164-a46e-fa7be0dfd03a";
+  diskID = "usb-Lexar_USB_Flash_Drive_201303211246293590E4-0:0";
+
+  modulesPath = "${import ../../nixpkgs-path.nix}/nixos/modules";
+in {
+  vuizvui.user.aszlig.profiles.workstation.enable = true;
+  imports = [ "${modulesPath}/profiles/all-hardware.nix" ];
+
+  boot = {
+    kernelPackages = pkgs.linuxPackages_latest;
+    initrd.kernelModules = [ "fbcon" "usb_storage" ];
+    loader.grub.device = "/dev/disk/by-id/${diskID}";
+    loader.grub.memtest86.enable = true;
+  };
+
+  networking.hostName = "arilou";
+  networking.wireless.enable = lib.mkForce true;
+
+  fileSystems."/".device = "/dev/disk/by-uuid/${rootUUID}";
+  fileSystems."/".fsType = "btrfs";
+  fileSystems."/".options = [
+    "ssd"
+    "space_cache"
+    "compress-force=zlib"
+    "noatime"
+  ];
+
+  fileSystems."/tmp".device = "none";
+  fileSystems."/tmp".fsType = "tmpfs";
+  fileSystems."/tmp".options = [ "nosuid" "nodev" "relatime" ];
+
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-uuid/${swapUUID}";
+  };
+
+  services.openssh.enable = lib.mkForce false;
+  services.xserver.videoDrivers = [ "intel" "ati" "nouveau" ];
+
+  nix.maxJobs = lib.mkForce 2;
+}
diff --git a/machines/aszlig/dnyarri.nix b/machines/aszlig/dnyarri.nix
new file mode 100644
index 00000000..30103a86
--- /dev/null
+++ b/machines/aszlig/dnyarri.nix
@@ -0,0 +1,117 @@
+{ pkgs, lib, ... }:
+
+let
+  vaultPath = "/dev/mapper/${vaultDevice.name}";
+
+  mkDevice = category: num: uuid: {
+    name = "dnyarri-${category}-crypt-${toString num}";
+    device = "/dev/disk/by-uuid/${uuid}";
+    keyFile = vaultPath;
+    keyFileSize = 1048576;
+  };
+
+  vaultDevice = {
+    name = "dnyarri-crypt-vault";
+    device = "/dev/disk/by-uuid/61e971d2-be93-4e60-8266-b2c6a71e2dc8";
+  };
+
+  cryptDevices = {
+    root = lib.imap (mkDevice "root") [
+      "b13d257e-b5fd-4f86-82b1-8bfe06335a75"
+      "a607c827-2fd7-49d9-a7d8-05279c8653a4"
+      "de32cb42-2e09-4e6a-84b4-244078d289c8"
+      "12dac5b2-7647-45de-b752-5efee23855d0"
+    ];
+    swap = lib.imap (mkDevice "swap") [
+      "e0a8281d-2c68-48ca-8e00-f0defaf51f38"
+      "d26e61d6-c238-4c01-8c57-b1ba0bdb8c93"
+    ];
+  };
+
+in {
+  vuizvui.user.aszlig.profiles.workstation.enable = true;
+
+  nix.maxJobs = 8;
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.grub.enable = lib.mkForce false;
+    loader.efi.canTouchEfiVariables = true;
+
+    initrd = {
+      availableKernelModules = [
+        "aes_x86_64" "af_alg" "algif_skcipher" "cbc" "cryptd" "crypto_simd"
+        "dm_crypt" "ecb" "gf128mul" "glue_helper" "xts"
+      ];
+
+      luks.devices = lib.singleton vaultDevice
+                  ++ lib.concatLists (lib.attrValues cryptDevices);
+      postDeviceCommands = lib.mkAfter ''
+        cryptsetup luksClose ${lib.escapeShellArg vaultPath}
+      '';
+    };
+  };
+
+  environment.systemPackages = [ pkgs.paperwork ];
+
+  # This is very ugly and I really want to avoid non-free packages on all
+  # of my workstations. But right now I need to get rid of useless paper.
+  nixpkgs.config = {
+    allowUnfreePredicate = pkg: let
+      inherit (builtins.parseDrvName pkg.name) name;
+    in name == "hplip";
+    packageOverrides = super: {
+      hplip = super.hplip.override { withPlugin = true; };
+    };
+  };
+
+  hardware.sane.enable = true;
+  hardware.sane.extraBackends = [ pkgs.hplip ];
+
+  vuizvui.user.aszlig.system.kernel.enable = true;
+  hardware.enableRedistributableFirmware = true;
+
+  networking.hostName = "dnyarri";
+
+  fileSystems = {
+    "/boot" = {
+      device = "/dev/disk/by-uuid/9A75-9A6E";
+      fsType = "vfat";
+    };
+    "/" = {
+      label = "dnyarri-root";
+      fsType = "btrfs";
+      options = [ "autodefrag" "space_cache" "compress=lzo" "noatime" ];
+    };
+  };
+
+  powerManagement.powerUpCommands = ''
+    ${pkgs.hdparm}/sbin/hdparm -B 255 /dev/disk/by-id/ata-ST31500541AS_5XW0AMNH
+    ${pkgs.hdparm}/sbin/hdparm -B 255 /dev/disk/by-id/ata-ST31500541AS_6XW0M217
+  '';
+
+  swapDevices = map ({ name, ... }: {
+    device = "/dev/mapper/${name}";
+  }) cryptDevices.swap;
+
+  users.users.aszlig.extraGroups = [
+    "scanner"
+    # TODO: Try to avoid this, but as there is only a single user using audio
+    # on this machine, it's okay for now. But remember that this will break
+    # heavily, should there be another user accessing the audio devices.
+    "audio"
+  ];
+
+  services.xserver.videoDrivers = [ "ati" ];
+  services.xserver.xrandrHeads = [ "DVI-0" "HDMI-0" ];
+
+  vuizvui.user.aszlig.services.i3.workspaces."1" = {
+    label = "XMPP";
+    assign = lib.singleton { class = "^(?:Tkabber|Gajim)\$"; };
+  };
+
+  vuizvui.user.aszlig.services.i3.workspaces."3" = {
+    label = "Chromium";
+    assign = lib.singleton { class = "^Chromium(?:-browser)?\$"; };
+  };
+}
diff --git a/machines/aszlig/kzerza.nix b/machines/aszlig/kzerza.nix
new file mode 100644
index 00000000..98b8867a
--- /dev/null
+++ b/machines/aszlig/kzerza.nix
@@ -0,0 +1,69 @@
+{ pkgs, lib, ... }:
+
+let
+  rootUUID = "ad41f848-d14a-4a89-9d04-3e48bd73dc5c";
+  diskID = "usb-0000_Removable_Drive_23372707080836980013-0:0";
+in {
+  vuizvui.user.aszlig.profiles.base.enable = true;
+  vuizvui.createISO = true;
+
+  services.xserver.enable = lib.mkForce false;
+
+  services.gpm.enable = true;
+  services.gpm.protocol = "exps2";
+
+  boot = {
+    kernelParams = lib.singleton "consoleblank=0";
+    initrd.kernelModules = [ "fbcon" "usb_storage" ];
+    loader.grub.device = "/dev/disk/by-id/${diskID}";
+  };
+
+  networking.hostName = "kzerza";
+
+  fileSystems."/".device = "/dev/disk/by-uuid/${rootUUID}";
+  fileSystems."/".fsType = "btrfs";
+  fileSystems."/".options = [
+    "ssd"
+    "space_cache"
+    "compress-force=zlib"
+    "noatime"
+  ];
+
+  services.udev.extraRules = ''
+    SUBSYSTEM=="usb*|tty", ACTION=="add|change", ATTRS{idVendor}=="0403", \
+      ATTRS{idProduct}=="6001", OWNER="grandpa"
+  '';
+
+  fileSystems."/tmp".device = "none";
+  fileSystems."/tmp".fsType = "tmpfs";
+  fileSystems."/tmp".options = [ "nosuid" "nodev" "relatime" ];
+
+  users.groups.grandpa.gid = 666;
+  users.users.grandpa = {
+    uid = 666;
+    description = "GrandPA User";
+    group = "grandpa";
+    createHome = true;
+  };
+
+  systemd.services.grandpa = {
+    description = "GrandPA Lighting Controller";
+    wantedBy = [ "multi-user.target" ];
+    preStart = "${pkgs.kbd}/bin/chvt 7";
+    serviceConfig = {
+      Type = "idle";
+      ExecStart = "${pkgs.vuizvui.aszlig.grandpa}/bin/grandpa";
+      ExecStopPost = "${pkgs.systemd}/bin/systemctl poweroff";
+      Restart = "on-failure";
+      StandardInput = "tty";
+      StandardOutput = "tty";
+      TTYPath = "/dev/tty7";
+      TTYVTDisallocate = true;
+      User = "grandpa";
+      Group = "grandpa";
+      PermissionsStartOnly = true;
+      PrivateTmp = true;
+      PrivateNetwork = true;
+    };
+  };
+}
diff --git a/machines/aszlig/managed/brawndo.nix b/machines/aszlig/managed/brawndo.nix
new file mode 100644
index 00000000..ea592bcb
--- /dev/null
+++ b/machines/aszlig/managed/brawndo.nix
@@ -0,0 +1,116 @@
+{ config, pkgs, unfreePkgs, unfreeAndNonDistributablePkgs, lib, ... }:
+
+let
+  mainDisk = "ata-WDC_WD5000LPVX-22V0TT0_WD-WXG1E2559AYH";
+  rootUUID = "dbbd5a35-3ac0-4d5a-837d-914457de14a4";
+
+in {
+  boot = {
+    initrd.availableKernelModules = [
+      "xhci_pci" "ehci_pci" "ahci" "usb_storage" "sd_mod" "sr_mod"
+      "rtsx_pci_sdmmc"
+    ];
+    kernelModules = [ "kvm-intel" "wl" ];
+    kernelPackages = pkgs.linuxPackages_latest;
+    extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ];
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+  };
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-uuid/${rootUUID}";
+    fsType = "btrfs";
+    options = [ "compress=zlib" "space_cache" "noatime" ];
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-uuid/534F-980B";
+    fsType = "vfat";
+  };
+
+  hardware = {
+    cpu.intel.updateMicrocode = true;
+    enableAllFirmware = true;
+    opengl.s3tcSupport = true;
+    opengl.driSupport32Bit = true;
+    pulseaudio.enable = true;
+  };
+
+  networking = {
+    firewall.enable = false;
+    hostName = "brawndo";
+    networkmanager.enable = true;
+  };
+
+  nix = {
+    maxJobs = 4;
+    useSandbox = true;
+    readOnlyStore = true;
+    buildCores = 0;
+  };
+
+  nixpkgs.config = {
+    allowUnfree = true; # XXX: More granularity!
+    chromium.enablePepperFlash = true;
+    pulseaudio = true;
+  };
+
+  environment.systemPackages = with pkgs; [
+    vuizvui.aszlig.axbo
+    chromium
+    file
+    vuizvui.aszlig.gajim
+    gimp
+    git
+    gpodder
+    htop
+    kdeApplications.gwenview
+    kdeApplications.okular
+    libreoffice
+    mpv
+    opentyrian
+    pavucontrol
+    pulseaudioFull
+    samba
+    unfreePkgs.steam
+    unfreeAndNonDistributablePkgs.skype
+    thunderbird
+    wine
+    xpdf
+    youtubeDL
+  ];
+
+  i18n = {
+    consoleFont = "lat9w-16";
+    consoleKeyMap = "de";
+    defaultLocale = "en_US.UTF-8";
+  };
+
+  services = {
+    deluge.enable = true;
+    printing.drivers = [ pkgs.cups-bjnp ];
+    tlp.enable = true;
+
+    xserver = {
+      enable = true;
+      layout = "de";
+      xkbOptions = "eurosign:e";
+
+      synaptics.enable = true;
+      synaptics.twoFingerScroll = true;
+
+      displayManager.sddm.enable = true;
+      desktopManager.plasma5.enable = true;
+    };
+  };
+
+  swapDevices = lib.singleton { label = "swap"; };
+
+  time.timeZone = "Europe/Berlin";
+
+  vuizvui.user.aszlig.profiles.managed.enable = true;
+  vuizvui.user.aszlig.profiles.managed.mainUser = "dwenola";
+
+  vuizvui.user.aszlig.programs.vim.enable = true;
+  vuizvui.enableGlobalNixpkgsConfig = true;
+}
diff --git a/machines/aszlig/managed/tyree.nix b/machines/aszlig/managed/tyree.nix
new file mode 100644
index 00000000..557f690c
--- /dev/null
+++ b/machines/aszlig/managed/tyree.nix
@@ -0,0 +1,86 @@
+{ config, pkgs, unfreeAndNonDistributablePkgs, lib, ... }:
+
+{
+  boot.initrd.availableKernelModules = [ "usbhid" ];
+  boot.kernelModules = [ "kvm-intel" ];
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  environment.systemPackages = with pkgs; [
+    calibre cdparanoia chromium figlet vuizvui.aszlig.gajim gimp htop inkscape
+    thunderbird kdeApplications.gwenview kdeApplications.okular krita
+    libreoffice mosh mpv pciutils vlc wget wine youtubeDL
+    unfreeAndNonDistributablePkgs.skype
+  ];
+
+  fileSystems."/boot".device = "/dev/disk/by-uuid/A0D5-269D";
+  fileSystems."/boot".fsType = "vfat";
+
+  fileSystems."/".label = "tyree-root";
+  fileSystems."/".fsType = "btrfs";
+  fileSystems."/".options = [
+    "compress=lzo"
+    "discard"
+    "noatime"
+    "space_cache"
+    "ssd"
+  ];
+
+  swapDevices = lib.singleton {
+    label = "tyree-swap";
+  };
+
+  hardware.cpu.intel.updateMicrocode = true;
+  hardware.pulseaudio.enable = true;
+  hardware.pulseaudio.package = pkgs.pulseaudioFull;
+
+  i18n.consoleUseXkbConfig = true;
+  i18n.defaultLocale = "de_DE.UTF-8";
+
+  networking.hostName = "tyree";
+  networking.firewall.enable = false;
+  networking.wireless.enable = false;
+  networking.networkmanager.enable = true;
+  networking.useNetworkd = true;
+
+  # Temporary, seems to be a bug in NetworkManager:
+  networking.usePredictableInterfaceNames = false;
+
+  nix.maxJobs = 4;
+  nix.useSandbox = true;
+  nix.readOnlyStore = true;
+  nix.buildCores = 0;
+  nix.extraOptions = ''
+    auto-optimise-store = true
+  '';
+
+  nixpkgs.config = {
+    pulseaudio = true;
+    chromium.enablePepperFlash = true;
+  };
+
+  programs.bash.enableCompletion = true;
+  programs.bash.interactiveShellInit = lib.mkBefore ''
+    export LANG=en_US.UTF-8
+  '';
+
+  services.openssh.enable = true;
+  services.tlp.enable = true;
+
+  services.xserver.enable = true;
+  services.xserver.layout = "de";
+  services.xserver.xkbOptions = "eurosign:e,caps:none";
+  services.xserver.displayManager.sddm.enable = true;
+  services.xserver.desktopManager.plasma5.enable = true;
+  services.xserver.desktopManager.xterm.enable = false;
+  services.xserver.wacom.enable = true;
+
+  time.timeZone = "Europe/Berlin";
+
+  vuizvui.user.aszlig.profiles.managed.enable = true;
+  vuizvui.user.aszlig.profiles.managed.mainUser = "bla";
+
+  vuizvui.hardware.t100ha.enable = true;
+  vuizvui.user.aszlig.programs.vim.enable = true;
+}
diff --git a/machines/aszlig/meshuggah.nix b/machines/aszlig/meshuggah.nix
new file mode 100644
index 00000000..4a98022d
--- /dev/null
+++ b/machines/aszlig/meshuggah.nix
@@ -0,0 +1,56 @@
+{ pkgs, lib, ... }:
+
+{
+  vuizvui.user.aszlig.profiles.base.enable = true;
+  vuizvui.user.aszlig.programs.zsh.machineColor = "cyan";
+
+  boot.initrd.availableKernelModules = [ "sdhci_acpi" ];
+  boot.kernelPackages = pkgs.linuxPackages_latest;
+  boot.loader.grub.enable = lib.mkForce false;
+  boot.loader.efi.canTouchEfiVariables = true;
+  boot.loader.systemd-boot.enable = true;
+
+  networking.hostName = "meshuggah";
+
+  nix.maxJobs = 2;
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-uuid/9bddc8d3-88ee-4aac-b885-c9abca36b863";
+    fsType = "btrfs";
+    options = [
+      "compress=lzo"
+      "discard"
+      "noatime"
+      "space_cache"
+      "ssd"
+    ];
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-uuid/A318-8495";
+    fsType = "vfat";
+  };
+
+  services.openssh.enable = true;
+  services.openssh.permitRootLogin = "without-password";
+
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-uuid/2738fbe5-fee8-4835-a512-241f447252fa";
+  };
+
+  users.users.root.openssh.authorizedKeys.keys = let
+    mkKey = name: type: data: "${type} ${lib.concatStrings data} ${name}";
+  in lib.singleton (mkKey "openpgp:0x6321DF96" "ssh-rsa" [
+    "AAAAB3NzaC1yc2EAAAADAQABAAACAQCWc5omkAV4yV9gn11kHPlxfSXHlIROkJZAmn"
+    "towEMeUAyOI38gc3QNCTYRpo8bpD68U4X/p0NHIetm9v4t9t8Nsz/Tj3KHmh291DIj"
+    "W4IisrjCX1o9aj5ESu2bCNp+6oEWjb2GbecJjn3kf4eh82imh4F9s0PtGzCUB+iYgP"
+    "OiMCj4pfMK76yu6SKoyU43FwhkD+v5DgmBT0+GPftce4Dyrh3GV8qUotP32hohsFq3"
+    "aWxcRU6Y3yRwRiUskh1B6+H3W44peX+F+0j6jnX49DwAwXfFVAmqDqxLz4r37uRjYe"
+    "G+6UGt6fm2hgYyLf07ph8c6fQCOmYaPs7lvpBDB+zuadxAlk/bKHKgfr4xbXyoIjAV"
+    "L7uw3ui0NqbAMoBGEgi+Sk6t7JYQsyhvshauw3TyTi3Jjr1NBjHmbCNgguNLtWoFsJ"
+    "dd8Jgd1AVQbxnsLitWxSrTem9mlBRYWV+SxyBR5kqhrw36/yXVeWp+jX23Kg98Bco7"
+    "NA+QPWKXKE4HffAZQ3MJGEZJkS+9l1wCjrhAa5jjbH4Auh+bRNkKImXg26OdU7fU1O"
+    "Lgzi3J1vmcLV/HOqp66i8/biehsYL9GL2uHaqZS9Xaj177bTqveaVRjyomiNCkm0ed"
+    "+NPSboEuGyEE59D8w7FBsk/d3r3z3fZg1j++UvSwVkqXYByl5pzCnw=="
+  ]);
+}
diff --git a/machines/aszlig/tishtushi.nix b/machines/aszlig/tishtushi.nix
new file mode 100644
index 00000000..21ba9b3a
--- /dev/null
+++ b/machines/aszlig/tishtushi.nix
@@ -0,0 +1,51 @@
+{ config, pkgs, lib, ... }:
+
+let
+  rootUUID = "e33a3dda-a87d-473b-b113-37783aa35667";
+  swapUUID = "e9f59283-143c-4c36-978c-c730c6ca27c7";
+  storeUUID = "ce1db87b-d717-450d-a212-3685a224f626";
+  diskID = "ata-Hitachi_HTS543232A7A384_E2P31243FGB6PJ";
+in {
+  vuizvui.user.aszlig.profiles.workstation.enable = true;
+
+  vuizvui.user.aszlig.system.kernel.enable = true;
+
+  boot = {
+    initrd.kernelModules = [ "fbcon" "usb_storage" ];
+    loader.grub.device = "/dev/disk/by-id/${diskID}";
+    loader.timeout = 1;
+  };
+
+  networking.hostName = "tishtushi";
+  networking.wireless.enable = lib.mkForce true;
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-uuid/${rootUUID}";
+    fsType = "btrfs";
+    options = [ "space_cache" "compress=zlib" "noatime" ];
+  };
+
+  fileSystems."/nix/store" = {
+    device = "/dev/disk/by-uuid/${storeUUID}";
+    fsType = "btrfs";
+    options = [ "ssd" "compress-force=zlib" "noatime" ];
+  };
+
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-uuid/${swapUUID}";
+  };
+
+  services.synergy.client.enable = true;
+  services.synergy.client.serverAddress = "mmrnmhrm";
+
+  services.tlp.enable = true;
+
+  services.xserver.videoDrivers = [ "intel" ];
+  services.xserver.synaptics.enable = true;
+  services.xserver.synaptics.tapButtons = true;
+  services.xserver.synaptics.twoFingerScroll = true;
+  services.xserver.synaptics.vertEdgeScroll = false;
+  services.xserver.synaptics.accelFactor = "0.1";
+
+  nix.maxJobs = 4;
+}
diff --git a/machines/default.nix b/machines/default.nix
new file mode 100644
index 00000000..a976bbc4
--- /dev/null
+++ b/machines/default.nix
@@ -0,0 +1,30 @@
+with import ../lib;
+
+{
+  aszlig = {
+    dnyarri   = callMachine ./aszlig/dnyarri.nix {};
+    arilou    = callMachine ./aszlig/arilou.nix {};
+    kzerza    = callMachine ./aszlig/kzerza.nix {};
+    meshuggah = callMachine ./aszlig/meshuggah.nix {};
+    tishtushi = callMachine ./aszlig/tishtushi.nix {};
+    managed = {
+      brawndo = callMachine ./aszlig/managed/brawndo.nix {};
+      tyree   = callMachine ./aszlig/managed/tyree.nix {};
+    };
+  };
+  labnet = {
+    labtops = callNetwork ./labnet/labtops.nix {};
+  };
+  profpatsch = {
+    katara = callMachine ./profpatsch/katara.nix {};
+    haku   = callMachine ./profpatsch/haku.nix {};
+  };
+  misc = {
+    mailserver = callMachine ./misc/mailserver.nix {};
+  };
+  sternenseemann = {
+    fliewatuet   = callMachine ./sternenseemann/fliewatuet.nix {};
+    schnurrkadse = callMachine ./sternenseemann/schnurrkadse.nix {};
+    schaf        = callMachine ./sternenseemann/schaf.nix {};
+  };
+}
diff --git a/machines/labnet/buffer.patch b/machines/labnet/buffer.patch
new file mode 100644
index 00000000..379b3174
--- /dev/null
+++ b/machines/labnet/buffer.patch
@@ -0,0 +1,13 @@
+diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
+index 2fdebe0..7042ccc 100644
+--- a/src/modules/alsa/alsa-sink.c
++++ b/src/modules/alsa/alsa-sink.c
+@@ -63,7 +63,7 @@
+ #define DEFAULT_DEVICE "default"
+ 
+ #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)             /* 2s    -- Overall buffer size */
+-#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)        /* 20ms  -- Fill up when only this much is left in the buffer */
++#define DEFAULT_TSCHED_WATERMARK_USEC (500*PA_USEC_PER_MSEC)        /* 500ms  -- Fill up when only this much is left in the buffer */
+ 
+ #define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC)       /* 10ms  -- On underrun, increase watermark by this */
+ #define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC)        /* 5ms   -- When everything's great, decrease watermark by this */
diff --git a/machines/labnet/labtops.nix b/machines/labnet/labtops.nix
new file mode 100644
index 00000000..f3745901
--- /dev/null
+++ b/machines/labnet/labtops.nix
@@ -0,0 +1,61 @@
+{
+  labtop = {
+    vuizvui.user.openlab.labtops.enable = true;
+    boot.kernelModules = [ "kvm-intel" ];
+    boot.initrd.availableKernelModules = [
+      "uhci_hcd" "ehci_pci" "ata_piix" "firewire_ohci" "usb_storage"
+    ];
+
+    vuizvui.hardware.thinkpad.enable = true;
+
+    hardware.trackpoint.enable = false;
+
+    networking.enableIntel3945ABGFirmware = true;
+
+  };
+
+  hannswurscht = { pkgs, ... }:
+    let musicDir = "/data/music"; in
+  {
+    vuizvui.user.openlab.base.enable = true;
+
+    nixpkgs.system = "i686-linux";
+
+    users.users.openlab.extraGroups = [ "audio" ];
+    services.mingetty.autologinUser = "openlab";
+
+    hardware.pulseaudio = {
+      enable = true;
+      systemWide = true;
+      package = pkgs.pulseaudioFull;
+      zeroconf.discovery.enable = false;
+      zeroconf.publish.enable = true;
+      tcp.enable = true;
+      tcp.anonymousClients.allowedIpRanges = [ "172.16.0.0/16" "127.0.0.1" ];
+    };
+
+    hardware.sane.enable = true;
+    services.saned = {
+      enable = true;
+      extraConfig = ''
+        172.16.0.0/16
+      '';
+    };
+
+    vuizvui.user.openlab.stackenblocken = {
+      enable = true;
+      volume = 30;
+    };
+
+    services.logind.extraConfig = "HandleLidSwitch=ignore";
+
+    fileSystems = {
+      "${musicDir}" = {
+        device = "ftp.openlab.lan:/data/upload/music";
+        fsType = "nfs";
+        label = "lab-ftp";
+        options = [ "nolock" "x-systemd.automount"];
+      };
+    };
+  };
+}
diff --git a/machines/labnet/manual-setup.md b/machines/labnet/manual-setup.md
new file mode 100644
index 00000000..6e7f1d20
--- /dev/null
+++ b/machines/labnet/manual-setup.md
@@ -0,0 +1,24 @@
+# Manual setup for labtops
+
+## Poor man’s setup
+
+### Intro
+
+- download newest nixos setup
+- write to USB stick
+- boot live system
+
+### Install vanilla nixos
+
+- use parted to setup msdos table (mktable) & full-disk partition (mkpart 0% 100%)
+- mkfs.ext4 -L /dev/sda1
+- mount /dev/disk/by-label/labtop /mnt
+- nixos-generate-config --root mnt, comment out a few things in /mnt/etc/nixos/configuration.nix
+- nixos-install
+- set temporary root password (123456), will be overwritten by vuizvui
+- reboot
+
+### Install vuizvui
+
+- boot, login as root
+- follow “installing a machine” documentation from vuizvui wiki
diff --git a/machines/misc/mailserver.nix b/machines/misc/mailserver.nix
new file mode 100644
index 00000000..a9548fcb
--- /dev/null
+++ b/machines/misc/mailserver.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, lib, ... }: let
+  vhostMap = {
+    smtpd_sender_login_maps = [
+      "SELECT username AS allowedUser"
+      "FROM mailbox"
+      "WHERE username='%s' AND active = 1"
+      "UNION SELECT goto FROM alias"
+      "WHERE address='%s' AND active = 1"
+    ];
+
+    virtual_alias_maps = [
+      "SELECT goto"
+      "FROM alias"
+      "WHERE address='%s' AND active = '1'"
+    ];
+
+    virtual_mailbox_domains = [
+      "SELECT domain"
+      "FROM domain"
+      "WHERE domain='%s' AND active = '1'"
+    ];
+
+    virtual_mailbox_maps = [
+      "SELECT maildir"
+      "FROM mailbox"
+      "WHERE username='%s' AND active = '1'"
+    ];
+  };
+
+  mkDbMap = query: "proxy:pgsql:${pkgs.writeText "database.cf" ''
+    hosts = localhost
+    user = postfix
+    dbname = postfix
+    query = ${query}
+  ''}";
+
+in {
+  services.spamassassin.enable = true;
+
+  services.postfix.enable = true;
+  services.postfix.hostname = "mailtest.lan";
+
+  # TODO: This is a dummy, replace it once we know about the real root fs.
+  fileSystems."/".label = "root";
+  boot.loader.grub.device = "nodev";
+
+  vuizvui.services.postfix.enable = true;
+  vuizvui.services.postfix.restrictions = {
+    sender = [
+      "reject_authenticated_sender_login_mismatch"
+      "reject_unknown_sender_domain"
+    ];
+    recipient = [
+      "permit_sasl_authenticated"
+      "permit_mynetworks"
+      "reject_unauth_destination"
+      "reject_invalid_hostname"
+      "reject_non_fqdn_hostname"
+      "reject_non_fqdn_sender"
+      "reject_non_fqdn_recipient"
+      "reject_unknown_reverse_client_hostname"
+    ];
+    helo = [
+      "permit_sasl_authenticated"
+      "permit_mynetworks"
+      "reject_invalid_hostname"
+      "reject_unauth_pipelining"
+      "reject_non_fqdn_hostname"
+    ];
+  };
+
+  services.postfix.extraConfig = ''
+    ${lib.concatStrings (lib.mapAttrsToList (cfgvar: query: ''
+      ${cfgvar} = ${mkDbMap (lib.concatStringsSep " " query)}
+    '') vhostMap)}
+
+    # a bit more spam protection
+    disable_vrfy_command = yes
+
+    smtpd_sasl_type=dovecot
+    smtpd_sasl_path=private/auth_dovecot XXXXXXXXXXXXXXX
+    smtpd_sasl_auth_enable = yes
+    smtpd_sasl_authenticated_header = yes
+    broken_sasl_auth_clients = yes
+
+    proxy_read_maps = ${lib.concatStringsSep " " (map (s: "\$${s}") [
+      "local_recipient_maps" "mydestination" "virtual_alias_maps"
+      "virtual_alias_domains" "virtual_mailbox_maps" "virtual_mailbox_domains"
+      "relay_recipient_maps" "relay_domains" "canonical_maps"
+      "sender_canonical_maps" "recipient_canonical_maps" "relocated_maps"
+      "transport_maps" "mynetworks" "smtpd_sender_login_maps"
+    ])}
+
+    local_transport = virtual
+    virtual_transport = dovecot
+
+    virtual_uid_maps = static:5000 XXXXXXXXXXXX
+    virtual_gid_maps = static:5000 XXXXXXXXXXXX
+
+    smtpd_tls_cert_file=/etc/ssl/mail.crt XXXX: KEYS
+    smtpd_tls_key_file=/etc/ssl/mail.key XXXX: KEYS
+    smtpd_use_tls=yes
+  '';
+
+  services.postfix.extraMasterConf = ''
+    mailman unix - n n - - pipe
+      flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ''${nexthop} ''${user}
+      # ^^^ FIXME: maybe not needed!
+
+    dovecot unix - n n - - pipe
+      flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ''${recipient}
+      # ^^^ FIXME: maybe not needed!
+
+    spamassassin unix - n n - - pipe
+      user=${toString config.ids.uids.spamd} argv=${pkgs.spamassassin}/bin/spamc -f -e /var/setuid-wrappers/sendmail -oi -f ''${sender} ''${recipient}
+      # ^^^ FIXME: maybe not needed!
+  '';
+}
diff --git a/machines/profpatsch/base-server.nix b/machines/profpatsch/base-server.nix
new file mode 100644
index 00000000..893df1ae
--- /dev/null
+++ b/machines/profpatsch/base-server.nix
@@ -0,0 +1,31 @@
+{ config, pkgs, ... }:
+
+let
+  sshPort = 6879;
+
+in
+{
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+
+    boot.cleanTmpDir = true;
+
+    programs.mosh.enable = true;
+
+    services.openssh = {
+      enable = true;
+      listenAddresses = [ { addr = "0.0.0.0"; port = sshPort; } ];
+    };
+
+    networking.firewall = {
+      enable = true;
+      allowPing = true;
+      allowedTCPPorts = [ sshPort ];
+    };
+
+  };
+
+}
diff --git a/machines/profpatsch/base-workstation.nix b/machines/profpatsch/base-workstation.nix
new file mode 100644
index 00000000..5812d9ca
--- /dev/null
+++ b/machines/profpatsch/base-workstation.nix
@@ -0,0 +1,104 @@
+# A base configuration that still assumes a workstation
+{ pkgs, lib, ... }:
+let
+  myPkgs = import ./pkgs.nix { inherit pkgs lib myLib; };
+  myLib  = import ./lib.nix  { inherit pkgs lib; };
+
+  philip = myLib.philip;
+
+in {
+
+  imports = [
+    ./base.nix
+  ];
+
+  config = {
+
+    boot.loader = {
+      grub.enable = true;
+      grub.version = 2;
+    };
+
+    networking = {
+      firewall.enable = false;
+    };
+
+    i18n = {
+      consoleFont = "lat9w-16";
+      consoleKeyMap = "neo";
+      defaultLocale = "en_US.UTF-8";
+    };
+
+    programs.ssh.startAgent = false;
+
+    ###########
+    # Packages
+
+    environment.sessionVariables = { EDITOR = "${myPkgs.vim}/bin/vim"; };
+
+    environment.systemPackages = with pkgs;
+    let
+      # of utmost necessity for me to function
+      basePkgs = [
+        silver-searcher   # file content searcher, > ack > grep
+        dos2unix          # text file conversion
+        manpages          # system manpages (not included by default)
+        mkpasswd          # UNIX password creator
+        smartmontools     # check disk state
+        stow              # dotfile management
+        traceroute        # trace ip routes
+        wirelesstools     # iwlist (wifi scan)
+      ];
+    in basePkgs;
+
+    # friendly user shell
+   programs.fish.enable = true;
+
+    ###########
+    # Services
+
+    services.openssh.enable = true;
+
+    time.timeZone = "Europe/Berlin";
+
+    # bounded journal size
+    services.journald.extraConfig = "SystemMaxUse=50M";
+
+    services.xserver = {
+      # otherwise xterm is enabled, creating an xterm that spawns the window manager.
+      desktopManager.xterm.enable = false;
+
+      windowManager.xmonad = {
+        enable = true;
+        enableContribAndExtras = true;
+      };
+    };
+
+
+    # services.xserver = {
+      # libinput = {
+      #   enable = true;
+      #   naturalScrolling = true;
+      #   accelSpeed = "0.01";
+      # };
+
+      # # TODO: modify libinput module so that you can do libinput."trackpoint".scrollMethod = "button";
+      # # and maybe a convenience option for thinkpads (maybe in the hardware repo?).
+      # config = ''
+      #   Section "InputClass"
+      #     Identifier     "Enable libinput for TrackPoint"
+      #     MatchIsPointer "on"
+      #     Driver         "libinput"
+      #     Option         "ScrollMethod" "button"
+      #     Option         "ScrollButton" "8"
+      #   EndSection
+      # '';
+    # };
+
+    ########
+    # Users
+
+    users.users = { inherit philip; };
+
+  };
+}
diff --git a/machines/profpatsch/base.nix b/machines/profpatsch/base.nix
new file mode 100644
index 00000000..7a450d2e
--- /dev/null
+++ b/machines/profpatsch/base.nix
@@ -0,0 +1,41 @@
+# Base config shared by all machines
+{ pkgs, config, lib, ... }:
+
+let
+  # TODO: inject into every config from outside
+  myLib  = import ./lib.nix  { inherit pkgs lib; };
+  myPkgs = import ./pkgs.nix { inherit pkgs lib myLib; };
+
+in
+{
+  config = {
+    # correctness before speed
+    nix.useSandbox = true;
+
+    programs.bash = {
+      loginShellInit = ''
+        alias c='vim /etc/nixos/configuration.nix'
+        alias nsp='nix-shell -p'
+        alias nrs='nixos-rebuild switch'
+        alias tad='tmux attach -d'
+      '';
+    };
+
+    environment.systemPackages = with pkgs; [
+      curl              # transfer data to/from a URL
+      file              # file information
+      git               # version control system
+      htop              # top replacement
+      nmap              # stats about clients in the network
+      rsync             # file syncing tool
+      tmux              # detachable terminal multiplexer
+      wget              # the other URL file fetcher
+      myPkgs.vim        # slight improvement over vi
+    ];
+
+    # Nobody wants mutable state. :)
+    users.mutableUsers = false;
+
+  };
+
+}
diff --git a/machines/profpatsch/haku.nix b/machines/profpatsch/haku.nix
new file mode 100644
index 00000000..c94484aa
--- /dev/null
+++ b/machines/profpatsch/haku.nix
@@ -0,0 +1,84 @@
+{ config, pkgs, lib, ... }:
+
+let
+  myLib  = import ./lib.nix  { inherit pkgs lib; };
+  myPkgs = import ./pkgs.nix { inherit pkgs lib myLib; };
+
+  myKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNMQvmOfon956Z0ZVdp186YhPHtSBrXsBwaCt0JAbkf/U/P+4fG0OROA++fHDiFM4RrRHH6plsGY3W6L26mSsCM2LtlHJINFZtVILkI26MDEIKWEsfBatDW+XNAvkfYEahy16P5CBtTVNKEGsTcPD+VDistHseFNKiVlSLDCvJ0vMwOykHhq+rdJmjJ8tkUWC2bNqTIH26bU0UbhMAtJstWqaTUGnB0WVutKmkZbnylLMICAvnFoZLoMPmbvx8efgLYY2vD1pRd8Uwnq9MFV1EPbkJoinTf1XSo8VUo7WCjL79aYSIvHmXG+5qKB9ed2GWbBLolAoXkZ00E4WsVp9H philip@nyx";
+
+in
+
+{
+  imports = [
+    ./base-server.nix
+  ];
+
+  config = {
+
+    boot.loader.grub.device = "/dev/sda";
+    fileSystems = {
+      "/" = {
+        device = "/dev/sda3";
+        fsType = "ext4";
+      };
+      "/boot" = {
+        device = "/dev/sda2";
+        fsType = "ext4";
+      };
+    };
+
+    environment.systemPackages = with pkgs; [
+      rtorrent                          # bittorrent client
+      pkgs.vuizvui.profpatsch.warpspeed # trivial http file server
+    ];
+
+    users.users = {
+      root.openssh.authorizedKeys.keys = [ myKey ];
+
+      rtorrent = {
+        isNormalUser = true;
+      };
+      vorstand = {
+        isNormalUser = true;
+        openssh.authorizedKeys.keys = [ myKey
+          "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUgS0VB5XayQobQfOi0tYeqpSSCXzftTKEyII4OYDhuF0/CdXSqOIvdqnWQ8933lPZ5234qCXCniIlRJpJQLBPJdJ7/XnC6W37asuft6yVYxTZnZat8edCuJETMvwZJZNttxHC04k3JPf9RMj25luICWabICH5XP9Mz3GoWSaOz7IOm7jiLQiF3UtiFOG06w76d3UfcIVbqjImwWv8nysphi9IQfL0XgC24zNE6LSeE7IN5xTOxoZxORQGsCEnFNCPevReNcSB0pI9xQ1iao7evaZkpzT4D4iQ/K7Ss8dsfFWN30NPMQS5ReQTUKtmGn1YlgkitiYTEXbMjkYbQaQr daniel@shadow"
+          "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtfWeIH7YZpWUUOZ3oC5FB2/J+P3scxm29gUQdVij/K0TuxW1yN/HtcvrO1mwSshS6sNZ2N6/Kb6+kuGyx1mEnaFt87K5ucxC7TNqiURh4eeZE1xX7B5Ob8TVegrBxoe+vcfaoyxn7sUzgF719H0aYC7PP6p3AIbhq3hRLcvY26u9/gZ39H79A71wCunauvpcnpb+rqyJMN6m2YoeOcoloe7wUDI8Xw5dUetHpNKn9k1vzS16CdwP4pAKI8aBtdNK7ZojVMe9LfBG8HHPr9K+cwcaxQuXkFBJzrfrtBCfQwrgWppsu/W/kGBs1ybku2bOFI5UXJBnsraXQqr1NLIfL phj@phj-X220"
+        ];
+      };
+      stallmanbot = {
+        isSystemUser = true;
+        useDefaultShell = true;
+      };
+    };
+
+
+    services.nginx = {
+      enable = true;
+      virtualHosts."haku.profpatsch.de" = {
+        forceSSL = true;
+        enableACME = true;
+        locations."/pub/" = {
+          proxyPass = "http://localhost:1338/";
+        };
+        locations."/".root = pkgs.writeTextDir "index.html" ''hello world'';
+        serverAliases = [ "lojbanistan.de" ];
+      };
+    };
+
+
+    networking = {
+      hostName = "haku";
+      firewall = {
+        allowedTCPPorts =
+          [ 80 443 ];
+        allowedTCPPortRanges =
+          # rtorrent
+          [{ from = 6881; to = 6889; }];
+      };
+      nameservers = [
+        "62.210.16.6"
+        "62.210.16.7"
+      ];
+    };
+  };
+}
diff --git a/machines/profpatsch/katara.nix b/machines/profpatsch/katara.nix
new file mode 100644
index 00000000..1f0f1e3d
--- /dev/null
+++ b/machines/profpatsch/katara.nix
@@ -0,0 +1,401 @@
+{ config, pkgs, unfreeAndNonDistributablePkgs, lib, ... }:
+let
+
+  myLib  = import ./lib.nix  { inherit pkgs lib; };
+  myPkgs = import ./pkgs.nix { inherit pkgs lib myLib; };
+
+in {
+
+  imports = [
+    ./base-workstation.nix
+  ];
+
+  config = rec {
+
+    #########
+    # Kernel
+
+    boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" ];
+    boot.loader.grub.device = "/dev/sda";
+    boot.initrd.luks.devices = [ { device = "/dev/sda2"; name = "cryptroot"; } ];
+
+    ###########
+    # Hardware
+
+    fileSystems."/" = {
+      device = "/dev/dm-0";
+      fsType = "btrfs";
+      options = [ "ssd" ];
+    };
+
+    fileSystems."/boot" = {
+      device = "/dev/sda1";
+      fsType = "ext3";
+    };
+
+    hardware.trackpoint = {
+      speed = 280;
+    };
+
+    hardware.pulseaudio = {
+      enable = true;
+      zeroconf.discovery.enable = true;
+      # for Pillars of Eternity
+      support32Bit = true;
+    };
+    # steam
+    hardware.opengl.driSupport32Bit = true;
+
+
+    # needed by some games (TODO: general module for games)
+    # hardware.opengl.driSupport32Bit = true;
+    vuizvui.hardware.thinkpad.enable = true;
+
+    ######
+    # Nix
+
+    nix.maxJobs = 4;
+    # what was this activated for?!
+    # vuizvui.enableGlobalNixpkgsConfig = true;
+
+    ##########
+    # Network
+
+    networking.hostName = "katara";
+    # networking.supplicant.wlp3s0 = {
+    #   configFile = {
+    #     path = "/var/wifi-networks";
+    #     writable = true;
+    #   };
+    #   userControlled.enable = true;
+    # };
+    networking.networkmanager.enable = true;
+
+    # networking.bonds = {
+    #   wifiAndEthernet = {
+    #     interfaces = [ "wlp3s0" "enp0s25" ];
+    #     driverOptions = {
+    #       miimon = "100";
+    #       primary = "enp0s25";
+    #       mode = "active-backup";
+    #     };
+    #   };
+    # };
+
+    ###########
+    # Packages
+
+    environment.extraOutputsToInstall = [ "devdoc" ];
+    environment.systemPackages = with pkgs;
+    let
+      systemPkgs =
+      [
+        atool               # archive tools
+        gnupg gnupg1compat  # PGP encryption
+        imagemagick         # image conversion
+        pkgs.vuizvui.profpatsch.jmtpfs # MTP fuse
+        mosh                # ssh with stable connections
+        nfs-utils           # the filesystem of the future for 20 years
+        sshfsFuse           # mount ssh machines
+        tarsnap             # encrypting online backup tool
+        # TODO move into atool deps
+        unzip               # extract zip archives
+        networkmanagerapplet
+        wpa_supplicant_gui  # configure wireless connections
+      ];
+      xPkgs = [
+        dmenu             # simple UI menu builder
+        dunst             # notification daemon (interfaces with libnotify)
+        alock             # lock screen
+        libnotify         # notification library
+        xclip             # clipboard thingy
+        xorg.xkill        # X11 application kill
+      ];
+      guiPkgs = [
+        gnome3.adwaita-icon-theme
+        # TODO: get themes to work. See notes.org.
+        gnome3.gnome_themes_standard
+        pavucontrol
+      ];
+      hp = haskellPackages;
+      programmingTools = [
+        cabal2nix                    # convert cabal files to nixexprs
+        # myPkgs.fast-init             # fast-init of haskell projects
+        gitAndTools.git-annex        # version controlled binary file storage
+        gitAndTools.git-dit          # decentral issue tracking for git
+
+        httpie                       # nice http CLI
+        # jid                          # interactive/incremental JSON digger
+        # mercurial                    # the other version control system
+        telnet                       # tcp debugging
+        pkgs.vuizvui.profpatsch.nman # open man pages in temporary nix shell
+        pkgs.vuizvui.profpatsch.warpspeed # trivial http file server
+      ];
+      documentation = [
+        # mustache-spec NOT IN 16.09
+      ];
+      userPrograms = [
+        abcde                # high-level cd-ripper with tag support
+        anki                 # spaced repetition system
+        # TODO integrate lame into audacity
+        audacity lame.lib    # audio editor and mp3 codec
+        myPkgs.beets         # audio file metadata tagger
+        # chromium             # browser
+        (chromium.override { enablePepperFlash = true; })
+        pkgs.vuizvui.profpatsch.droopy # simple HTML upload server
+        unfreeAndNonDistributablePkgs.dropbox-cli # dropbox.com client
+        electrum             # bitcoin client
+        emacs                # pretty neat operating system i guess
+        feh                  # brother of meh, displays images in a meh way, but fast
+        filezilla            # FTP GUI business-ready interface framework
+        myPkgs.saneGhci      # <s>Glorious</s>Glasgow Haskell Compiler, mostly for ghci
+        gimp                 # graphics
+        gmpc                 # mpd client and best music player interface in the world
+        haskellPackages.hledger # plain text accounting
+        inkscape             # vector graphics
+        libreoffice          # a giant ball of C++, that sometimes helps with proprietary shitformats
+        lilyterm-git         # terminal emulator, best one around
+        myPkgs.mpv           # you are my sun and my stars, and you play my stuff.
+        newsbeuter           # RSS/Atom feed reader
+        pass                 # standard unix password manager
+        picard               # jean-luc, music tagger
+        poppler_utils        # pdfto*
+        ranger               # CLI file browser
+        remind               # calender & reminder program
+        rtorrent             # monster of a bittorrent client
+        myPkgs.sent          # suckless presentation tool
+        unfreeAndNonDistributablePkgs.steam # the one gaming platform
+        myPkgs.xmpp-client   # CLI XMPP Client
+        youtube-dl           # download videos
+        zathura              # pdf viewer
+      ];
+      userScripts = with pkgs.vuizvui.profpatsch; [
+        display-infos  # show time & battery
+        show-qr-code   # display a QR code
+        backlight      # adjust laptop backlight
+      ];
+      mailPkgs = [
+        elinks               # command line browser
+        mutt-with-sidebar    # has been sucking less since 1995
+        msmtp                # SMTP client
+        notmuch              # mail indexer
+        pythonPackages.alot  # the next cool thing!
+      ];
+      nixPkgs = [
+        nix-repl                  # nix REPL
+        nix-prefetch-scripts      # prefetch store paths from various destinations
+      ];
+      tmpPkgs = [
+        # TODO needs user service
+        redshift   # increases screen warmth at night (so i don’t have to feel cold)
+        # on remove keep pdfjam!
+        (texlive.combine { inherit (texlive) scheme-medium latexmk IEEEtran needspace; })
+        # myPkgs.nix-gen
+      ];
+    in systemPkgs ++ xPkgs ++ guiPkgs
+    ++ programmingTools ++ documentation
+    ++ userPrograms ++ userScripts
+    ++ mailPkgs ++ nixPkgs ++ tmpPkgs;
+    # system.extraDependencies = with pkgs; lib.singleton (
+    #    # Haskell packages I want to keep around
+    #    haskellPackages.ghcWithPackages (hpkgs: with hpkgs;
+    #      [
+    #        # frp
+    #        frpnow
+    #        gloss
+    #        gtk
+    #        frpnow-gtk
+    #        frpnow-gloss
+
+    #        lens
+    #        wreq
+    #        aeson-lens
+    #      ]))
+    #    ++
+    #    # other packages that I use sometimes in a shell
+    #    [
+    #    ];
+
+    ###########
+    # Services
+
+    services.searx = {
+      enable = true;
+      package = myPkgs.searx;
+    };
+
+    services.printing = {
+      enable = true;
+      gutenprint = true;
+      # TODO
+      # drivers = [ pkgs.cups-pdf ];
+      # TODO
+      # drivers = [ pkgs.foomatic_filters pkgs.foomatic-db-engine ];
+    };
+
+    # Automount
+    services.udisks2.enable = true;
+
+    services.logind.extraConfig = ''
+      # want to be able to listen to music while laptop closed
+      LidSwitchIgnoreInhibited=no
+    '';
+
+    ###################
+    # Graphical System
+
+    services.xserver = {
+      enable = true;
+      layout = "de";
+      xkbVariant = "neo";
+      xkbOptions = "altwin:swap_alt_win";
+      serverFlagsSection = ''
+        Option "StandbyTime" "10"
+        Option "SuspendTime" "20"
+        Option "OffTime" "30"
+      '';
+
+      synaptics = {
+        enable = true;
+        minSpeed = "0.6";
+        maxSpeed = "1.5";
+        accelFactor = "0.015";
+        twoFingerScroll = true;
+        vertEdgeScroll = false;
+      };
+
+
+      videoDrivers = [ "intel" ];
+
+      displayManager = {
+        sessionCommands = with pkgs; ''
+            #TODO add as nixpkg
+            export PATH+=":$HOME/scripts" #add utility scripts
+            export EDITOR=emacsclient
+
+            ${xorg.xset}/bin/xset r rate 250 35
+
+            set-background &
+            # TODO xbindkeys user service file
+            ${lib.getBin xbindkeys}/bin/xbindkeys
+            nice -n19 dropbox-cli start &
+            nm-applet &
+            # synchronize clipboards
+            ${lib.getBin autocutsel}/bin/autocutsel -s PRIMARY &
+            ${lib.getBin twmn}/bin/twmnd &
+          '';
+      };
+
+    };
+
+    fonts.fontconfig = {
+      enable = true;
+      defaultFonts = {
+        monospace = [ "Source Code Pro" "DejaVu Sans Mono" ]; # TODO does not work
+        sansSerif = [ "Liberation Sans" ];
+      };
+      ultimate = {
+        enable = true;
+        substitutions = "combi";
+        preset = "ultimate4";
+      };
+    };
+    fonts.fonts = with pkgs; [
+      unfreeAndNonDistributablePkgs.corefonts
+      source-han-sans-japanese
+      source-han-sans-korean
+      source-han-sans-simplified-chinese
+      source-code-pro
+      hasklig
+      dejavu_fonts
+      ubuntu_font_family
+      league-of-moveable-type
+      symbola # emoji
+    ];
+
+    ###########
+    # Programs
+
+    vuizvui.programs.gnupg = {
+      enable = true;
+      agent = {
+        enable = true;
+        sshSupport = true;
+      };
+    };
+
+    # TODO: base config?
+    vuizvui.programs.fish.fasd.enable = true;
+
+    # build derivation on taalo
+    vuizvui.user.aszlig.programs.taalo-build.enable = true;
+
+    vuizvui.user.profpatsch.programs.scanning.enable = true;
+
+    #######
+    # Misc
+
+    security.pki.certificateFiles = [ "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
+
+    ########
+    # Fixes
+
+    # fix for emacs ssh
+    programs.bash.promptInit = "PS1=\"# \"";
+
+    ################
+    # User services
+    systemd.user = lib.mkMerge [
+
+      (lib.mkIf config.vuizvui.programs.gnupg.enable {
+        services.unlock-password-store = {
+          description = "unlock the user password store";
+          wantedBy = [ "default.target" ];
+          # make sure gpg-agent is running
+          wants = [ "gpg-agent.service" ];
+          after = [ "gpg-agent.service" ];
+          serviceConfig = {
+            # use special unlock key in the password store (needs to exist of course)
+            ExecStart = "${lib.getBin pkgs.pass}/bin/pass misc/unlock";
+            StandardOutput = "null";
+          };
+        };
+        timers.unlock-password-store = {
+          description = "unlock password store on system start";
+          wantedBy = [ "timers.target" ];
+          timerConfig.OnStartupSec = "5s";
+        };
+       })
+
+      {
+        services.mbsync = {
+          description = "mbsync job";
+          wants = [ "notmuch.service" ];
+          before = [ "notmuch.service"];
+          path = [ pkgs.pass ];
+          serviceConfig = {
+            Restart = "no";
+            ExecStart = "${pkgs.isync}/bin/mbsync -a";
+            };
+        };
+        timers.mbsync = {
+          description = "run mbsync job every 15 minutes";
+          wantedBy = [ "timers.target" ];
+          timerConfig = {
+            OnStartupSec="10s";
+            OnUnitActiveSec ="15m";
+          };
+        };
+        services.notmuch = {
+          description = "notmuch job";
+          serviceConfig = {
+            Restart = "no";
+            ExecStart = "${pkgs.notmuch}/bin/notmuch new";
+            };
+        };
+      }
+
+    ];
+
+  };
+}
diff --git a/machines/profpatsch/lib.nix b/machines/profpatsch/lib.nix
new file mode 100644
index 00000000..745fc156
--- /dev/null
+++ b/machines/profpatsch/lib.nix
@@ -0,0 +1,17 @@
+{ lib, pkgs }:
+rec {
+  fish = pkgs.fish;
+
+  authKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJhthfk38lzDvoI7lPqRneI0yBpZEhLDGRBpcXzpPSu+V0YlgrDix5fHhBl+EKfw4aeQNvQNuAky3pDtX+BDK1b7idbz9ZMCExy2a1kBKDVJz/onLSQxiiZMuHlAljVj9iU4uoTOxX3vB85Ok9aZtMP1rByRIWR9e81/km4HdfZTCjFVRLWfvo0s29H7l0fnbG9bb2E6kydlvjnXJnZFXX+KUM16X11lK53ilPdPJdm87VtxeSKZ7GOiBz6q7FHzEd2Zc3CnzgupQiXGSblXrlN22IY3IWfm5S/8RTeQbMLVoH0TncgCeenXH7FU/sXD79ypqQV/WaVVDYMOirsnh/ philip@nyx"];
+
+  philip = rec {
+    name = "philip";
+    extraGroups = [ "wheel" "networkmanager" ];
+    uid = 1000;
+    createHome = true;
+    home = "/home/philip";
+    passwordFile = "${home}/.config/passwd";
+    shell = "${lib.getBin fish}/bin/fish";
+    openssh.authorizedKeys.keys = authKeys;
+  };
+}
diff --git a/machines/profpatsch/patches/searx-rm-soundcloud.patch b/machines/profpatsch/patches/searx-rm-soundcloud.patch
new file mode 100644
index 00000000..35b427b9
--- /dev/null
+++ b/machines/profpatsch/patches/searx-rm-soundcloud.patch
@@ -0,0 +1,15 @@
+diff --git a/searx/settings.yml b/searx/settings.yml
+index 053cb44..48a542b 100644
+--- a/searx/settings.yml
++++ b/searx/settings.yml
+@@ -437,10 +437,6 @@ engines:
+     engine : scanr_structures
+     disabled : True
+ 
+-  - name : soundcloud
+-    engine : soundcloud
+-    shortcut : sc
+-
+   - name : stackoverflow
+     engine : stackoverflow
+     shortcut : st
diff --git a/machines/profpatsch/patches/searx-secret-key.patch b/machines/profpatsch/patches/searx-secret-key.patch
new file mode 100644
index 00000000..13d45291
--- /dev/null
+++ b/machines/profpatsch/patches/searx-secret-key.patch
@@ -0,0 +1,251 @@
+diff --git a/README.rst b/README.rst
+index a0bb12f..9e32b53 100644
+--- a/README.rst
++++ b/README.rst
+@@ -18,8 +18,7 @@ Installation
+    ``git clone https://github.com/asciimoo/searx.git && cd searx``
+ -  install dependencies: ``./manage.sh update_packages``
+ -  edit your
+-   `settings.yml <https://github.com/asciimoo/searx/blob/master/searx/settings.yml>`__
+-   (set your ``secret_key``!)
++   `settings.yml <https://github.com/asciimoo/searx/blob/master/searx/settings.yml>`
+ -  run ``python searx/webapp.py`` to start the application
+ 
+ For all the details, follow this `step by step
+diff --git a/searx/settings.yml b/searx/settings.yml
+index 8515326..e65e5e8 100644
+--- a/searx/settings.yml
++++ b/searx/settings.yml
+@@ -10,7 +10,6 @@ search:
+ server:
+     port : 8888
+     bind_address : "127.0.0.1" # address to listen on
+-    secret_key : "ultrasecretkey" # change this!
+     base_url : False # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
+     image_proxy : False # Proxying image results through searx
+     http_protocol_version : "1.0"  # 1.0 and 1.1 are supported
+diff --git a/searx/settings_robot.yml b/searx/settings_robot.yml
+index dbaf2fd..2c8f7cf 100644
+--- a/searx/settings_robot.yml
++++ b/searx/settings_robot.yml
+@@ -10,7 +10,6 @@ search:
+ server:
+     port : 11111
+     bind_address : 127.0.0.1
+-    secret_key : "ultrasecretkey" # change this!
+     base_url : False
+     image_proxy : False
+     http_protocol_version : "1.0"
+diff --git a/searx/utils.py b/searx/utils.py
+index 35cb6f8..284087d 100644
+--- a/searx/utils.py
++++ b/searx/utils.py
+@@ -2,6 +2,8 @@ import cStringIO
+ import csv
+ import os
+ import re
++import stat
++import xdg.BaseDirectory
+ 
+ from babel.dates import format_date
+ from codecs import getincrementalencoder
+@@ -300,3 +302,61 @@ def load_module(filename, module_dir):
+     module = load_source(modname, filepath)
+     module.name = modname
+     return module
++
++
++class SecretAppKeyError(IOError):
++    def __init__(self, reason, caught=None):
++        self.reason = reason
++        self.caught = caught
++
++    def __str__(self):
++        err = ""
++        if self.caught != None:
++            err = '\n' + str(self.caught)
++        return repr(self.reason) + err
++
++
++_secret_app_key_length = 512
++
++
++_secret_app_key_file_name = "secret_key"
++
++
++# tries to read the secret key from the xdg cache directory,
++# if none exists it creates one
++# If directory is given it has to be an existing, readable directory.
++def get_secret_app_key(directory=None):
++
++    if directory is None:
++        try:
++            directory = xdg.BaseDirectory.save_cache_path("searx")
++        except OSError as e:
++            raise(SecretAppKeyError("could not get XDG_CACHE_DIR"))
++
++
++    # we save it as plaintext, assuming only the owner has access
++    f = os.path.join(directory, _secret_app_key_file_name)
++
++    def saError(msg, e=None):
++        raise SecretAppKeyError("{} {}".format(f, msg), e)
++
++    # if it exists, read it
++    if os.path.isfile(f):
++        try:
++            with open(f, 'r') as fh:
++                return fh.read()
++        except IOError as e:
++            saError("could not be read", e)
++    # if it doesn't, create it
++    else:
++        key = os.urandom(_secret_app_key_length)
++        try:
++            with open(f, 'w') as fh:
++                fh.write(key)
++            # the file should be readable/writable only by the owner
++            os.chmod(f, stat.S_IRUSR | stat.S_IWUSR)
++            return key
++        except IOError as e:
++            saError("could not be created", e)
++        except OSError as e:
++            saError("could not be chmodded to 600", e)
+diff --git a/searx/webapp.py b/searx/webapp.py
+index 929d9e2..31395af 100644
+--- a/searx/webapp.py
++++ b/searx/webapp.py
+@@ -28,6 +28,7 @@ import hmac
+ import json
+ import os
+ import requests
++import xdg
+ 
+ from searx import logger
+ logger = logger.getChild('webapp')
+@@ -59,7 +60,7 @@ from searx.engines import (
+ from searx.utils import (
+     UnicodeWriter, highlight_content, html_to_text, get_themes,
+     get_static_files, get_result_templates, gen_useragent, dict_subset,
+-    prettify_url
++    prettify_url, get_secret_app_key
+ )
+ from searx.version import VERSION_STRING
+ from searx.languages import language_codes
+@@ -103,7 +104,11 @@ app = Flask(
+ 
+ app.jinja_env.trim_blocks = True
+ app.jinja_env.lstrip_blocks = True
+-app.secret_key = settings['server']['secret_key']
++
++# notify the user that the secret_key is no longer used
++if 'secret_key' in settings['server']:
++    logger.warning(' The "secret_key" config key is no longer used.')
++app.secret_key = get_secret_app_key()
+ 
+ if not searx_debug or os.environ.get("WERKZEUG_RUN_MAIN") == "true":
+     initialize_engines(settings['engines'])
+@@ -265,7 +270,7 @@ def proxify(url):
+                                            url.encode('utf-8'),
+                                            hashlib.sha256).hexdigest()
+ 
+-    return '{0}?{1}'.format(settings['result_proxy']['url'],
++    return '{0}?{1}'.format(settings['re sult_proxy']['url'],
+                             urlencode(url_params))
+ 
+ 
+@@ -280,7 +285,7 @@ def image_proxify(url):
+     if settings.get('result_proxy'):
+         return proxify(url)
+ 
+-    h = hmac.new(settings['server']['secret_key'], url.encode('utf-8'), hashlib.sha256).hexdigest()
++    h = hmac.new(app.secret_key, url.encode('utf-8'), hashlib.sha256).hexdigest()
+ 
+     return '{0}?{1}'.format(url_for('image_proxy'),
+                             urlencode(dict(url=url.encode('utf-8'), h=h)))
+@@ -684,7 +689,7 @@ def image_proxy():
+     if not url:
+         return '', 400
+ 
+-    h = hmac.new(settings['server']['secret_key'], url, hashlib.sha256).hexdigest()
++    h = hmac.new(app.secret_key, url, hashlib.sha256).hexdigest()
+ 
+     if h != request.args.get('h'):
+         return '', 400
+diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
+index 0448079..7c88445 100644
+--- a/tests/unit/test_utils.py
++++ b/tests/unit/test_utils.py
+@@ -1,4 +1,8 @@
+ # -*- coding: utf-8 -*-
++import os
++import tempfile
++import stat
++
+ import mock
+ from searx.testing import SearxTestCase
+ from searx import utils
+@@ -99,3 +103,63 @@ class TestUnicodeWriter(SearxTestCase):
+         rows = [1, 2, 3]
+         self.unicode_writer.writerows(rows)
+         self.assertEqual(self.unicode_writer.writerow.call_count, len(rows))
++
++
++class TestSecretAppKey(SearxTestCase):
++
++    def setUp(self):
++        self.getkey = utils.get_secret_app_key
++        self.fn = utils._secret_app_key_file_name
++
++    def keyfile(self, dir_):
++        return os.path.join(dir_, self.fn)
++
++    @staticmethod
++    def freshdir():
++        return tempfile.mkdtemp()
++
++    # generation of a key
++    def test_empty_dir(self):
++        dir_ = self.freshdir()
++        key = self.getkey(dir_)
++        self.assertNotEqual(key, "")
++        file_ = self.keyfile(dir_)
++        self.assertTrue(os.path.isfile(file_))
++        mode = os.stat(file_).st_mode
++        # equal to read and write for user
++        self.assertEquals(mode & (stat.S_IRWXG | stat.S_IRWXU | stat.S_IRWXO),
++                          (stat.S_IRUSR | stat.S_IWUSR))
++
++    # generation & successive read of the generated key
++    def test_existing_key(self):
++        dir_ = self.freshdir()
++        key = self.getkey(dir_)
++        key2 = self.getkey(dir_)
++        self.assertEquals(key, key2)
++
++    def test_not_nice(self):
++        def touch(f, mode):
++            open(f, 'w').close()
++            os.chmod(f, mode)
++
++        def raisesappkeyerror(dir_):
++            with self.assertRaises(utils.SecretAppKeyError):
++                self.getkey(dir_)
++
++        # input dir doesn't exist
++        raisesappkeyerror("<nonexisting file>")
++
++        # read-only
++        d1 = self.freshdir()
++        touch(self.keyfile(d1), 0)
++        raisesappkeyerror(d1)
++
++        # dir
++        d2 = self.freshdir()
++        os.mkdir(self.keyfile(d2))
++        raisesappkeyerror(d2)
++
++        # non-writable dir
++        d3 = self.freshdir()
++        os.chmod(d3, stat.S_IRUSR)
++        raisesappkeyerror(d3)
diff --git a/machines/profpatsch/patches/sent-bg.patch b/machines/profpatsch/patches/sent-bg.patch
new file mode 100644
index 00000000..acf92ebc
--- /dev/null
+++ b/machines/profpatsch/patches/sent-bg.patch
@@ -0,0 +1,18 @@
+diff --git a/config.def.h b/config.def.h
+index 92d577c..87baee5 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -8,8 +8,11 @@ static char *fontfallbacks[] = {
+ #define NUMFONTSCALES 42
+ #define FONTSZ(x) ((int)(10.0 * powf(1.1288, (x)))) /* x in [0, NUMFONTSCALES-1] */
+ 
+-static const char *fgcol = "#000000";
+-static const char *bgcol = "#FFFFFF";
++/* FUCK YOU */
++/* static const char *fgcol = "#000000"; */
++/* static const char *bgcol = "#FFFFFF"; */
++static const char *bgcol = "#000000";
++static const char *fgcol = "#FFFFFF";
+ 
+ static const float linespacing = 1.4;
+ 
diff --git a/machines/profpatsch/patches/taffybar-color.patch b/machines/profpatsch/patches/taffybar-color.patch
new file mode 100644
index 00000000..cfa1cce8
--- /dev/null
+++ b/machines/profpatsch/patches/taffybar-color.patch
@@ -0,0 +1,40 @@
+From 45ff40992c8fe5593d5f3e03ef0765168c6325fb Mon Sep 17 00:00:00 2001
+From: Peder Stray <peder@inne.proxdynamics.com>
+Date: Thu, 29 Oct 2015 17:37:56 +0100
+Subject: [PATCH] Load taffybar gtk configs after initGUI
+
+Since gtkrc files from themes seem to load anyway, load the default and
+user gtkrc for taffybar after initGUI.  please note that setting
+gtk_color_scheme from any of these causes a lot of already loaded gtkrc
+files to be reloaded.
+---
+ src/System/Taffybar.hs | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/src/System/Taffybar.hs b/src/System/Taffybar.hs
+index f119852..f7afe5e 100644
+--- a/src/System/Taffybar.hs
++++ b/src/System/Taffybar.hs
+@@ -261,16 +261,15 @@ setTaffybarSize cfg window = do
+ 
+ taffybarMain :: TaffybarConfig -> IO ()
+ taffybarMain cfg = do
+-  -- Override the default GTK theme path settings.  This causes the
+-  -- bar (by design) to ignore the real GTK theme and just use the
+-  -- provided minimal theme to set the background and text colors.
+-  -- Users can override this default.
+-  defaultGtkConfig <- getDefaultConfigFile "taffybar.rc"
+-  userGtkConfig <- getUserConfigFile "taffybar" "taffybar.rc"
+-  rcSetDefaultFiles [ defaultGtkConfig, userGtkConfig ]
+ 
+   _ <- initGUI
+ 
++  -- Load default and user gtk resources
++  defaultGtkConfig <- getDefaultConfigFile "taffybar.rc"
++  userGtkConfig <- getUserConfigFile "taffybar" "taffybar.rc"
++  rcParse defaultGtkConfig
++  rcParse userGtkConfig
++
+   Just disp <- displayGetDefault
+   nscreens <- displayGetNScreens disp
+   screen <- case screenNumber cfg < nscreens of
diff --git a/machines/profpatsch/patches/taffybar.patch b/machines/profpatsch/patches/taffybar.patch
new file mode 100644
index 00000000..a93fca1a
--- /dev/null
+++ b/machines/profpatsch/patches/taffybar.patch
@@ -0,0 +1,71 @@
+diff --git a/src/System/Taffybar/Battery.hs b/src/System/Taffybar/Battery.hs
+index 5335eff..32c7efa 100644
+--- a/src/System/Taffybar/Battery.hs
++++ b/src/System/Taffybar/Battery.hs
+@@ -9,6 +9,7 @@
+ -- more advanced features could be supported if there is interest.
+ module System.Taffybar.Battery (
+   batteryBarNew,
++  batteryIconNew,
+   textBatteryNew,
+   defaultBatteryConfig
+   ) where
+@@ -108,30 +109,22 @@ defaultBatteryConfig =
+       | pct < 0.9 = (0.5, 0.5, 0.5)
+       | otherwise = (0, 1, 0)
+ 
+--- | A fancy graphical battery widget that represents the current
+--- charge as a colored vertical bar.  There is also a textual
+--- percentage readout next to the bar.
++-- | 
+ batteryBarNew :: BarConfig -- ^ Configuration options for the bar display
+-                 -> Double -- ^ Polling period in seconds
+                  -> IO Widget
+-batteryBarNew battCfg pollSeconds = do
++batteryBarNew battCfg = do
+   battCtxt <- batteryContextNew
+-  case battCtxt of
+-    Nothing -> do
+-      let lbl :: Maybe String
+-          lbl = Just "No battery"
+-      labelNew lbl >>= return . toWidget
+-    Just ctxt -> do
+-      -- This is currently pretty inefficient - each poll period it
+-      -- queries the battery twice (once for the label and once for
+-      -- the bar).
+-      --
+-      -- Converting it to combine the two shouldn't be hard.
+-      b <- hBoxNew False 1
+-      txt <- textBatteryNew "$percentage$%" pollSeconds
+-      r <- newIORef ctxt
+-      bar <- pollingBarNew battCfg pollSeconds (battPct r)
+-      boxPackStart b bar PackNatural 0
+-      boxPackStart b txt PackNatural 0
+-      widgetShowAll b
+-      return (toWidget b)
++  let noBat = toWidget <$> labelNew (Just "No battery" :: Maybe String)
++  maybe noBat (batteryIconNew battCfg) battCtxt
++
++-- | A fancy graphical battery widget that represents the current
++-- charge as a colored vertical bar.
++batteryIconNew :: BarConfig
++                  -> BatteryContext
++                  -> IO Widget
++batteryIconNew cfg ctxt = do
++    icon <- pollingBarNew cfg pollSeconds . battPct =<< newIORef ctxt
++    widgetShowAll icon
++    return icon
++      where
++        pollSeconds = 5
+diff --git a/src/System/Taffybar/Widgets/PollingBar.hs b/src/System/Taffybar/Widgets/PollingBar.hs
+index d30adaf..01f161c 100644
+--- a/src/System/Taffybar/Widgets/PollingBar.hs
++++ b/src/System/Taffybar/Widgets/PollingBar.hs
+@@ -16,6 +16,7 @@ import Control.Monad ( forever )
+ import Graphics.UI.Gtk
+ 
+ import System.Taffybar.Widgets.VerticalBar
++import Debug.Trace
+ 
+ pollingBarNew :: BarConfig -> Double -> IO Double -> IO Widget
+ pollingBarNew cfg pollSeconds action = do
diff --git a/machines/profpatsch/pkgs.nix b/machines/profpatsch/pkgs.nix
new file mode 100644
index 00000000..50d49693
--- /dev/null
+++ b/machines/profpatsch/pkgs.nix
@@ -0,0 +1,90 @@
+{ pkgs, lib, myLib }:
+
+with pkgs;
+let
+
+  addPythonRuntimeDeps = drv: deps: drv.overrideDerivation (old: {
+    propagatedNativeBuildInputs = old.propagatedNativeBuildInputs ++ deps;
+  });
+
+  # containered = name: packages: users: { ... }:
+  #   {
+  #     containers."${name}" = {
+  #       config = {
+  #         environment.systemPackages = packages;
+  #         users.users = users;
+  #         services.sshd.enable = true;
+  #       };
+  #       privateNetwork = true;
+  #       localAddress = "127.0.0.2";
+  #     };
+  #     nixpkgs.config.allowUnfree = true;
+  #   };
+
+  # pkgs
+
+  taffybar = pkgs.taffybar.override {
+    ghcWithPackages = (pkgs.haskellPackages.override {
+      overrides = _: super: {
+        taffybar = super.taffybar.overrideDerivation (old: {
+          name = old.name + "foo";
+          patches = (old.patches or []) ++ [ ./patches/taffybar.patch ];
+          postPatch = old.postPathPhase or "" + ''
+            patch -R ${./patches/taffybar-color.patch}
+          '';
+        });
+      };
+    }).ghcWithPackages;
+  };
+
+  sent = pkgs.sent.override { patches = [ ./patches/sent-bg.patch ]; };
+
+  mpv = pkgs.mpv.override { scripts = [ pkgs.mpvScripts.convert ]; };
+
+  beets = pkgs.beets.override { enableAlternatives = true; };
+
+  # git-annex = hplts.git-annex.overrideDerivation (old: {
+  #     buildInputs = old.buildInputs ++ [ pkgs.makeWrapper ];
+  #     postFixup = ''
+  #       wrapProgram $out/bin/git-annex --prefix PATH ":" "${getBin pkgs.lsof}/bin";
+  #     '';
+  # });
+
+  poezio = pkgs.python34Packages.poezio;
+
+  vim = vim_configurable;
+
+  fast-init = pkgs.haskellPackages.callPackage (import "${(pkgs.fetchFromGitHub {
+    owner = "Profpatsch";
+    repo = "fast-init";
+    # TODO fix version
+    rev = "master";
+    sha256 = "03006xzs250knzcyr6j564kn9jf2a6cp3mxkpqsqmmyp6v28w90z";
+  })}/overrides.nix") {};
+
+  xmpp-client = pkgs.callPackage (import ./xmpp-client.nix myLib.philip.home "irc/xmppOla.wtf") { inherit (pkgs) xmpp-client; };
+
+  searx = pkgs.pythonPackages.searx.overrideAttrs (old: {
+    propagatedBuildInputs = old.propagatedBuildInputs ++ [ pythonPackages.pyxdg ];
+    patches = old.patches or [] ++ [
+      ./patches/searx-secret-key.patch
+      ./patches/searx-rm-soundcloud.patch
+    ];
+  });
+
+  # A ghci with some sane default packages in scope, & hoogle
+  saneGhci = haskellPackages.ghcWithHoogle (h: with h; [ protolude pretty-show ]);
+
+  # # not upstream-compatible yet
+  # nix-gen = haskellPackages.mkDerivation {
+  #   pname = "nix-gen";
+  #   version = "0.0.1";
+  #   license = lib.licenses.gpl3;
+  #   isExecutable = true;
+  #   src = /home/philip/code/nix/nix-gen;
+  #   buildDepends = with haskellPackages; [ hnix ansi-wl-pprint protolude data-fix ];
+  # };
+
+in
+{ inherit taffybar sent mpv beets poezio vim
+          fast-init xmpp-client saneGhci /*nix-gen*/ searx; }
diff --git a/machines/profpatsch/xmpp-client.nix b/machines/profpatsch/xmpp-client.nix
new file mode 100644
index 00000000..f960dd29
--- /dev/null
+++ b/machines/profpatsch/xmpp-client.nix
@@ -0,0 +1,31 @@
+home: passwordentry:
+{ lib, writeScriptBin, xmpp-client, pass, fetchFromGitHub }:
+
+let
+  myClient = xmpp-client.overrideDerivation (old: {
+    src = fetchFromGitHub {
+      rev = "785ad1629930bab03a73cc858951db1f78156743";
+      owner = "Profpatsch";
+      repo = "xmpp-client";
+      sha256 = "02nlx5kx0s1rz9rsyncgi9hmb62i1pl322a91ama4sm00qbi4fs7";
+    };
+  });
+
+in
+writeScriptBin "xmpp-client" ''
+  #!/usr/bin/env bash
+  PASS=$(${lib.getBin pass}/bin/pass "${passwordentry}" | head -n1)
+
+  # pipe config with password in fifo
+  TMP="$(mktemp -d)/fifo"
+  mkfifo "$TMP"
+  sed "s/@PASS@/$PASS/" ${home}/.config/xmpp-client/config > "$TMP" &
+
+
+  # execute the client with logging enabled
+  mkdir -p ${home}/.local/share/xmpp-client
+  LOG=${home}/.local/share/xmpp-client/history
+  CMD="${lib.getBin myClient}/bin/xmpp-client --config-file $TMP"
+  script --append --command "$CMD" "$LOG"
+''
+
diff --git a/machines/sternenseemann/fliewatuet.nix b/machines/sternenseemann/fliewatuet.nix
new file mode 100644
index 00000000..b1ee646b
--- /dev/null
+++ b/machines/sternenseemann/fliewatuet.nix
@@ -0,0 +1,277 @@
+{ config, lib, pkgs, ... }:
+
+let
+  myPkgs = import ./pkgs.nix { inherit pkgs lib; };
+
+in {
+  nixpkgs.config = {
+    allowUnfree = true;
+    packageOverrides = pkgs: {
+      bluez = pkgs.bluez5;
+    };
+  };
+
+  # hardware
+  boot.blacklistedKernelModules = [ "nouveau" "nvidia" ];
+  boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.initrd.luks.devices = [ { device = "/dev/sda2"; name = "crypted"; } ];
+
+  fileSystems."/" = {
+    device = "/dev/dm-0";
+    fsType = "btrfs";
+  };
+  fileSystems."/boot/" = {
+    device = "/dev/sda1";
+    fsType = "vfat";
+  };
+
+  swapDevices = [ ];
+
+  nix.maxJobs = 8;
+  nix.useSandbox = true;
+  nix.extraOptions = "gc-keep-derivations = false";
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.timeout = 5;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # limit journal size
+  services.journald.extraConfig = "SystemMaxUse=100M";
+
+  # sound
+  # fix sound
+  boot.extraModprobeConfig = ''
+  options snd-hda-intel index=1,0 enable_msi=1
+  '';
+
+  hardware.pulseaudio = {
+    enable = true;
+    support32Bit = true;
+    package = pkgs.pulseaudioFull;
+    zeroconf.discovery.enable = true;
+    daemon.config.flat-volumes = "no";
+  };
+
+  hardware.bluetooth.enable = true;
+
+  hardware.opengl.driSupport32Bit = true;
+  hardware.enableRedistributableFirmware = true;
+
+  hardware.trackpoint = {
+    enable = true;
+    emulateWheel = true;
+    speed = 250;
+    sensitivity = 140;
+  };
+
+  networking.hostName = "fliewatuet";
+  networking.firewall.enable = false;
+
+  networking.supplicant = {
+    wlp4s0 = {
+      configFile.path = "/etc/wpa_supplicant.conf";
+      userControlled.enable = true;
+      userControlled.group = "users";
+      driver = "wext";
+      extraConf = ''
+        ap_scan=1
+      '';
+    };
+  };
+
+
+  i18n = {
+    consoleFont = "Lat2-Terminus16";
+    consoleKeyMap = "de-latin1";
+    defaultLocale = "en_US.UTF-8";
+  };
+
+  time.timeZone = "Europe/Berlin";
+
+  environment.systemPackages = with pkgs; [
+    ## tools
+    remind
+    attic
+    pass
+    wget
+    curl
+    stow
+    scrot
+    dmenu
+    mosh
+    gnupg
+    signing-party
+    pinentry
+    gpgme
+    sudo
+    silver-searcher
+    graphicsmagick
+    mkpasswd
+    nmap
+    file
+    zip
+    unzip
+    atool
+    manpages
+    man_db
+    sshuttle
+    youtube-dl
+    psmisc
+    bar-xft
+    unison
+    ddate
+    # aspell
+    aspell
+    aspellDicts.en
+    aspellDicts.de
+
+    ## dev
+    git
+    neovim
+    ghc
+    cabal-install
+    haskellPackages.cabal2nix
+
+    ## applications
+    tmux
+    htop
+    mutt
+    tor
+    torbrowser
+    zathura
+    msmtp
+    isync
+    notmuch
+    irssi
+    myPkgs.texlive
+    firefox
+    chromium
+    elinks
+    termite
+    imv
+    gimp
+    rawtherapee
+    pavucontrol
+    cbatticon
+    filezilla
+    screen-message
+    jackline
+    w3m
+
+    ## GUI
+    # wm etc.
+    xdotool
+    xbindkeys
+    alock
+    dunst
+    libnotify
+    xorg.xbacklight
+    hicolor_icon_theme
+    xsel
+
+    ## audio / video
+    myPkgs.mpv
+    spotify
+    audacity
+    lame
+    ffmpeg
+    beets
+
+    ## services
+    acpi
+
+    ## games
+    steam
+  ];
+
+  fonts.fontconfig = {
+    defaultFonts = {
+      monospace = [ "Inconsolata" ];
+      sansSerif = [ "Open Sans" ];
+      serif     = [ "Linux Libertine" ];
+    };
+    ultimate = {
+      enable = true;
+      substitutions = "combi";
+    };
+  };
+
+  fonts.fonts = with pkgs; [
+    corefonts
+    opensans-ttf
+    dejavu_fonts
+    inconsolata
+    tewi-font
+    libertine
+    google-fonts
+    shrikhand # because not in google fonts :(
+    xorg.fontbitstream100dpi
+    xorg.fontbitstreamtype1
+    freefont_ttf
+    unifont
+    unifont_upper
+    poly
+    junicode
+  ];
+
+  # to make Ctrl-Shift-t work in termite
+  environment.etc."vte.sh" = { source = "${pkgs.gnome3.vte}/etc/profile.d/vte.sh"; };
+
+  services.openssh.enable = true;
+
+  services.tor = {
+    enable = true;
+    controlPort = 9051;
+  };
+
+  services.printing = {
+    enable = true;
+    drivers = [ pkgs.gutenprint ];
+  };
+
+  services.tlp.enable = true;
+
+  # Enable the X11 windowing system.
+  services.xserver = {
+    enable = true;
+    layout = "de";
+    xkbVariant = "neo";
+
+    desktopManager.xterm.enable = false;
+    windowManager.herbstluftwm.enable = true;
+
+    displayManager = {
+      sessionCommands =
+        ''
+          ${pkgs.redshift}/bin/redshift -c .redshift &
+          ${pkgs.xorg.xmodmap}/bin/xmodmap -e "pointer = 1 25 3 4 5 6 7 8 9"
+          ${pkgs.xbindkeys}/bin/xbindkeys
+          ${pkgs.cbatticon}/bin/cbatticon &
+        '';
+    };
+
+    synaptics.enable = true;
+    synaptics.tapButtons = false;
+    synaptics.twoFingerScroll = false;
+
+    videoDrivers = [ "intel" ];
+  };
+
+  programs.fish.enable = true;
+
+  users.mutableUsers = false;
+  users.users.lukas = {
+    isNormalUser = true;
+    uid = 1000;
+    home = "/home/lukas";
+    shell = "/run/current-system/sw/bin/fish";
+    group = "users";
+    passwordFile = "/home/lukas/.config/passwd";
+    extraGroups = [ "audio" "wheel" "networkmanager" ];
+  };
+
+  system.stateVersion = "unstable";
+
+  programs.ssh.startAgent = false;
+}
diff --git a/machines/sternenseemann/patches/2bwm-config.patch b/machines/sternenseemann/patches/2bwm-config.patch
new file mode 100644
index 00000000..f541666e
--- /dev/null
+++ b/machines/sternenseemann/patches/2bwm-config.patch
@@ -0,0 +1,131 @@
+diff --git a/config.h b/config.h
+index ce0d1d4..1748dfd 100644
+--- a/config.h
++++ b/config.h
+@@ -35,19 +35,13 @@ static const bool inverted_colors = true;
+ static const uint8_t borders[] = {3,5,5,4};
+ /* Windows that won't have a border.*/
+ #define LOOK_INTO "WM_NAME"
+-static const char *ignore_names[] = {"bar", "xclock"};
++static const char *ignore_names[] = {"lemonbar", "bar", "xclock"};
+ ///--Menus and Programs---///
+-static const char *menucmd[]   = { "my_menu.sh", NULL };
+-static const char *gmrun[]     = { "my_menu2.sh",NULL};
+-static const char *terminal[]  = { "urxvtc", NULL };
++static const char *menucmd[]   = { "dmenu_run", NULL };
++static const char *terminal[]  = { "termite", NULL };
+ static const char *click1[]    = { "xdotool","click", "1", NULL };
+ static const char *click2[]    = { "xdotool","click", "2", NULL };
+ static const char *click3[]    = { "xdotool","click", "3", NULL };
+-static const char *vol_up[]    = { "pamixer", "-u", "-i", "3", "--allow-boost", NULL };
+-static const char *vol_down[]  = { "pamixer", "-u", "-d", "3", "--allow-boost", NULL };
+-static const char *vol_mute[]  = { "amixer", "set", "Master", "mute", "-q", NULL };
+-static const char *bright_up[]  = { "light", "-A", "5", NULL };
+-static const char *bright_down[]  = { "light", "-U", "5", NULL };
+ ///--Custom foo---///
+ static void halfandcentered(const Arg *arg)
+ {
+@@ -93,26 +87,15 @@ static key keys[] = {
+     // Kill a window
+     {  MOD ,              XK_q,          deletewin,         {}},
+     // Resize a window
+-    {  MOD |SHIFT,        XK_k,          resizestep,        {.i=TWOBWM_RESIZE_UP}},
+-    {  MOD |SHIFT,        XK_j,          resizestep,        {.i=TWOBWM_RESIZE_DOWN}},
+-    {  MOD |SHIFT,        XK_l,          resizestep,        {.i=TWOBWM_RESIZE_RIGHT}},
+-    {  MOD |SHIFT,        XK_h,          resizestep,        {.i=TWOBWM_RESIZE_LEFT}},
+-    // Resize a window slower
+-    {  MOD |SHIFT|CONTROL,XK_k,          resizestep,        {.i=TWOBWM_RESIZE_UP_SLOW}},
+-    {  MOD |SHIFT|CONTROL,XK_j,          resizestep,        {.i=TWOBWM_RESIZE_DOWN_SLOW}},
+-    {  MOD |SHIFT|CONTROL,XK_l,          resizestep,        {.i=TWOBWM_RESIZE_RIGHT_SLOW}},
+-    {  MOD |SHIFT|CONTROL,XK_h,          resizestep,        {.i=TWOBWM_RESIZE_LEFT_SLOW}},
++    {  MOD |SHIFT,        XK_l,          resizestep,        {.i=TWOBWM_RESIZE_UP}},
++    {  MOD |SHIFT,        XK_a,          resizestep,        {.i=TWOBWM_RESIZE_DOWN}},
++    {  MOD |SHIFT,        XK_i,          resizestep,        {.i=TWOBWM_RESIZE_RIGHT}},
++    {  MOD |SHIFT,        XK_e,          resizestep,        {.i=TWOBWM_RESIZE_LEFT}},
+     // Move a window
+-    {  MOD ,              XK_k,          movestep,          {.i=TWOBWM_MOVE_UP}},
+-    {  MOD ,              XK_j,          movestep,          {.i=TWOBWM_MOVE_DOWN}},
+-    {  MOD ,              XK_l,          movestep,          {.i=TWOBWM_MOVE_RIGHT}},
+-    {  MOD ,              XK_h,          movestep,          {.i=TWOBWM_MOVE_LEFT}},
+-    // Move a window slower
+-    {  MOD |CONTROL,      XK_k,          movestep,          {.i=TWOBWM_MOVE_UP_SLOW}},
+-    {  MOD |CONTROL,      XK_j,          movestep,          {.i=TWOBWM_MOVE_DOWN_SLOW}},
+-    {  MOD |CONTROL,      XK_l,          movestep,          {.i=TWOBWM_MOVE_RIGHT_SLOW}},
+-    {  MOD |CONTROL,      XK_h,          movestep,          {.i=TWOBWM_MOVE_LEFT_SLOW}},
+-    // Teleport the window to an area of the screen.
++    {  MOD ,              XK_l,          movestep,          {.i=TWOBWM_MOVE_UP}},
++    {  MOD ,              XK_a,          movestep,          {.i=TWOBWM_MOVE_DOWN}},
++    {  MOD ,              XK_i,          movestep,          {.i=TWOBWM_MOVE_RIGHT}},
++    {  MOD ,              XK_e,          movestep,          {.i=TWOBWM_MOVE_LEFT}},
+     // Center:
+     {  MOD ,              XK_g,          teleport,          {.i=TWOBWM_TELEPORT_CENTER}},
+     // Center y:
+@@ -131,9 +114,9 @@ static key keys[] = {
+     {  MOD ,              XK_Home,       resizestep_aspect, {.i=TWOBWM_RESIZE_KEEP_ASPECT_GROW}},
+     {  MOD ,              XK_End,        resizestep_aspect, {.i=TWOBWM_RESIZE_KEEP_ASPECT_SHRINK}},
+     // Full screen window without borders
+-    {  MOD ,              XK_x,         maximize,          {.i=TWOBWM_FULLSCREEN}},
++    {  MOD ,              XK_f,         maximize,          {.i=TWOBWM_FULLSCREEN}},
+     //Full screen window without borders overiding offsets
+-    {  MOD |SHIFT ,       XK_x,          maximize,          {.i=TWOBWM_FULLSCREEN_OVERRIDE_OFFSETS}},
++    {  MOD |SHIFT ,       XK_f,          maximize,          {.i=TWOBWM_FULLSCREEN_OVERRIDE_OFFSETS}},
+     // Maximize vertically
+     {  MOD ,              XK_m,          maxvert_hor,       {.i=TWOBWM_MAXIMIZE_VERTICALLY}},
+     // Maximize horizontally
+@@ -162,45 +145,22 @@ static key keys[] = {
+     {  MOD ,              XK_r,          raiseorlower,      {}},
+     // Next/Previous workspace
+     {  MOD ,              XK_v,          nextworkspace,     {}},
+-    {  MOD ,              XK_c,          prevworkspace,     {}},
++    {  MOD ,              XK_x,          prevworkspace,     {}},
+     // Move to Next/Previous workspace
+     {  MOD |SHIFT ,       XK_v,          sendtonextworkspace,{}},
+-    {  MOD |SHIFT ,       XK_c,          sendtoprevworkspace,{}},
++    {  MOD |SHIFT ,       XK_x,          sendtoprevworkspace,{}},
+     // Iconify the window
+     //{  MOD ,              XK_i,          hide,              {}},
+     // Make the window unkillable
+     {  MOD ,              XK_a,          unkillable,        {}},
+     // Make the window appear always on top
+     {  MOD,               XK_t,          always_on_top,     {}},
+-    // Make the window stay on all workspaces
+-    {  MOD ,              XK_f,          fix,               {}},
+-    // Move the cursor
+-    {  MOD ,              XK_Up,         cursor_move,       {.i=TWOBWM_CURSOR_UP_SLOW}},
+-    {  MOD ,              XK_Down,       cursor_move,       {.i=TWOBWM_CURSOR_DOWN_SLOW}},
+-    {  MOD ,              XK_Right,      cursor_move,       {.i=TWOBWM_CURSOR_RIGHT_SLOW}},
+-    {  MOD ,              XK_Left,       cursor_move,       {.i=TWOBWM_CURSOR_LEFT_SLOW}},
+-    // Move the cursor faster
+-    {  MOD |SHIFT,        XK_Up,         cursor_move,       {.i=TWOBWM_CURSOR_UP}},
+-    {  MOD |SHIFT,        XK_Down,       cursor_move,       {.i=TWOBWM_CURSOR_DOWN}},
+-    {  MOD |SHIFT,        XK_Right,      cursor_move,       {.i=TWOBWM_CURSOR_RIGHT}},
+-    {  MOD |SHIFT,        XK_Left,       cursor_move,       {.i=TWOBWM_CURSOR_LEFT}},
+     // Start programs
+     {  MOD ,              XK_Return,     start,             {.com = terminal}},
+-    {  MOD ,              XK_w,          start,             {.com = menucmd}},
+-    {  MOD |SHIFT,        XK_w,          start,             {.com = gmrun}},
++    {  MOD ,              XK_d,          start,             {.com = menucmd}},
+     // Exit or restart 2bwm
+     {  MOD |CONTROL,      XK_q,          twobwm_exit,       {.i=0}},
+-    {  MOD |CONTROL,      XK_r,          twobwm_restart,    {.i=0}},
+     {  MOD ,              XK_space,      halfandcentered,   {.i=0}},
+-    // Fake clicks using xdotool
+-    {  MOD |CONTROL,      XK_Up,         start,             {.com = click1}},
+-    {  MOD |CONTROL,      XK_Down,       start,             {.com = click2}},
+-	{  MOD |CONTROL,      XK_Right,      start,             {.com = click3}},
+-    {  0x000000,          0x1008ff13, start,             {.com = vol_up}},
+-    {  0x000000,          0x1008ff11,  start,             {.com = vol_down}},
+-    {  0x000000,          0x1008ff15, start,             {.com = vol_mute}},
+-    {  0x000000,          0x1008ff02, start,             {.com = bright_up}},
+-    {  0x000000,          0x1008ff03,  start,             {.com = bright_down}},
+     // Change current workspace
+        DESKTOPCHANGE(     XK_1,                             0)
+        DESKTOPCHANGE(     XK_2,                             1)
+@@ -216,7 +176,6 @@ static key keys[] = {
+ static Button buttons[] = {
+     {  MOD        ,XCB_BUTTON_INDEX_1,     mousemotion,   {.i=TWOBWM_MOVE}},
+     {  MOD        ,XCB_BUTTON_INDEX_3,     mousemotion,   {.i=TWOBWM_RESIZE}},
+-//    {  MOD|CONTROL,XCB_BUTTON_INDEX_3,     start,         {.com = menucmd}},
+     {  MOD|SHIFT,  XCB_BUTTON_INDEX_1,     changeworkspace, {.i=0}},
+     {  MOD|SHIFT,  XCB_BUTTON_INDEX_3,     changeworkspace, {.i=1}},
+     {  MOD|ALT,    XCB_BUTTON_INDEX_1,     changescreen,    {.i=1}},
diff --git a/machines/sternenseemann/pkgs.nix b/machines/sternenseemann/pkgs.nix
new file mode 100644
index 00000000..6f0beb36
--- /dev/null
+++ b/machines/sternenseemann/pkgs.nix
@@ -0,0 +1,11 @@
+{ pkgs, lib }:
+
+let
+
+  mpv = pkgs.mpv.override { scripts = [ pkgs.mpvScripts.convert ]; };
+
+  texlive = with pkgs.texlive; combine { inherit scheme-medium minted units collection-bibtexextra wrapfig libertine; };
+
+  urxvt = pkgs.rxvt_unicode-with-plugins.override { plugins = [ pkgs.urxvt_perls ]; };
+
+in { inherit mpv texlive urxvt; }
diff --git a/machines/sternenseemann/schaf.nix b/machines/sternenseemann/schaf.nix
new file mode 100644
index 00000000..3fa7da00
--- /dev/null
+++ b/machines/sternenseemann/schaf.nix
@@ -0,0 +1,105 @@
+{ config, pkgs, lib, ... }:
+{
+  boot.loader.grub.enable = false;
+  boot.loader.generic-extlinux-compatible.enable = true;
+
+  boot.kernelPackages = pkgs.linuxPackages_latest;
+
+  services.nixosManual.enable = false;
+
+  nix.binaryCaches = [ "http://nixos-arm.dezgeg.me/channel" ];
+
+  nix.binaryCachePublicKeys = [
+    "nixos-arm.dezgeg.me-1:xBaUKS3n17BZPKeyxL4JfbTqECsT+ysbDJz29kLFRW0=%"
+  ];
+
+  nix.maxJobs = 3;
+  nix.extraOptions = ''
+    gc-keep-derivations = false
+  '';
+
+  nixpkgs.system = "armv7l-linux";
+  hardware.opengl.enable = false;
+  powerManagement.enable = false;
+
+  networking.hostName = "schaf";
+  networking.dhcpcd.allowInterfaces = [ "eth0" ];
+
+  time.timeZone = "Europe/Berlin";
+
+
+  fileSystems = {
+    "/boot" = {
+      device = "/dev/disk/by-label/NIXOS_BOOT";
+      fsType = "vfat";
+    };
+    "/" = {
+      device = "/dev/disk/by-label/NIXOS_SD";
+      fsType = "ext4";
+    };
+    "/home" = {
+      device = "/dev/disk/by-label/SCHAF_HOME";
+      fsType = "ext4";
+    };
+    "/nix" = {
+      device = "/dev/disk/by-label/NIX_SCHAF";
+      fsType = "ext4";
+    };
+  };
+
+  swapDevices = [ { device = "/swapfile"; size = 1024; } ];
+
+  services.openssh.enable = true;
+  services.openssh.permitRootLogin = "without-password";
+  networking.firewall.enable = false;
+
+  services.journald.extraConfig = "SystemMaxUse=10M";
+
+  environment.systemPackages = with pkgs; [
+    (unison.override { enableX11 = false; })
+    vim
+    sudo
+    git
+    dtach
+    cryptsetup
+  ];
+
+  security.apparmor.enable = true;
+  virtualisation.lxc = {
+    enable = true;
+    usernetConfig = ''
+      lukas veth lxcbr0 10
+    '';
+  };
+
+  services.tor.enable = true;
+  services.tor.extraConfig = ''
+    HiddenServiceDir /var/lib/tor/hs
+    HiddenServicePort 22 127.0.0.1:22
+
+    HiddenServiceDir /var/lib/tor/books
+    HiddenServicePorT 22 127.0.0.1:22
+  '';
+
+  users.extraUsers.lukas = {
+    isNormalUser = true;
+    uid = 1000;
+    openssh.authorizedKeys.keys = [
+      "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDdsggyI+fin8c9FySJ1QtiwrExUD6UW5HCNQ5gniz1iUPkdxK9Xb1GcVIbQ2l7h9W7hz5w5bksPa6/ngrXoBRgztok1qzNT1eNA3GGhqVNqO7gQdag+vOReIxyHtcFUc/W7wIERC2zLCwfwCC+jAqsjonjZvnJsX0B0Ds6uGJ69B2U7U57/GcLoewEVjimrPl4aFWmnEMlCmse/vYAQLnTSBMskA2PEYMzs+YIlJtzhw3qv5Xuz3+AgRGarPM4mxSK/oIu4bIX8pwVbiX/GtDiqp8wz+hcQXIt4lnY3dvS5KklctB6VJPmXzrlRz9ujcrHmdOnuE4RSGim92QRMZiZVO7ET9G3wmxV/FiXAy9QBIn8xySr41rDdO5PVSwsKFBNOtLxubfeTlQRJ2eblp2ZJsATo1AdvMx+7PI7ZxrtaEuRyaPLnVn21YFq7slRzNeXx4UNxmRcMk/nOsVoMhFRUui+Vxv55aTZ03rThsV5fF02x5hT4zjCv3DjAQlhbNpBO8K1Dx09lubDa8aChqtamD9NQUgG8bhafeHtwUPoPK9yghEzlYKNh/I0Xv/V51lFkZLcfevMnVuKNEaVchBcx6Oh93kJhMpaASQ0UYvICYDU9hrSue42aR6riEx3XUZeBtJcvRKoBQSw0Crs0nzSPz1NOud2LcVGncp5pX/ICw== git@lukasepple.de"
+      "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCb+uVp/SRQnff7JxUIp+VomFrJBpo+ZIU7hyoaln9tAyVx3RW0B5XZlyZJSDXB4G4mn2fc0qpmY7AlEC2be4fzQSC8US5mKOgaoUz0nItdHg8MxDrBCxc8gR6s7/sbupEr0l48M+7GVQOhZV5yKjEF0XN3XnfDpL67tqjPSCxi9KYXLr8zEJCMaE0dKrAWMBUq3P/Q+pdciV1AOvjkrfiFWw1lM+CefOehEp3hwuCkUKOazKIGskx2MymtkFYdIjTeL/WJkT0mpzlUS3uJ3KCCsCgwDBs/hc7Fad4seDEWCAR7sP6OTXcM3Xd23Ygas9ogxLkinIVQzkfOM3eWoQ8JhjZXG2/tnf1JYHappjiBwm3uTxkCy+qRPwiF8+c6J/qHGKC4EPthaZWejpc9ZbZc6xPZEAtPr4MPdC7AtC12uNsJmWfQQVKUuBKAMrkh5LVjIRAfa3pDy1Vzf1wxohH+CVjCp/lpNr9nzhoY1ahAxS+r22zLdmM70R0R1B8PGRRFIIDj7r+0dRG4Oneg1Y9WvuIscrBaqcH9HGS2zfy+r1EvDoXZBQ4jdfQdMp8OHlqOLLV3F/BkMk8NN6rEqZ+flcK++E98ZodIGE4Ekis3eWuyk496d4Tzc5L/tEITl1d6V1GOBbdVNMWJAvL5T3WZVxlrywOcxLjIop9pgcdhnw== git@lukasepple.de"
+      ];
+    shell = "${pkgs.fish}/bin/fish";
+    group = "users";
+    extraGroups = [ "wheel" ];
+  };
+
+  users.extraUsers.books = {
+    uid = 1001;
+    isNormalUser = true;
+    group = "users";
+  };
+
+  programs.fish.enable = true;
+
+  system.stateVersion = "unstable";
+}
diff --git a/machines/sternenseemann/schnurrkadse.nix b/machines/sternenseemann/schnurrkadse.nix
new file mode 100644
index 00000000..93500eac
--- /dev/null
+++ b/machines/sternenseemann/schnurrkadse.nix
@@ -0,0 +1,179 @@
+{ config, lib, pkgs, ... }:
+
+let
+  myPkgs = import ./pkgs.nix { inherit pkgs lib; };
+
+in {
+  nixpkgs.config.allowUnfree = true;
+  nixpkgs.system = "i686-linux";
+
+  boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "usb_storage" "floppy" "usblp" "pcspkr" "btusb" ];
+  boot.kernelModules = [ ];
+  boot.extraModulePackages = [ ];
+
+  boot.initrd.luks.devices =
+    [ { name = "schnurrkadse";
+        device = "/dev/disk/by-uuid/544529b8-81cb-4e8e-9b6b-44f828ea2a7b";
+        preLVM = true; } ];
+
+  fileSystems."/" =
+    { device = "/dev/mapper/schnurrkadse-root";
+      fsType = "btrfs"; };
+
+  fileSystems."/boot" =
+    { device = "/dev/disk/by-uuid/e42bd75d-627d-4469-90cb-282dca7fdd4f";
+      fsType = "ext4"; };
+
+  swapDevices = [ { device = "/dev/mapper/schnurrkadse-swap"; } ];
+
+  nix.maxJobs = 1;
+
+  hardware.pulseaudio.enable = true;
+
+  hardware.trackpoint = {
+    enable = true;
+    emulateWheel = true;
+    speed = 250;
+    sensitivity = 140;
+  };
+
+  boot.loader.grub.enable = true;
+  boot.loader.grub.version = 2;
+  boot.loader.grub.device = "/dev/sda";
+
+  networking.hostName = "schnurrkadse";
+  networking.enableIntel2200BGFirmware = true;
+  networking.supplicant = {
+    wlp4s2 = {
+      configFile.path = "/etc/wpa_supplicant.conf";
+      userControlled.enable = true;
+      userControlled.group = "users";
+      driver = "wext";
+      extraConf = ''
+        ap_scan=1
+      '';
+    };
+  };
+
+  i18n = {
+    consoleFont = "Lat2-Terminus16";
+    consoleKeyMap = "de";
+    defaultLocale = "en_US.UTF-8";
+  };
+
+  time.timeZone = "Europe/Berlin";
+
+  environment.systemPackages = with pkgs; [
+    unzip
+    zip
+    bzip2
+    wget
+    neovim
+    git
+    stow
+    acpi
+    myPkgs.urxvt
+    xsel
+    sudo
+    mosh
+    dmenu
+    bar-xft
+    alock
+    silver-searcher
+    pavucontrol
+    unison
+
+    myPkgs.texlive
+    pythonPackages.pygments
+    python
+
+    elinks
+    torbrowser
+    chromium
+    myPkgs.mpv
+    htop
+    imv
+    screen-message
+    zathura
+    youtube-dl
+    pass
+    aspell
+    aspellDicts.de
+    aspellDicts.en
+
+    mutt
+    notmuch
+    msmtp
+    isync
+    gnupg
+    gpgme
+    w3m
+  ];
+
+  fonts.fontconfig = {
+    defaultFonts = {
+      monospace = [ "Inconsolata" "Source Code Pro" "DejaVu Sans Mono" ];
+      sansSerif = [ "DejaVu Sans" ];
+      serif = [ "Vollkorn" ];
+    };
+    ultimate.enable = true;
+    ultimate.substitutions = "combi";
+  };
+  fonts.fonts = with pkgs; [
+    corefonts
+    dejavu_fonts
+    inconsolata
+    libertine
+    unifont
+    google-fonts
+  ];
+
+  services.openssh.enable = true;
+
+  services.tor.enable = true;
+  services.tor.controlPort = 9051;
+
+  services.printing = {
+    enable = true;
+    drivers = [ pkgs.gutenprint pkgs.hplip ];
+  };
+
+  services.tlp.enable = true;
+
+  services.xserver = {
+    enable = true;
+    layout = "de";
+    xkbVariant = "neo";
+
+    desktopManager.xterm.enable = false;
+
+    windowManager.herbstluftwm.enable = true;
+
+    displayManager = {
+      sessionCommands =
+        ''
+            ${myPkgs.urxvt}/bin/urxvtd -q -f -o
+        '';
+    };
+
+    synaptics.enable = true;
+    synaptics.tapButtons = false;
+    synaptics.twoFingerScroll = false;
+
+    videoDrivers = [ "intel" ];
+  };
+
+  programs.fish.enable = true;
+
+  users.users.lukas = {
+    isNormalUser = true;
+    uid = 1000;
+    shell = "${pkgs.fish}/bin/fish";
+    group = "users";
+    extraGroups = [ "audio" "wheel" "networkmanager" "uucp" ];
+  };
+
+  programs.ssh.startAgent = false;
+
+  system.stateVersion = "unstable";
+}
diff --git a/modules/README.md b/modules/README.md
new file mode 100644
index 00000000..d9dd5851
--- /dev/null
+++ b/modules/README.md
@@ -0,0 +1,49 @@
+This directory contains various NixOS modules.
+
+If you add a module here, make sure that you define all options using a
+`vuizvui.*` namespace, so that the documentation is generated and you don't
+clash with modules from upstream [nixpkgs](https://github.com/NixOS/nixpkgs).
+
+When writing modules, make sure to categorize them accordingly:
+
+<table>
+  <tr>
+    <th>hardware</th>
+    <td>Hardware-related options</td>
+  </tr>
+  <tr>
+    <th>profiles</th>
+    <td>Options for a specific domain (like for example
+        `desktop`, `router`, `music`, ...)
+    </td>
+  </tr>
+  <tr>
+    <th>programs</th>
+    <td>Program-specific configuration options</td>
+  </tr>
+  <tr>
+    <th>services</th>
+    <td>Modules that implement systemd services</td>
+  </tr>
+  <tr>
+    <th>system</th>
+    <td>Everything system-related (like for example kernel)</td>
+  </tr>
+  <tr>
+    <th>tasks</th>
+    <td>Various one-shot services</td>
+  </tr>
+</table>
+
+If a module is highly specific to your own configuration, use the same
+categories but put them under `user/$category/$module`.
+
+Don't forget to add your module to `module-list.nix`, but make sure you have
+options in place to disable them by default.
+
+## Module option reference
+
+There is also a Hydra job for the currently available options which are
+specific to all of the modules listed in `module-list.nix`:
+
+https://headcounter.org/hydra/job/openlab/vuizvui/manual/latest/download/1/manual.html
diff --git a/modules/core/common.nix b/modules/core/common.nix
new file mode 100644
index 00000000..0a8e696c
--- /dev/null
+++ b/modules/core/common.nix
@@ -0,0 +1,83 @@
+{ config, options, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options.vuizvui = {
+    modifyNixPath = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to modify NIX_PATH for vuizvui, so that &lt;nixpkgs&gt; points
+        to the path within the Nix channel instead of the
+        <literal>nixpkgs</literal> or <literal>nixos</literal> channel from the
+        root user.
+      '';
+    };
+
+    enableGlobalNixpkgsConfig = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enabling this links <literal>nixos-config</literal> to be used by
+        <literal>nixpkgs-config</literal>, which essentially means that
+        attributes defined in <option>nixpkgs.config</option> are also in effect
+        for user environments.
+      '';
+    };
+
+    channelName = mkOption {
+      type = types.str;
+      default = "vuizvui";
+      description = ''
+        The channel name which is used to refer to <literal>vuizvui</literal>.
+      '';
+    };
+  };
+
+  config = let
+    nixpkgs = import ../../nixpkgs-path.nix;
+    system = config.nixpkgs.system;
+
+  in {
+    nixpkgs.config.packageOverrides = pkgs: {
+      inherit (import ../../pkgs {
+        # We need to make sure to incorporate other package overrides,
+        # otherwise we are unable to override packages in vuizvui.*.
+        pkgs = pkgs // config.nixpkgs.config.packageOverrides pkgs;
+      }) vuizvui;
+    };
+
+    nix.binaryCaches = options.nix.binaryCaches.default ++ [
+      "https://headcounter.org/hydra/"
+    ];
+    nix.binaryCachePublicKeys = [
+      "headcounter.org:/7YANMvnQnyvcVB6rgFTdb8p5LG1OTXaO+21CaOSBzg="
+    ];
+
+    environment.variables.NIXPKGS_CONFIG = let
+      nixpkgsCfg = toString (pkgs.writeText "nixpkgs-try-config.nix" ''
+        if (builtins.tryEval <nixpkgs-config>).success
+        then import <nixpkgs-config>
+        else {}
+      '');
+    in mkIf config.vuizvui.enableGlobalNixpkgsConfig (mkForce nixpkgsCfg);
+
+    nix.nixPath = let
+      rootChannelsPath = "/nix/var/nix/profiles/per-user/root/channels";
+      channelPath = "${rootChannelsPath}/${config.vuizvui.channelName}";
+      nixosConfig = "/etc/nixos/configuration.nix";
+      nixpkgsConfig = "nixpkgs-config=${pkgs.writeText "nixpkgs-config.nix" ''
+        (import ${nixpkgs}/nixos/lib/eval-config.nix {
+          modules = [ ${nixosConfig} ];
+        }).config.nixpkgs.config
+      ''}";
+      nixPath = [
+        "vuizvui=${channelPath}"
+        "nixpkgs=${channelPath}/nixpkgs"
+        "nixos-config=${nixosConfig}"
+        rootChannelsPath
+      ] ++ optional config.vuizvui.enableGlobalNixpkgsConfig nixpkgsConfig;
+    in mkIf config.vuizvui.modifyNixPath (mkOverride 90 nixPath);
+  };
+}
diff --git a/modules/core/lazy-packages.nix b/modules/core/lazy-packages.nix
new file mode 100644
index 00000000..939ece52
--- /dev/null
+++ b/modules/core/lazy-packages.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (lib) escapeShellArg;
+  inherit (config.vuizvui) lazyPackages;
+
+  # Encode the store path in base 64 so that the wrapper
+  # doesn't have a direct dependency on the package.
+  encoder = "${escapeShellArg "${pkgs.coreutils}/bin/base64"} -w0";
+  decoder = "${escapeShellArg "${pkgs.coreutils}/bin/base64"} -d";
+
+  # The command used to fetch the store path from the binary cache.
+  fetchSubstitute = "${escapeShellArg "${pkgs.nix}/bin/nix-store"} -r";
+
+  mkWrapper = package: pkgs.runCommand "${package.name}-lazy" {
+    inherit package;
+  } ''
+    encoded="$(echo "$package" | ${encoder})"
+
+    if [ ! -e "$package/bin" ]; then
+      echo "Store path $package doesn't have a \`bin' directory" \
+           "so we can't create lazy wrappers for it. Please" \
+           "remove \`${escapeShellArg package.name}' from" \
+           "\`vuizvui.lazyPackages'." >&2
+      exit 1
+    fi
+
+    for bin in "$package"/bin/*; do
+      [ -x "$bin" -a ! -d "$bin" ] || continue
+      binpath="''${bin#$package/}"
+
+      mkdir -p "$out/bin"
+      ( echo ${escapeShellArg "#!${pkgs.stdenv.shell}"}
+        echo "encoded='$encoded'"
+        echo "binpath='$binpath'"
+        echo -n ${escapeShellArg ''
+          storepath="$(echo "$encoded" | ${decoder})"
+          program="$storepath/$binpath"
+          if [ ! -e "$storepath" ]; then
+            ${fetchSubstitute} "$storepath" || exit $?
+          fi
+          exec "$program" "$@"
+        ''}
+      ) > "$out/bin/$(basename "$bin")"
+      chmod +x "$out/bin/$(basename "$bin")"
+    done
+  '';
+
+  wrappers = map mkWrapper config.vuizvui.lazyPackages;
+
+in {
+  options.vuizvui.lazyPackages = lib.mkOption {
+    type = lib.types.listOf lib.types.package;
+    default = [];
+    example = lib.literalExample "[ pkgs.gimp pkgs.libreoffice ]";
+    description = ''
+      Packages which are built for this system but instead of being a full
+      runtime dependency, only wrappers of all executables that reside in the
+      <literal>bin</literal> directory are actually runtime dependencies.
+
+      As soon as one of these wrappers is executed, the real package is fetched
+      and the corresponding binary is executed.
+    '';
+  };
+
+  config.environment.systemPackages = map mkWrapper lazyPackages;
+}
diff --git a/modules/core/licensing.nix b/modules/core/licensing.nix
new file mode 100644
index 00000000..1a7c9390
--- /dev/null
+++ b/modules/core/licensing.nix
@@ -0,0 +1,19 @@
+{ config, lib, ... }:
+
+let
+  overrideConfig = newConfig: import (import ../../nixpkgs-path.nix) {
+    inherit (config.nixpkgs) system;
+    config = config.nixpkgs.config // newConfig;
+  };
+
+in {
+  _module.args = {
+    unfreePkgs = overrideConfig {
+      whitelistedLicenses = [ lib.licenses.unfreeRedistributable ];
+    };
+
+    unfreeAndNonDistributablePkgs = overrideConfig {
+      allowUnfree = true;
+    };
+  };
+}
diff --git a/modules/core/tests.nix b/modules/core/tests.nix
new file mode 100644
index 00000000..fb1ea7e4
--- /dev/null
+++ b/modules/core/tests.nix
@@ -0,0 +1,415 @@
+{ options, config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  whichNet = if config.networking.useNetworkd then "networkd" else "scripted";
+
+  mkTest = attrs: if attrs.check then attrs.paths or [ attrs.path ] else [];
+
+  anyAttrs = pred: cfg: any id (mapAttrsToList (const pred) cfg);
+
+  upstreamTests = concatMap mkTest [
+    { check = config.services.avahi.enable;
+      path  = ["nixos" "avahi"];
+    }
+    { check = config.vuizvui.createISO;
+      paths = [
+        ["nixos" "boot" "biosCdrom"]
+        ["nixos" "boot" "biosUsb"]
+        ["nixos" "boot" "netboot"]
+        ["nixos" "boot" "uefiCdrom"]
+        ["nixos" "boot" "uefiUsb"]
+      ];
+    }
+    { check = true;
+      path  = ["nixos" "boot-stage1"];
+    }
+    { check = config.services.cadvisor.enable;
+      path  = ["nixos" "cadvisor"];
+    }
+    { check = config.services.cjdns.enable;
+      path  = ["nixos" "cjdns"];
+    }
+    { check = config.boot.enableContainers
+           && config.containers != {};
+      path  = ["nixos" "containers"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.hostBridge != null) config.containers;
+      path  = ["nixos" "containers-bridge"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.extraVeths != {}) config.containers;
+      path  = ["nixos" "containers-extra_veth"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.localAddress != []) config.containers;
+      path  = ["nixos" "containers-hosts"];
+    }
+    { check = config.boot.enableContainers;
+      path  = ["nixos" "containers-imperative"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.hostAddress  != null
+                        || i.localAddress != null) config.containers;
+      path  = ["nixos" "containers-ipv4"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.hostAddress6  != null
+                        || i.localAddress6 != null) config.containers;
+      path  = ["nixos" "containers-ipv6"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.macvlans != []) config.containers;
+      path  = ["nixos" "containers-macvlans"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.interfaces != []) config.containers;
+      path  = ["nixos" "containers-physical_interfaces"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.privateNetwork) config.containers;
+      path  = ["nixos" "containers-restart_networking"];
+    }
+    { check = config.boot.enableContainers
+           && anyAttrs (i: i.tmpfs != []) config.containers;
+      path  = ["nixos" "containers-tmpfs"];
+    }
+    { check = config.services.dnscrypt-proxy.enable;
+      path  = ["nixos" "dnscrypt-proxy"];
+    }
+    { check = config.virtualisation.docker.enable;
+      path  = ["nixos" "docker"];
+    }
+    { check = config.security.pam.enableEcryptfs;
+      path  = ["nixos" "ecryptfs"];
+    }
+    { check = config.services.etcd.enable;
+      path  = ["nixos" "etcd"];
+    }
+    { check = config.services.ferm.enable;
+      path  = ["nixos" "ferm"];
+    }
+    { check = config.networking.firewall.enable;
+      path  = ["nixos" "firewall"];
+    }
+    { check = config.services.fleet.enable;
+      path  = ["nixos" "fleet"];
+    }
+    { check = config.virtualisation.openstack.glance.enable;
+      path  = ["nixos" "glance"];
+    }
+    { check = config.services.xserver.desktopManager.gnome3.enable;
+      path  = ["nixos" "gnome3"];
+    }
+    { check = config.services.xserver.displayManager.gdm.enable;
+      path  = ["nixos" "gnome3-gdm"];
+    }
+    { check = config.services.gocd-agent.enable;
+      path  = ["nixos" "gocd-agent"];
+    }
+    { check = config.services.gocd-server.enable;
+      path  = ["nixos" "gocd-server"];
+    }
+    { check = config.boot.kernelPackages.kernel.features.grsecurity or false;
+      path  = ["nixos" "grsecurity"];
+    }
+    { check = true;
+      path  = ["nixos" "hibernate"];
+    }
+    { check = config.services.hound.enable;
+      path  = ["nixos" "hound"];
+    }
+    { check = config.services.xserver.windowManager.i3.enable;
+      path  = ["nixos" "i3wm"];
+    }
+    { check = config.boot.initrd.network.enable;
+      path  = ["nixos" "initrdNetwork"];
+    }
+    { check = elem "btrfs" config.boot.supportedFilesystems;
+      paths = [
+        ["nixos" "installer" "btrfsSimple"]
+        ["nixos" "installer" "btrfsSubvols"]
+        ["nixos" "installer" "btrfsSubvolDefault"]
+      ];
+    }
+    { check = config.boot.loader.grub.version == 1;
+      path  = ["nixos" "installer" "grub1"];
+    }
+    { check = config.boot.initrd.luks.devices != [];
+      path  = ["nixos" "installer" "luksroot"];
+    }
+    { check = true;
+      path  = ["nixos" "installer" "lvm"];
+    }
+    { check = config.fileSystems ? "/boot";
+      path  = ["nixos" "installer" "separateBoot"];
+    }
+    { check = config.fileSystems ? "/boot"
+           && config.fileSystems."/boot".fsType == "vfat";
+      path  = ["nixos" "installer" "separateBootFat"];
+    }
+    { check = elem "ext3" config.boot.supportedFilesystems;
+      path  = ["nixos" "installer" "simple"];
+    }
+    { check = config.boot.loader.systemd-boot.enable;
+      path  = ["nixos" "installer" "simpleUefiGummiboot"];
+    }
+    { check = config.boot.loader.grub.fsIdentifier == "label";
+      path  = ["nixos" "installer" "simpleLabels"];
+    }
+    { check = config.boot.loader.grub.fsIdentifier == "provided";
+      path  = ["nixos" "installer" "simpleProvided"];
+    }
+    { check = config.boot.initrd.mdadmConf != "";
+      path  = ["nixos" "installer" "swraid"];
+    }
+    { check = config.services.influxdb.enable;
+      path  = ["nixos" "influxdb"];
+    }
+    { check = config.networking.enableIPv6;
+      path  = ["nixos" "ipv6"];
+    }
+    { check = config.services.jenkins.enable;
+      path  = ["nixos" "jenkins"];
+    }
+    { check = config.i18n.consoleKeyMap          == "azerty/fr"
+           || config.services.xserver.layout     == "fr";
+      path  = ["nixos" "keymap" "azerty"];
+    }
+    { check = config.i18n.consoleKeyMap          == "en-latin9"
+           || config.services.xserver.xkbVariant == "colemak";
+      path  = ["nixos" "keymap" "colemak"];
+    }
+    { check = config.i18n.consoleKeyMap          == "dvorak"
+           || config.services.xserver.layout     == "dvorak";
+      path  = ["nixos" "keymap" "dvorak"];
+    }
+    { check = config.i18n.consoleKeyMap          == "dvp"
+           || config.services.xserver.xkbVariant == "dvp";
+      path  = ["nixos" "keymap" "dvp"];
+    }
+    { check = config.i18n.consoleKeyMap          == "neo"
+           || config.services.xserver.xkbVariant == "neo";
+      path  = ["nixos" "keymap" "neo"];
+    }
+    { check = config.i18n.consoleKeyMap          == "de"
+           || config.services.xserver.layout     == "de";
+      path  = ["nixos" "keymap" "qwertz"];
+    }
+    { check = config.virtualisation.openstack.keystone.enable;
+      path  = ["nixos" "keystone"];
+    }
+    { check = with config.services.kubernetes; apiserver.enable
+           || scheduler.enable || controllerManager.enable || kubelet.enable
+           || proxy.enable;
+      path  = ["nixos" "kubernetes"];
+    }
+    { check = config.boot.kernelPackages.kernel.version
+           == pkgs.linuxPackages_latest.kernel.version;
+      path  = ["nixos" "latestKernel" "login"];
+    }
+    { check = config.services.leaps.enable;
+      path  = ["nixos" "leaps"];
+    }
+    { check = true;
+      path  = ["nixos" "login"];
+    }
+    { check = config.services.mathics.enable;
+      path  = ["nixos" "mathics"];
+    }
+    { check = true;
+      path  = ["nixos" "misc"];
+    }
+    { check = config.services.mongodb.enable;
+      path  = ["nixos" "mongodb"];
+    }
+    { check = config.services.murmur.enable;
+      path  = ["nixos" "mumble"];
+    }
+    { check = config.services.munin-node.enable
+           || config.services.munin-cron.enable;
+      path  = ["nixos" "munin"];
+    }
+    { check = config.services.mysql.enable;
+      path  = ["nixos" "mysql"];
+    }
+    { check = config.services.mysql.enable
+           && config.services.mysql.replication.role != "none";
+      path  = ["nixos" "mysqlReplication"];
+    }
+    { check = config.networking.nat.enable
+           && config.networking.firewall.enable;
+      path  = ["nixos" "nat" "firewall"];
+    }
+    { check = with config.networking; let
+        isIptables = nat.enable || firewall.enable;
+        hasConntrack = firewall.connectionTrackingModules != []
+                    || firewall.autoLoadConntrackHelpers;
+      in isIptables && hasConntrack;
+      path  = ["nixos" "nat" "firewall-conntrack"];
+    }
+    { check = config.networking.nat.enable
+           && !config.networking.firewall.enable;
+      path  = ["nixos" "nat" "standalone"];
+    }
+    { check = config.networking.bonds != {};
+      path  = ["nixos" "networking" whichNet "bond"];
+    }
+    { check = config.networking.bridges != {};
+      path  = ["nixos" "networking" whichNet "bridge"];
+    }
+    { check = anyAttrs (i: i.useDHCP == true) config.networking.interfaces;
+      path  = ["nixos" "networking" whichNet "dhcpOneIf"];
+    }
+    { check = config.networking.useDHCP;
+      path  = ["nixos" "networking" whichNet "dhcpSimple"];
+    }
+    { check = true;
+      path  = ["nixos" "networking" whichNet "loopback"];
+    }
+    { check = config.networking.macvlans != {};
+      path  = ["nixos" "networking" whichNet "macvlan"];
+    }
+    { check = config.networking.sits != {};
+      path  = ["nixos" "networking" whichNet "sit"];
+    }
+    { check = anyAttrs (i: i.ip4 != []) config.networking.interfaces;
+      path  = ["nixos" "networking" whichNet "static"];
+    }
+    { check = config.networking.vlans != {};
+      path  = ["nixos" "networking" whichNet "vlan"];
+    }
+    { check = with config.networking.proxy; any (val: val != null)
+            [ default allProxy ftpProxy httpProxy httpsProxy noProxy
+              rsyncProxy
+            ];
+      path  = ["nixos" "networkingProxy"];
+    }
+    { check = elem "nfs" config.boot.supportedFilesystems;
+      paths = [
+        ["nixos" "nfs3"]
+        ["nixos" "nfs4"]
+      ];
+    }
+    { check = config.services.nginx.enable;
+      path  = ["nixos" "nginx"];
+    }
+    { check = config.services.nsd.enable;
+      path  = ["nixos" "nsd"];
+    }
+    { check = config.services.openssh.enable;
+      path  = ["nixos" "openssh"];
+    }
+    { check = config.security.pam.oath.enable;
+      path  = ["nixos" "pam-oath-login"];
+    }
+    { check = config.services.peerflix.enable;
+      path  = ["nixos" "peerflix"];
+    }
+    { check = config.services.postgresql.enable
+           && elem pkgs.pgjwt config.services.postgresql.extraPlugins;
+      path  = ["nixos" "pgjwt"];
+    }
+    { check = config.services.xserver.desktopManager.plasma5.enable;
+      path  = ["nixos" "plasma5"];
+    }
+    { check = config.services.postgresql.enable;
+      path  = let
+        filterPg = name: drv: hasPrefix "postgresql" name
+                           && drv == config.services.postgresql.package;
+        pgPackage = head (attrNames (filterAttrs filterPg pkgs));
+      in ["nixos" "postgresql" pgPackage];
+    }
+    { check = config.services.printing.enable;
+      path  = ["nixos" "printing"];
+    }
+    { check = config.services.httpd.enable
+           && elem "proxy_balancer" config.services.httpd.extraModules;
+      path  = ["nixos" "proxy"];
+    }
+    { check = config.services.pumpio.enable;
+      path  = ["nixos" "pumpio"];
+    }
+    # TODO: The upstream service was disabled because it broke
+    # { check = config.services.quagga.ospf.enable;
+    #   path  = ["nixos" "quagga"];
+    # }
+    { check = config.hardware.opengl.driSupport
+           && config.services.xserver.enable;
+      path  = ["nixos" "quake3"];
+    }
+    { check = true;
+      path  = ["nixos" "runInMachine"];
+    }
+    { check = config.services.samba.enable;
+      path  = ["nixos" "samba"];
+    }
+    { check = config.services.xserver.displayManager.sddm.enable;
+      paths = [
+        ["nixos" "sddm" "default"]
+        ["nixos" "sddm" "autoLogin"]
+      ];
+    }
+    { check = true;
+      path  = ["nixos" "simple"];
+    }
+    { check = config.services.xserver.displayManager.slim.enable;
+      path  = ["nixos" "slim"];
+    }
+    { check = config.services.smokeping.enable;
+      path  = ["nixos" "smokeping"];
+    }
+    { check = config.services.taskserver.enable;
+      path  = ["nixos" "taskserver"];
+    }
+    { check = config.services.tomcat.enable;
+      path  = ["nixos" "tomcat"];
+    }
+    { check = config.services.udisks2.enable;
+      path  = ["nixos" "udisks2"];
+    }
+    { check = config.virtualisation.virtualbox.host.enable;
+      paths = [
+        ["nixos" "virtualbox" "host-usb-permissions"]
+        ["nixos" "virtualbox" "net-hostonlyif"]
+        ["nixos" "virtualbox" "simple-cli"]
+        ["nixos" "virtualbox" "simple-gui"]
+        ["nixos" "virtualbox" "systemd-detect-virt"]
+      ];
+    }
+    { check = config.virtualisation.virtualbox.host.enable
+           && config.virtualisation.virtualbox.headless;
+      path  = ["nixos" "virtualbox" "headless"];
+    }
+    { check = let
+        hasWPSubServiceType = any (y: y.serviceType == "wordpress");
+        hasWPSubService = any (x: hasWPSubServiceType x.extraSubservices);
+        hasWordPress = config.services.httpd.virtualHosts;
+      in config.services.httpd.enable && hasWordPress;
+      path  = ["nixos" "wordpress"];
+    }
+    { check = config.services.xserver.desktopManager.xfce.enable;
+      path  = ["nixos" "xfce"];
+    }
+  ];
+
+in {
+  options.vuizvui = {
+    requiresTests = mkOption {
+      type = types.listOf (types.listOf types.str);
+      default = [];
+      example = [ ["nixos" "nat" "firewall"] ["vuizvui" "foo"] ];
+      description = ''
+        A list of attribute paths to the tests which need to succeed in order to
+        trigger a channel update for the current configuration/machine.
+
+        Every attribute path itself is a list of attribute names, which are
+        queried using <function>lib.getAttrFromPath</function>.
+      '';
+    };
+  };
+
+  config.vuizvui.requiresTests = upstreamTests;
+}
diff --git a/modules/hardware/gamecontroller.nix b/modules/hardware/gamecontroller.nix
new file mode 100644
index 00000000..5cfe9d16
--- /dev/null
+++ b/modules/hardware/gamecontroller.nix
@@ -0,0 +1,109 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  mappingType = (types.addCheck types.str (val: let
+    pattern = "[ab][0-9]+|h[0-9]+\.[0-9]+";
+  in builtins.match pattern val == [])) // {
+    name = "aI (axis), bI (button) or hI.M (hat) where I=index, M=mask";
+  };
+
+  mkAssignmentOption = example: name: description: mkOption {
+    type = types.nullOr mappingType;
+    default = null;
+    inherit example;
+    description = "Assignment for ${description}.";
+  };
+
+  mkAxisOption = mkAssignmentOption "a0";
+  mkButtonOption = mkAssignmentOption "b0";
+
+  axes = {
+    leftx = "left stick X axis";
+    lefty = "left stick Y axis";
+    rightx = "right stick X axis";
+    righty = "right stick Y axis";
+    lefttrigger = "left trigger";
+    righttrigger = "right trigger";
+  };
+
+  buttons = {
+    a = "A button (down)";
+    b = "B button (right)";
+    x = "X button (left)";
+    y = "Y button (up)";
+    back = "XBox <literal>back</literal> button";
+    guide = "XBox <literal>guide</literal> button";
+    start = "<literal>start</literal> button";
+    leftstick = "pressing the left stick";
+    rightstick = "pressing the right stick";
+    leftshoulder = "left shoulder/bumper button";
+    rightshoulder = "right shoulder/bumper button";
+    dpup = "directional pad up";
+    dpdown = "directional pad down";
+    dpleft = "directional pad left";
+    dpright = "directional pad right";
+  };
+
+  gcSubModule = { name, ... }: {
+    options = {
+      name = mkOption {
+        type = types.str;
+        default = name;
+        description = ''
+          The name of this controller, doesn't have special meaning and is only
+          there to make it easier to dinguish various mappings.
+        '';
+      };
+
+      guid = mkOption {
+        type = types.uniq types.str;
+        default = name;
+        description = ''
+          The SDL2 GUID to uniquely identify this controller.
+
+          Use <literal>vuizvui.list-gamecontrollers</literal> to list them.
+        '';
+      };
+
+      mapping = mapAttrs mkAxisOption axes // mapAttrs mkButtonOption buttons;
+    };
+  };
+
+  mkGCLine = const (cfg: let
+    validMappings = attrNames axes ++ attrNames buttons;
+    mkMappingVal = name: let
+      val = cfg.mapping.${name} or null;
+    in if val == null then null else "${name}:${val}";
+    attrs = [ cfg.guid cfg.name "platform:Linux" ]
+         ++ remove null (map mkMappingVal validMappings);
+  in concatStringsSep "," attrs);
+
+  controllers = mapAttrsToList mkGCLine config.vuizvui.hardware.gameController;
+  controllerConfig = concatStringsSep "\n" controllers;
+
+in {
+  options.vuizvui.hardware.gameController = mkOption {
+    type = types.attrsOf (types.submodule gcSubModule);
+    default = {};
+    description = let
+      url =
+      "https://upload.wikimedia.org/wikipedia/commons/2/2c/360_controller.svg";
+    in ''
+      A mapping of the game controllers to use with SDL2 games.
+
+      The mapping is always based on the XBox reference controller, so even if
+      you don't use an XBox controller, you still have to map your keys
+      according to this layout:
+
+      <link xlink:href="${
+        "https://upload.wikimedia.org/wikipedia/commons/2/2c/360_controller.svg"
+      }"/>
+    '';
+  };
+
+  config = mkIf (config.vuizvui.hardware.gameController != {}) {
+    environment.sessionVariables.SDL_GAMECONTROLLERCONFIG = controllerConfig;
+  };
+}
diff --git a/modules/hardware/rtl8192cu/default.nix b/modules/hardware/rtl8192cu/default.nix
new file mode 100644
index 00000000..b1c037c5
--- /dev/null
+++ b/modules/hardware/rtl8192cu/default.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (config.boot.kernelPackages) kernel;
+
+  modBaseDir = "kernel/drivers/net/wireless";
+
+  rtl8192cu = pkgs.stdenv.mkDerivation {
+    name = "rtl8192cu-${kernel.version}";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "pvaret";
+      repo = "rtl8192cu-fixes";
+      rev = "6a58e2f77d75ca9a3b80868a344ed4e2ea1816df";
+      sha256 = "130ym6ag5kgg1hdwpsfpg1i5l08lwqp1ylgjhfyhmz31h92b3h2x";
+    };
+
+    hardeningDisable = [ "stackprotector" "pic" ];
+
+    postPatch = ''
+      substituteInPlace Makefile --replace /sbin/depmod :
+    '';
+
+    makeFlags = [
+      "BUILD_KERNEL=${kernel.modDirVersion}"
+      "KSRC=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
+      "MODDESTDIR=$(out)/lib/modules/${kernel.modDirVersion}/${modBaseDir}/"
+    ];
+
+    preInstall = ''
+      mkdir -p "$out/lib/modules/${kernel.modDirVersion}/${modBaseDir}"
+    '';
+
+    enableParallelBuilding = true;
+  };
+
+in {
+  options.vuizvui.hardware.rtl8192cu = {
+    enable = lib.mkEnableOption "support for RTL8192CU wireless chipset";
+  };
+
+  config = lib.mkIf config.vuizvui.hardware.rtl8192cu.enable {
+    boot.extraModulePackages = [ rtl8192cu ];
+    # Note that the module is called "8192cu" so we don't blacklist the module
+    # we actually want to use. The ones we blacklist here are the modules from
+    # the mainline kernel, which unfortunately do not seem to work very well.
+    boot.blacklistedKernelModules = [ "rtl8192cu" "rtl8192c_common" "rtlwifi" ];
+    networking.enableRTL8192cFirmware = true;
+  };
+}
diff --git a/modules/hardware/t100ha/brcmfmac43340-sdio.txt b/modules/hardware/t100ha/brcmfmac43340-sdio.txt
new file mode 100644
index 00000000..db22fef6
--- /dev/null
+++ b/modules/hardware/t100ha/brcmfmac43340-sdio.txt
Binary files differdiff --git a/modules/hardware/t100ha/default.nix b/modules/hardware/t100ha/default.nix
new file mode 100644
index 00000000..70a9fc38
--- /dev/null
+++ b/modules/hardware/t100ha/default.nix
@@ -0,0 +1,107 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.vuizvui.hardware.t100ha;
+  desc = "hardware support for the ASUS T100HA convertible";
+
+in {
+  options.vuizvui.hardware.t100ha.enable = lib.mkEnableOption desc;
+
+  config = lib.mkIf cfg.enable {
+    hardware.firmware = lib.singleton (pkgs.runCommand "t100ha-firmware" {
+      params = ./brcmfmac43340-sdio.txt;
+      fwpkg = pkgs.firmwareLinuxNonfree;
+      install = "install -vD -m 0644";
+    } ''
+      for fw in brcm/brcmfmac43340-sdio intel/fw_sst_22a8; do
+        $install "$fwpkg/lib/firmware/$fw.bin" "$out/lib/firmware/$fw.bin"
+      done
+      $install "$params" "$out/lib/firmware/brcm/brcmfmac43340-sdio.txt"
+    '');
+
+    boot.kernelPatches = [
+      { name = "drm";
+        patch = ./drm.patch;
+      }
+      { name = "meta-keys";
+        patch = ./meta-keys.patch;
+      }
+      { name = "sdio";
+        patch = ./sdio.patch;
+      }
+      { name = "sound";
+        patch = ./sound.patch;
+      }
+    ];
+
+    boot.kernelPackages = let
+      nixpkgs = import ../../../nixpkgs-path.nix;
+      t100haKernel = pkgs.linux_4_9.override {
+        # Missing device drivers:
+        #
+        #   808622B8 -> Intel(R) Imaging Signal Processor 2401
+        #   808622D8 -> Intel(R) Integrated Sensor Solution
+        #   HIMX2051 -> Camera Sensor Unicam hm2051
+        #   IMPJ0003 -> Impinj RFID Device (MonzaX 8K)
+        #   OVTI5670 -> Camera Sensor ov5670
+        #
+        extraConfig = ''
+          # CPU
+          MATOM y
+
+          # MMC
+          MMC y
+          MMC_BLOCK y
+          MMC_SDHCI y
+          MMC_SDHCI_ACPI y
+
+          # PMIC
+          INTEL_PMC_IPC y
+          INTEL_SOC_PMIC y
+          MFD_AXP20X y
+          MFD_AXP20X_I2C y
+
+          # GPU
+          AGP n
+          DRM y
+          DRM_I915 y
+
+          # Thermal
+          INT3406_THERMAL y
+          INT340X_THERMAL y
+
+          # GPIO
+          PINCTRL_CHERRYVIEW y
+
+          # I2C
+          I2C_DESIGNWARE_BAYTRAIL y
+          I2C_DESIGNWARE_PLATFORM y
+
+          # HID
+          INTEL_HID_EVENT y
+
+          # MEI
+          INTEL_MEI y
+          INTEL_MEI_TXE y
+        '';
+      };
+    in pkgs.linuxPackagesFor t100haKernel;
+
+    # By default the console is rotated by 90 degrees to the right.
+    boot.kernelParams = [ "fbcon=rotate:3" ];
+    services.xserver.deviceSection = ''
+      Option "monitor-DSI1" "Monitor[0]"
+    '';
+    services.xserver.monitorSection = ''
+      Option "Rotate" "left"
+    '';
+    services.xserver.videoDriver = "intel";
+
+    # The touch screen needs to be rotated as well:
+    services.xserver.inputClassSections = lib.singleton ''
+      Identifier "touchscreen"
+      MatchProduct "SIS0457"
+      Option "TransformationMatrix" "0 -1 1 1 0 0 0 0 1"
+    '';
+  };
+}
diff --git a/modules/hardware/t100ha/drm.patch b/modules/hardware/t100ha/drm.patch
new file mode 100644
index 00000000..167be86d
--- /dev/null
+++ b/modules/hardware/t100ha/drm.patch
@@ -0,0 +1,733 @@
+diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
+index 8405b5a367d7..7e3545f65257 100644
+--- a/drivers/gpu/drm/i915/intel_bios.h
++++ b/drivers/gpu/drm/i915/intel_bios.h
+@@ -46,14 +46,20 @@ struct edp_power_seq {
+ 	u16 t11_t12;
+ } __packed;
+ 
+-/* MIPI Sequence Block definitions */
++/*
++ * MIPI Sequence Block definitions
++ *
++ * Note the VBT spec has AssertReset / DeassertReset swapped from their
++ * usual naming, we use the proper names here to avoid confusion when
++ * reading the code.
++ */
+ enum mipi_seq {
+ 	MIPI_SEQ_END = 0,
+-	MIPI_SEQ_ASSERT_RESET,
++	MIPI_SEQ_DEASSERT_RESET,	/* Spec says MipiAssertResetPin */
+ 	MIPI_SEQ_INIT_OTP,
+ 	MIPI_SEQ_DISPLAY_ON,
+ 	MIPI_SEQ_DISPLAY_OFF,
+-	MIPI_SEQ_DEASSERT_RESET,
++	MIPI_SEQ_ASSERT_RESET,		/* Spec says MipiDeassertResetPin */
+ 	MIPI_SEQ_BACKLIGHT_ON,		/* sequence block v2+ */
+ 	MIPI_SEQ_BACKLIGHT_OFF,		/* sequence block v2+ */
+ 	MIPI_SEQ_TEAR_ON,		/* sequence block v2+ */
+diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
+index c9e83f39ec0a..6a6ae51996d0 100644
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -13764,6 +13764,13 @@ static void update_scanline_offset(struct intel_crtc *crtc)
+ 	 * type. For DP ports it behaves like most other platforms, but on HDMI
+ 	 * there's an extra 1 line difference. So we need to add two instead of
+ 	 * one to the value.
++	 *
++	 * On VLV/CHV DSI the scanline counter would appear to increment
++	 * approx. 1/3 of a scanline before start of vblank. Unfortunately
++	 * that we can't tell whether we're in vblank or not while we're
++	 * on that particular line. We must set scanline_offset to 1 so
++	 * that the vblank timestamps come out correct when we query
++	 * the scanline counter from the vblank interrupt handler.
+ 	 */
+ 	if (IS_GEN2(dev)) {
+ 		const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
+index b2e3d3a334f7..c07ef5e9de95 100644
+--- a/drivers/gpu/drm/i915/intel_dsi.c
++++ b/drivers/gpu/drm/i915/intel_dsi.c
+@@ -80,7 +80,7 @@ enum mipi_dsi_pixel_format pixel_format_from_register_bits(u32 fmt)
+ 	}
+ }
+ 
+-static void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port)
++void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port)
+ {
+ 	struct drm_encoder *encoder = &intel_dsi->base.base;
+ 	struct drm_device *dev = encoder->dev;
+@@ -505,37 +505,83 @@ static void intel_dsi_port_disable(struct intel_encoder *encoder)
+ 	}
+ }
+ 
+-static void intel_dsi_enable(struct intel_encoder *encoder)
+-{
+-	struct drm_device *dev = encoder->base.dev;
+-	struct drm_i915_private *dev_priv = to_i915(dev);
+-	struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+-	enum port port;
+-
+-	DRM_DEBUG_KMS("\n");
+-
+-	if (is_cmd_mode(intel_dsi)) {
+-		for_each_dsi_port(port, intel_dsi->ports)
+-			I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(port), 8 * 4);
+-	} else {
+-		msleep(20); /* XXX */
+-		for_each_dsi_port(port, intel_dsi->ports)
+-			dpi_send_cmd(intel_dsi, TURN_ON, false, port);
+-		msleep(100);
+-
+-		drm_panel_enable(intel_dsi->panel);
++static void intel_dsi_prepare(struct intel_encoder *intel_encoder,
++			      struct intel_crtc_state *pipe_config);
+ 
+-		for_each_dsi_port(port, intel_dsi->ports)
+-			wait_for_dsi_fifo_empty(intel_dsi, port);
++static void intel_dsi_msleep(struct intel_dsi *intel_dsi, int msec)
++{
++	struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev);
+ 
+-		intel_dsi_port_enable(encoder);
+-	}
++	/* For v3 VBTs in vid-mode the delays are part of the VBT sequences */
++	if (is_vid_mode(intel_dsi) && dev_priv->vbt.dsi.seq_version >= 3)
++		return;
+ 
+-	intel_panel_enable_backlight(intel_dsi->attached_connector);
++	msleep(msec);
+ }
+ 
+-static void intel_dsi_prepare(struct intel_encoder *intel_encoder,
+-			      struct intel_crtc_state *pipe_config);
++/*
++ * Panel enable/disable sequences from the spec:
++ *
++ * v2 sequence for video mode:
++ * - power on
++ * - wait t1+t2
++ * - MIPIDeassertResetPin
++ * - clk/data lines to lp-11
++ * - MIPISendInitialDcsCmds
++ * - turn on DPI
++ * - MIPIDisplayOn
++ * - wait t5
++ * - backlight on
++ * ...
++ * - backlight off
++ * - wait t6
++ * - MIPIDisplayOff
++ * - turn off DPI
++ * - clk/data lines to lp-00
++ * - MIPIAssertResetPin
++ * - wait t3
++ * - power off
++ * - wait t4
++ *
++ * v3 sequence for video mode:
++ * - MIPIPanelPowerOn
++ * - MIPIDeassertResetPin
++ * - set clk/data lines to lp-11
++ * - MIPISendInitialDcsCmds (LP)
++ * - turn on DPI
++ * - MIPITearOn (command mode only) + MIPIDisplayOn (LP and HS)
++ * - MIPIBacklightOn
++ * ...
++ * - MIPIBacklightOff
++ * - turn off DPI
++ * - MIPITearOff + MIPIDisplayOff (LP)
++ * - clk/data lines to lp-00
++ * - MIPIAssertResetPin
++ * - MIPIPanelPowerOff
++ *
++ * sequence for command mode:
++ * - power on
++ * - wait t1+t2
++ * - MIPIDeassertResetPin
++ * - clk/data lines to lp-11
++ * - MIPISendInitialDcsCmds
++ * - MIPITearOn
++ * - MIPIDisplayOn
++ * - set pipe to dsr mode
++ * - wait t5
++ * - backlight on
++ * ... issue write_mem_start/write_mem_continue commands ...
++ * - backlight off
++ * - wait t6
++ * - disable pipe dsr mode
++ * - MIPITearOff
++ * - MIPIDisplayOff
++ * - clk/data lines to lp-00
++ * - MIPIAssertResetPin
++ * - wait t3
++ * - power off
++ * - wait t4
++ */
+ 
+ static void intel_dsi_pre_enable(struct intel_encoder *encoder,
+ 				 struct intel_crtc_state *pipe_config,
+@@ -554,14 +600,6 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder,
+ 	intel_disable_dsi_pll(encoder);
+ 	intel_enable_dsi_pll(encoder, pipe_config);
+ 
+-	intel_dsi_prepare(encoder, pipe_config);
+-
+-	/* Panel Enable over CRC PMIC */
+-	if (intel_dsi->gpio_panel)
+-		gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1);
+-
+-	msleep(intel_dsi->panel_on_delay);
+-
+ 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+ 		u32 val;
+ 
+@@ -571,17 +609,44 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder,
+ 		I915_WRITE(DSPCLK_GATE_D, val);
+ 	}
+ 
+-	/* put device in ready state */
+-	intel_dsi_device_ready(encoder);
++	intel_dsi_prepare(encoder, pipe_config);
++
++	/* Power on, try both CRC pmic gpio and VBT */
++	if (intel_dsi->gpio_panel)
++		gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1);
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_POWER_ON);
++	intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
+ 
+-	drm_panel_prepare(intel_dsi->panel);
++	/* Deassert reset */
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
+ 
+-	for_each_dsi_port(port, intel_dsi->ports)
+-		wait_for_dsi_fifo_empty(intel_dsi, port);
++	/* Put device in ready state (LP-11) */
++	intel_dsi_device_ready(encoder);
++
++	/* Send initialization commands in LP mode */
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_INIT_OTP);
+ 
+ 	/* Enable port in pre-enable phase itself because as per hw team
+ 	 * recommendation, port should be enabled befor plane & pipe */
+-	intel_dsi_enable(encoder);
++	if (is_cmd_mode(intel_dsi)) {
++		for_each_dsi_port(port, intel_dsi->ports)
++			I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(port), 8 * 4);
++		intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_TEAR_ON);
++		intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON);
++	} else {
++		msleep(20); /* XXX */
++		for_each_dsi_port(port, intel_dsi->ports)
++			dpi_send_cmd(intel_dsi, TURN_ON, false, port);
++		intel_dsi_msleep(intel_dsi, 100);
++
++		intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON);
++
++		intel_dsi_port_enable(encoder);
++	}
++
++	/* Enable backlight, both pwm and VBT */
++	intel_panel_enable_backlight(intel_dsi->attached_connector);
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_ON);
+ }
+ 
+ static void intel_dsi_enable_nop(struct intel_encoder *encoder,
+@@ -605,8 +670,15 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder,
+ 
+ 	DRM_DEBUG_KMS("\n");
+ 
++	/* Disable backlight, both VBT and pwm */
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_OFF);
+ 	intel_panel_disable_backlight(intel_dsi->attached_connector);
+ 
++	/*
++	 * XXX: According to the spec we should send SHUTDOWN before
++	 * MIPI_SEQ_DISPLAY_OFF only for v3+ VBTs, but testing in the field
++	 * has shown that we should do this for v2 VBTs too?
++	 */
+ 	if (is_vid_mode(intel_dsi)) {
+ 		/* Send Shutdown command to the panel in LP mode */
+ 		for_each_dsi_port(port, intel_dsi->ports)
+@@ -615,45 +687,6 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder,
+ 	}
+ }
+ 
+-static void intel_dsi_disable(struct intel_encoder *encoder)
+-{
+-	struct drm_device *dev = encoder->base.dev;
+-	struct drm_i915_private *dev_priv = to_i915(dev);
+-	struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+-	enum port port;
+-	u32 temp;
+-
+-	DRM_DEBUG_KMS("\n");
+-
+-	if (is_vid_mode(intel_dsi)) {
+-		for_each_dsi_port(port, intel_dsi->ports)
+-			wait_for_dsi_fifo_empty(intel_dsi, port);
+-
+-		intel_dsi_port_disable(encoder);
+-		msleep(2);
+-	}
+-
+-	for_each_dsi_port(port, intel_dsi->ports) {
+-		/* Panel commands can be sent when clock is in LP11 */
+-		I915_WRITE(MIPI_DEVICE_READY(port), 0x0);
+-
+-		intel_dsi_reset_clocks(encoder, port);
+-		I915_WRITE(MIPI_EOT_DISABLE(port), CLOCKSTOP);
+-
+-		temp = I915_READ(MIPI_DSI_FUNC_PRG(port));
+-		temp &= ~VID_MODE_FORMAT_MASK;
+-		I915_WRITE(MIPI_DSI_FUNC_PRG(port), temp);
+-
+-		I915_WRITE(MIPI_DEVICE_READY(port), 0x1);
+-	}
+-	/* if disable packets are sent before sending shutdown packet then in
+-	 * some next enable sequence send turn on packet error is observed */
+-	drm_panel_disable(intel_dsi->panel);
+-
+-	for_each_dsi_port(port, intel_dsi->ports)
+-		wait_for_dsi_fifo_empty(intel_dsi, port);
+-}
+-
+ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
+ {
+ 	struct drm_device *dev = encoder->base.dev;
+@@ -696,8 +729,6 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
+ 		I915_WRITE(MIPI_DEVICE_READY(port), 0x00);
+ 		usleep_range(2000, 2500);
+ 	}
+-
+-	intel_disable_dsi_pll(encoder);
+ }
+ 
+ static void intel_dsi_post_disable(struct intel_encoder *encoder,
+@@ -706,13 +737,50 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder,
+ {
+ 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+ 	struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
++	struct intel_connector *intel_connector = intel_dsi->attached_connector;
++	enum port port;
++	u32 temp;
+ 
+ 	DRM_DEBUG_KMS("\n");
+ 
+-	intel_dsi_disable(encoder);
++	if (is_vid_mode(intel_dsi)) {
++		for_each_dsi_port(port, intel_dsi->ports)
++			wait_for_dsi_fifo_empty(intel_dsi, port);
++
++		intel_dsi_port_disable(encoder);
++		usleep_range(2000, 5000);
++	}
++
++	intel_panel_disable_backlight(intel_connector);
++
++	for_each_dsi_port(port, intel_dsi->ports) {
++		/* Panel commands can be sent when clock is in LP11 */
++		I915_WRITE(MIPI_DEVICE_READY(port), 0x0);
+ 
++		intel_dsi_reset_clocks(encoder, port);
++		I915_WRITE(MIPI_EOT_DISABLE(port), CLOCKSTOP);
++
++		temp = I915_READ(MIPI_DSI_FUNC_PRG(port));
++		temp &= ~VID_MODE_FORMAT_MASK;
++		I915_WRITE(MIPI_DSI_FUNC_PRG(port), temp);
++
++		I915_WRITE(MIPI_DEVICE_READY(port), 0x1);
++	}
++
++	/*
++	 * if disable packets are sent before sending shutdown packet then in
++	 * some next enable sequence send turn on packet error is observed
++	 * XXX spec specifies SHUTDOWN before MIPI_SEQ_DISPLAY_OFF for
++	 * v3 VBTs, but not for v2 VBTs?
++	 */
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_TEAR_OFF);
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_OFF);
++
++	/* Transition to LP-00 */
+ 	intel_dsi_clear_device_ready(encoder);
+ 
++	intel_disable_dsi_pll(encoder);
++
+ 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+ 		u32 val;
+ 
+@@ -721,11 +789,12 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder,
+ 		I915_WRITE(DSPCLK_GATE_D, val);
+ 	}
+ 
+-	drm_panel_unprepare(intel_dsi->panel);
+-
+-	msleep(intel_dsi->panel_off_delay);
++	/* Assert reset */
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_ASSERT_RESET);
+ 
+-	/* Panel Disable over CRC PMIC */
++	/* Power off, try both CRC pmic gpio and VBT */
++	intel_dsi_msleep(intel_dsi, intel_dsi->panel_off_delay);
++	intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_POWER_OFF);
+ 	if (intel_dsi->gpio_panel)
+ 		gpiod_set_value_cansleep(intel_dsi->gpio_panel, 0);
+ 
+@@ -733,7 +802,7 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder,
+ 	 * FIXME As we do with eDP, just make a note of the time here
+ 	 * and perform the wait before the next panel power on.
+ 	 */
+-	msleep(intel_dsi->panel_pwr_cycle_delay);
++	intel_dsi_msleep(intel_dsi, intel_dsi->panel_pwr_cycle_delay);
+ }
+ 
+ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
+@@ -1376,6 +1445,7 @@ static void intel_dsi_connector_destroy(struct drm_connector *connector)
+ 
+ 	DRM_DEBUG_KMS("\n");
+ 	intel_panel_fini(&intel_connector->panel);
++	intel_panel_destroy_backlight(connector);
+ 	drm_connector_cleanup(connector);
+ 	kfree(connector);
+ }
+diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
+index 5967ea6d6045..548649158abd 100644
+--- a/drivers/gpu/drm/i915/intel_dsi.h
++++ b/drivers/gpu/drm/i915/intel_dsi.h
+@@ -130,6 +130,11 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
+ 	return container_of(encoder, struct intel_dsi, base.base);
+ }
+ 
++void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port);
++
++void intel_dsi_exec_vbt_sequence(struct intel_dsi *intel_dsi,
++				 enum mipi_seq seq_id);
++
+ bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv);
+ int intel_compute_dsi_pll(struct intel_encoder *encoder,
+ 			  struct intel_crtc_state *config);
+diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
+index 34601574fc6e..a4e3c642b39a 100644
+--- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
++++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
+@@ -189,6 +189,8 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi,
+ 		break;
+ 	}
+ 
++	wait_for_dsi_fifo_empty(intel_dsi, port);
++
+ out:
+ 	data += len;
+ 
+@@ -353,11 +355,11 @@ static const fn_mipi_elem_exec exec_elem[] = {
+  */
+ 
+ static const char * const seq_name[] = {
+-	[MIPI_SEQ_ASSERT_RESET] = "MIPI_SEQ_ASSERT_RESET",
++	[MIPI_SEQ_DEASSERT_RESET] = "MIPI_SEQ_DEASSERT_RESET",
+ 	[MIPI_SEQ_INIT_OTP] = "MIPI_SEQ_INIT_OTP",
+ 	[MIPI_SEQ_DISPLAY_ON] = "MIPI_SEQ_DISPLAY_ON",
+ 	[MIPI_SEQ_DISPLAY_OFF]  = "MIPI_SEQ_DISPLAY_OFF",
+-	[MIPI_SEQ_DEASSERT_RESET] = "MIPI_SEQ_DEASSERT_RESET",
++	[MIPI_SEQ_ASSERT_RESET] = "MIPI_SEQ_ASSERT_RESET",
+ 	[MIPI_SEQ_BACKLIGHT_ON] = "MIPI_SEQ_BACKLIGHT_ON",
+ 	[MIPI_SEQ_BACKLIGHT_OFF] = "MIPI_SEQ_BACKLIGHT_OFF",
+ 	[MIPI_SEQ_TEAR_ON] = "MIPI_SEQ_TEAR_ON",
+@@ -374,10 +376,9 @@ static const char *sequence_name(enum mipi_seq seq_id)
+ 		return "(unknown)";
+ }
+ 
+-static void generic_exec_sequence(struct drm_panel *panel, enum mipi_seq seq_id)
++void intel_dsi_exec_vbt_sequence(struct intel_dsi *intel_dsi,
++				 enum mipi_seq seq_id)
+ {
+-	struct vbt_panel *vbt_panel = to_vbt_panel(panel);
+-	struct intel_dsi *intel_dsi = vbt_panel->intel_dsi;
+ 	struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev);
+ 	const u8 *data;
+ 	fn_mipi_elem_exec mipi_elem_exec;
+@@ -436,35 +437,6 @@ static void generic_exec_sequence(struct drm_panel *panel, enum mipi_seq seq_id)
+ 	}
+ }
+ 
+-static int vbt_panel_prepare(struct drm_panel *panel)
+-{
+-	generic_exec_sequence(panel, MIPI_SEQ_ASSERT_RESET);
+-	generic_exec_sequence(panel, MIPI_SEQ_INIT_OTP);
+-
+-	return 0;
+-}
+-
+-static int vbt_panel_unprepare(struct drm_panel *panel)
+-{
+-	generic_exec_sequence(panel, MIPI_SEQ_DEASSERT_RESET);
+-
+-	return 0;
+-}
+-
+-static int vbt_panel_enable(struct drm_panel *panel)
+-{
+-	generic_exec_sequence(panel, MIPI_SEQ_DISPLAY_ON);
+-
+-	return 0;
+-}
+-
+-static int vbt_panel_disable(struct drm_panel *panel)
+-{
+-	generic_exec_sequence(panel, MIPI_SEQ_DISPLAY_OFF);
+-
+-	return 0;
+-}
+-
+ static int vbt_panel_get_modes(struct drm_panel *panel)
+ {
+ 	struct vbt_panel *vbt_panel = to_vbt_panel(panel);
+@@ -488,10 +460,6 @@ static int vbt_panel_get_modes(struct drm_panel *panel)
+ }
+ 
+ static const struct drm_panel_funcs vbt_panel_funcs = {
+-	.disable = vbt_panel_disable,
+-	.unprepare = vbt_panel_unprepare,
+-	.prepare = vbt_panel_prepare,
+-	.enable = vbt_panel_enable,
+ 	.get_modes = vbt_panel_get_modes,
+ };
+ 
+diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
+index be4b4d546fd9..5aac10c3239f 100644
+--- a/drivers/gpu/drm/i915/intel_panel.c
++++ b/drivers/gpu/drm/i915/intel_panel.c
+@@ -32,6 +32,7 @@
+ 
+ #include <linux/kernel.h>
+ #include <linux/moduleparam.h>
++#include <linux/mfd/intel_soc_pmic.h>
+ #include <linux/pwm.h>
+ #include "intel_drv.h"
+ 
+@@ -544,6 +545,11 @@ static u32 pwm_get_backlight(struct intel_connector *connector)
+ 	return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS);
+ }
+ 
++static u32 vlv_pmic_get_backlight(struct intel_connector *connector)
++{
++	return intel_soc_pmic_readb(0x4E);
++}
++
+ static u32 intel_panel_get_backlight(struct intel_connector *connector)
+ {
+ 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+@@ -635,6 +641,11 @@ static void pwm_set_backlight(struct intel_connector *connector, u32 level)
+ 	pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
+ }
+ 
++static void vlv_pmic_set_backlight(struct intel_connector *connector, u32 level)
++{
++	intel_soc_pmic_writeb(0x4E, level);
++}
++
+ static void
+ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level)
+ {
+@@ -808,6 +819,14 @@ static void pwm_disable_backlight(struct intel_connector *connector)
+ 	pwm_disable(panel->backlight.pwm);
+ }
+ 
++static void vlv_pmic_disable_backlight(struct intel_connector *connector)
++{
++	intel_panel_actually_set_backlight(connector, 0);
++
++	intel_soc_pmic_writeb(0x51, 0x00);
++	intel_soc_pmic_writeb(0x4B, 0x7F);
++}
++
+ void intel_panel_disable_backlight(struct intel_connector *connector)
+ {
+ 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+@@ -1089,6 +1108,17 @@ static void pwm_enable_backlight(struct intel_connector *connector)
+ 	intel_panel_actually_set_backlight(connector, panel->backlight.level);
+ }
+ 
++static void vlv_pmic_enable_backlight(struct intel_connector *connector)
++{
++	struct intel_panel *panel = &connector->panel;
++
++	intel_soc_pmic_writeb(0x4B, 0xFF);
++	intel_soc_pmic_writeb(0x4E, 0xFF);
++	intel_soc_pmic_writeb(0x51, 0x01);
++
++	intel_panel_actually_set_backlight(connector, panel->backlight.level);
++}
++
+ void intel_panel_enable_backlight(struct intel_connector *connector)
+ {
+ 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+@@ -1687,6 +1717,20 @@ static int pwm_setup_backlight(struct intel_connector *connector,
+ 	return 0;
+ }
+ 
++static int vlv_pmic_setup_backlight(struct intel_connector *connector,
++		enum pipe unused)
++{
++	struct intel_panel *panel = &connector->panel;
++
++	panel->backlight.present = 1;
++	panel->backlight.min = 0x00;
++	panel->backlight.max = 0xFF;
++	panel->backlight.level = 0x5A;
++	panel->backlight.enabled = 1;
++
++	return 0;
++}
++
+ int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe)
+ {
+ 	struct drm_i915_private *dev_priv = to_i915(connector->dev);
+@@ -1694,6 +1738,8 @@ int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe)
+ 	struct intel_panel *panel = &intel_connector->panel;
+ 	int ret;
+ 
++	intel_backlight_device_register(intel_connector);
++
+ 	if (!dev_priv->vbt.backlight.present) {
+ 		if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) {
+ 			DRM_DEBUG_KMS("no backlight present per VBT, but present per quirk\n");
+@@ -1783,18 +1829,17 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
+ 		panel->backlight.hz_to_pwm = pch_hz_to_pwm;
+ 	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+ 		if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) {
+-			panel->backlight.setup = pwm_setup_backlight;
+-			panel->backlight.enable = pwm_enable_backlight;
+-			panel->backlight.disable = pwm_disable_backlight;
+-			panel->backlight.set = pwm_set_backlight;
+-			panel->backlight.get = pwm_get_backlight;
++			panel->backlight.setup = vlv_pmic_setup_backlight;
++			panel->backlight.enable = vlv_pmic_enable_backlight;
++			panel->backlight.disable = vlv_pmic_disable_backlight;
++			panel->backlight.set = vlv_pmic_set_backlight;
++			panel->backlight.get = vlv_pmic_get_backlight;
+ 		} else {
+ 			panel->backlight.setup = vlv_setup_backlight;
+ 			panel->backlight.enable = vlv_enable_backlight;
+ 			panel->backlight.disable = vlv_disable_backlight;
+ 			panel->backlight.set = vlv_set_backlight;
+ 			panel->backlight.get = vlv_get_backlight;
+-			panel->backlight.hz_to_pwm = vlv_hz_to_pwm;
+ 		}
+ 	} else if (IS_GEN4(dev_priv)) {
+ 		panel->backlight.setup = i965_setup_backlight;
+diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
+index dbed12c484c9..a2f9a4671193 100644
+--- a/drivers/gpu/drm/i915/intel_sprite.c
++++ b/drivers/gpu/drm/i915/intel_sprite.c
+@@ -81,10 +81,13 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
+  */
+ void intel_pipe_update_start(struct intel_crtc *crtc)
+ {
++	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ 	const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+ 	long timeout = msecs_to_jiffies_timeout(1);
+ 	int scanline, min, max, vblank_start;
+ 	wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base);
++	bool need_vlv_dsi_wa = (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) &&
++		intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI);
+ 	DEFINE_WAIT(wait);
+ 
+ 	vblank_start = adjusted_mode->crtc_vblank_start;
+@@ -136,6 +139,25 @@ void intel_pipe_update_start(struct intel_crtc *crtc)
+ 
+ 	drm_crtc_vblank_put(&crtc->base);
+ 
++	/*
++	 * On VLV/CHV DSI the scanline counter would appear to
++	 * increment approx. 1/3 of a scanline before start of vblank.
++	 * The registers still get latched at start of vblank however.
++	 * This means we must not write any registers on the first
++	 * line of vblank (since not the whole line is actually in
++	 * vblank). And unfortunately we can't use the interrupt to
++	 * wait here since it may have already fired before we check
++	 * the scanline. We could use the frame start interrupt instead
++	 * since it will fire after the critical scanline, but that would
++	 * require more changes in the interrupt code. So for now we'll
++	 * just do the nasty thing and poll for the bad scanline to
++	 * pass us by.
++	 *
++	 * FIXME figure out if BXT+ DSI suffers from this as well
++	 */
++	while (need_vlv_dsi_wa && scanline == vblank_start)
++		scanline = intel_get_crtc_scanline(crtc);
++
+ 	crtc->debug.scanline_start = scanline;
+ 	crtc->debug.start_vbl_time = ktime_get();
+ 	crtc->debug.start_vbl_count = intel_crtc_get_vblank_counter(crtc);
+diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c
+index 12d6ebb4ae5d..8583e77deae0 100644
+--- a/drivers/mfd/intel_soc_pmic_core.c
++++ b/drivers/mfd/intel_soc_pmic_core.c
+@@ -44,6 +44,8 @@ static struct pwm_lookup crc_pwm_lookup[] = {
+ 	PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL),
+ };
+ 
++static struct intel_soc_pmic *pmic_hack = NULL;
++
+ static int intel_soc_pmic_find_gpio_irq(struct device *dev)
+ {
+ 	struct gpio_desc *desc;
+@@ -77,6 +79,7 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
+ 	config = (struct intel_soc_pmic_config *)id->driver_data;
+ 
+ 	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
++	pmic_hack = pmic;
+ 	if (!pmic)
+ 		return -ENOMEM;
+ 
+@@ -168,6 +171,37 @@ static int intel_soc_pmic_resume(struct device *dev)
+ }
+ #endif
+ 
++int intel_soc_pmic_readb(int reg)
++{
++	int ret;
++	unsigned int val;
++
++	if (!pmic_hack) {
++		ret = -EIO;
++	} else {
++		ret = regmap_read(pmic_hack->regmap, reg, &val);
++		if (!ret) {
++			ret = val;
++		}
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL(intel_soc_pmic_readb);
++
++int intel_soc_pmic_writeb(int reg, u8 val)
++{
++	int ret;
++
++	if (!pmic_hack) {
++		ret = -EIO;
++	} else {
++		ret = regmap_write(pmic_hack->regmap, reg, val);
++	}
++	return ret;
++}
++EXPORT_SYMBOL(intel_soc_pmic_writeb);
++
+ static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend,
+ 			 intel_soc_pmic_resume);
+ 
+diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
+index cf619dbeace2..52ad034f606a 100644
+--- a/include/linux/mfd/intel_soc_pmic.h
++++ b/include/linux/mfd/intel_soc_pmic.h
+@@ -29,4 +29,7 @@ struct intel_soc_pmic {
+ 	struct device *dev;
+ };
+ 
++int intel_soc_pmic_readb(int reg);
++int intel_soc_pmic_writeb(int reg, u8 val);
++
+ #endif	/* __INTEL_SOC_PMIC_H__ */
diff --git a/modules/hardware/t100ha/meta-keys.patch b/modules/hardware/t100ha/meta-keys.patch
new file mode 100644
index 00000000..26fd4fda
--- /dev/null
+++ b/modules/hardware/t100ha/meta-keys.patch
@@ -0,0 +1,44 @@
+diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
+index bcfaf32..c54cb28 100644
+--- a/drivers/hid/hid-input.c
++++ b/drivers/hid/hid-input.c
+@@ -502,7 +502,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
+ 
+ 	field->hidinput = hidinput;
+ 
+-	if (field->flags & HID_MAIN_ITEM_CONSTANT)
++	if ((field->flags & HID_MAIN_ITEM_CONSTANT) &&
++		(usage->hid & HID_USAGE_PAGE) != HID_UP_ASUSVENDOR)
+ 		goto ignore;
+ 
+ 	/* Ignore if report count is out of bounds. */
+@@ -980,6 +981,17 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
+ 		}
+ 		break;
+ 
++	case HID_UP_ASUSVENDOR:
++		switch (usage->hid & HID_USAGE) {
++			case 0x06C: map_key_clear(KEY_SLEEP);           break; /* Fn+F1: Sleep */
++			case 0x088: map_key_clear(KEY_WLAN);            break; /* Fn+F2: Wifi & BT */
++			case 0x010: map_key_clear(KEY_BRIGHTNESSDOWN);  break; /* Fn+F5: Brightness down */
++			case 0x020: map_key_clear(KEY_BRIGHTNESSUP);    break; /* Fn+F6: Brightness up */
++			case 0x06B: map_key_clear(KEY_F24);             break; /* Fn+F9: Touchpad */
++			default: goto ignore;
++		}
++		break;
++
+ 	default:
+ 	unknown:
+ 		if (field->report_size == 1) {
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index 75b66ec..16a64fd 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -172,6 +172,7 @@ struct hid_item {
+ #define HID_UP_LOGIVENDOR3   0xff430000
+ #define HID_UP_LNVENDOR		0xffa00000
+ #define HID_UP_SENSOR		0x00200000
++#define HID_UP_ASUSVENDOR	0xff310000
+ 
+ #define HID_USAGE		0x0000ffff
+ 
diff --git a/modules/hardware/t100ha/sdio.patch b/modules/hardware/t100ha/sdio.patch
new file mode 100644
index 00000000..3bfef26d
--- /dev/null
+++ b/modules/hardware/t100ha/sdio.patch
@@ -0,0 +1,52 @@
+Patch from https://github.com/hadess/rtl8723bs/issues/80#issuecomment-252259702
+
+diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
+index 159f7f1..2076427 100644
+--- a/drivers/acpi/acpi_platform.c
++++ b/drivers/acpi/acpi_platform.c
+@@ -50,8 +50,10 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
+ 	int count;
+ 
+ 	/* If the ACPI node already has a physical device attached, skip it. */
+-	if (adev->physical_node_count)
+-		return NULL;
++	if (adev->physical_node_count && !(
++	    !strcmp(acpi_device_hid(adev), "80860F14") &&
++	    !strcmp(adev->pnp.unique_id, "2")
++	)) return NULL;
+ 
+ 	if (!acpi_match_device_ids(adev, forbidden_id_list))
+ 		return ERR_PTR(-EINVAL);
+diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
+index 458ffb7..2807f78 100644
+--- a/drivers/mmc/host/sdhci-acpi.c
++++ b/drivers/mmc/host/sdhci-acpi.c
+@@ -47,6 +47,7 @@
+ #endif
+ 
+ #include "sdhci.h"
++#include <linux/pci.h>
+ 
+ enum {
+ 	SDHCI_ACPI_SD_CD		= BIT(0),
+@@ -381,6 +382,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
+ 	struct acpi_device *device, *child;
+ 	struct sdhci_acpi_host *c;
+ 	struct sdhci_host *host;
++	struct pci_dev *pcidev = NULL;
+ 	struct resource *iomem;
+ 	resource_size_t len;
+ 	const char *hid;
+@@ -404,6 +406,12 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
+ 	hid = acpi_device_hid(device);
+ 	uid = device->pnp.unique_id;
+ 
++	/* Workaround for CherryTrail x5-z8xxx: Make sure the SDIO controller
++	 * doesn't get added via PCI. */
++	if (!strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
++	    (pcidev = pci_get_device(0x8086, 0x2280, NULL)) != NULL)
++		pci_stop_and_remove_bus_device(pcidev);
++
+ 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ 	if (!iomem)
+ 		return -ENOMEM;
diff --git a/modules/hardware/t100ha/sound.patch b/modules/hardware/t100ha/sound.patch
new file mode 100644
index 00000000..15a2a883
--- /dev/null
+++ b/modules/hardware/t100ha/sound.patch
@@ -0,0 +1,5620 @@
+diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
+index bada636d1065..233898ec1fbd 100644
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -2756,6 +2756,7 @@ config X86_DMA_REMAP
+ config PMC_ATOM
+ 	def_bool y
+         depends on PCI
++	select COMMON_CLK
+ 
+ source "net/Kconfig"
+ 
+diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
+index aa8744c77c6d..59f95168fa8d 100644
+--- a/arch/x86/include/asm/pmc_atom.h
++++ b/arch/x86/include/asm/pmc_atom.h
+@@ -152,6 +152,12 @@
+ #define	SLEEP_TYPE_S5		0x1C00
+ #define	SLEEP_ENABLE		0x2000
+ 
++struct pmc_clk {
++	const char *name;
++	unsigned long freq;
++	const char *parent_name;
++};
++
+ extern int pmc_atom_read(int offset, u32 *value);
+ extern int pmc_atom_write(int offset, u32 value);
+ 
+diff --git a/arch/x86/platform/atom/pmc_atom.c b/arch/x86/platform/atom/pmc_atom.c
+index 964ff4fc61f9..e2de3e234959 100644
+--- a/arch/x86/platform/atom/pmc_atom.c
++++ b/arch/x86/platform/atom/pmc_atom.c
+@@ -21,6 +21,7 @@
+ #include <linux/debugfs.h>
+ #include <linux/seq_file.h>
+ #include <linux/io.h>
++#include <linux/platform_device.h>
+ 
+ #include <asm/pmc_atom.h>
+ 
+@@ -37,6 +38,11 @@ struct pmc_reg_map {
+ 	const struct pmc_bit_map *pss;
+ };
+ 
++struct pmc_data {
++	const struct pmc_reg_map *map;
++	const struct pmc_clk *clks;
++};
++
+ struct pmc_dev {
+ 	u32 base_addr;
+ 	void __iomem *regmap;
+@@ -50,6 +56,29 @@ struct pmc_dev {
+ static struct pmc_dev pmc_device;
+ static u32 acpi_base_addr;
+ 
++static const struct pmc_clk byt_clks[] = {
++	{
++		.name = "xtal",
++		.freq = 25000000,
++		.parent_name = NULL,
++	},
++	{
++		.name = "pll",
++		.freq = 19200000,
++		.parent_name = "xtal",
++	},
++	{},
++};
++
++static const struct pmc_clk cht_clks[] = {
++	{
++		.name = "xtal",
++		.freq = 19200000,
++		.parent_name = NULL,
++	},
++	{},
++};
++
+ static const struct pmc_bit_map d3_sts_0_map[] = {
+ 	{"LPSS1_F0_DMA",	BIT_LPSS1_F0_DMA},
+ 	{"LPSS1_F1_PWM1",	BIT_LPSS1_F1_PWM1},
+@@ -169,6 +198,16 @@ static const struct pmc_reg_map cht_reg_map = {
+ 	.pss		= cht_pss_map,
+ };
+ 
++static const struct pmc_data byt_data = {
++	.map = &byt_reg_map,
++	.clks = byt_clks,
++};
++
++static const struct pmc_data cht_data = {
++	.map = &cht_reg_map,
++	.clks = cht_clks,
++};
++
+ static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+ {
+ 	return readl(pmc->regmap + reg_offset);
+@@ -384,8 +423,11 @@ static int pmc_dbgfs_register(struct pmc_dev *pmc)
+ 
+ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
++	struct platform_device *clkdev;
+ 	struct pmc_dev *pmc = &pmc_device;
+-	const struct pmc_reg_map *map = (struct pmc_reg_map *)ent->driver_data;
++	const struct pmc_data *data = (struct pmc_data *)ent->driver_data;
++	const struct pmc_reg_map *map = data->map;
++	const struct pmc_clk *clks = data->clks;
+ 	int ret;
+ 
+ 	/* Obtain ACPI base address */
+@@ -414,6 +456,13 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+ 	if (ret)
+ 		dev_warn(&pdev->dev, "debugfs register failed\n");
+ 
++	/* Register platform clocks - PMC_PLT_CLK [5:0] */
++	clkdev = platform_device_register_data(NULL, "clk-byt-plt", -1,
++					       &clks, sizeof(clks));
++	if (IS_ERR(clkdev))
++		dev_warn(&pdev->dev, "platform clocks register failed: %ld\n",
++			 PTR_ERR(clkdev));
++
+ 	pmc->init = true;
+ 	return ret;
+ }
+@@ -424,8 +473,8 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
+  * used by pci_match_id() call below.
+  */
+ static const struct pci_device_id pmc_pci_ids[] = {
+-	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_reg_map },
+-	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_reg_map },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data },
+ 	{ 0, },
+ };
+ 
+diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
+index 04781389d0fb..cbdc8cc00060 100644
+--- a/drivers/clk/x86/Makefile
++++ b/drivers/clk/x86/Makefile
+@@ -1,2 +1,3 @@
+ clk-x86-lpss-objs		:= clk-lpt.o
+ obj-$(CONFIG_X86_INTEL_LPSS)	+= clk-x86-lpss.o
++obj-$(CONFIG_PMC_ATOM)		+= clk-byt-plt.o
+diff --git a/drivers/clk/x86/clk-byt-plt.c b/drivers/clk/x86/clk-byt-plt.c
+new file mode 100644
+index 000000000000..330cd35d1d27
+--- /dev/null
++++ b/drivers/clk/x86/clk-byt-plt.c
+@@ -0,0 +1,413 @@
++/*
++ * Intel Atom platform clocks driver for Baytrail and CherryTrail SoC.
++ *
++ * Copyright (C) 2016, Intel Corporation
++ * Author: Irina Tirdea <irina.tirdea@intel.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
++ * more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/clkdev.h>
++
++#include <asm/pmc_atom.h>
++
++#define PLT_CLK_NAME_BASE	"pmc_plt_clk_"
++#define PLT_CLK_DRIVER_NAME	"clk-byt-plt"
++
++#define PMC_CLK_CTL_0		0x60
++#define PMC_CLK_CTL_SIZE	4
++#define PMC_CLK_NUM		6
++#define PMC_MASK_CLK_CTL	GENMASK(1, 0)
++#define PMC_MASK_CLK_FREQ	BIT(2)
++#define PMC_CLK_CTL_GATED_ON_D3	0x0
++#define PMC_CLK_CTL_FORCE_ON	0x1
++#define PMC_CLK_CTL_FORCE_OFF	0x2
++#define PMC_CLK_CTL_RESERVED	0x3
++#define PMC_CLK_FREQ_XTAL	0x0	/* 25 MHz */
++#define PMC_CLK_FREQ_PLL	0x4	/* 19.2 MHz */
++
++struct clk_plt_fixed {
++	struct clk *clk;
++	struct clk_lookup *lookup;
++};
++
++struct clk_plt {
++	struct clk_hw hw;
++	u8 id;
++	u32 offset;
++	struct clk_lookup *lookup;
++	spinlock_t lock;
++};
++
++#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
++
++struct clk_plt_data {
++	struct clk_plt_fixed **parents;
++	u8 nparents;
++	struct clk *clks[PMC_CLK_NUM];
++};
++
++static inline int plt_reg_to_parent(int reg)
++{
++	switch (reg & PMC_MASK_CLK_FREQ) {
++	case PMC_CLK_FREQ_XTAL:
++		return 0;	/* index 0 in parents[] */
++	case PMC_CLK_FREQ_PLL:
++		return 1;	/* index 1 in parents[] */
++	}
++
++	return 0;
++}
++
++static inline int plt_parent_to_reg(int index)
++{
++	switch (index) {
++	case 0:	/* index 0 in parents[] */
++		return PMC_CLK_FREQ_XTAL;
++	case 1:	/* index 0 in parents[] */
++		return PMC_CLK_FREQ_PLL;
++	}
++
++	return PMC_CLK_FREQ_XTAL;
++}
++
++static inline int plt_reg_to_enabled(int reg)
++{
++	switch (reg & PMC_MASK_CLK_CTL) {
++	case PMC_CLK_CTL_GATED_ON_D3:
++	case PMC_CLK_CTL_FORCE_ON:
++		return 1;	/* enabled */
++	case PMC_CLK_CTL_FORCE_OFF:
++	case PMC_CLK_CTL_RESERVED:
++	default:
++		return 0;	/* disabled */
++	}
++}
++
++static int plt_pmc_atom_update(struct clk_plt *clk, u32 mask, u32 val)
++{
++	int ret;
++	u32 orig, tmp;
++	unsigned long flags = 0;
++
++	spin_lock_irqsave(&clk->lock, flags);
++
++	ret = pmc_atom_read(clk->offset, &orig);
++	if (ret)
++		goto out;
++
++	tmp = orig & ~mask;
++	tmp |= val & mask;
++
++	if (tmp == orig)
++		goto out;
++
++	ret = pmc_atom_write(clk->offset, tmp);
++	if (ret)
++		goto out;
++
++out:
++	spin_unlock_irqrestore(&clk->lock, flags);
++
++	return ret;
++}
++
++static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	return plt_pmc_atom_update(clk, PMC_MASK_CLK_FREQ,
++				   plt_parent_to_reg(index));
++}
++
++static u8 plt_clk_get_parent(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++	u32 value;
++	int ret;
++
++	ret = pmc_atom_read(clk->offset, &value);
++	if (ret)
++		return ret;
++
++	return plt_reg_to_parent(value);
++}
++
++static int plt_clk_enable(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	return plt_pmc_atom_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
++}
++
++static void plt_clk_disable(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++
++	plt_pmc_atom_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
++}
++
++static int plt_clk_is_enabled(struct clk_hw *hw)
++{
++	struct clk_plt *clk = to_clk_plt(hw);
++	u32 value;
++	int ret;
++
++	ret = pmc_atom_read(clk->offset, &value);
++	if (ret)
++		return ret;
++
++	return plt_reg_to_enabled(value);
++}
++
++static const struct clk_ops plt_clk_ops = {
++	.enable = plt_clk_enable,
++	.disable = plt_clk_disable,
++	.is_enabled = plt_clk_is_enabled,
++	.get_parent = plt_clk_get_parent,
++	.set_parent = plt_clk_set_parent,
++	.determine_rate = __clk_mux_determine_rate,
++};
++
++static struct clk *plt_clk_register(struct platform_device *pdev, int id,
++				    const char **parent_names, int num_parents)
++{
++	struct clk_plt *pclk;
++	struct clk *clk;
++	struct clk_init_data init;
++	int ret = 0;
++
++	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
++	if (!pclk)
++		return ERR_PTR(-ENOMEM);
++
++	init.name =  kasprintf(GFP_KERNEL, "%s%d", PLT_CLK_NAME_BASE, id);
++	init.ops = &plt_clk_ops;
++	init.flags = 0;
++	init.parent_names = parent_names;
++	init.num_parents = num_parents;
++
++	pclk->hw.init = &init;
++	pclk->id = id;
++	pclk->offset = PMC_CLK_CTL_0 + id * PMC_CLK_CTL_SIZE;
++	spin_lock_init(&pclk->lock);
++
++	clk = clk_register(&pdev->dev, &pclk->hw);
++	if (IS_ERR(clk)) {
++		ret = PTR_ERR(clk);
++		goto err_free_pclk;
++	}
++
++	pclk->lookup = clkdev_create(clk, init.name, NULL);
++	if (!pclk->lookup) {
++		ret = -ENOMEM;
++		goto err_clk_unregister;
++	}
++
++	kfree(init.name);
++
++	return clk;
++
++err_clk_unregister:
++	clk_unregister(clk);
++err_free_pclk:
++	kfree(init.name);
++	return ERR_PTR(ret);
++}
++
++static void plt_clk_unregister(struct clk *clk)
++{
++	struct clk_plt *pclk;
++	struct clk_hw *hw;
++
++	hw = __clk_get_hw(clk);
++	if (!hw)
++		return;
++
++	pclk = to_clk_plt(hw);
++
++	clkdev_drop(pclk->lookup);
++	clk_unregister(clk);
++}
++
++static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
++						 const char *name,
++						 const char *parent_name,
++						 unsigned long fixed_rate)
++{
++	struct clk_plt_fixed *pclk;
++	int ret = 0;
++
++	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
++	if (!pclk)
++		return ERR_PTR(-ENOMEM);
++
++	pclk->clk = clk_register_fixed_rate(&pdev->dev, name, parent_name,
++					    0, fixed_rate);
++	if (IS_ERR(pclk->clk)) {
++		ret = PTR_ERR(pclk->clk);
++		return ERR_PTR(ret);
++	}
++
++	pclk->lookup = clkdev_create(pclk->clk, name, NULL);
++	if (!pclk->lookup) {
++		ret = -ENOMEM;
++		goto err_clk_unregister;
++	}
++
++	return pclk;
++
++err_clk_unregister:
++	clk_unregister_fixed_rate(pclk->clk);
++	return ERR_PTR(ret);
++}
++
++static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
++{
++	clkdev_drop(pclk->lookup);
++	clk_unregister_fixed_rate(pclk->clk);
++}
++
++static const char **plt_clk_register_parents(struct platform_device *pdev,
++					     struct clk_plt_data *data)
++{
++	struct pmc_clk **pclks, *clks;
++	const char **parent_names;
++	int i, err;
++
++	data->nparents = 0;
++	pclks = dev_get_platdata(&pdev->dev);
++	if (!pclks)
++		return NULL;
++
++	clks = *pclks;
++	while (clks[data->nparents].name)
++		data->nparents++;
++
++	data->parents = devm_kzalloc(&pdev->dev,
++				     sizeof(*data->parents) * data->nparents,
++				     GFP_KERNEL);
++	if (!data->parents) {
++		err = -ENOMEM;
++		goto err_out;
++	}
++
++	parent_names = kcalloc(data->nparents, sizeof(*parent_names),
++			       GFP_KERNEL);
++	if (!parent_names) {
++		err = -ENOMEM;
++		goto err_out;
++	}
++
++	for (i = 0; i < data->nparents; i++) {
++		data->parents[i] =
++			plt_clk_register_fixed_rate(pdev, clks[i].name,
++						    clks[i].parent_name,
++						    clks[i].freq);
++		if (IS_ERR(data->parents[i])) {
++			err = PTR_ERR(data->parents[i]);
++			goto err_unreg;
++		}
++		parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
++	}
++
++	return parent_names;
++
++err_unreg:
++	for (i--; i >= 0; i--) {
++		plt_clk_unregister_fixed_rate(data->parents[i]);
++		kfree_const(parent_names[i]);
++	}
++	kfree(parent_names);
++err_out:
++	data->nparents = 0;
++	return ERR_PTR(err);
++}
++
++static void plt_clk_unregister_parents(struct clk_plt_data *data)
++{
++	int i;
++
++	for (i = 0; i < data->nparents; i++)
++		plt_clk_unregister_fixed_rate(data->parents[i]);
++}
++
++static int plt_clk_probe(struct platform_device *pdev)
++{
++	struct clk_plt_data *data;
++	int i, err;
++	const char **parent_names;
++
++	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	parent_names = plt_clk_register_parents(pdev, data);
++	if (IS_ERR(parent_names))
++		return PTR_ERR(parent_names);
++
++	for (i = 0; i < PMC_CLK_NUM; i++) {
++		data->clks[i] = plt_clk_register(pdev, i, parent_names,
++						 data->nparents);
++		if (IS_ERR(data->clks[i])) {
++			err = PTR_ERR(data->clks[i]);
++			goto err_unreg_clk_plt;
++		}
++	}
++
++	for (i = 0; i < data->nparents; i++)
++		kfree_const(parent_names[i]);
++	kfree(parent_names);
++
++	dev_set_drvdata(&pdev->dev, data);
++	return 0;
++
++err_unreg_clk_plt:
++	for (i--; i >= 0; i--)
++		plt_clk_unregister(data->clks[i]);
++	plt_clk_unregister_parents(data);
++	for (i = 0; i < data->nparents; i++)
++		kfree_const(parent_names[i]);
++	kfree(parent_names);
++	return err;
++}
++
++static int plt_clk_remove(struct platform_device *pdev)
++{
++	struct clk_plt_data *data;
++	int i;
++
++	data = dev_get_drvdata(&pdev->dev);
++	if (!data)
++		return 0;
++
++	for (i = 0; i < PMC_CLK_NUM; i++)
++		plt_clk_unregister(data->clks[i]);
++	plt_clk_unregister_parents(data);
++	return 0;
++}
++
++static struct platform_driver plt_clk_driver = {
++	.driver = {
++		.name = PLT_CLK_DRIVER_NAME,
++	},
++	.probe = plt_clk_probe,
++	.remove = plt_clk_remove,
++};
++module_platform_driver(plt_clk_driver);
++
++MODULE_DESCRIPTION("Intel Atom platform clocks driver");
++MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index c67667bb970f..655e2f4d1dd0 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -61,6 +61,7 @@ config SND_SOC_ALL_CODECS
+ 	select SND_SOC_CS47L24 if MFD_CS47L24
+ 	select SND_SOC_CS53L30 if I2C
+ 	select SND_SOC_CX20442 if TTY
++	select SND_SOC_CX2072X if I2C
+ 	select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
+ 	select SND_SOC_DA7213 if I2C
+ 	select SND_SOC_DA7218 if I2C
+@@ -478,6 +479,10 @@ config SND_SOC_CX20442
+ 	tristate
+ 	depends on TTY
+ 
++config SND_SOC_CX2072X
++	tristate "Conexant CX2072X CODEC"
++	depends on I2C
++	
+ config SND_SOC_JZ4740_CODEC
+ 	select REGMAP_MMIO
+ 	tristate
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index 958cd4912fbc..1dded20a4d84 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -54,6 +54,7 @@ snd-soc-cs4349-objs := cs4349.o
+ snd-soc-cs47l24-objs := cs47l24.o
+ snd-soc-cs53l30-objs := cs53l30.o
+ snd-soc-cx20442-objs := cx20442.o
++snd-soc-cx2072x-objs := cx2072x.o
+ snd-soc-da7210-objs := da7210.o
+ snd-soc-da7213-objs := da7213.o
+ snd-soc-da7218-objs := da7218.o
+@@ -279,6 +280,7 @@ obj-$(CONFIG_SND_SOC_CS4349)	+= snd-soc-cs4349.o
+ obj-$(CONFIG_SND_SOC_CS47L24)	+= snd-soc-cs47l24.o
+ obj-$(CONFIG_SND_SOC_CS53L30)	+= snd-soc-cs53l30.o
+ obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
++obj-$(CONFIG_SND_SOC_CX2072X)	+= snd-soc-cx2072x.o
+ obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
+ obj-$(CONFIG_SND_SOC_DA7213)	+= snd-soc-da7213.o
+ obj-$(CONFIG_SND_SOC_DA7218)	+= snd-soc-da7218.o
+diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
+new file mode 100644
+index 000000000000..aaec3722854c
+--- /dev/null
++++ b/sound/soc/codecs/cx2072x.c
+@@ -0,0 +1,2124 @@
++/*
++ * ALSA SoC CX20721/cx20723 Solana codec driver
++ * Copyright:   (C) 2016 Conexant Systems, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ ************************************************************************/
++#define DEBUG
++/*#define INTEL_MCLK_CONTROL*/
++
++/*#define ENABLE_MIC_POP_WA*/
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/platform_device.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++#include <sound/tlv.h>
++#include <linux/of_gpio.h>
++#include <linux/gpio.h>
++#include <sound/jack.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/firmware.h>
++#include <linux/regmap.h>
++#include <linux/proc_fs.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/acpi.h>
++#ifdef INTEL_MCLK_CONTROL
++#include <linux/vlv2_plat_clock.h>
++#endif
++#include "cx2072x.h"
++
++#define SUPPORT_RKI2S_FORMAT
++#define CX2072X_REV_A2 0x00100002
++#define CXDBG_REG_DUMP
++
++/*FIXME: need to move the EQ/DRC setting to device tree*/
++static unsigned char cx2072x_eq_coeff_array[MAX_EQ_BAND][MAC_EQ_COEFF] = {
++	{0x77, 0x26, 0x13, 0xb3, 0x76, 0x26, 0x0a, 0x3d, 0xd4, 0xe2, 0x04},
++	{0x97, 0x3e, 0xb3, 0x86, 0xc2, 0x3b, 0x4d, 0x79, 0xa7, 0xc5, 0x03},
++	{0x0f, 0x39, 0x76, 0xa3, 0x1b, 0x2b, 0x89, 0x5c, 0xd7, 0xdb, 0x03},
++	{0x21, 0x46, 0xfe, 0xa6, 0xec, 0x24, 0x01, 0x59, 0xf4, 0xd4, 0x03},
++	{0xe9, 0x78, 0x9c, 0xb0, 0x8a, 0x56, 0x64, 0x4f, 0x8d, 0xb0, 0x02},
++	{0x60, 0x6e, 0x57, 0xee, 0xec, 0x18, 0xa8, 0x11, 0xb5, 0xf8, 0x02},
++	{0x5a, 0x14, 0x68, 0xe9, 0x1d, 0x06, 0xb9, 0x5f, 0x68, 0xdc, 0x03},
++};
++
++static unsigned char cx2072x_drc_array[MAX_DRC_REGS] = {
++	0x65, 0x55, 0x3C, 0x01, 0x05, 0x39, 0x76, 0x1A, 0x00
++};
++
++/*#define CXDBG_REG_DUMP*/
++#ifdef CXDBG_REG_DUMP
++
++#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S24_LE \
++	| SNDRV_PCM_FMTBIT_S16_LE)
++#define BITS_PER_SLOT 8
++
++#define _REG(_name_, _size_, _access_, _volatile_) { \
++	 #_name_, _name_, _size_ | _access_ | _volatile_}
++
++struct CX2072X_REG_DEF {
++	const char  *name;
++	unsigned int addr;
++	unsigned int attr;
++};
++
++#define WO 0x0100
++#define RO 0x0200
++#define RW 0x0300
++#define VO 0x8000
++#define NV 0x0000
++#define REGISTER_SIZE_MASK 0x000F
++#define REGISTER_ASSCESS_MASK 0x0F00
++#define REGISTER_VOLATILE_MASK 0x8000
++#define UNAVAILABLE 0
++
++
++static const struct CX2072X_REG_DEF cx2072x_regs[] = {
++	_REG(CX2072X_VENDOR_ID,                      4, RO, VO),
++	_REG(CX2072X_REVISION_ID,                    4, RO, VO),
++	_REG(CX2072X_CURRENT_BCLK_FREQUENCY,         4, RO, VO),
++	_REG(CX2072X_AFG_POWER_STATE,                1, RW, NV),
++	_REG(CX2072X_UM_RESPONSE,                    1, RW, NV),
++	_REG(CX2072X_GPIO_DATA,                      1, RW, NV),
++	_REG(CX2072X_GPIO_ENABLE,                    1, RW, NV),
++	_REG(CX2072X_GPIO_DIRECTION,                 1, RW, NV),
++	_REG(CX2072X_GPIO_WAKE,                      1, RW, NV),
++	_REG(CX2072X_GPIO_UM_ENABLE,                 1, RW, NV),
++	_REG(CX2072X_GPIO_STICKY_MASK,               1, RW, NV),
++	_REG(CX2072X_AFG_FUNCTION_RESET,             1, WO, NV),
++	_REG(CX2072X_DAC1_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_DAC1_AMP_GAIN_RIGHT,            1, RW, NV),
++	_REG(CX2072X_DAC1_AMP_GAIN_LEFT,             1, RW, NV),
++	_REG(CX2072X_DAC1_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_DAC1_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_DAC1_EAPD_ENABLE,               1, RW, NV),
++	_REG(CX2072X_DAC2_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_DAC2_AMP_GAIN_RIGHT,            1, RW, NV),
++	_REG(CX2072X_DAC2_AMP_GAIN_LEFT,             1, RW, NV),
++	_REG(CX2072X_DAC2_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_DAC2_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_ADC1_CONVERTER_FORMAT,          2, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_0,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_0,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_1,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_1,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_2,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_2,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_3,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_3,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_4,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_4,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_5,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_5,           1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_RIGHT_6,          1, RW, NV),
++	_REG(CX2072X_ADC1_AMP_GAIN_LEFT_6,           1, RW, NV),
++	_REG(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 1, RW, NV),
++	_REG(CX2072X_ADC1_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_ADC1_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_ADC2_CONVERTER_FORMAT,          2, WO, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_0,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_0,           1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_1,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_1,           1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_RIGHT_2,          1, RW, NV),
++	_REG(CX2072X_ADC2_AMP_GAIN_LEFT_2,           1, RW, NV),
++	_REG(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 1, RW, NV),
++	_REG(CX2072X_ADC2_POWER_STATE,               1, RW, NV),
++	_REG(CX2072X_ADC2_CONVERTER_STREAM_CHANNEL,  1, RW, NV),
++	_REG(CX2072X_PORTA_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTA_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTA_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTA_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTA_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTA_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTB_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTB_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTB_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTB_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTB_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTC_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTC_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTC_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTC_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTD_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTD_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTD_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTD_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTD_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTD_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTE_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTE_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTE_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTE_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTE_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTE_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTE_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTE_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTF_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTF_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTF_UNSOLICITED_RESPONSE,     1, RW, NV),
++	_REG(CX2072X_PORTF_PIN_SENSE,                4, RO, VO),
++	_REG(CX2072X_PORTF_GAIN_RIGHT,               1, RW, NV),
++	_REG(CX2072X_PORTF_GAIN_LEFT,                1, RW, NV),
++	_REG(CX2072X_PORTG_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTG_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTG_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTG_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_PORTM_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_PORTM_PIN_CTRL,                 1, RW, NV),
++	_REG(CX2072X_PORTM_CONNECTION_SELECT_CTRL,   1, RW, NV),
++	_REG(CX2072X_PORTM_EAPD_BTL,                 1, RW, NV),
++	_REG(CX2072X_MIXER_POWER_STATE,              1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_RIGHT_0,             1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_LEFT_0,              1, WO, NV),
++	_REG(CX2072X_MIXER_GAIN_RIGHT_1,             1, RW, NV),
++	_REG(CX2072X_MIXER_GAIN_LEFT_1,              1, RW, NV),
++	_REG(CX2072X_EQ_ENABLE_BYPASS,               2, RW, NV),
++	_REG(CX2072X_EQ_B0_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_B1_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_B2_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_A1_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_A2_COEFF,                    2, WO, VO),
++	_REG(CX2072X_EQ_G_COEFF,                     1, WO, VO),
++	_REG(CX2072X_EQ_BAND,                        1, WO, VO),
++	_REG(CX2072X_SPKR_DRC_ENABLE_STEP,           1, RW, NV),
++	_REG(CX2072X_SPKR_DRC_CONTROL,               4, RW, NV),
++	_REG(CX2072X_SPKR_DRC_TEST,                  4, RW, NV),
++	_REG(CX2072X_DIGITAL_BIOS_TEST0,             4, RW, NV),
++	_REG(CX2072X_DIGITAL_BIOS_TEST2,             4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL1,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL2,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL3,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL4,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL5,                4, RW, NV),
++	_REG(CX2072X_I2SPCM_CONTROL6,                4, RW, NV),
++	_REG(CX2072X_UM_INTERRUPT_CRTL_E,            4, RW, NV),
++	_REG(CX2072X_CODEC_TEST20,                   2, RW, NV),
++	_REG(CX2072X_CODEC_TEST26,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST4,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST5,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST6,                   2, WO, NV),
++	_REG(CX2072X_ANALOG_TEST7,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST8,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST9,                   2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST10,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST11,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST12,                  2, RW, NV),
++	_REG(CX2072X_ANALOG_TEST13,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST0,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST1,                  2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST11,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST12,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST15,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST16,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST17,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST18,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST19,                 2, RW, NV),
++	_REG(CX2072X_DIGITAL_TEST20,                 2, RW, NV),
++};
++#endif
++
++/* codec private data */
++struct cx2072x_priv {
++	struct regmap *regmap;
++	unsigned int mclk;
++	struct device *dev;
++	struct snd_soc_codec *codec;
++	struct snd_soc_dai_driver *dai_drv;
++	int is_biason;
++	struct snd_soc_jack *jack;
++	bool jack_detecting;
++	bool jack_mic;
++	int jack_mode;
++	int jack_flips;
++	unsigned int jack_state;
++	int audsmt_enable;
++	struct mutex lock;
++	unsigned int bclk_ratio;
++
++#ifdef ENABLE_MIC_POP_WA
++	struct delayed_work mic_pop_workq;
++#endif
++
++	bool plbk_dsp_en;
++	bool plbk_dsp_changed;
++	bool plbk_dsp_init;
++	bool pll_changed;
++	bool i2spcm_changed;
++	int sample_size; /*used for non-PCM mode*/
++	int frame_size; /*used for non-PCM mode*/
++	int sample_rate;
++	unsigned int dai_fmt;
++	int tdm_rx_mask;
++	int tdm_tx_mask;
++	int tdm_slot_width;
++	int tdm_slots;
++	u32 rev_id;
++};
++
++/*
++* DAC/ADC Volume
++*
++* max : 74 : 0 dB
++*       ( in 1 dB  step )
++* min : 0 : -74 dB
++*/
++static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0);
++static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0);
++static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0);
++
++#define get_cx2072x_priv(_codec_) \
++	((struct cx2072x_priv *) snd_soc_codec_get_drvdata(_codec_))
++
++/* Lookup table for PRE_DIV*/
++static struct {
++	unsigned int mclk;
++	unsigned int div;
++} MCLK_PRE_DIV[] = {
++	{ 6144000, 1 },
++	{ 12288000, 2 },
++	{ 19200000, 3 },
++	{ 26000000, 4 },
++	{ 28224000, 5 },
++	{ 36864000, 6 },
++	{ 36864000, 7 },/* Don't use div 7*/
++	{ 48000000, 8 },
++	{ 49152000, 8 },
++};
++
++/*
++ * cx2072x register cache.
++ */
++static const struct reg_default cx2072x_reg_defaults[] = {
++	{ 0x0414, 0x00000003 },	/*2072X_AFG_POWER_STATE */
++	{ 0x0420, 0x00000000 },	/*2072X_UM_RESPONSE */
++	{ 0x0454, 0x00000000 },	/*2072X_GPIO_DATA */
++	{ 0x0458, 0x00000000 },	/*2072X_GPIO_ENABLE */
++	{ 0x045c, 0x00000000 },	/*2072X_GPIO_DIRECTION */
++	{ 0x0460, 0x00000000 },	/*2072X_GPIO_WAKE */
++	{ 0x0464, 0x00000000 },	/*2072X_GPIO_UM_ENABLE */
++	{ 0x0468, 0x00000000 },	/*2072X_GPIO_STICKY_MASK */
++	{ 0x43c8, 0x00000031 },	/*2072X_DAC1_CONVERTER_FORMAT */
++	{ 0x41c0, 0x0000004a },	/*2072X_DAC1_AMP_GAIN_RIGHT */
++	{ 0x41e0, 0x0000004a },	/*2072X_DAC1_AMP_GAIN_LEFT */
++	{ 0x4014, 0x00000433 },	/*2072X_DAC1_POWER_STATE */
++	{ 0x4018, 0x00000000 },	/*2072X_DAC1_CONVERTER_STREAM_CHANNEL */
++	{ 0x4030, 0x00000000 },	/*2072X_DAC1_EAPD_ENABLE */
++	{ 0x47c8, 0x00000031 },	/*2072X_DAC2_CONVERTER_FORMAT */
++	{ 0x45c0, 0x0000004a },	/*2072X_DAC2_AMP_GAIN_RIGHT */
++	{ 0x45e0, 0x0000004a },	/*2072X_DAC2_AMP_GAIN_LEFT */
++	{ 0x4414, 0x00000433 },	/*2072X_DAC2_POWER_STATE */
++	{ 0x4418, 0x00000000 },	/*2072X_DAC2_CONVERTER_STREAM_CHANNEL */
++	{ 0x4fc8, 0x00000031 },	/*2072X_ADC1_CONVERTER_FORMAT */
++	{ 0x4d80, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_0 */
++	{ 0x4da0, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_0  */
++	{ 0x4d84, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_1 */
++	{ 0x4da4, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_1 */
++	{ 0x4d88, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_2  */
++	{ 0x4da8, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_2  */
++	{ 0x4d8c, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_3  */
++	{ 0x4dac, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_3  */
++	{ 0x4d90, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_4  */
++	{ 0x4db0, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_4  */
++	{ 0x4d94, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_5  */
++	{ 0x4db4, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_5 */
++	{ 0x4d98, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_RIGHT_6 */
++	{ 0x4db8, 0x0000004a },	/*2072X_ADC1_AMP_GAIN_LEFT_6 */
++	{ 0x4c04, 0x00000000 },	/*2072X_ADC1_CONNECTION_SELECT_CONTROL */
++	{ 0x4c14, 0x00000433 },	/*2072X_ADC1_POWER_STATE */
++	{ 0x4c18, 0x00000000 },	/*2072X_ADC1_CONVERTER_STREAM_CHANNEL */
++	{ 0x53c8, 0x00000031 },	/*2072X_ADC2_CONVERTER_FORMAT */
++	{ 0x5180, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_0 */
++	{ 0x51a0, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_0 */
++	{ 0x5184, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_1 */
++	{ 0x51a4, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_1 */
++	{ 0x5188, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_RIGHT_2 */
++	{ 0x51a8, 0x0000004a },	/*2072X_ADC2_AMP_GAIN_LEFT_2 */
++	{ 0x5004, 0x00000000 },	/*2072X_ADC2_CONNECTION_SELECT_CONTROL */
++	{ 0x5014, 0x00000433 },	/*2072X_ADC2_POWER_STATE */
++	{ 0x5018, 0x00000000 },	/*2072X_ADC2_CONVERTER_STREAM_CHANNEL */
++	{ 0x5804, 0x00000000 },	/*2072X_PORTA_CONNECTION_SELECT_CTRL */
++	{ 0x5814, 0x00000433 },	/*2072X_PORTA_POWER_STATE */
++	{ 0x581c, 0x000000c0 },	/*2072X_PORTA_PIN_CTRL */
++	{ 0x5820, 0x00000000 },	/*2072X_PORTA_UNSOLICITED_RESPONSE */
++	{ 0x5824, 0x00000000 },	/*2072X_PORTA_PIN_SENSE */
++	{ 0x5830, 0x00000002 },	/*2072X_PORTA_EAPD_BTL */
++	{ 0x6014, 0x00000433 },	/*2072X_PORTB_POWER_STATE */
++	{ 0x601c, 0x00000000 },	/*2072X_PORTB_PIN_CTRL */
++	{ 0x6020, 0x00000000 },	/*2072X_PORTB_UNSOLICITED_RESPONSE */
++	{ 0x6024, 0x00000000 },	/*2072X_PORTB_PIN_SENSE */
++	{ 0x6030, 0x00000002 },	/*2072X_PORTB_EAPD_BTL */
++	{ 0x6180, 0x00000000 },	/*2072X_PORTB_GAIN_RIGHT */
++	{ 0x61a0, 0x00000000 },	/*2072X_PORTB_GAIN_LEFT */
++	{ 0x6814, 0x00000433 },	/*2072X_PORTC_POWER_STATE */
++	{ 0x681c, 0x00000000 },	/*2072X_PORTC_PIN_CTRL */
++	{ 0x6980, 0x00000000 },	/*2072X_PORTC_GAIN_RIGHT */
++	{ 0x69a0, 0x00000000 },	/*2072X_PORTC_GAIN_LEFT */
++	{ 0x6414, 0x00000433 },	/*2072X_PORTD_POWER_STATE */
++	{ 0x641c, 0x00000020 },	/*2072X_PORTD_PIN_CTRL */
++	{ 0x6420, 0x00000000 },	/*2072X_PORTD_UNSOLICITED_RESPONSE */
++	{ 0x6424, 0x00000000 },	/*2072X_PORTD_PIN_SENSE */
++	{ 0x6580, 0x00000000 },	/*2072X_PORTD_GAIN_RIGHT */
++	{ 0x65a0, 0x00000000 },	/*2072X_PORTD_GAIN_LEFT */
++	{ 0x7404, 0x00000000 },	/*2072X_PORTE_CONNECTION_SELECT_CTRL */
++	{ 0x7414, 0x00000433 },	/*2072X_PORTE_POWER_STATE */
++	{ 0x741c, 0x00000040 },	/*2072X_PORTE_PIN_CTRL */
++	{ 0x7420, 0x00000000 },	/*2072X_PORTE_UNSOLICITED_RESPONSE */
++	{ 0x7424, 0x00000000 },	/*2072X_PORTE_PIN_SENSE */
++	{ 0x7430, 0x00000002 },	/*2072X_PORTE_EAPD_BTL */
++	{ 0x7580, 0x00000000 },	/*2072X_PORTE_GAIN_RIGHT */
++	{ 0x75a0, 0x00000000 },	/*2072X_PORTE_GAIN_LEFT */
++	{ 0x7814, 0x00000433 },	/*2072X_PORTF_POWER_STATE */
++	{ 0x781c, 0x00000000 },	/*2072X_PORTF_PIN_CTRL */
++	{ 0x7820, 0x00000000 },	/*2072X_PORTF_UNSOLICITED_RESPONSE */
++	{ 0x7824, 0x00000000 },	/*2072X_PORTF_PIN_SENSE */
++	{ 0x7980, 0x00000000 },	/*2072X_PORTF_GAIN_RIGHT */
++	{ 0x79a0, 0x00000000 },	/*2072X_PORTF_GAIN_LEFT */
++	{ 0x5c14, 0x00000433 },	/*2072X_PORTG_POWER_STATE */
++	{ 0x5c1c, 0x00000040 },	/*2072X_PORTG_PIN_CTRL */
++	{ 0x5c04, 0x00000000 },	/*2072X_PORTG_CONNECTION_SELECT_CTRL */
++	{ 0x5c30, 0x00000002 },	/*2072X_PORTG_EAPD_BTL */
++	{ 0x8814, 0x00000433 },	/*2072X_PORTM_POWER_STATE */
++	{ 0x881c, 0x00000000 },	/*2072X_PORTM_PIN_CTRL */
++	{ 0x8804, 0x00000000 },	/*2072X_PORTM_CONNECTION_SELECT_CTRL */
++	{ 0x8830, 0x00000002 },	/*2072X_PORTM_EAPD_BTL */
++	{ 0x5414, 0x00000433 },	/*2072X_MIXER_POWER_STATE */
++	{ 0x5580, 0x0000004a },	/*2072X_MIXER_GAIN_RIGHT_0 */
++	{ 0x55a0, 0x0000004a },	/*2072X_MIXER_GAIN_LEFT_0 */
++	{ 0x5584, 0x0000004a },	/*2072X_MIXER_GAIN_RIGHT_1 */
++	{ 0x55a4, 0x0000004a },	/*2072X_MIXER_GAIN_LEFT_1 */
++	{ 0x6d00, 0x0000720c },	/*2072X_EQ_ENABLE_BYPASS */
++	{ 0x6d10, 0x040065a4 },	/*2072X_SPKR_DRC_ENABLE_STEP */
++	{ 0x6d14, 0x007b0024 },	/*2072X_SPKR_DRC_CONTROL */
++	{ 0X6D18, 0x00000000 },	/*2072X_SPKR_DRC_TEST */
++	{ 0x6d80, 0x001f008a },	/*2072X_DIGITAL_BIOS_TEST0 */
++	{ 0x6d84, 0x00990026 },	/*2072X_DIGITAL_BIOS_TEST2 */
++	{ 0x6e00, 0x00010001 },	/*2072X_I2SPCM_CONTROL1 */
++	{ 0x6e04, 0x00000000 },	/*2072X_I2SPCM_CONTROL2 */
++	{ 0x6e08, 0x00000000 },	/*2072X_I2SPCM_CONTROL3 */
++	{ 0x6e0c, 0x00000000 },	/*2072X_I2SPCM_CONTROL4 */
++	{ 0x6e10, 0x00000000 },	/*2072X_I2SPCM_CONTROL5 */
++	{ 0x6e18, 0x00000000 },	/*2072X_I2SPCM_CONTROL6 */
++	{ 0x6e14, 0x00000000 },	/*2072X_UM_INTERRUPT_CRTL_E */
++	{ 0x7310, 0x00000600 },	/*2072X_CODEC_TEST20 */
++	{ 0x7328, 0x00000208 },	/*2072X_CODEC_TEST26 */
++	{ 0x7190, 0x00000000 },	/*2072X_ANALOG_TEST4 */
++	{ 0x7194, 0x00000000 },	/*2072X_ANALOG_TEST5 */
++	{ 0x7198, 0x0000059a },	/*2072X_ANALOG_TEST6 */
++	{ 0x719c, 0x000000a7 },	/*2072X_ANALOG_TEST7 */
++	{ 0x71a0, 0x00000017 },	/*2072X_ANALOG_TEST8 */
++	{ 0x71a4, 0x00000000 },	/*2072X_ANALOG_TEST9 */
++	{ 0x71a8, 0x00000285 },	/*2072X_ANALOG_TEST10 */
++	{ 0x71ac, 0x00000000 },	/*2072X_ANALOG_TEST11 */
++	{ 0x71b0, 0x00000000 },	/*2072X_ANALOG_TEST12 */
++	{ 0x71b4, 0x00000000 },	/*2072X_ANALOG_TEST13 */
++	{ 0x7204, 0x00000242 },	/*2072X_DIGITAL_TEST1 */
++	{ 0x7224, 0x00000000 },	/*2072X_DIGITAL_TEST11 */
++	{ 0x7230, 0x00000084 },	/*2072X_DIGITAL_TEST12 */
++	{ 0x723c, 0x00000077 },	/*2072X_DIGITAL_TEST15 */
++	{ 0x7080, 0x00000021 },	/*2072X_DIGITAL_TEST16 */
++	{ 0x7084, 0x00000018 },	/*2072X_DIGITAL_TEST17 */
++	{ 0x7088, 0x00000024 },	/*2072X_DIGITAL_TEST18 */
++	{ 0x708c, 0x00000001 },	/*2072X_DIGITAL_TEST19 */
++	{ 0x7090, 0x00000002 },	/*2072X_DIGITAL_TEST20 */
++};
++
++
++/*
++ * cx2072x patch.
++ */
++static const struct reg_sequence cx2072x_patch[] = {
++	{ 0x71A4, 0x080 }, /* DC offset Calibration        */
++	{ 0x71a8, 0x287 }, /* Set max spk power to 1.5 W   */
++	{ 0x7328, 0xa8c }, /* Set average spk power to 1.5W*/
++	{ 0x7310, 0xf01 }, /*                             */
++	{ 0x7328, 0xa8f }, /*                              */
++	{ 0x7124, 0x001 }, /* Enable 30 Hz High pass filter*/
++	{ 0x718c, 0x300 }, /* Disable PCBEEP pad           */
++	{ 0x731c, 0x100 }, /* Disable SnM mode             */
++	{ 0x641c, 0x020 }, /* Enable PortD input           */
++	{ 0x0458, 0x040 }, /* Enable GPIO7 pin for button  */
++	{ 0x0464, 0x040 }, /* Enable UM for GPIO7          */
++	{ 0x0420, 0x080 }, /* Enable button response       */
++	{ 0x7230, 0x0c4 }, /* Enable headset button        */
++	{ 0x7200, 0x415 }, /* Power down class-d during idle*/
++};
++
++
++/* return register size */
++static unsigned int cx2072x_register_size(struct device *dev,
++	unsigned int reg)
++{
++	switch (reg) {
++	case CX2072X_VENDOR_ID:
++	case CX2072X_REVISION_ID:
++	case CX2072X_PORTA_PIN_SENSE:
++	case CX2072X_PORTB_PIN_SENSE:
++	case CX2072X_PORTD_PIN_SENSE:
++	case CX2072X_PORTE_PIN_SENSE:
++	case CX2072X_PORTF_PIN_SENSE:
++	case CX2072X_I2SPCM_CONTROL1:
++	case CX2072X_I2SPCM_CONTROL2:
++	case CX2072X_I2SPCM_CONTROL3:
++	case CX2072X_I2SPCM_CONTROL4:
++	case CX2072X_I2SPCM_CONTROL5:
++	case CX2072X_I2SPCM_CONTROL6:
++	case CX2072X_UM_INTERRUPT_CRTL_E:
++	case CX2072X_EQ_G_COEFF:
++		/*case CX2072X_SPKR_DRC_ENABLE_STEP:*/
++	case CX2072X_SPKR_DRC_CONTROL:
++	case CX2072X_SPKR_DRC_TEST:
++	case CX2072X_DIGITAL_BIOS_TEST0:
++	case CX2072X_DIGITAL_BIOS_TEST2:
++		return 4;
++	case CX2072X_EQ_ENABLE_BYPASS:
++	case CX2072X_EQ_B0_COEFF:
++	case CX2072X_EQ_B1_COEFF:
++	case CX2072X_EQ_B2_COEFF:
++	case CX2072X_EQ_A1_COEFF:
++	case CX2072X_EQ_A2_COEFF:
++	case CX2072X_DAC1_CONVERTER_FORMAT:
++	case CX2072X_DAC2_CONVERTER_FORMAT:
++	case CX2072X_ADC1_CONVERTER_FORMAT:
++	case CX2072X_ADC2_CONVERTER_FORMAT:
++	case CX2072X_CODEC_TEST20:
++	case CX2072X_CODEC_TEST26:
++	case CX2072X_ANALOG_TEST3:
++	case CX2072X_ANALOG_TEST4:
++	case CX2072X_ANALOG_TEST5:
++	case CX2072X_ANALOG_TEST6:
++	case CX2072X_ANALOG_TEST7:
++	case CX2072X_ANALOG_TEST8:
++	case CX2072X_ANALOG_TEST9:
++	case CX2072X_ANALOG_TEST10:
++	case CX2072X_ANALOG_TEST11:
++	case CX2072X_ANALOG_TEST12:
++	case CX2072X_ANALOG_TEST13:
++	case CX2072X_DIGITAL_TEST0:
++	case CX2072X_DIGITAL_TEST1:
++	case CX2072X_DIGITAL_TEST11:
++	case CX2072X_DIGITAL_TEST12:
++	case CX2072X_DIGITAL_TEST15:
++	case CX2072X_DIGITAL_TEST16:
++	case CX2072X_DIGITAL_TEST17:
++	case CX2072X_DIGITAL_TEST18:
++	case CX2072X_DIGITAL_TEST19:
++	case CX2072X_DIGITAL_TEST20:
++		return 2;
++	default:
++		return 1;
++	}
++
++}
++
++#ifdef CXDBG_REG_DUMP
++static const char *cx2072x_get_reg_name(struct device *dev, unsigned int reg)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(cx2072x_regs); i++)
++		if (cx2072x_regs[i].addr == reg)
++			return cx2072x_regs[i].name;
++
++	dev_err(dev, "Unkown reg %08x\n", reg);
++
++	return "Unknown reg";
++}
++#endif
++
++static int cx2072x_reg_write(void *context, unsigned int reg,
++			      unsigned int value)
++{
++	struct i2c_client *client = context;
++	unsigned int i;
++	unsigned int size;
++	uint8_t buf[6];
++	int ret;
++	struct device *dev = &client->dev;
++
++	size = cx2072x_register_size(dev, reg);
++	if (size == 0)
++		return -EINVAL;
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev, "I2C write address %40s,%d <= %08x\n",
++		cx2072x_get_reg_name(dev, reg), size, value);
++#endif
++
++	if (reg == CX2072X_UM_INTERRUPT_CRTL_E) {
++		/* workaround to update the MSB byte only */
++		reg += 3;
++		size = 1;
++		value >>= 24;
++	}
++
++	buf[0] = reg >> 8;
++	buf[1] = reg & 0xff;
++
++	for (i = 2; i < (size + 2); ++i) {
++		buf[i] = value;
++		value >>= 8;
++	}
++
++	ret = i2c_master_send(client, buf, size + 2);
++	if (ret == size + 2) {
++		ret =  0;
++	} else if (ret < 0) {
++		dev_err(dev,
++			"I2C write address failed\n");
++	} else {
++		dev_err(dev,
++			"I2C write failed\n");
++		ret =  -EIO;
++	}
++	return ret;
++}
++
++static int cx2072x_reg_bulk_write(struct snd_soc_codec *codec,
++	unsigned int reg, const void *val, size_t val_count)
++{
++	/*fix me*/
++	struct i2c_client *client = to_i2c_client(codec->dev);
++	uint8_t buf[2 + MAC_EQ_COEFF];
++	int ret;
++	struct device *dev = &client->dev;
++
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev, "I2C bulk write address %40s,%zd\n",
++		cx2072x_get_reg_name(dev, reg), val_count);
++#endif
++
++	if (val_count > MAC_EQ_COEFF) {
++		dev_err(dev,
++			"cx2072x_reg_bulk_write failed, writing count = %zd\n",
++			val_count);
++		return -EINVAL;
++	}
++
++	buf[0] = reg >> 8;
++	buf[1] = reg & 0xff;
++
++	memcpy(&buf[2], val, val_count);
++
++	ret = i2c_master_send(client, buf, val_count + 2);
++	if (ret == val_count + 2)
++		return 0;
++	else if (ret < 0) {
++		dev_err(dev,
++			"I2C bulk write address failed\n");
++	} else {
++		dev_err(dev,
++			"I2C bulk write address failed\n");
++		ret = -EIO;
++	}
++	return ret;
++}
++
++
++/* get suggested pre_div valuce from mclk frequency */
++static unsigned int get_div_from_mclk(unsigned int mclk)
++{
++	unsigned int div = 8;
++	int i = 0;
++
++	for (i = 0; i < ARRAY_SIZE(MCLK_PRE_DIV); i++) {
++		if (mclk <= MCLK_PRE_DIV[i].mclk) {
++			div = MCLK_PRE_DIV[i].div;
++			break;
++		}
++	}
++	return div;
++}
++static int cx2072x_reg_read(void *context, unsigned int reg,
++	unsigned int *value)
++{
++	int ret;
++	unsigned int size;
++	uint8_t send_buf[2];
++	unsigned int recv_buf = 0;
++	struct i2c_client *client = context;
++	struct i2c_msg msgs[2];
++	struct device *dev = &client->dev;
++
++	size = cx2072x_register_size(dev, reg);
++	if (size == 0)
++		return -EINVAL;
++
++	send_buf[0] = reg >> 8;
++	send_buf[1] = reg & 0xff;
++
++	msgs[0].addr = client->addr;
++	msgs[0].len = sizeof(send_buf);
++	msgs[0].buf = send_buf;
++	msgs[0].flags = 0;
++
++	msgs[1].addr = client->addr;
++	msgs[1].len = size;
++	msgs[1].buf = (uint8_t *)&recv_buf;
++	msgs[1].flags = I2C_M_RD;
++
++	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++	if (ret < 0) {
++		dev_err(dev,
++			"Failed to register codec: %d\n", ret);
++		return ret;
++	} else if (ret != ARRAY_SIZE(msgs)) {
++		dev_err(dev,
++			"Failed to register codec: %d\n", ret);
++		return -EIO;
++	}
++
++	*value = recv_buf;
++
++#ifdef CXDBG_REG_DUMP
++	dev_dbg(dev,
++		"I2C read address %40s,%d => %08x\n",
++		cx2072x_get_reg_name(dev, reg), size, *value);
++#endif
++	return 0;
++}
++
++static int cx2072x_config_headset_det(struct cx2072x_priv *cx2072x)
++{
++	const int interrupt_gpio_pin = 1;
++
++	dev_dbg(cx2072x->dev,
++		"Configure interrupt pin: %d\n", interrupt_gpio_pin);
++	/*No-sticky input type*/
++	regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f);
++	/*Use GPOI0 as intrrupt output pin*/
++	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
++
++	/* Enables unsolitited message on PortA*/
++	regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x80);
++
++	/* support both nokia and apple headset set. Monitor time = 275 ms*/
++	regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST15, 0x73);
++
++	/* Disable TIP detection*/
++	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST12, 0x300);
++
++	/* Switch MusicD3Live pin to GPIO */
++	regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST1, 0);
++
++	return 0;
++}
++
++static int cx2072x_config_pll(struct cx2072x_priv *cx2072x)
++{
++	struct device *dev = cx2072x->dev;
++	unsigned int pre_div;
++	unsigned int pre_div_val;
++	unsigned int pll_input;
++	unsigned int pll_output;
++	unsigned int int_div;
++	unsigned int frac_div;
++	unsigned int frac_num = 0;
++	unsigned int sample_rate = cx2072x->sample_rate;
++	int pt_sample_per_sync = 2;
++	int pt_clock_per_sample = 96;
++
++	switch (sample_rate) {
++	case 48000:
++	case 32000:
++	case 24000:
++	case 16000:
++		break;
++	case 96000:
++		pt_sample_per_sync = 1;
++		pt_clock_per_sample = 48;
++		break;
++	case 192000:
++		pt_sample_per_sync = 0;
++		pt_clock_per_sample = 24;
++		break;
++	default:
++		dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
++		return -EINVAL;
++	}
++
++
++	/*Configure PLL settings*/
++	pre_div = get_div_from_mclk(cx2072x->mclk);
++	pll_input = cx2072x->mclk / pre_div;
++	pll_output = sample_rate * 3072;
++	int_div = pll_output / pll_input;
++	frac_div = pll_output - (int_div * pll_input);
++
++	if (frac_div) {
++		frac_div *= 1000;
++		frac_div /= pll_input;
++		frac_num = ((4000 + frac_div) * ((1 << 20) - 4));
++		do_div(frac_num, 7);
++		frac_num = (frac_num + 499) / 1000;
++	}
++	pre_div_val = (pre_div - 1) * 2;
++
++	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST4, 0X40
++		| (pre_div_val << 8));
++	if (frac_div == 0) {
++		/*Int mode*/
++		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, 0x100);
++	} else {
++		/*frac mode*/
++		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST6,
++			frac_num & 0xfff);
++		regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7,
++			(unsigned char)(frac_num >> 12));
++	}
++	int_div--;
++	regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8,
++		(unsigned char)int_div & 0xffff);
++
++	/* configure PLL tracking*/
++	if (frac_div == 0) {
++		/* disable PLL tracking*/
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, 0x00);
++	} else {
++		/* configure and enable PLL tracking*/
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
++			(pt_sample_per_sync << 4) & 0xf0);
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST17,
++			pt_clock_per_sample);
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST18,
++			pt_clock_per_sample * 3 / 2);
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST19, 0x01);
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST20, 0x02);
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST16,
++			0X01, 0X01);
++	}
++
++	return 0;
++}
++
++
++static int cx2072x_config_i2spcm(struct cx2072x_priv *cx2072x)
++{
++	struct device *dev = cx2072x->dev;
++
++	int is_i2s = 0;
++	int has_one_bit_delay = 0;
++	int is_right_j = 0;
++	int is_frame_inv = 0;
++	int is_bclk_inv = 0;
++	int pulse_len = 1;
++	int frame_len = cx2072x->frame_size;
++	int sample_size = cx2072x->sample_size;
++	int i2s_right_slot;
++	int i2s_right_pause_interval = 0;
++	int i2s_right_pause_pos;
++	int is_big_endian = 1;
++	const int slots_per_channel = cx2072x->tdm_slot_width / BITS_PER_SLOT;
++	const unsigned fmt = cx2072x->dai_fmt;
++
++	REG_I2SPCM_CTRL_REG1 reg1 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG2 reg2 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG3 reg3 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG4 reg4 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG5 reg5 = { .ulVal = 0 };
++	REG_I2SPCM_CTRL_REG6 reg6 = { .ulVal = 0 };
++	REG_DIGITAL_BIOS_TEST2 regDBT2 = { .ulVal = 0 };
++
++	if (frame_len <= 0) {
++		dev_err(dev, "Incorrect frame len %d\n", frame_len);
++		return -EINVAL;
++	}
++
++	if (sample_size <= 0) {
++		dev_err(dev, "Incorrect sample size %d\n", sample_size);
++		return -EINVAL;
++	}
++
++	/*fix me */
++	regDBT2.ulVal = 0xac;
++
++	/* set master/slave */
++	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++	case SND_SOC_DAIFMT_CBM_CFM:
++		reg2.r.tx_master = 1;
++		reg3.r.rx_master = 1;
++		dev_err(dev, "DAI master mode is not implemented yet.\n");
++		return -EINVAL;
++	case SND_SOC_DAIFMT_CBS_CFS:
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI master mode\n");
++		return -EINVAL;
++	}
++
++	/* set format */
++	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++	case SND_SOC_DAIFMT_I2S:
++		is_i2s = 1;
++		has_one_bit_delay = 1;
++		pulse_len = frame_len / 2;
++		break;
++	case SND_SOC_DAIFMT_RIGHT_J:
++		is_i2s = 1;
++		is_right_j = 1;
++		pulse_len = frame_len / 2;
++		break;
++	case SND_SOC_DAIFMT_LEFT_J:
++		is_i2s = 1;
++		pulse_len = frame_len / 2;
++		break;
++	case SND_SOC_DAIFMT_DSP_A:
++		has_one_bit_delay = 1;
++		break;
++	case SND_SOC_DAIFMT_DSP_B:
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI format\n");
++		return -EINVAL;
++	}
++
++	/* clock inversion */
++	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++	case SND_SOC_DAIFMT_NB_NF:
++		is_frame_inv = is_i2s ? 1 : 0;
++		is_bclk_inv = is_i2s ? 1 : 0;
++		break;
++	case SND_SOC_DAIFMT_IB_IF:
++		is_frame_inv = is_i2s ? 0 : 1;
++		is_bclk_inv = is_i2s ? 0 : 1;
++		break;
++	case SND_SOC_DAIFMT_IB_NF:
++		is_frame_inv = is_i2s ? 1 : 0;
++		is_bclk_inv = is_i2s ? 0 : 1;
++		break;
++	case SND_SOC_DAIFMT_NB_IF:
++		is_frame_inv = is_i2s ? 0 : 1;
++		is_bclk_inv = is_i2s ? 1 : 0;
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI clock inversion\n");
++		return -EINVAL;
++	}
++
++	cx2072x->dai_fmt = fmt;
++
++	reg1.r.rx_data_one_line = 1;
++	reg1.r.tx_data_one_line = 1;
++
++	if (is_i2s) {
++		i2s_right_slot = (frame_len / 2) / BITS_PER_SLOT;
++		i2s_right_pause_interval = (frame_len / 2) % BITS_PER_SLOT;
++		i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT;
++	}
++
++	reg1.r.rx_ws_pol = is_frame_inv;
++	reg1.r.rx_ws_wid = pulse_len - 1;
++
++	reg1.r.rx_frm_len = frame_len / BITS_PER_SLOT - 1;
++	reg1.r.rx_sa_size = (sample_size / BITS_PER_SLOT) - 1;
++
++	reg1.r.tx_ws_pol = reg1.r.rx_ws_pol;
++	reg1.r.tx_ws_wid = pulse_len - 1;
++	reg1.r.tx_frm_len = reg1.r.rx_frm_len;
++	reg1.r.tx_sa_size = reg1.r.rx_sa_size;
++
++	reg2.r.tx_endian_sel = is_big_endian ? 0 : 1;
++	reg2.r.tx_dstart_dly = has_one_bit_delay;
++
++	reg3.r.rx_endian_sel = is_big_endian ? 0 : 1;
++	reg3.r.rx_dstart_dly = has_one_bit_delay;
++
++	if (is_i2s) {
++		reg2.r.tx_en_ch1 = 1;
++		reg2.r.tx_en_ch2 = 1;
++		reg2.r.tx_slot_1 = 0;
++		reg2.r.tx_slot_2 = i2s_right_slot;
++		reg3.r.rx_en_ch1 = 1;
++		reg3.r.rx_en_ch2 = 1;
++		reg3.r.rx_slot_1 = 0;
++		reg3.r.rx_slot_2 = i2s_right_slot;
++		reg6.r.rx_pause_start_pos = i2s_right_pause_pos;
++		reg6.r.rx_pause_cycles = i2s_right_pause_interval;
++		reg6.r.tx_pause_start_pos = i2s_right_pause_pos;
++		reg6.r.tx_pause_cycles = i2s_right_pause_interval;
++	} else {
++		reg2.r.tx_en_ch1 = cx2072x->tdm_tx_mask & 0x01 ? 1 : 0;
++		reg2.r.tx_en_ch2 = cx2072x->tdm_tx_mask & 0x02 ? 1 : 0;
++		reg2.r.tx_en_ch3 = cx2072x->tdm_tx_mask & 0x04 ? 1 : 0;
++		reg2.r.tx_en_ch4 = cx2072x->tdm_tx_mask & 0x08 ? 1 : 0;
++		reg2.r.tx_slot_1 = 0;
++		reg2.r.tx_slot_2 = slots_per_channel * 1;
++		reg2.r.tx_slot_3 = slots_per_channel * 2;
++		reg2.r.tx_slot_4 = slots_per_channel * 3;
++
++		reg3.r.rx_en_ch1 = cx2072x->tdm_rx_mask & 0x01 ? 1 : 0;
++		reg3.r.rx_en_ch2 = cx2072x->tdm_rx_mask & 0x02 ? 1 : 0;
++		reg3.r.rx_en_ch3 = cx2072x->tdm_rx_mask & 0x04 ? 1 : 0;
++		reg3.r.rx_en_ch4 = cx2072x->tdm_rx_mask & 0x08 ? 1 : 0;
++		reg3.r.rx_slot_1 = 0;
++		reg3.r.rx_slot_2 = slots_per_channel * 1;
++		reg3.r.rx_slot_3 = slots_per_channel * 2;
++		reg3.r.rx_slot_4 = slots_per_channel * 3;
++	}
++	regDBT2.r.i2s_bclk_invert = is_bclk_inv;
++
++	reg1.r.rx_data_one_line = 1;
++	reg1.r.tx_data_one_line = 1;
++
++#ifdef ENABLE_MIC_POP_WA
++	/*Mute I2S TX*/
++	reg4.ulVal |= 0x2;
++#endif
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, reg2.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, reg3.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulVal);
++	regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulVal);
++	/*enable bclk and EAPD input*/
++	if (cx2072x->rev_id == CX2072X_REV_A2)
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			0x84, 0xFF);
++	else
++		regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			regDBT2.ulVal);
++
++	return 0;
++}
++
++
++static void cx2072x_dsp_init(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int i, j, band;
++	unsigned char *pCoef = &cx2072x_eq_coeff_array[0][0];
++
++	regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS, 0x6e0f);
++
++	for (i = 0; i < MAX_EQ_BAND; i++) {
++		for (j = 0; j < 2; j++) {
++			cx2072x_reg_bulk_write(codec, CX2072X_EQ_B0_COEFF,
++				pCoef + (MAC_EQ_COEFF*i), MAC_EQ_COEFF);
++			band = i + (j << 3) + (1 << 6);
++			regmap_write(cx2072x->regmap, CX2072X_EQ_BAND, band);
++			mdelay(5);
++		}
++	}
++
++	cx2072x_reg_bulk_write(codec, CX2072X_SPKR_DRC_ENABLE_STEP,
++		cx2072x_drc_array, MAX_DRC_REGS);
++}
++static void cx2072x_update_dsp(struct snd_soc_codec *codec)
++{
++	unsigned int afg_reg;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	regmap_read(cx2072x->regmap, CX2072X_AFG_POWER_STATE, &afg_reg);
++
++	if (!cx2072x->plbk_dsp_changed) {
++		/*nothing change*/
++		return;
++	}
++
++	if ((afg_reg & 0xf) != 0) {
++		/*skip since device is on D3 mode*/
++		return;
++	}
++
++	if (cx2072x->plbk_dsp_en && !cx2072x->plbk_dsp_init) {
++		cx2072x_dsp_init(codec);
++		cx2072x->plbk_dsp_init = true;
++	}
++
++	if (cx2072x->plbk_dsp_en) {
++		regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
++			0x6203);
++		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
++			0x65);
++	} else {
++		/*By pass DRC and EQ.*/
++		regmap_write(cx2072x->regmap, CX2072X_EQ_ENABLE_BYPASS,
++			0x620c);
++		regmap_write(cx2072x->regmap, CX2072X_SPKR_DRC_ENABLE_STEP,
++			0xa4);
++	}
++	cx2072x->plbk_dsp_changed = false;
++}
++
++
++
++static int afg_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	switch (event) {
++	case SND_SOC_DAPM_POST_PMU:
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
++			0x00, 0x10);
++		break;
++
++	case SND_SOC_DAPM_PRE_PMD:
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
++			0x10, 0x10);
++		break;
++	}
++
++	return 0;
++}
++
++#ifdef ENABLE_MIC_POP_WA
++/*
++ * This work will be called at ADC widget power on time.
++ * to reduce initial mic pop noise caused by hardware
++ */
++
++static void cx2072x_anit_mic_pop_work(struct work_struct *work)
++{
++	struct snd_soc_dapm_context *dapm =
++		container_of(work, struct snd_soc_dapm_context,
++		delayed_work.work);
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	dev_dbg(cx2072x->dev, "Unmute I2S TX\n");
++	/*Unmute I2S TX*/
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4,
++		0x2, 0x0);
++}
++#endif
++
++static int adc1_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++#ifdef ENABLE_MIC_POP_WA
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	switch (event) {
++	case SND_SOC_DAPM_POST_PMU:
++		/* Umute I2S TX after 300 ms to get around the mic
++		 * pop noise issue.
++		 */
++		schedule_delayed_work(&codec->dapm.delayed_work,
++			msecs_to_jiffies(300));
++		break;
++
++	case SND_SOC_DAPM_POST_PMD:
++		/*Mute TX I2S*/
++
++		regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4,
++			0x2, 0x2);
++		break;
++	}
++#endif
++	return 0;
++}
++
++
++static int portg_power_ev(struct snd_soc_dapm_widget *w,
++	struct snd_kcontrol *kcontrol, int event)
++{
++	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
++
++	switch (event) {
++	case SND_SOC_DAPM_POST_PMU:
++		cx2072x_update_dsp(codec);
++		break;
++	default:
++		break;
++	}
++	return 0;
++}
++static int cx2072x_plbk_dsp_info(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++	uinfo->count = 1;
++	uinfo->value.integer.min = 0;
++	uinfo->value.integer.max = 1;
++	return 0;
++}
++
++static int cx2072x_plbk_dsp_get(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	ucontrol->value.integer.value[0] = cx2072x->plbk_dsp_en;
++
++	return 0;
++}
++
++static int cx2072x_plbk_dsp_put(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	const bool en_dsp = ucontrol->value.integer.value[0];
++
++	if (ucontrol->value.integer.value[0] > 1)
++		return -EINVAL;
++
++	if (cx2072x->plbk_dsp_en != en_dsp) {
++		cx2072x->plbk_dsp_en = en_dsp;
++		cx2072x->plbk_dsp_changed = true;
++		cx2072x_update_dsp(codec);
++	}
++	return 0;
++}
++
++
++#define CX2072X_PLBK_DSP_SWITCH(xname) {\
++	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
++	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.info = cx2072x_plbk_dsp_info, \
++	.get = cx2072x_plbk_dsp_get, .put = cx2072x_plbk_dsp_put}
++
++static const struct snd_kcontrol_new cx2072x_snd_controls[] = {
++
++	SOC_DOUBLE_R_TLV("PortD Boost", CX2072X_PORTD_GAIN_LEFT,
++	CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv),
++	SOC_DOUBLE_R_TLV("PortC Boost", CX2072X_PORTC_GAIN_LEFT,
++	CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv),
++	SOC_DOUBLE_R_TLV("PortB Boost", CX2072X_PORTB_GAIN_LEFT,
++	CX2072X_PORTB_GAIN_RIGHT, 0, 3, 0, boost_tlv),
++
++	SOC_DOUBLE_R_TLV("PortD ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_1,
++	CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0, 0x4a, 0, adc_tlv),
++	SOC_DOUBLE_R_TLV("PortC ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_2,
++	CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0, 0x4a, 0, adc_tlv),
++	SOC_DOUBLE_R_TLV("PortB ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_0,
++	CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0, 0x4a, 0, adc_tlv),
++
++	SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT,
++	CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
++
++	SOC_DOUBLE_R("DAC1 Mute", CX2072X_DAC1_AMP_GAIN_LEFT,
++		CX2072X_DAC1_AMP_GAIN_RIGHT, 7,  1, 0),
++
++	SOC_DOUBLE_R_TLV("DAC2 Volume", CX2072X_DAC2_AMP_GAIN_LEFT,
++	CX2072X_DAC2_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv),
++
++	CX2072X_PLBK_DSP_SWITCH("Playback DSP Switch"),
++};
++
++/**
++* cx2072x_hs_jack_report: Report jack notification to upper layer
++* @codec : pointer variable to codec having information related to codec
++* @jack : Pointer variable to snd_soc_jack having information of codec
++* and pin number$
++* @report : Provides informaton of whether it is headphone or microphone
++*
++*/
++int cx2072x_hs_jack_report(struct snd_soc_codec *codec)
++{
++	unsigned int jack;
++	unsigned int type = 0;
++	int  state = 0;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++
++	regcache_cache_bypass(cx2072x->regmap, true);
++	cx2072x->jack_state = CX_JACK_NONE;
++	regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack);
++	jack = jack >> 24;
++	regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type);
++	regcache_cache_bypass(cx2072x->regmap, false);
++	if (jack == 0x80) {
++		type = type >> 8;
++
++		if (type & 0x8) {
++			state |= SND_JACK_HEADSET;
++			cx2072x->jack_state = CX_JACK_APPLE_HEADSET;
++			if (type & 0x2)
++				state |= SND_JACK_BTN_0;
++		} else if (type & 0x4) {
++			state |= SND_JACK_HEADPHONE;
++			cx2072x->jack_state = CX_JACK_NOKIE_HEADSET;
++		} else {
++			state |= SND_JACK_HEADPHONE;
++			cx2072x->jack_state = CX_JACK_HEADPHONE;
++		}
++
++	}
++
++
++	/* clear interrupt */
++	regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24);
++
++	dev_err(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n",
++		type, state);
++	return state;
++}
++EXPORT_SYMBOL_GPL(cx2072x_hs_jack_report);
++
++
++
++
++
++static int cx2072x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
++	unsigned int rx_mask, int slots, int slot_width)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = snd_soc_codec_get_drvdata(codec);
++
++	if (slots == 0)
++		goto out;
++
++
++	switch (rx_mask) {
++	case 1 ... 0xf:
++	default:
++		return -EINVAL;
++	}
++
++
++	switch (tx_mask) {
++	case 1 ... 0xf:
++	default:
++		return -EINVAL;
++	}
++
++out:
++	cx2072x->tdm_rx_mask = rx_mask;
++	cx2072x->tdm_tx_mask = tx_mask;
++	cx2072x->tdm_slot_width = slot_width;
++	cx2072x->tdm_slots = slots;
++	return 0;
++}
++
++static int cx2072x_hw_params(struct snd_pcm_substream *substream,
++	struct snd_pcm_hw_params *params,
++	struct snd_soc_dai *dai)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	struct device *dev = codec->dev;
++	const unsigned int sample_rate = params_rate(params);
++	int sample_size, frame_size;
++
++	/* Data sizes if not using TDM */
++	sample_size = params_width(params);
++
++	if (sample_size < 0)
++		return sample_size;
++
++	frame_size = snd_soc_params_to_frame_size(params);
++	if (frame_size < 0)
++		return frame_size;
++
++	if (cx2072x->bclk_ratio)
++		frame_size = cx2072x->bclk_ratio;
++
++	switch (sample_rate) {
++	case 48000:
++	case 32000:
++	case 24000:
++	case 16000:
++	case 96000:
++	case 192000:
++		break;
++	default:
++		dev_err(dev, "Unsupported sample rate %d\n", sample_rate);
++		return -EINVAL;
++	}
++
++	dev_dbg(dev, "Sample size %d bits, frame = %d bits, rate = %d Hz\n",
++		sample_size, frame_size, sample_rate);
++
++	cx2072x->frame_size = frame_size;
++	cx2072x->sample_size = sample_size;
++	cx2072x->sample_rate = sample_rate;
++
++	if (cx2072x->pll_changed) {
++		cx2072x_config_pll(cx2072x);
++		cx2072x->pll_changed = false;
++	}
++	if (cx2072x->i2spcm_changed) {
++		cx2072x_config_i2spcm(cx2072x);
++		cx2072x->i2spcm_changed = false;
++	}
++
++	return 0;
++}
++
++
++static int cx2072x_digital_mute(struct snd_soc_dai *dai, int mute)
++{
++	return 0;
++}
++
++static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai,
++				      unsigned int ratio)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++
++	cx2072x->bclk_ratio = ratio;
++	return 0;
++}
++
++
++static int cx2072x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
++	unsigned int freq, int dir)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	struct device *dev = codec->dev;
++
++	if (freq == 0) {
++		dev_dbg(dev, "MCLK: Switch to internal OSC\n");
++		return 0;
++	}
++	cx2072x->mclk = freq;
++	switch (clk_id) {
++	case CX2072X_MCLK_EXTERNAL_PLL:
++		dev_dbg(dev, "MCLK: Switch to external PLL\n");
++		break;
++	case CX2072X_MCLK_INTERNAL_OSC:
++		dev_err(dev, "Unsupported DAI format\n");
++		break;
++	default:
++		dev_dbg(dev, "the MCLK is not configured\n");
++		break;
++	}
++
++	return 0;
++}
++
++static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai,
++	unsigned int fmt)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	struct device *dev = codec->dev;
++
++	/* set master/slave */
++	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++	case SND_SOC_DAIFMT_CBM_CFM:
++	case SND_SOC_DAIFMT_CBS_CFS:
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI master mode\n");
++		return -EINVAL;
++	}
++
++	/* set format */
++	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++	case SND_SOC_DAIFMT_I2S:
++	case SND_SOC_DAIFMT_RIGHT_J:
++	case SND_SOC_DAIFMT_LEFT_J:
++	case SND_SOC_DAIFMT_DSP_A:
++	case SND_SOC_DAIFMT_DSP_B:
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI format\n");
++		return -EINVAL;
++	}
++
++	/* clock inversion */
++	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++	case SND_SOC_DAIFMT_NB_NF:
++	case SND_SOC_DAIFMT_IB_IF:
++	case SND_SOC_DAIFMT_IB_NF:
++	case SND_SOC_DAIFMT_NB_IF:
++		break;
++	default:
++		dev_err(dev, "Unsupported DAI clock inversion\n");
++		return -EINVAL;
++	}
++
++	cx2072x->dai_fmt = fmt;
++	return 0;
++}
++
++
++static const char * const dac_enum_text[] = {
++	"DAC1 Switch", "DAC2 Switch",
++};
++
++static const struct soc_enum porta_dac_enum =
++SOC_ENUM_SINGLE(CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
++
++static const struct snd_kcontrol_new porta_mux =
++SOC_DAPM_ENUM("PortA Mux", porta_dac_enum);
++
++static const struct soc_enum portg_dac_enum =
++SOC_ENUM_SINGLE(CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
++
++static const struct snd_kcontrol_new portg_mux =
++SOC_DAPM_ENUM("PortG Mux", portg_dac_enum);
++
++static const struct soc_enum porte_dac_enum =
++SOC_ENUM_SINGLE(CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text);
++
++static const struct snd_kcontrol_new porte_mux =
++SOC_DAPM_ENUM("PortE Mux", porte_dac_enum);
++
++static const char * const adc1in_sel_text[] = {
++	"PortB Switch", "PortD Switch", "PortC Switch", "Widget15 Switch",
++	"PortE Switch", "PortF Switch", "PortH Switch"
++};
++
++static const struct soc_enum adc1in_sel_enum =
++SOC_ENUM_SINGLE(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0, 7, adc1in_sel_text);
++
++static const struct snd_kcontrol_new adc1_mux =
++SOC_DAPM_ENUM("ADC1 Mux", adc1in_sel_enum);
++
++#define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask,  won_val, \
++	woff_val, wevent, wflags) \
++	{.id = snd_soc_dapm_supply, .name = wname, .kcontrol_news = NULL, \
++	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
++	.on_val = won_val, .off_val = woff_val, \
++	.subseq = wsubseq, .event = wevent, .event_flags = wflags}
++
++#define CX2072X_DAPM_SWITCH(wname,  wreg, wshift, wmask,  won_val, woff_val, \
++	wevent, wflags) \
++	{.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
++	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
++	.on_val = won_val, .off_val = woff_val, \
++	.event = wevent, .event_flags = wflags}
++
++
++#define CX2072X_DAPM_SWITCH(wname,  wreg, wshift, wmask,  won_val, woff_val, \
++	wevent, wflags) \
++	{.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \
++	.num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \
++	.on_val = won_val, .off_val = woff_val, \
++	.event = wevent, .event_flags = wflags}
++
++#define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \
++				wevent, wflags) \
++	{.id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
++	.reg = wreg, .shift = wshift, .mask = wmask, \
++	.on_val = won_val, .off_val = woff_val, \
++	.event = wevent, .event_flags = wflags}
++
++static const struct snd_soc_dapm_widget cx2072x_dapm_widgets[] = {
++	/*Playback*/
++	SND_SOC_DAPM_AIF_IN("In AIF", "Playback", 0, SND_SOC_NOPM, 0, 0),
++
++	SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC1", CX2072X_DAC1_POWER_STATE,
++	0, 0xFFF, 0x00, 0x03),
++
++	SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC2", CX2072X_DAC2_POWER_STATE,
++	0, 0xFFF, 0x00, 0x03),
++
++	SND_SOC_DAPM_MUX("PortA Mux", SND_SOC_NOPM, 0, 0, &porta_mux),
++	SND_SOC_DAPM_MUX("PortG Mux", SND_SOC_NOPM, 0, 0, &portg_mux),
++	SND_SOC_DAPM_MUX("PortE Mux", SND_SOC_NOPM, 0, 0, &porte_mux),
++
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortA",
++		CX2072X_PORTA_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortG",
++		CX2072X_PORTG_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	CX2072X_DAPM_SWITCH("PortG", CX2072X_PORTG_POWER_STATE, 0, 0xFF,
++		0x00, 0x03, portg_power_ev, SND_SOC_DAPM_POST_PMU),
++
++	CX2072X_DAPM_SUPPLY_S("AFG Power", 0, CX2072X_AFG_POWER_STATE,
++		0, 0xFFF, 0x00, 0x03, afg_power_ev,
++		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
++
++	SND_SOC_DAPM_OUTPUT("PORTA"),
++	SND_SOC_DAPM_OUTPUT("PORTG"),
++	SND_SOC_DAPM_OUTPUT("PORTE"),
++
++	/*Capture*/
++	SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0),
++
++	CX2072X_DAPM_REG_E(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE,
++		0, 0xFF, 0x00, 0x03, adc1_power_ev,
++		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
++
++	SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2", CX2072X_ADC2_POWER_STATE,
++		0, 0xFF, 0x00, 0x03),
++
++	SND_SOC_DAPM_MUX("ADC1 Mux", SND_SOC_NOPM, 0, 0, &adc1_mux),
++
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortB",
++		CX2072X_PORTB_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortC",
++		CX2072X_PORTC_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortD",
++		CX2072X_PORTD_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "PortE",
++		CX2072X_PORTE_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++	SND_SOC_DAPM_REG(snd_soc_dapm_switch, "Widget15",
++		CX2072X_MIXER_POWER_STATE, 0, 0xFFF, 0x00, 0x03),
++
++	SND_SOC_DAPM_INPUT("PORTB"),
++	SND_SOC_DAPM_INPUT("PORTC"),
++	SND_SOC_DAPM_INPUT("PORTD"),
++
++	SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0),
++	SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0),
++	SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0),
++
++};
++
++static const struct snd_soc_dapm_route cx2072x_intercon[] = {
++
++	/* Playback */
++	{"In AIF", NULL, "AFG Power"},
++	{"DAC1", NULL, "In AIF"},
++	{"DAC2", NULL, "In AIF"},
++	{"PortA Mux", "DAC1 Switch", "DAC1"},
++	{"PortA Mux", "DAC2 Switch", "DAC2"},
++	{"PortG Mux", "DAC1 Switch", "DAC1"},
++	{"PortG Mux", "DAC2 Switch", "DAC2"},
++	{"PortE Mux", "DAC1 Switch", "DAC1"},
++	{"PortE Mux", "DAC2 Switch", "DAC2"},
++	{"Widget15", NULL, "DAC1"},
++	{"Widget15", NULL, "DAC2"},
++	{"PortA", NULL, "PortA Mux"},
++	{"PortG", NULL, "PortG Mux"},
++	{"PortE", NULL, "PortE Mux"},
++	{"PORTA", NULL, "PortA"},
++	{"PORTG", NULL, "PortG"},
++	{"PORTE", NULL, "PortE"},
++
++	/* Capture */
++	{"PORTD", NULL, "Headset Bias"},
++	{"PortD", NULL, "PORTD"},
++	{"PortC", NULL, "PORTC"},
++	{"PortB", NULL, "PORTB"},
++	{"ADC1 Mux", "PortD Switch", "PortD"},
++	{"ADC1 Mux", "PortC Switch", "PortC"},
++	{"ADC1 Mux", "PortB Switch", "PortB"},
++	{"ADC1 Mux", "Widget15 Switch", "Widget15"},
++	{"ADC1", NULL, "ADC1 Mux"},
++	{"Out AIF", NULL, "ADC1"},
++	{"Out AIF", NULL, "AFG Power"},
++
++};
++
++
++static void cx2072x_sw_reset(struct cx2072x_priv *cx2072x)
++{
++
++	regmap_write(cx2072x->regmap, CX2072X_AFG_FUNCTION_RESET, 0x01);
++	regmap_write(cx2072x->regmap, CX2072X_AFG_FUNCTION_RESET, 0x01);
++}
++
++static int cx2072x_init(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int ret;
++
++	cx2072x_sw_reset(cx2072x);
++
++	cx2072x->plbk_dsp_changed = true;
++	cx2072x->plbk_dsp_init = false;
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST15,
++		0x00, 0x06);  /* reduce the monitor time.*/
++
++	cx2072x_config_headset_det(cx2072x);
++
++	regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
++		0x20, 0x20);  /*reduce the monitor time*/
++
++	ret = regmap_register_patch(cx2072x->regmap, cx2072x_patch,
++		ARRAY_SIZE(cx2072x_patch));
++	if (ret)
++		return ret;
++
++	/*enable bclk and EAPD input*/
++	if (cx2072x->rev_id == CX2072X_REV_A2)
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2,
++			0x84, 0xFF);
++
++	return 0;
++}
++static int cx2072x_set_bias_level(struct snd_soc_codec *codec,
++enum snd_soc_bias_level level)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	const enum snd_soc_bias_level old_level = 
++		 snd_soc_codec_get_bias_level(codec);
++
++	switch (level) {
++	case SND_SOC_BIAS_ON:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_ON\n");
++		/* Enable Headset Mic Bias */
++		if (cx2072x->is_biason == 0)
++			cx2072x->is_biason = 1;
++		break;
++	case SND_SOC_BIAS_PREPARE:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_PREPARE\n");
++		if (SND_SOC_BIAS_STANDBY == old_level) {
++			dev_dbg(cx2072x->dev, "SND_SOC_BIAS_STANDBY = > SND_SOC_BIAS_PREPARE\n");
++#ifdef INTEL_MCLK_CONTROL
++			/*FIXME: added some delay to make sure the MCLK
++			 * is stable before Codec is on. */
++			mdelay(3);
++			dev_dbg(cx2072x->dev, "Turn on MCLK\n");
++#endif
++		}
++		break;
++	case SND_SOC_BIAS_STANDBY:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_STANDBY\n");
++
++		if (SND_SOC_BIAS_OFF == old_level) {
++			dev_dbg(codec->dev, "Initialize codec\n");
++#ifdef INTEL_MCLK_CONTROL
++			dev_dbg(cx2072x->dev, "Turn on MCLK\n");
++			/*FIXME: added some delay to make sure the MCLK is
++			  stable before Codec is on. */
++			mdelay(3);
++#endif
++			cx2072x_init(codec);
++		}
++	/*power device into standby mode*/
++#ifdef INTEL_MCLK_CONTROL
++		dev_dbg(cx2072x->dev, "Turn off MCLK\n");
++#endif
++		break;
++	case SND_SOC_BIAS_OFF:
++		dev_dbg(cx2072x->dev, "SND_SOC_BIAS_OFF\n");
++		/* Gracefully shutdwon codec*/
++
++		regmap_write(cx2072x->regmap, CX2072X_PORTA_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_PORTB_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_PORTC_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_PORTD_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_PORTG_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_ADC1_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_ADC2_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_DAC1_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_DAC2_POWER_STATE, 3);
++		regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3);
++		regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0,
++			0x10, 0x10);
++		/*Shutdown codec completely*/
++		cx2072x_sw_reset(cx2072x);
++		break;
++	}
++	return 0;
++}
++
++
++static int cx2072x_probe(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	int ret = 0;
++	unsigned int ven_id;
++
++	cx2072x->codec = codec;
++	codec->control_data = cx2072x->regmap;
++
++	dev_dbg(codec->dev, "codec version: 4.4.18\n");
++	regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id);
++	regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &cx2072x->rev_id);
++	dev_dbg(codec->dev, "codec version: %08x,%08x\n",
++		ven_id, cx2072x->rev_id);
++
++#ifdef ENABLE_MIC_POP_WA
++	INIT_DELAYED_WORK(&codec->dapm.delayed_work,
++		cx2072x_anit_mic_pop_work);
++#endif
++
++	/*power on device*/
++	cx2072x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++
++	return ret;
++}
++
++static int cx2072x_remove(struct snd_soc_codec *codec)
++{
++	struct cx2072x_priv *cx2072x = get_cx2072x_priv(codec);
++	/*power off device*/
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int cx2072x_runtime_suspend(struct device *dev)
++{
++	struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
++
++	dev_dbg(cx2072x->codec->dev, "%s----%d\n", __func__, __LINE__);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++
++	return 0;
++}
++
++static int cx2072x_runtime_resume(struct device *dev)
++{
++	struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
++
++	dev_dbg(cx2072x->codec->dev, "%s----%d\n", __func__, __LINE__);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_STANDBY);
++	return 0;
++}
++#else
++#define cx2072x_suspend NULL
++#define cx2072x_resume NULL
++#endif
++
++
++
++
++static bool cx2072x_readable_register(struct device *dev, unsigned int reg)
++{
++	switch (reg) {
++
++	case CX2072X_VENDOR_ID:
++	case CX2072X_REVISION_ID:
++	case CX2072X_CURRENT_BCLK_FREQUENCY:
++	case CX2072X_AFG_POWER_STATE:
++	case CX2072X_UM_RESPONSE:
++	case CX2072X_GPIO_DATA:
++	case CX2072X_GPIO_ENABLE:
++	case CX2072X_GPIO_DIRECTION:
++	case CX2072X_GPIO_WAKE:
++	case CX2072X_GPIO_UM_ENABLE:
++	case CX2072X_GPIO_STICKY_MASK:
++	case CX2072X_DAC1_CONVERTER_FORMAT:
++	case CX2072X_DAC1_AMP_GAIN_RIGHT:
++	case CX2072X_DAC1_AMP_GAIN_LEFT:
++	case CX2072X_DAC1_POWER_STATE:
++	case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL:
++	case CX2072X_DAC1_EAPD_ENABLE:
++	case CX2072X_DAC2_CONVERTER_FORMAT:
++	case CX2072X_DAC2_AMP_GAIN_RIGHT:
++	case CX2072X_DAC2_AMP_GAIN_LEFT:
++	case CX2072X_DAC2_POWER_STATE:
++	case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL:
++	case CX2072X_ADC1_CONVERTER_FORMAT:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_0:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_0:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_1:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_1:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_2:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_2:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_3:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_3:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_4:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_4:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_5:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_5:
++	case CX2072X_ADC1_AMP_GAIN_RIGHT_6:
++	case CX2072X_ADC1_AMP_GAIN_LEFT_6:
++	case CX2072X_ADC1_CONNECTION_SELECT_CONTROL:
++	case CX2072X_ADC1_POWER_STATE:
++	case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL:
++	case CX2072X_ADC2_CONVERTER_FORMAT:
++	case CX2072X_ADC2_AMP_GAIN_RIGHT_0:
++	case CX2072X_ADC2_AMP_GAIN_LEFT_0:
++	case CX2072X_ADC2_AMP_GAIN_RIGHT_1:
++	case CX2072X_ADC2_AMP_GAIN_LEFT_1:
++	case CX2072X_ADC2_AMP_GAIN_RIGHT_2:
++	case CX2072X_ADC2_AMP_GAIN_LEFT_2:
++	case CX2072X_ADC2_CONNECTION_SELECT_CONTROL:
++	case CX2072X_ADC2_POWER_STATE:
++	case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL:
++	case CX2072X_PORTA_CONNECTION_SELECT_CTRL:
++	case CX2072X_PORTA_POWER_STATE:
++	case CX2072X_PORTA_PIN_CTRL:
++	case CX2072X_PORTA_UNSOLICITED_RESPONSE:
++	case CX2072X_PORTA_PIN_SENSE:
++	case CX2072X_PORTA_EAPD_BTL:
++	case CX2072X_PORTB_POWER_STATE:
++	case CX2072X_PORTB_PIN_CTRL:
++	case CX2072X_PORTB_UNSOLICITED_RESPONSE:
++	case CX2072X_PORTB_PIN_SENSE:
++	case CX2072X_PORTB_EAPD_BTL:
++	case CX2072X_PORTB_GAIN_RIGHT:
++	case CX2072X_PORTB_GAIN_LEFT:
++	case CX2072X_PORTC_POWER_STATE:
++	case CX2072X_PORTC_PIN_CTRL:
++	case CX2072X_PORTC_GAIN_RIGHT:
++	case CX2072X_PORTC_GAIN_LEFT:
++	case CX2072X_PORTD_POWER_STATE:
++	case CX2072X_PORTD_PIN_CTRL:
++	case CX2072X_PORTD_UNSOLICITED_RESPONSE:
++	case CX2072X_PORTD_PIN_SENSE:
++	case CX2072X_PORTD_GAIN_RIGHT:
++	case CX2072X_PORTD_GAIN_LEFT:
++	case CX2072X_PORTE_CONNECTION_SELECT_CTRL:
++	case CX2072X_PORTE_POWER_STATE:
++	case CX2072X_PORTE_PIN_CTRL:
++	case CX2072X_PORTE_UNSOLICITED_RESPONSE:
++	case CX2072X_PORTE_PIN_SENSE:
++	case CX2072X_PORTE_EAPD_BTL:
++	case CX2072X_PORTE_GAIN_RIGHT:
++	case CX2072X_PORTE_GAIN_LEFT:
++	case CX2072X_PORTF_POWER_STATE:
++	case CX2072X_PORTF_PIN_CTRL:
++	case CX2072X_PORTF_UNSOLICITED_RESPONSE:
++	case CX2072X_PORTF_PIN_SENSE:
++	case CX2072X_PORTF_GAIN_RIGHT:
++	case CX2072X_PORTF_GAIN_LEFT:
++	case CX2072X_PORTG_POWER_STATE:
++	case CX2072X_PORTG_PIN_CTRL:
++	case CX2072X_PORTG_CONNECTION_SELECT_CTRL:
++	case CX2072X_PORTG_EAPD_BTL:
++	case CX2072X_PORTM_POWER_STATE:
++	case CX2072X_PORTM_PIN_CTRL:
++	case CX2072X_PORTM_CONNECTION_SELECT_CTRL:
++	case CX2072X_PORTM_EAPD_BTL:
++	case CX2072X_MIXER_POWER_STATE:
++	case CX2072X_MIXER_GAIN_RIGHT_0:
++	case CX2072X_MIXER_GAIN_LEFT_0:
++	case CX2072X_MIXER_GAIN_RIGHT_1:
++	case CX2072X_MIXER_GAIN_LEFT_1:
++	case CX2072X_EQ_ENABLE_BYPASS:
++	case CX2072X_EQ_B0_COEFF:
++	case CX2072X_EQ_B1_COEFF:
++	case CX2072X_EQ_B2_COEFF:
++	case CX2072X_EQ_A1_COEFF:
++	case CX2072X_EQ_A2_COEFF:
++	case CX2072X_EQ_G_COEFF:
++	case CX2072X_SPKR_DRC_ENABLE_STEP:
++	case CX2072X_SPKR_DRC_CONTROL:
++	case CX2072X_SPKR_DRC_TEST:
++	case CX2072X_DIGITAL_BIOS_TEST0:
++	case CX2072X_DIGITAL_BIOS_TEST2:
++	case CX2072X_I2SPCM_CONTROL1:
++	case CX2072X_I2SPCM_CONTROL2:
++	case CX2072X_I2SPCM_CONTROL3:
++	case CX2072X_I2SPCM_CONTROL4:
++	case CX2072X_I2SPCM_CONTROL5:
++	case CX2072X_I2SPCM_CONTROL6:
++	case CX2072X_UM_INTERRUPT_CRTL_E:
++	case CX2072X_CODEC_TEST2:
++	case CX2072X_CODEC_TEST20:
++	case CX2072X_CODEC_TEST26:
++	case CX2072X_ANALOG_TEST4:
++	case CX2072X_ANALOG_TEST5:
++	case CX2072X_ANALOG_TEST6:
++	case CX2072X_ANALOG_TEST7:
++	case CX2072X_ANALOG_TEST8:
++	case CX2072X_ANALOG_TEST9:
++	case CX2072X_ANALOG_TEST10:
++	case CX2072X_ANALOG_TEST11:
++	case CX2072X_ANALOG_TEST12:
++	case CX2072X_ANALOG_TEST13:
++	case CX2072X_DIGITAL_TEST0:
++	case CX2072X_DIGITAL_TEST1:
++	case CX2072X_DIGITAL_TEST11:
++	case CX2072X_DIGITAL_TEST12:
++	case CX2072X_DIGITAL_TEST15:
++	case CX2072X_DIGITAL_TEST16:
++	case CX2072X_DIGITAL_TEST17:
++	case CX2072X_DIGITAL_TEST18:
++	case CX2072X_DIGITAL_TEST19:
++	case CX2072X_DIGITAL_TEST20:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static bool cx2072x_volatile_register(struct device *dev, unsigned int reg)
++{
++	switch (reg) {
++	case CX2072X_VENDOR_ID:
++	case CX2072X_REVISION_ID:
++	case CX2072X_UM_INTERRUPT_CRTL_E:
++	case CX2072X_DIGITAL_TEST11:
++	case CX2072X_PORTA_PIN_SENSE:
++	case CX2072X_PORTB_PIN_SENSE:
++	case CX2072X_PORTD_PIN_SENSE:
++	case CX2072X_PORTE_PIN_SENSE:
++	case CX2072X_PORTF_PIN_SENSE:
++	case CX2072X_EQ_G_COEFF:
++	case CX2072X_EQ_BAND:
++		return true;
++	default:
++		return false;
++
++	}
++}
++
++static struct snd_soc_codec_driver soc_codec_driver_cx2072x = {
++	.probe = cx2072x_probe,
++	.remove = cx2072x_remove,
++	.set_bias_level = cx2072x_set_bias_level,
++	.component_driver = {
++		.controls = cx2072x_snd_controls,
++		.num_controls = ARRAY_SIZE(cx2072x_snd_controls),
++		.dapm_widgets = cx2072x_dapm_widgets,
++		.num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets),
++		.dapm_routes = cx2072x_intercon,
++		.num_dapm_routes = ARRAY_SIZE(cx2072x_intercon),
++	}
++};
++
++/*
++ * DAI ops
++ */
++static struct snd_soc_dai_ops cx2072x_dai_ops = {
++	.set_sysclk = cx2072x_set_dai_sysclk,
++	.set_fmt = cx2072x_set_dai_fmt,
++	.set_tdm_slot = cx2072x_set_tdm_slot,
++	.hw_params = cx2072x_hw_params,
++	.digital_mute = cx2072x_digital_mute,
++	.set_bclk_ratio	= cx2072x_set_dai_bclk_ratio
++};
++
++/*
++ * DAI driver
++ */
++static struct snd_soc_dai_driver soc_codec_cx2072x_dai = {
++	.name = "cx2072x-hifi",
++	.playback = {
++		.stream_name = "Playback",
++		.channels_min = 1,
++		.channels_max = 2,
++		.rates = CX2072X_RATES_DSP,
++		.formats = CX2072X_FORMATS,
++	},
++	.capture = {
++		.stream_name = "Capture",
++		.channels_min = 1,
++		.channels_max = 2,
++		.rates = CX2072X_RATES_DSP,
++		.formats = CX2072X_FORMATS,
++	},
++	.ops = &cx2072x_dai_ops,
++	.symmetric_rates = 1,
++};
++EXPORT_SYMBOL_GPL(soc_codec_cx2072x_dai);
++
++static const struct regmap_config cx2072x_regmap = {
++	.reg_bits = 16,
++	.val_bits = 32,
++	.max_register = CX2072X_REG_MAX, .reg_defaults = cx2072x_reg_defaults,
++	.num_reg_defaults = ARRAY_SIZE(cx2072x_reg_defaults),
++	.cache_type = REGCACHE_RBTREE,
++
++	.readable_reg = cx2072x_readable_register,
++	.volatile_reg = cx2072x_volatile_register,
++	.reg_read = cx2072x_reg_read,
++	.reg_write = cx2072x_reg_write,
++};
++
++
++static int cx2072x_i2c_probe(struct i2c_client *i2c,
++	const struct i2c_device_id *id)
++{
++	int ret = -1;
++	struct cx2072x_priv *cx2072x;
++
++	dev_dbg(&i2c->dev, "CX2072X codec driver i2c probe() is called\n");
++
++	cx2072x = (struct cx2072x_priv *)devm_kzalloc(
++		&i2c->dev, sizeof(struct cx2072x_priv), GFP_KERNEL);
++	if (cx2072x == NULL) {
++		dev_err(&i2c->dev, "Out of memory!\n");
++		return -ENOMEM;
++	}
++
++	mutex_init(&cx2072x->lock);
++
++	cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c,
++		&cx2072x_regmap);
++	if (IS_ERR(&cx2072x->regmap)) {
++		ret = PTR_ERR(cx2072x->regmap);
++		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
++		return ret;
++	}
++
++	i2c_set_clientdata(i2c, cx2072x);
++
++	cx2072x->dev = &i2c->dev;
++	cx2072x->pll_changed = true;
++	cx2072x->i2spcm_changed = true;
++
++	/* setts the frame size to
++	 * Frame size = number of channel * sample width
++	 */
++	cx2072x->bclk_ratio = 0;
++
++	ret = snd_soc_register_codec(cx2072x->dev,
++		&soc_codec_driver_cx2072x, &soc_codec_cx2072x_dai,
++		1);
++	if (ret < 0)
++		dev_err(cx2072x->dev,
++		"Failed to register codec: %d\n", ret);
++	else
++		dev_dbg(cx2072x->dev,
++		"%s: Register codec.\n", __func__);
++
++	return ret;
++}
++
++static int cx2072x_i2c_remove(struct i2c_client *client)
++{
++	snd_soc_unregister_codec(&client->dev);
++	return 0;
++}
++
++static void cx2072x_i2c_shutdown(struct i2c_client *client)
++{
++	struct cx2072x_priv *cx2072x = i2c_get_clientdata(client);
++	cx2072x_set_bias_level(cx2072x->codec, SND_SOC_BIAS_OFF);
++}
++
++
++const struct dev_pm_ops cx2072x_pm_ops = {
++	SET_RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume,
++	NULL)
++};
++static const struct i2c_device_id cx2072x_i2c_id[] = {
++	{ "cx20721", 0 },
++	{ "cx20722", 0 },
++	{ "14F10720", 0 },
++	{}
++};
++MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
++
++static const struct of_device_id cx2072x_of_match[] = {
++	{ .compatible = "cnxt,cx20721", },
++	{ .compatible = "cnxt,cx20723", },
++	{ .compatible = "cnxt,cx7601", },
++	{}
++};
++MODULE_DEVICE_TABLE(of, cx2072x_of_match);
++#ifdef CONFIG_ACPI
++static struct acpi_device_id cx2072x_acpi_match[] = {
++	{ "14F10720", 0 },
++	{},
++};
++MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match);
++#endif
++
++static struct i2c_driver cx2072x_i2c_driver = {
++	.probe = cx2072x_i2c_probe,
++	.remove = cx2072x_i2c_remove,
++	.shutdown = cx2072x_i2c_shutdown,
++	.id_table = cx2072x_i2c_id,
++	.driver = {
++		.name = "cx2072x",
++		.owner = THIS_MODULE,
++		.of_match_table = cx2072x_of_match,
++#ifdef CONFIG_ACPI
++		.acpi_match_table = ACPI_PTR(cx2072x_acpi_match),
++#endif
++		.pm	= &cx2072x_pm_ops,
++	},
++};
++
++module_i2c_driver(cx2072x_i2c_driver);
++
++MODULE_DESCRIPTION("ASoC cx2072x Codec Driver");
++MODULE_AUTHOR("Simon Ho <simon.ho@conexant.com>");
++MODULE_LICENSE("GPL");
++
+diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h
+new file mode 100644
+index 000000000000..67b90c184c6b
+--- /dev/null
++++ b/sound/soc/codecs/cx2072x.h
+@@ -0,0 +1,339 @@
++/*
++ * ALSA SoC CX2072x Solana codec driver
++ *
++ * Copyright:   (C) 2016 Conexant Systems, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ ************************************************************************/
++
++#define NUM_OF_DAI 1
++#define CX2072X_MCLK_PLL 1
++#define CX2072X_MCLK_EXTERNAL_PLL 1
++#define CX2072X_MCLK_INTERNAL_OSC 2
++
++/*#define CX2072X_RATES		SNDRV_PCM_RATE_8000_192000*/
++#define CX2072X_RATES_DSP	SNDRV_PCM_RATE_48000
++
++#define CX2072X_REG_MAX 0x8a3c
++#define AUDDRV_VERSION(major0, major1, minor, build) \
++		((major0)<<24 | (major1)<<16 | (minor)<<8 | (build))
++
++#define CX2072X_VENDOR_ID                                0x0200
++#define CX2072X_REVISION_ID                              0x0208
++#define CX2072X_CURRENT_BCLK_FREQUENCY                   0x00dc
++#define CX2072X_AFG_POWER_STATE                          0x0414
++#define CX2072X_UM_RESPONSE                              0x0420
++#define CX2072X_GPIO_DATA                                0x0454
++#define CX2072X_GPIO_ENABLE                              0x0458
++#define CX2072X_GPIO_DIRECTION                           0x045c
++#define CX2072X_GPIO_WAKE                                0x0460
++#define CX2072X_GPIO_UM_ENABLE                           0x0464
++#define CX2072X_GPIO_STICKY_MASK                         0x0468
++#define CX2072X_AFG_FUNCTION_RESET                       0x07FC
++#define CX2072X_DAC1_CONVERTER_FORMAT                    0x43c8
++#define CX2072X_DAC1_AMP_GAIN_RIGHT                      0x41c0
++#define CX2072X_DAC1_AMP_GAIN_LEFT                       0x41e0
++#define CX2072X_DAC1_POWER_STATE                         0x4014
++#define CX2072X_DAC1_CONVERTER_STREAM_CHANNEL            0x4018
++#define CX2072X_DAC1_EAPD_ENABLE                         0x4030
++#define CX2072X_DAC2_CONVERTER_FORMAT                    0x47c8
++#define CX2072X_DAC2_AMP_GAIN_RIGHT                      0x45c0
++#define CX2072X_DAC2_AMP_GAIN_LEFT                       0x45e0
++#define CX2072X_DAC2_POWER_STATE                         0x4414
++#define CX2072X_DAC2_CONVERTER_STREAM_CHANNEL            0x4418
++#define CX2072X_ADC1_CONVERTER_FORMAT                    0x4fc8
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_0                    0x4d80
++#define CX2072X_ADC1_AMP_GAIN_LEFT_0                     0x4da0
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_1                    0x4d84
++#define CX2072X_ADC1_AMP_GAIN_LEFT_1                     0x4da4
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_2                    0x4d88
++#define CX2072X_ADC1_AMP_GAIN_LEFT_2                     0x4da8
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_3                    0x4d8c
++#define CX2072X_ADC1_AMP_GAIN_LEFT_3                     0x4dac
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_4                    0x4d90
++#define CX2072X_ADC1_AMP_GAIN_LEFT_4                     0x4db0
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_5                    0x4d94
++#define CX2072X_ADC1_AMP_GAIN_LEFT_5                     0x4db4
++#define CX2072X_ADC1_AMP_GAIN_RIGHT_6                    0x4d98
++#define CX2072X_ADC1_AMP_GAIN_LEFT_6                     0x4db8
++#define CX2072X_ADC1_CONNECTION_SELECT_CONTROL           0x4c04
++#define CX2072X_ADC1_POWER_STATE                         0x4c14
++#define CX2072X_ADC1_CONVERTER_STREAM_CHANNEL            0x4c18
++#define CX2072X_ADC2_CONVERTER_FORMAT                    0x53c8
++#define CX2072X_ADC2_AMP_GAIN_RIGHT_0                    0x5180
++#define CX2072X_ADC2_AMP_GAIN_LEFT_0                     0x51a0
++#define CX2072X_ADC2_AMP_GAIN_RIGHT_1                    0x5184
++#define CX2072X_ADC2_AMP_GAIN_LEFT_1                     0x51a4
++#define CX2072X_ADC2_AMP_GAIN_RIGHT_2                    0x5188
++#define CX2072X_ADC2_AMP_GAIN_LEFT_2                     0x51a8
++#define CX2072X_ADC2_CONNECTION_SELECT_CONTROL           0x5004
++#define CX2072X_ADC2_POWER_STATE                         0x5014
++#define CX2072X_ADC2_CONVERTER_STREAM_CHANNEL            0x5018
++#define CX2072X_PORTA_CONNECTION_SELECT_CTRL             0x5804
++#define CX2072X_PORTA_POWER_STATE                        0x5814
++#define CX2072X_PORTA_PIN_CTRL                           0x581c
++#define CX2072X_PORTA_UNSOLICITED_RESPONSE               0x5820
++#define CX2072X_PORTA_PIN_SENSE                          0x5824
++#define CX2072X_PORTA_EAPD_BTL                           0x5830
++#define CX2072X_PORTB_POWER_STATE                        0x6014
++#define CX2072X_PORTB_PIN_CTRL                           0x601c
++#define CX2072X_PORTB_UNSOLICITED_RESPONSE               0x6020
++#define CX2072X_PORTB_PIN_SENSE                          0x6024
++#define CX2072X_PORTB_EAPD_BTL                           0x6030
++#define CX2072X_PORTB_GAIN_RIGHT                         0x6180
++#define CX2072X_PORTB_GAIN_LEFT                          0x61a0
++#define CX2072X_PORTC_POWER_STATE                        0x6814
++#define CX2072X_PORTC_PIN_CTRL                           0x681c
++#define CX2072X_PORTC_GAIN_RIGHT                         0x6980
++#define CX2072X_PORTC_GAIN_LEFT                          0x69a0
++#define CX2072X_PORTD_POWER_STATE                        0x6414
++#define CX2072X_PORTD_PIN_CTRL                           0x641c
++#define CX2072X_PORTD_UNSOLICITED_RESPONSE               0x6420
++#define CX2072X_PORTD_PIN_SENSE                          0x6424
++#define CX2072X_PORTD_GAIN_RIGHT                         0x6580
++#define CX2072X_PORTD_GAIN_LEFT                          0x65a0
++#define CX2072X_PORTE_CONNECTION_SELECT_CTRL             0x7404
++#define CX2072X_PORTE_POWER_STATE                        0x7414
++#define CX2072X_PORTE_PIN_CTRL                           0x741c
++#define CX2072X_PORTE_UNSOLICITED_RESPONSE               0x7420
++#define CX2072X_PORTE_PIN_SENSE                          0x7424
++#define CX2072X_PORTE_EAPD_BTL                           0x7430
++#define CX2072X_PORTE_GAIN_RIGHT                         0x7580
++#define CX2072X_PORTE_GAIN_LEFT                          0x75a0
++#define CX2072X_PORTF_POWER_STATE                        0x7814
++#define CX2072X_PORTF_PIN_CTRL                           0x781c
++#define CX2072X_PORTF_UNSOLICITED_RESPONSE               0x7820
++#define CX2072X_PORTF_PIN_SENSE                          0x7824
++#define CX2072X_PORTF_GAIN_RIGHT                         0x7980
++#define CX2072X_PORTF_GAIN_LEFT                          0x79a0
++#define CX2072X_PORTG_POWER_STATE                        0x5c14
++#define CX2072X_PORTG_PIN_CTRL                           0x5c1c
++#define CX2072X_PORTG_CONNECTION_SELECT_CTRL             0x5c04
++#define CX2072X_PORTG_EAPD_BTL                           0x5c30
++#define CX2072X_PORTM_POWER_STATE                        0x8814
++#define CX2072X_PORTM_PIN_CTRL                           0x881c
++#define CX2072X_PORTM_CONNECTION_SELECT_CTRL             0x8804
++#define CX2072X_PORTM_EAPD_BTL                           0x8830
++#define CX2072X_MIXER_POWER_STATE                        0x5414
++#define CX2072X_MIXER_GAIN_RIGHT_0                       0x5580
++#define CX2072X_MIXER_GAIN_LEFT_0                        0x55a0
++#define CX2072X_MIXER_GAIN_RIGHT_1                       0x5584
++#define CX2072X_MIXER_GAIN_LEFT_1                        0x55a4
++#define CX2072X_EQ_ENABLE_BYPASS                         0x6d00
++#define CX2072X_EQ_B0_COEFF                              0x6d02
++#define CX2072X_EQ_B1_COEFF                              0x6d04
++#define CX2072X_EQ_B2_COEFF                              0x6d06
++#define CX2072X_EQ_A1_COEFF                              0x6d08
++#define CX2072X_EQ_A2_COEFF                              0x6d0a
++#define CX2072X_EQ_G_COEFF                               0x6d0c
++#define CX2072X_EQ_BAND                                  0x6d0d
++#define CX2072X_SPKR_DRC_ENABLE_STEP                     0x6d10
++#define CX2072X_SPKR_DRC_CONTROL                         0x6d14
++#define CX2072X_SPKR_DRC_TEST                            0X6D18
++#define CX2072X_DIGITAL_BIOS_TEST0                       0x6d80
++#define CX2072X_DIGITAL_BIOS_TEST2                       0x6d84
++#define CX2072X_I2SPCM_CONTROL1                          0x6e00
++#define CX2072X_I2SPCM_CONTROL2                          0x6e04
++#define CX2072X_I2SPCM_CONTROL3                          0x6e08
++#define CX2072X_I2SPCM_CONTROL4                          0x6e0c
++#define CX2072X_I2SPCM_CONTROL5                          0x6e10
++#define CX2072X_I2SPCM_CONTROL6                          0x6e18
++#define CX2072X_UM_INTERRUPT_CRTL_E                      0x6e14
++#define CX2072X_CODEC_TEST2                              0x7108
++#define CX2072X_CODEC_TEST20                             0x7310
++#define CX2072X_CODEC_TEST26                             0x7328
++#define CX2072X_ANALOG_TEST3                             0x718c
++#define CX2072X_ANALOG_TEST4                             0x7190
++#define CX2072X_ANALOG_TEST5                             0x7194
++#define CX2072X_ANALOG_TEST6                             0x7198
++#define CX2072X_ANALOG_TEST7                             0x719c
++#define CX2072X_ANALOG_TEST8                             0x71a0
++#define CX2072X_ANALOG_TEST9                             0x71a4
++#define CX2072X_ANALOG_TEST10                            0x71a8
++#define CX2072X_ANALOG_TEST11                            0x71ac
++#define CX2072X_ANALOG_TEST12                            0x71b0
++#define CX2072X_ANALOG_TEST13                            0x71b4
++#define CX2072X_DIGITAL_TEST0                            0x7200
++#define CX2072X_DIGITAL_TEST1                            0x7204
++#define CX2072X_DIGITAL_TEST11                           0x722c
++#define CX2072X_DIGITAL_TEST12                           0x7230
++#define CX2072X_DIGITAL_TEST15                           0x723c
++#define CX2072X_DIGITAL_TEST16                           0x7080
++#define CX2072X_DIGITAL_TEST17                           0x7084
++#define CX2072X_DIGITAL_TEST18                           0x7088
++#define CX2072X_DIGITAL_TEST19                           0x708c
++#define CX2072X_DIGITAL_TEST20                           0x7090
++
++#define INVALID_GPIO -1
++#define MAX_EQ_BAND 7
++#define MAC_EQ_COEFF 11
++#define MAX_DRC_REGS 9
++#define MIC_EQ_COEFF 10
++/*
++static unsigned char cx2072x_eq_coeff_flat[MAX_EQ_BAND][MAC_EQ_COEFF] = {
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
++};
++*/
++/*
++static unsigned char cx2072x_miceq_coeff_array[][MIC_EQ_COEFF] = {
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++	{0x42, 0x17, 0x85, 0x2e, 0x42, 0x17, 0x0a, 0x5d, 0xed, 0xc5},
++};
++*/
++
++
++enum cx2072x_jack_types {
++	CX_JACK_NONE = 0x0000,
++	CX_JACK_HEADPHONE = 0x0001,
++	CX_JACK_APPLE_HEADSET = 0x0002,
++	CX_JACK_NOKIE_HEADSET = 0x0003,
++
++};
++
++int cx2072x_hs_jack_report(struct snd_soc_codec *codec);
++
++typedef enum _REG_SAMPLE_SIZE{
++	SAMPLE_SIZE_8_BITS = 0 ,
++	SAMPLE_SIZE_16_BITS = 1 ,
++	SAMPLE_SIZE_24_BITS = 2 ,
++	SAMPLE_SIZE_RESERVED = 3 
++}REG_SAMPLE_SIZE;
++
++typedef union _REG_I2SPCM_CTRL_REG1{
++	struct {
++		u32 rx_data_one_line :1;
++		u32 rx_ws_pol        :1;
++		u32 rx_ws_wid       :7;
++		u32 rx_frm_len      :5;
++		u32 rx_sa_size     :2;
++		u32 tx_data_one_line :1;
++		u32 tx_ws_pol        :1;
++		u32 tx_ws_wid       :7;
++		u32 tx_frm_len      :5;
++		u32 tx_sa_size     :2; 
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG1;
++
++
++typedef union _REG_I2SPCM_CTRL_REG2{
++	struct {
++		u32 tx_en_ch1		:1;
++		u32 tx_en_ch2		:1;
++		u32 tx_en_ch3		:1;
++		u32 tx_en_ch4		:1;
++		u32 tx_en_ch5		:1;
++		u32 tx_en_ch6		:1;
++		u32 tx_slot_1         :5;
++		u32 tx_slot_2         :5;
++		u32 tx_slot_3         :5;
++		u32 tx_slot_4         :5;
++		u32  res               :1;
++		u32  tx_data_neg_bclk  :1;
++		u32  tx_master         :1;
++		u32  tx_tri_n          :1;
++		u32  tx_endian_sel     :1;
++		u32  tx_dstart_dly     :1; 
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG2;
++
++typedef union _REG_I2SPCM_CTRL_REG3{
++	struct {
++		u32 rx_en_ch1		:1;
++		u32 rx_en_ch2		:1;
++		u32 rx_en_ch3		:1;
++		u32 rx_en_ch4		:1;
++		u32 rx_en_ch5		:1;
++		u32 rx_en_ch6		:1;
++		u32 rx_slot_1         :5;
++		u32 rx_slot_2         :5;
++		u32 rx_slot_3         :5;
++		u32 rx_slot_4         :5;
++		u32  res               :1;
++		u32  rx_data_neg_bclk  :1;
++		u32  rx_master         :1;
++		u32  rx_tri_n          :1;
++		u32  rx_endian_sel     :1;
++		u32  rx_dstart_dly     :1; 
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG3;
++
++
++typedef union _REG_I2SPCM_CTRL_REG4{
++	struct {
++		u32 rx_mute		:1;
++		u32 tx_mute		:1;
++		u32 reserved		:1;
++		u32 dac_34_independent	:1;
++		u32 dac_bclk_lrck_share:1;
++		u32 bclk_lrck_share_en :1;
++		u32 reserved2          :2;  
++		u32 rx_last_dac_ch_en  :1;
++		u32 rx_last_dac_ch     :3;
++		u32 tx_last_adc_ch_en  :1;
++		u32 tx_last_adc_ch     :3;
++		u32 rx_slot_5         	:5;
++		u32 rx_slot_6         	:5;
++		u32 reserved3		:6;
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG4;
++
++
++typedef union _REG_I2SPCM_CTRL_REG5{
++	struct {
++		u32 tx_slot_5         :5;
++		u32 reserved          :3;
++		u32 tx_slot_6         :5;
++		u32 reserved2         :3;
++		u32 reserved3         :8;
++		u32 i2s_pcm_clk_div   :7;
++		u32  i2s_pcm_clk_div_chan_en :1;
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG5;
++
++typedef union _REG_I2SPCM_CTRL_REG6{
++	struct {
++		u32 reserved		:5;
++		u32 rx_pause_cycles	:3;
++		u32 rx_pause_start_pos	:8;
++		u32 reserved2		:5;
++		u32 tx_pause_cycles	:3;
++		u32 tx_pause_start_pos	:8;
++	}r;
++	u32 ulVal;
++}REG_I2SPCM_CTRL_REG6;
++
++typedef union _REG_DIGITAL_BIOS_TEST2{
++	struct {
++		u32 pull_down_eapd :2;
++		u32  input_en_eapd_pad :1;
++		u32  push_pull_mode    :1;
++		u32 eapd_pad_output_driver :2;
++		u32  pll_source        :1;
++		u32  i2s_bclk_en       :1; 
++		u32  i2s_bclk_invert   :1;
++		u32  pll_ref_clock     :1;
++		u32  class_d_sheild_clk:1;
++		u32  audio_pll_bypass_mode:1;
++		u32  reserved          :4;
++	}r;
++	u32 ulVal;
++}REG_DIGITAL_BIOS_TEST2;
+diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
+index 10c2a564a715..77bde13147bd 100644
+--- a/sound/soc/codecs/rt5645.c
++++ b/sound/soc/codecs/rt5645.c
+@@ -3545,8 +3545,10 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id rt5645_acpi_match[] = {
+ 	{ "10EC5645", 0 },
++	{ "10EC5648", 0 },
+ 	{ "10EC5650", 0 },
+ 	{ "10EC5640", 0 },
++	{ "10EC3270", 0 },
+ 	{},
+ };
+ MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
+@@ -3658,8 +3660,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
+ 						       GPIOD_IN);
+ 
+ 	if (IS_ERR(rt5645->gpiod_hp_det)) {
+-		dev_err(&i2c->dev, "failed to initialize gpiod\n");
+-		return PTR_ERR(rt5645->gpiod_hp_det);
++		dev_info(&i2c->dev, "failed to initialize gpiod\n");
+ 	}
+ 
+ 	for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
+diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
+index 49caf1393aeb..fdc14e50d3b9 100644
+--- a/sound/soc/codecs/rt5670.c
++++ b/sound/soc/codecs/rt5670.c
+@@ -2813,6 +2813,8 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id rt5670_acpi_match[] = {
+ 	{ "10EC5670", 0},
++	{ "10EC5672", 0},
++	{ "10EC5640", 0}, /* quirk */
+ 	{ },
+ };
+ MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
+index be1a64bfd320..a1b4e14b347a 100644
+--- a/sound/soc/codecs/tlv320aic31xx.c
++++ b/sound/soc/codecs/tlv320aic31xx.c
+@@ -192,46 +192,68 @@ static const struct aic31xx_rate_divs aic31xx_divs[] = {
+ 	{12000000,   8000,	8, 1920,	128,  48,  2,	128,  48,  2},
+ 	{12000000,   8000,	8, 1920,	128,  32,  3,	128,  32,  3},
+ 	{12500000,   8000,	7, 8643,	128,  48,  2,	128,  48,  2},
++	{19200000,   8000,	5, 1200,	128,  48,  2,	128,  48,  2},
++	{19200000,   8000,	5, 1200,	128,  32,  3,	128,  32,  3},
+ 	/* 11.025k rate */
+ 	{12000000,  11025,	7, 5264,	128,  32,  2,	128,  32,  2},
+ 	{12000000,  11025,	8, 4672,	128,  24,  3,	128,  24,  3},
+ 	{12500000,  11025,	7, 2253,	128,  32,  2,	128,  32,  2},
++	{19200000,  11025,	4, 7040,	128,  24,  2,	128,  32,  2},
++	{19200000,  11025,      5, 2920,	128,  32,  3,	128,  24,  3},
+ 	/* 16k rate */
+ 	{12000000,  16000,	8, 1920,	128,  24,  2,	128,  24,  2},
+ 	{12000000,  16000,	8, 1920,	128,  16,  3,	128,  16,  3},
+ 	{12500000,  16000,	7, 8643,	128,  24,  2,	128,  24,  2},
++	{19200000,  16000,	5, 1200,	128,  24,  2,	128,  24,  2},
++	{19200000,  16000,	5, 1200,	128,  16,  3,	128,  16,  3},
+ 	/* 22.05k rate */
+ 	{12000000,  22050,	7, 5264,	128,  16,  2,	128,  16,  2},
+ 	{12000000,  22050,	8, 4672,	128,  12,  3,	128,  12,  3},
+ 	{12500000,  22050,	7, 2253,	128,  16,  2,	128,  16,  2},
++	{19200000,  22050,	4, 7040,	128,  16,  2,	128,  16,  2},
++	{19200000,  22050,      5, 2920,	128,  12,  3,	128,  12,  3},
+ 	/* 32k rate */
+ 	{12000000,  32000,	8, 1920,	128,  12,  2,	128,  12,  2},
+ 	{12000000,  32000,	8, 1920,	128,   8,  3,	128,   8,  3},
+ 	{12500000,  32000,	7, 8643,	128,  12,  2,	128,  12,  2},
++	{19200000,  32000,	1, 0000,	 32,   1,  2,	 32,   1,  2},
++	{19200000,  32000,	5, 1200,	128,   8,  3,	128,   8,  3},
+ 	/* 44.1k rate */
+ 	{12000000,  44100,	7, 5264,	128,   8,  2,	128,   8,  2},
+ 	{12000000,  44100,	8, 4672,	128,   6,  3,	128,   6,  3},
+ 	{12500000,  44100,	7, 2253,	128,   8,  2,	128,   8,  2},
++	{19200000,  44100,	4, 7040,	128,   8,  2,	128,   8,  2},
++	{19200000,  44100,      5, 2920,	128,   6,  3,	128,   6,  3},
+ 	/* 48k rate */
+ 	{12000000,  48000,	8, 1920,	128,   8,  2,	128,   8,  2},
+ 	{12000000,  48000,	7, 6800,	 96,   5,  4,	 96,   5,  4},
+ 	{12500000,  48000,	7, 8643,	128,   8,  2,	128,   8,  2},
++	{19200000,  48000,	5, 1200,	128,   8,  2,	128,   8,  2},
++	{19200000,  48000,	4, 8000,	 96,   5,  4,	 96,   5,  4},
+ 	/* 88.2k rate */
+ 	{12000000,  88200,	7, 5264,	 64,   8,  2,	 64,   8,  2},
+ 	{12000000,  88200,	8, 4672,	 64,   6,  3,	 64,   6,  3},
+ 	{12500000,  88200,	7, 2253,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  88200,	4, 7040,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  88200,      5, 2920,	 64,   6,  3,	 64,   6,  3},
+ 	/* 96k rate */
+ 	{12000000,  96000,	8, 1920,	 64,   8,  2,	 64,   8,  2},
+ 	{12000000,  96000,	7, 6800,	 48,   5,  4,	 48,   5,  4},
+ 	{12500000,  96000,	7, 8643,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  96000,	5, 1200,	 64,   8,  2,	 64,   8,  2},
++	{19200000,  96000,	4, 8000,	 48,   5,  4,	 48,   5,  4},
+ 	/* 176.4k rate */
+ 	{12000000, 176400,	7, 5264,	 32,   8,  2,	 32,   8,  2},
+ 	{12000000, 176400,	8, 4672,	 32,   6,  3,	 32,   6,  3},
+ 	{12500000, 176400,	7, 2253,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 176400,	4, 7040,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 176400,      5, 2920,	 32,   6,  3,	 32,   6,  3},
+ 	/* 192k rate */
+ 	{12000000, 192000,	8, 1920,	 32,   8,  2,	 32,   8,  2},
+ 	{12000000, 192000,	7, 6800,	 24,   5,  4,	 24,   5,  4},
+ 	{12500000, 192000,	7, 8643,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 192000,	5, 1200,	 32,   8,  2,	 32,   8,  2},
++	{19200000, 192000,	4, 8000,	 24,   5,  4,	 24,   5,  4},
+ };
+ 
+ static const char * const ldac_in_text[] = {
+diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
+index fd5d1e091038..76de542c66b7 100644
+--- a/sound/soc/intel/Kconfig
++++ b/sound/soc/intel/Kconfig
+@@ -172,6 +172,32 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
+           Say Y if you have such a device.
+           If unsure select "N".
+ 
++config SND_SOC_INTEL_BYTCR_AIC3100_MACH
++        tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with TLV320AIC31XX codec"
++	depends on X86 && I2C && ACPI
++	select SND_SOC_TLV320AIC31XX
++	select SND_SST_MFLD_PLATFORM
++	select SND_SST_IPC_ACPI
++	select SND_SOC_INTEL_SST_MATCH if ACPI
++	help
++          This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
++          platforms with TLV320AIC31XX audio codec.
++          Say Y if you have such a device.
++          If unsure select "N".
++
++config SND_SOC_INTEL_CHT_CX2072X_MACH
++        tristate "ASoC Audio driver for Intel Baytrail and Cherrytrail with CX2072X codec"
++        depends on X86_INTEL_LPSS && I2C && ACPI
++        select SND_SOC_CX2072X
++        select SND_SST_MFLD_PLATFORM
++        select SND_SST_IPC_ACPI
++	select SND_SOC_INTEL_SST_MATCH if ACPI
++        help
++          This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
++          platforms with Conexant CX2072X audio codec.
++          Say Y if you have such a device.
++          If unsure select "N".
++
+ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+         tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+         depends on X86_INTEL_LPSS && I2C && ACPI
+diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
+index 0a88537ca58a..4e230282205d 100644
+--- a/sound/soc/intel/atom/sst/sst_acpi.c
++++ b/sound/soc/intel/atom/sst/sst_acpi.c
+@@ -400,6 +400,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
+ static unsigned long cht_machine_id;
+ 
+ #define CHT_SURFACE_MACH 1
++#define BYT_THINKPAD_10  2
+ 
+ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+ {
+@@ -407,6 +408,23 @@ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+ 	return 1;
+ }
+ 
++static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id)
++{
++	cht_machine_id = BYT_THINKPAD_10;
++	return 1;
++}
++
++
++static const struct dmi_system_id byt_table[] = {
++	{
++		.callback = byt_thinkpad10_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
++		},
++	},
++	{ }
++};
+ 
+ static const struct dmi_system_id cht_table[] = {
+ 	{
+@@ -424,6 +442,10 @@ static struct sst_acpi_mach cht_surface_mach = {
+ 	"10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 								&chv_platform_data };
+ 
++static struct sst_acpi_mach byt_thinkpad_10 = {
++	"10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++	                                                        &byt_rvp_platform_data };
++
+ static struct sst_acpi_mach *cht_quirk(void *arg)
+ {
+ 	struct sst_acpi_mach *mach = arg;
+@@ -436,8 +458,21 @@ static struct sst_acpi_mach *cht_quirk(void *arg)
+ 		return mach;
+ }
+ 
++static struct sst_acpi_mach *byt_quirk(void *arg)
++{
++	struct sst_acpi_mach *mach = arg;
++
++	dmi_check_system(byt_table);
++
++	if (cht_machine_id == BYT_THINKPAD_10)
++		return &byt_thinkpad_10;
++	else
++		return mach;
++}
++
++
+ static struct sst_acpi_mach sst_acpi_bytcr[] = {
+-	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
++	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk,
+ 						&byt_rvp_platform_data },
+ 	{"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
+ 						&byt_rvp_platform_data },
+@@ -445,6 +480,16 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
+ 						&byt_rvp_platform_data },
+ 	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
+ 						&byt_rvp_platform_data },
++	/* some Baytrail platforms rely on RT5645, use CHT machine driver */
++	{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	{"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	/* use CHT driver to Baytrail Chromebooks */
++	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
++	{"14F10720", "cht-cx2072x", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
++						&byt_rvp_platform_data },
+ 	{},
+ };
+ 
+@@ -456,7 +501,9 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
+ 						&chv_platform_data },
+ 	{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 						&chv_platform_data },
+-	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
++	{"10EC3270", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
++						&chv_platform_data },
++	{"14F10720", "cht-cx2072x", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ 						&chv_platform_data },
+ 	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
+ 	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
+diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
+index 5639f10774e6..68dd93d91de9 100644
+--- a/sound/soc/intel/boards/Makefile
++++ b/sound/soc/intel/boards/Makefile
+@@ -7,6 +7,8 @@ snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
+ snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
+ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
+ snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
++snd-soc-sst-bytcr-aic3100-objs := bytcr_aic3100.o
++snd-soc-sst-cht-cx2072x-objs := cht_cx2072x.o
+ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
+ snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+ snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+@@ -23,6 +25,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+ obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
++obj-$(CONFIG_SND_SOC_INTEL_BYTCR_AIC3100_MACH) += snd-soc-sst-bytcr-aic3100.o
++obj-$(CONFIG_SND_SOC_INTEL_CHT_CX2072X_MACH) += snd-soc-sst-cht-cx2072x.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+diff --git a/sound/soc/intel/boards/bytcr_aic3100.c b/sound/soc/intel/boards/bytcr_aic3100.c
+new file mode 100644
+index 000000000000..f6a45f685933
+--- /dev/null
++++ b/sound/soc/intel/boards/bytcr_aic3100.c
+@@ -0,0 +1,608 @@
++/*
++ *  bytcr_aic3100.c - ASoc Machine driver for Intel Byt CR platform
++ *
++ *  Copyright (C) 2016 Intel Corporation
++ *
++ *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
++ *  Based on earlier 3.14 work by Praveen Diwakar and Gurudatta Bhakte
++ *
++ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License.
++ *
++ *  This program is distributed in the hope that it will be useful, but
++ *  WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ *  General Public License for more details.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/acpi.h>
++#include <linux/device.h>
++#include <linux/dmi.h>
++#include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++#include "../../codecs/tlv320aic31xx.h"
++#include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++#include "../common/sst-dsp.h"
++
++enum {
++	BYT_AIC3100_IN1_MAP,
++};
++
++#define BYT_AIC3100_MAP(quirk)	((quirk) & 0xff)
++#define BYT_AIC3100_SSP2_AIF    BIT(16) /* default true, use SSP2 */
++#define BYT_AIC3100_MCLK_25MHZ	BIT(18) /* default false, use 19.2MHz */
++
++struct byt_aic3100_private {
++	struct clk *mclk;
++};
++
++static unsigned long byt_aic3100_quirk =
++	BYT_AIC3100_IN1_MAP | BYT_AIC3100_SSP2_AIF;
++
++static void log_quirks(struct device *dev)
++{
++	if (BYT_AIC3100_MAP(byt_aic3100_quirk) == BYT_AIC3100_IN1_MAP)
++		dev_info(dev, "quirk IN1_MAP enabled");
++	if (byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF)
++		dev_info(dev, "quirk SSP2_AIF enabled");
++	else
++		dev_info(dev, "quirk SSP0_AIF enabled");
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ)
++		dev_info(dev, "quirk MCLK_25MHZ enabled");
++	else
++		dev_info(dev, "quirk MCLK_19200kHZ enabled");
++}
++
++#define BYT_CODEC_DAI1	"tlv320aic31xx-hifi"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct byt_aic3100_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++
++		if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++			ret = snd_soc_dai_set_sysclk(codec_dai,
++						AIC31XX_PLL_CLKIN_MCLK,
++						25000000,
++						SND_SOC_CLOCK_IN);
++		} else {
++			ret = snd_soc_dai_set_sysclk(codec_dai,
++						AIC31XX_PLL_CLKIN_MCLK,
++						19200000,
++						SND_SOC_CLOCK_IN);
++		}
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++		/* FIXME: use RC oscillator? */
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					0, SND_SOC_CLOCK_IN);
++		if (!ret) {
++			if (priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_dapm_widget byt_aic3100_widgets[] = {
++	SND_SOC_DAPM_HP("Headphone", NULL),
++	SND_SOC_DAPM_MIC("Headset Mic", NULL),
++	SND_SOC_DAPM_MIC("Internal Mic", NULL),
++	SND_SOC_DAPM_SPK("Speakers", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
++
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_audio_map[] = {
++	/* External Speakers: HFL, HFR */
++	{"Speakers", NULL, "SPK"},
++
++	/* Headset Stereophone(Headphone): HSOL, HSOR */
++	{"Headphone", NULL, "HPL"},
++	{"Headphone", NULL, "HPR"},
++
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Internal Mic", NULL, "Platform Clock"},
++	{"Speaker", NULL, "Platform Clock"},
++
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_intmic_in1_map[] = {
++	{"micbias", NULL, "Internal Mic"},
++	/* Headset Mic: Headset Mic with bias */
++	{"MIC1RP", NULL, "Headset Mic"},
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_ssp2_aif_map[] = {
++	{"ssp2 Tx", NULL, "codec_out0"},
++	{"ssp2 Tx", NULL, "codec_out1"},
++	{"codec_in0", NULL, "ssp2 Rx"},
++	{"codec_in1", NULL, "ssp2 Rx"},
++
++	{"Playback", NULL, "ssp2 Tx"},
++	{"ssp2 Rx", NULL, "Capture"},
++};
++
++static const struct snd_soc_dapm_route byt_aic3100_ssp0_aif_map[] = {
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx"},
++
++	{"Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Rx", NULL, "Capture"},
++};
++
++static const struct snd_kcontrol_new byt_aic3100_controls[] = {
++	SOC_DAPM_PIN_SWITCH("Headphone"),
++	SOC_DAPM_PIN_SWITCH("Headset Mic"),
++	SOC_DAPM_PIN_SWITCH("Internal Mic"),
++	SOC_DAPM_PIN_SWITCH("Speaker"),
++};
++
++static int byt_aic3100_aif1_hw_params(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_dai *codec_dai = rtd->codec_dai;
++	int ret;
++
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					25000000,
++					SND_SOC_CLOCK_IN);
++	} else {
++		ret = snd_soc_dai_set_sysclk(codec_dai, AIC31XX_PLL_CLKIN_MCLK,
++					19200000,
++					SND_SOC_CLOCK_IN);
++	}
++
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec clock %d\n", ret);
++		return ret;
++	}
++
++	if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ) {
++		ret = snd_soc_dai_set_pll(codec_dai, 0, AIC31XX_PLL_CLKIN_MCLK,
++					250000000, params_rate(params));
++	} else {
++		ret = snd_soc_dai_set_pll(codec_dai, 0, AIC31XX_PLL_CLKIN_MCLK,
++					19200000, params_rate(params));
++
++	}
++
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++/* uncomment when we have an actual quirk
++static int byt_aic3100_quirk_cb(const struct dmi_system_id *id)
++{
++	byt_aic3100_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++*/
++
++static const struct dmi_system_id byt_aic3100_quirk_table[] = {
++	{}
++};
++
++static int byt_aic3100_init(struct snd_soc_pcm_runtime *runtime)
++{
++	int ret;
++	struct snd_soc_card *card = runtime->card;
++	const struct snd_soc_dapm_route *custom_map;
++	struct byt_aic3100_private *priv = snd_soc_card_get_drvdata(card);
++	int num_routes;
++
++	card->dapm.idle_bias_off = true;
++
++	ret = snd_soc_add_card_controls(card, byt_aic3100_controls,
++					ARRAY_SIZE(byt_aic3100_controls));
++	if (ret) {
++		dev_err(card->dev, "unable to add card controls\n");
++		return ret;
++	}
++
++	switch (BYT_AIC3100_MAP(byt_aic3100_quirk)) {
++	case BYT_AIC3100_IN1_MAP:
++	default:
++		custom_map = byt_aic3100_intmic_in1_map;
++		num_routes = ARRAY_SIZE(byt_aic3100_intmic_in1_map);
++	}
++
++	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
++	if (ret)
++		return ret;
++
++	if (byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					byt_aic3100_ssp2_aif_map,
++					ARRAY_SIZE(byt_aic3100_ssp2_aif_map));
++	} else {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					byt_aic3100_ssp0_aif_map,
++					ARRAY_SIZE(byt_aic3100_ssp0_aif_map));
++	}
++	if (ret)
++		return ret;
++
++	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
++	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
++
++	if (priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		if (byt_aic3100_quirk & BYT_AIC3100_MCLK_25MHZ)
++			ret = clk_set_rate(priv->mclk, 25000000);
++		else
++			ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
++	return ret;
++}
++
++static const struct snd_soc_pcm_stream byt_aic3100_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int byt_aic3100_codec_fixup(struct snd_soc_pcm_runtime *rtd,
++			    struct snd_pcm_hw_params *params)
++{
++	struct snd_interval *rate = hw_param_interval(params,
++			SNDRV_PCM_HW_PARAM_RATE);
++	struct snd_interval *channels = hw_param_interval(params,
++						SNDRV_PCM_HW_PARAM_CHANNELS);
++	int ret;
++
++	/* The DSP will covert the FE rate to 48k, stereo */
++	rate->min = rate->max = 48000;
++	channels->min = channels->max = 2;
++
++	if (!(byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF)) {
++
++		/* set SSP0 to 16-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
++
++		/*
++		 * Default mode for SSP configuration is TDM 4 slot,
++		 * override config with explicit setting to I2S 2ch 16-bit.
++		 * The word length is set with dai_set_tdm_slot() since there
++		 * is no other API exposed
++		 */
++		ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
++					SND_SOC_DAIFMT_I2S     |
++					SND_SOC_DAIFMT_NB_IF   |
++					SND_SOC_DAIFMT_CBS_CFS
++			);
++		if (ret < 0) {
++			dev_err(rtd->dev,
++				"can't set format to I2S, err %d\n", ret);
++			return ret;
++		}
++
++		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
++		if (ret < 0) {
++			dev_err(rtd->dev,
++				"can't set I2S config, err %d\n", ret);
++			return ret;
++		}
++
++	} else {
++
++		/* set SSP2 to 24-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++
++		/*
++		 * Default mode for SSP configuration is TDM 4 slot,
++		 * override config with explicit setting to I2S 2ch 24-bit.
++		 * The word length is set with dai_set_tdm_slot() since
++		 * there is no other API exposed
++		 */
++		ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
++					SND_SOC_DAIFMT_I2S     |
++					SND_SOC_DAIFMT_NB_IF   |
++					SND_SOC_DAIFMT_CBS_CFS
++			);
++		if (ret < 0) {
++			dev_err(rtd->dev,
++				"can't set format to I2S, err %d\n", ret);
++			return ret;
++		}
++
++		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
++		if (ret < 0) {
++			dev_err(rtd->dev,
++				"can't set I2S config, err %d\n", ret);
++			return ret;
++		}
++	}
++	return 0;
++}
++
++static int byt_aic3100_aif1_startup(struct snd_pcm_substream *substream)
++{
++	return snd_pcm_hw_constraint_single(substream->runtime,
++			SNDRV_PCM_HW_PARAM_RATE, 48000);
++}
++
++static struct snd_soc_ops byt_aic3100_aif1_ops = {
++	.startup = byt_aic3100_aif1_startup,
++};
++
++static struct snd_soc_ops byt_aic3100_be_ssp2_ops = {
++	.hw_params = byt_aic3100_aif1_hw_params,
++};
++
++static struct snd_soc_dai_link byt_aic3100_dais[] = {
++	[MERR_DPCM_AUDIO] = {
++		.name = "Baytrail Audio Port",
++		.stream_name = "Baytrail Audio",
++		.cpu_dai_name = "media-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++		.ignore_suspend = 1,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.ops = &byt_aic3100_aif1_ops,
++	},
++	[MERR_DPCM_DEEP_BUFFER] = {
++		.name = "Deep-Buffer Audio Port",
++		.stream_name = "Deep-Buffer Audio",
++		.cpu_dai_name = "deepbuffer-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++		.ignore_suspend = 1,
++		.nonatomic = true,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.ops = &byt_aic3100_aif1_ops,
++	},
++	[MERR_DPCM_COMPR] = {
++		.name = "Baytrail Compressed Port",
++		.stream_name = "Baytrail Compress",
++		.cpu_dai_name = "compress-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++	},
++		/* back ends */
++	{
++		.name = "SSP2-Codec",
++		.id = 1,
++		.cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
++		.platform_name = "sst-mfld-platform",
++		.no_pcm = 1,
++		.codec_dai_name = "tlv320aic31xx-hifi",
++		.codec_name = "i2c-10TI3100:00", /* overwritten with HID */
++		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
++						| SND_SOC_DAIFMT_CBS_CFS,
++		.be_hw_params_fixup = byt_aic3100_codec_fixup,
++		.ignore_suspend = 1,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.init = byt_aic3100_init,
++		.ops = &byt_aic3100_be_ssp2_ops,
++	},
++};
++
++/* SoC card */
++static struct snd_soc_card byt_aic3100_card = {
++	.name = "bytcr-aic3100",
++	.owner = THIS_MODULE,
++	.dai_link = byt_aic3100_dais,
++	.num_links = ARRAY_SIZE(byt_aic3100_dais),
++	.dapm_widgets = byt_aic3100_widgets,
++	.num_dapm_widgets = ARRAY_SIZE(byt_aic3100_widgets),
++	.dapm_routes = byt_aic3100_audio_map,
++	.num_dapm_routes = ARRAY_SIZE(byt_aic3100_audio_map),
++	.fully_routed = true,
++};
++
++static char byt_aic3100_codec_name[16];/* i2c-<HID>:00 with HID being 8 chars */
++static char byt_aic3100_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++
++static int snd_byt_aic3100_mc_probe(struct platform_device *pdev)
++{
++	int ret_val = 0;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	int i;
++	int dai_index;
++	struct byt_aic3100_private *priv;
++	bool is_bytcr = false;
++
++	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
++	if (!priv)
++		return -ENOMEM;
++
++	/* register the soc card */
++	byt_aic3100_card.dev = &pdev->dev;
++	mach = byt_aic3100_card.dev->platform_data;
++	snd_soc_card_set_drvdata(&byt_aic3100_card, priv);
++
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(byt_aic3100_dais); i++) {
++		if (!strcmp(byt_aic3100_dais[i].codec_name,
++				"i2c-10TI3100:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(byt_aic3100_codec_name, sizeof(byt_aic3100_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		byt_aic3100_dais[dai_index].codec_name = byt_aic3100_codec_name;
++	}
++
++	/*
++	 * swap SSP0 if bytcr is detected
++	 * (will be overridden if DMI quirk is detected)
++	 */
++	if (is_valleyview()) {
++		struct sst_platform_info *p_info = mach->pdata;
++		const struct sst_res_info *res_info = p_info->res_info;
++
++		if (res_info->acpi_ipc_irq_index == 0) {
++			is_bytcr = true;
++			byt_aic3100_quirk &= ~BYT_AIC3100_SSP2_AIF;
++		}
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(byt_aic3100_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if (!(byt_aic3100_quirk & BYT_AIC3100_SSP2_AIF))  {
++
++		/* fixup cpu dai name name */
++		snprintf(byt_aic3100_cpu_dai_name,
++			sizeof(byt_aic3100_cpu_dai_name),
++			"%s", "ssp0-port");
++
++		byt_aic3100_dais[dai_index].cpu_dai_name =
++			byt_aic3100_cpu_dai_name;
++	}
++
++	if (is_valleyview()) {
++		priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(priv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(priv->mclk));
++
++			/*
++			 * Audio output only works with MCLK enabled
++			 */
++			return PTR_ERR(priv->mclk);
++		}
++	}
++
++	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_aic3100_card);
++
++	if (ret_val) {
++		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
++			ret_val);
++		return ret_val;
++	}
++	platform_set_drvdata(pdev, &byt_aic3100_card);
++	return ret_val;
++}
++
++static struct platform_driver snd_byt_aic3100_mc_driver = {
++	.driver = {
++		.name = "bytcr_aic3100",
++		.pm = &snd_soc_pm_ops,
++	},
++	.probe = snd_byt_aic3100_mc_probe,
++};
++
++module_platform_driver(snd_byt_aic3100_mc_driver);
++
++MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
++MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:bytcr_aic3100");
+diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
+index bff77a1f27fc..c2ecbdc28459 100644
+--- a/sound/soc/intel/boards/bytcr_rt5640.c
++++ b/sound/soc/intel/boards/bytcr_rt5640.c
+@@ -389,6 +389,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
+ 						 BYT_RT5640_SSP0_AIF1),
+ 
+ 	},
++	{
++		.callback = byt_rt5640_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
++		},
++		.driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
++						 BYT_RT5640_MCLK_EN |
++						 BYT_RT5640_SSP0_AIF1),
++
++	},
+ 	{}
+ };
+ 
+@@ -689,6 +699,10 @@ static bool is_valleyview(void)
+ 	return true;
+ }
+ 
++struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */
++	u64 aif_value;       /* 1: AIF1, 2: AIF2 */
++	u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */
++};
+ 
+ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ {
+@@ -698,6 +712,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 	int i;
+ 	int dai_index;
+ 	struct byt_rt5640_private *priv;
++	bool is_bytcr = false;
+ 
+ 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+ 	if (!priv)
+@@ -734,8 +749,52 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 		struct sst_platform_info *p_info = mach->pdata;
+ 		const struct sst_res_info *res_info = p_info->res_info;
+ 
+-		/* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
+-		if (res_info->acpi_ipc_irq_index == 0) {
++		if (res_info->acpi_ipc_irq_index == 0)
++			is_bytcr = true;
++	}
++
++	if (is_bytcr) {
++		/*
++		 * Baytrail CR platforms may have CHAN package in BIOS, try
++		 * to find relevant routing quirk based as done on Windows
++		 * platforms. We have to read the information directly from the
++		 * BIOS, at this stage the card is not created and the links
++		 * with the codec driver/pdata are non-existent
++		 */
++
++		struct acpi_chan_package chan_package;
++
++		/* format specified: 2 64-bit integers */
++		struct acpi_buffer format = {sizeof("NN"), "NN"};
++		struct acpi_buffer state = {0, NULL};
++		struct sst_acpi_package_context pkg_ctx;
++		bool pkg_found = false;
++
++		state.length = sizeof(chan_package);
++		state.pointer = &chan_package;
++
++		pkg_ctx.name = "CHAN";
++		pkg_ctx.length = 2;
++		pkg_ctx.format = &format;
++		pkg_ctx.state = &state;
++		pkg_ctx.data_valid = false;
++
++		pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
++		if (pkg_found) {
++			if (chan_package.aif_value == 1) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
++				byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1;
++			} else  if (chan_package.aif_value == 2) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
++				byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
++			} else {
++				dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
++				pkg_found = false;
++			}
++		}
++
++		if (!pkg_found) {
++			/* no BIOS indications, assume SSP0-AIF2 connection */
+ 			byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ 		}
+ 	}
+@@ -774,7 +833,21 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
+ 			dev_err(&pdev->dev,
+ 				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+ 				PTR_ERR(priv->mclk));
+-			return PTR_ERR(priv->mclk);
++
++			if (is_bytcr) {
++				/*
++				 * Audio output on Baytrail CR only works
++				 * with MCLK enabled
++				 */
++				return PTR_ERR(priv->mclk);
++			} else {
++				/*
++				 * Audio output can work with bitclock only
++				 * on Baytrail, with a limited audio quality
++				 * degradation
++				 */
++				byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN;
++			}
+ 		}
+ 	}
+ 
+diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
+index 35f591eab3c9..1c672768fc35 100644
+--- a/sound/soc/intel/boards/bytcr_rt5651.c
++++ b/sound/soc/intel/boards/bytcr_rt5651.c
+@@ -24,21 +24,134 @@
+ #include <linux/device.h>
+ #include <linux/dmi.h>
+ #include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+ #include <sound/jack.h>
+ #include "../../codecs/rt5651.h"
+ #include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++
++enum {
++	BYT_RT5651_DMIC1_MAP,
++	BYT_RT5651_DMIC2_MAP,
++	BYT_RT5651_IN1_MAP,
++};
++
++#define BYT_RT5651_MAP(quirk)	((quirk) & 0xff)
++#define BYT_RT5651_DMIC_EN	BIT(16)
++#define BYT_RT5651_MCLK_EN	BIT(17)
++#define BYT_RT5651_MCLK_25MHZ	BIT(18)
++
++struct byt_rt5651_private {
++	struct clk *mclk;
++};
++
++static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
++					BYT_RT5651_DMIC_EN |
++	                                BYT_RT5651_MCLK_EN;
++
++static void log_quirks(struct device *dev)
++{
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC1_MAP)
++		dev_info(dev, "quirk DMIC1_MAP enabled");
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC2_MAP)
++		dev_info(dev, "quirk DMIC2_MAP enabled");
++	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP)
++		dev_info(dev, "quirk IN1_MAP enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
++		dev_info(dev, "quirk DMIC enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
++		dev_info(dev, "quirk MCLK_EN enabled");
++	if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
++		dev_info(dev, "quirk MCLK_25MHZ enabled");
++}
++
++#define BYT_CODEC_DAI1	"rt5651-aif1"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
++					     48000 * 512,
++					     SND_SOC_CLOCK_IN);
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK,
++					     0,
++					     SND_SOC_CLOCK_IN);
++		if (!ret) {
++			if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
+ 
+ static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = {
+ 	SND_SOC_DAPM_HP("Headphone", NULL),
+ 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ 	SND_SOC_DAPM_MIC("Internal Mic", NULL),
+ 	SND_SOC_DAPM_SPK("Speaker", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
++
+ };
+ 
+ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = {
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Internal Mic", NULL, "Platform Clock"},
++	{"Speaker", NULL, "Platform Clock"},
++
+ 	{"AIF1 Playback", NULL, "ssp2 Tx"},
+ 	{"ssp2 Tx", NULL, "codec_out0"},
+ 	{"ssp2 Tx", NULL, "codec_out1"},
+@@ -67,18 +180,6 @@ static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = {
+ 	{"IN1P", NULL, "Internal Mic"},
+ };
+ 
+-enum {
+-	BYT_RT5651_DMIC1_MAP,
+-	BYT_RT5651_DMIC2_MAP,
+-	BYT_RT5651_IN1_MAP,
+-};
+-
+-#define BYT_RT5651_MAP(quirk)	((quirk) & 0xff)
+-#define BYT_RT5651_DMIC_EN	BIT(16)
+-
+-static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
+-					BYT_RT5651_DMIC_EN;
+-
+ static const struct snd_kcontrol_new byt_rt5651_controls[] = {
+ 	SOC_DAPM_PIN_SWITCH("Headphone"),
+ 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+@@ -103,9 +204,27 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
+ 		return ret;
+ 	}
+ 
+-	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1,
+-				  params_rate(params) * 50,
+-				  params_rate(params) * 512);
++	if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) {
++		/* 2x25 bit slots on SSP2 */
++		ret = snd_soc_dai_set_pll(codec_dai, 0,
++					RT5651_PLL1_S_BCLK1,
++					params_rate(params) * 50,
++					params_rate(params) * 512);
++	}
++	else {
++		if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) {
++			ret = snd_soc_dai_set_pll(codec_dai, 0,
++						RT5651_PLL1_S_MCLK,
++						25000000,
++						params_rate(params) * 512);
++		} else {
++			ret = snd_soc_dai_set_pll(codec_dai, 0,
++						RT5651_PLL1_S_MCLK,
++						19200000,
++						params_rate(params) * 512);
++		}
++	}
++
+ 	if (ret < 0) {
+ 		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ 		return ret;
+@@ -114,8 +233,22 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
+ 	return 0;
+ }
+ 
++static int byt_rt5651_quirk_cb(const struct dmi_system_id *id)
++{
++	byt_rt5651_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++
+ static const struct dmi_system_id byt_rt5651_quirk_table[] = {
+-	{}
++	{
++		.callback = byt_rt5651_quirk_cb,
++		.matches = {
++			DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
++			DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
++		},
++		.driver_data = (unsigned long *)(BYT_RT5651_DMIC1_MAP |
++						 BYT_RT5651_DMIC_EN),
++	},
+ };
+ 
+ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+@@ -123,6 +256,7 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 	int ret;
+ 	struct snd_soc_card *card = runtime->card;
+ 	const struct snd_soc_dapm_route *custom_map;
++	struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card);
+ 	int num_routes;
+ 
+ 	card->dapm.idle_bias_off = true;
+@@ -141,6 +275,9 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 		custom_map = byt_rt5651_intmic_dmic1_map;
+ 		num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map);
+ 	}
++	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
++	if (ret)
++		return ret;
+ 
+ 	ret = snd_soc_add_card_controls(card, byt_rt5651_controls,
+ 					ARRAY_SIZE(byt_rt5651_controls));
+@@ -151,6 +288,30 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
+ 	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ 	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ 
++	if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ)
++			ret = clk_set_rate(priv->mclk, 25000000);
++		else
++			ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
+ 	return ret;
+ }
+ 
+@@ -298,12 +459,73 @@ static struct snd_soc_card byt_rt5651_card = {
+ 	.fully_routed = true,
+ };
+ 
++static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
+ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
+ {
+ 	int ret_val = 0;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	int i;
++	int dai_index;
++	struct byt_rt5651_private *priv;
++
++	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
++	if (!priv)
++		return -ENOMEM;
+ 
+ 	/* register the soc card */
+ 	byt_rt5651_card.dev = &pdev->dev;
++	mach = byt_rt5651_card.dev->platform_data;
++
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) {
++		if (!strcmp(byt_rt5651_dais[i].codec_name, "i2c-10EC5651:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name;
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(byt_rt5651_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if ((byt_rt5651_quirk & BYT_RT5651_MCLK_EN) && (is_valleyview())) {
++		priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(priv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(priv->mclk));
++			/*
++			 * Audio output can work with bitclock only
++			 * on Baytrail, with a limited audio quality
++			 * degradation
++			 */
++			byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN;
++		}
++	}
+ 
+ 	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
+ 
+diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+index cdcced9f32b6..a95ae141e3a6 100644
+--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
++++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+@@ -23,6 +23,9 @@
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+ #include <linux/acpi.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+@@ -37,6 +40,7 @@
+ struct cht_mc_private {
+ 	struct snd_soc_jack jack;
+ 	bool ts3a227e_present;
++	struct clk *mclk;
+ };
+ 
+ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+@@ -51,11 +55,51 @@ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+ 	return NULL;
+ }
+ 
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++					  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = cht_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (ctx->mclk) {
++			ret = clk_prepare_enable(ctx->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++	} else {
++
++		/* FIXME: if there is no clock can jack detection work ? */
++
++		if (ctx->mclk) {
++			clk_disable_unprepare(ctx->mclk);
++		}
++	}
++
++	return 0;
++}
++
++
+ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ 	SND_SOC_DAPM_HP("Headphone", NULL),
+ 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ 	SND_SOC_DAPM_MIC("Int Mic", NULL),
+ 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			    platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			    SND_SOC_DAPM_POST_PMD),
+ };
+ 
+ static const struct snd_soc_dapm_route cht_audio_map[] = {
+@@ -72,6 +116,10 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
+ 	{"codec_in0", NULL, "ssp2 Rx" },
+ 	{"codec_in1", NULL, "ssp2 Rx" },
+ 	{"ssp2 Rx", NULL, "HiFi Capture"},
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Int Mic", NULL, "Platform Clock"},
++	{"Ext Spk", NULL, "Platform Clock"},
+ };
+ 
+ static const struct snd_kcontrol_new cht_mc_controls[] = {
+@@ -153,6 +201,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 	if (ctx->ts3a227e_present)
+ 		snd_soc_jack_notifier_register(jack, &cht_jack_nb);
+ 
++	if (ctx->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(ctx->mclk);
++		if (!ret)
++			clk_disable_unprepare(ctx->mclk);
++
++		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
++
++		if (ret)
++			dev_err(runtime->dev, "unable to set MCLK rate\n");
++	}
+ 	return ret;
+ }
+ 
+@@ -287,6 +355,18 @@ static struct snd_soc_card snd_soc_card_cht = {
+ 	.num_controls = ARRAY_SIZE(cht_mc_controls),
+ };
+ 
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
+ static int snd_cht_mc_probe(struct platform_device *pdev)
+ {
+ 	int ret_val = 0;
+@@ -306,6 +386,17 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
+ 	/* register the soc card */
+ 	snd_soc_card_cht.dev = &pdev->dev;
+ 	snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
++
++	if (is_valleyview()) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			return PTR_ERR(drv->mclk);
++		}
++	}
++
+ 	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ 	if (ret_val) {
+ 		dev_err(&pdev->dev,
+diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
+index 16c94c45ce50..e792f9849f98 100644
+--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
++++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
+@@ -23,7 +23,11 @@
+ #include <linux/module.h>
+ #include <linux/acpi.h>
+ #include <linux/platform_device.h>
++#include <linux/dmi.h>
+ #include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+@@ -33,7 +37,8 @@
+ #include "../common/sst-acpi.h"
+ 
+ #define CHT_PLAT_CLK_3_HZ	19200000
+-#define CHT_CODEC_DAI	"rt5645-aif1"
++#define CHT_CODEC_DAI1	"rt5645-aif1"
++#define CHT_CODEC_DAI2	"rt5645-aif2"
+ 
+ struct cht_acpi_card {
+ 	char *codec_id;
+@@ -45,15 +50,36 @@ struct cht_mc_private {
+ 	struct snd_soc_jack jack;
+ 	struct cht_acpi_card *acpi_card;
+ 	char codec_name[16];
++	struct clk *mclk;
+ };
+ 
++#define CHT_RT5645_MAP(quirk)	((quirk) & 0xff)
++#define CHT_RT5645_SSP2_AIF2     BIT(16) /* default is using AIF1  */
++#define CHT_RT5645_SSP0_AIF1     BIT(17)
++#define CHT_RT5645_SSP0_AIF2     BIT(18)
++
++static unsigned long cht_rt5645_quirk = 0;
++
++static void log_quirks(struct device *dev)
++{
++	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2)
++		dev_info(dev, "quirk SSP2_AIF2 enabled");
++	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1)
++		dev_info(dev, "quirk SSP0_AIF1 enabled");
++	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
++		dev_info(dev, "quirk SSP0_AIF2 enabled");
++}
++
+ static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+ {
+ 	struct snd_soc_pcm_runtime *rtd;
+ 
+ 	list_for_each_entry(rtd, &card->rtd_list, list) {
+-		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+-			     strlen(CHT_CODEC_DAI)))
++		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1,
++			     strlen(CHT_CODEC_DAI1)))
++			return rtd->codec_dai;
++		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2,
++			     strlen(CHT_CODEC_DAI2)))
+ 			return rtd->codec_dai;
+ 	}
+ 	return NULL;
+@@ -65,6 +91,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ 	struct snd_soc_dapm_context *dapm = w->dapm;
+ 	struct snd_soc_card *card = dapm->card;
+ 	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ 	int ret;
+ 
+ 	codec_dai = cht_get_codec_dai(card);
+@@ -73,19 +100,30 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ 		return -EIO;
+ 	}
+ 
+-	if (!SND_SOC_DAPM_EVENT_OFF(event))
+-		return 0;
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if (ctx->mclk) {
++			ret = clk_prepare_enable(ctx->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++	} else {
++		/* Set codec sysclk source to its internal clock because codec PLL will
++		 * be off when idle and MCLK will also be off when codec is
++		 * runtime suspended. Codec needs clock for jack detection and button
++		 * press. MCLK is turned off with clock framework or ACPI.
++		 */
++		ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
++					0, SND_SOC_CLOCK_IN);
++		if (ret < 0) {
++			dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++			return ret;
++		}
+ 
+-	/* Set codec sysclk source to its internal clock because codec PLL will
+-	 * be off when idle and MCLK will also be off by ACPI when codec is
+-	 * runtime suspended. Codec needs clock for jack detection and button
+-	 * press.
+-	 */
+-	ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+-			0, SND_SOC_CLOCK_IN);
+-	if (ret < 0) {
+-		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+-		return ret;
++		if (ctx->mclk)
++			clk_disable_unprepare(ctx->mclk);
+ 	}
+ 
+ 	return 0;
+@@ -97,7 +135,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ 	SND_SOC_DAPM_MIC("Int Mic", NULL),
+ 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ 	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+-			platform_clock_control, SND_SOC_DAPM_POST_PMD),
++			platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ };
+ 
+ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
+@@ -109,12 +147,6 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
+ 	{"Headphone", NULL, "HPOR"},
+ 	{"Ext Spk", NULL, "SPOL"},
+ 	{"Ext Spk", NULL, "SPOR"},
+-	{"AIF1 Playback", NULL, "ssp2 Tx"},
+-	{"ssp2 Tx", NULL, "codec_out0"},
+-	{"ssp2 Tx", NULL, "codec_out1"},
+-	{"codec_in0", NULL, "ssp2 Rx" },
+-	{"codec_in1", NULL, "ssp2 Rx" },
+-	{"ssp2 Rx", NULL, "AIF1 Capture"},
+ 	{"Headphone", NULL, "Platform Clock"},
+ 	{"Headset Mic", NULL, "Platform Clock"},
+ 	{"Int Mic", NULL, "Platform Clock"},
+@@ -130,16 +162,42 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
+ 	{"Headphone", NULL, "HPOR"},
+ 	{"Ext Spk", NULL, "SPOL"},
+ 	{"Ext Spk", NULL, "SPOR"},
++	{"Headphone", NULL, "Platform Clock"},
++	{"Headset Mic", NULL, "Platform Clock"},
++	{"Int Mic", NULL, "Platform Clock"},
++	{"Ext Spk", NULL, "Platform Clock"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = {
+ 	{"AIF1 Playback", NULL, "ssp2 Tx"},
+ 	{"ssp2 Tx", NULL, "codec_out0"},
+ 	{"ssp2 Tx", NULL, "codec_out1"},
+ 	{"codec_in0", NULL, "ssp2 Rx" },
+ 	{"codec_in1", NULL, "ssp2 Rx" },
+ 	{"ssp2 Rx", NULL, "AIF1 Capture"},
+-	{"Headphone", NULL, "Platform Clock"},
+-	{"Headset Mic", NULL, "Platform Clock"},
+-	{"Int Mic", NULL, "Platform Clock"},
+-	{"Ext Spk", NULL, "Platform Clock"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = {
++	{"AIF2 Playback", NULL, "ssp2 Tx"},
++	{"ssp2 Tx", NULL, "codec_out0"},
++	{"ssp2 Tx", NULL, "codec_out1"},
++	{"codec_in0", NULL, "ssp2 Rx" },
++	{"codec_in1", NULL, "ssp2 Rx" },
++	{"ssp2 Rx", NULL, "AIF2 Capture"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = {
++	{"AIF1 Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx" },
++	{"ssp0 Rx", NULL, "AIF1 Capture"},
++};
++
++static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = {
++	{"AIF2 Playback", NULL, "ssp0 Tx"},
++	{"ssp0 Tx", NULL, "modem_out"},
++	{"modem_in", NULL, "ssp0 Rx" },
++	{"ssp0 Rx", NULL, "AIF2 Capture"},
+ };
+ 
+ static const struct snd_kcontrol_new cht_mc_controls[] = {
+@@ -185,11 +243,25 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ 	return 0;
+ }
+ 
++/* uncomment when we have a real quirk
++static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
++{
++	cht_rt5645_quirk = (unsigned long)id->driver_data;
++	return 1;
++}
++*/
++
++static const struct dmi_system_id cht_rt5645_quirk_table[] = {
++	{
++	},
++};
++
+ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ {
+ 	int ret;
+ 	int jack_type;
+ 	struct snd_soc_codec *codec = runtime->codec;
++	struct snd_soc_card *card = runtime->card;
+ 	struct snd_soc_dai *codec_dai = runtime->codec_dai;
+ 	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+ 
+@@ -201,6 +273,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 				RT5645_AD_STEREO_FILTER,
+ 				RT5645_CLK_SEL_I2S1_ASRC);
+ 
++	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp2_aif2_map,
++					ARRAY_SIZE(cht_rt5645_ssp2_aif2_map));
++	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp0_aif1_map,
++					ARRAY_SIZE(cht_rt5645_ssp0_aif1_map));
++	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp0_aif2_map,
++					ARRAY_SIZE(cht_rt5645_ssp0_aif2_map));
++	} else {
++		ret = snd_soc_dapm_add_routes(&card->dapm,
++					cht_rt5645_ssp2_aif1_map,
++					ARRAY_SIZE(cht_rt5645_ssp2_aif1_map));
++	}
++	if (ret)
++		return ret;
++
+ 	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ 	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ 	if (ret < 0) {
+@@ -225,12 +317,33 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+ 
+ 	rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
+ 
++	if (ctx->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(ctx->mclk);
++		if (!ret)
++			clk_disable_unprepare(ctx->mclk);
++
++		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
++
++		if (ret)
++			dev_err(runtime->dev, "unable to set MCLK rate\n");
++	}
+ 	return ret;
+ }
+ 
+ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ 			    struct snd_pcm_hw_params *params)
+ {
++	int ret;
+ 	struct snd_interval *rate = hw_param_interval(params,
+ 			SNDRV_PCM_HW_PARAM_RATE);
+ 	struct snd_interval *channels = hw_param_interval(params,
+@@ -240,8 +353,39 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ 	rate->min = rate->max = 48000;
+ 	channels->min = channels->max = 2;
+ 
+-	/* set SSP2 to 24-bit */
+-	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* set SSP0 to 16-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
++
++		/*
++		 * Default mode for SSP configuration is TDM 4 slot, override config
++		 * with explicit setting to I2S 2ch 16-bit. The word length is set with
++		 * dai_set_tdm_slot() since there is no other API exposed
++		 */
++		ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
++					SND_SOC_DAIFMT_I2S     |
++					SND_SOC_DAIFMT_NB_IF   |
++					SND_SOC_DAIFMT_CBS_CFS
++			);
++		if (ret < 0) {
++			dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
++			return ret;
++		}
++
++		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
++		if (ret < 0) {
++			dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
++			return ret;
++		}
++
++	} else {
++
++		/* set SSP2 to 24-bit */
++		params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++
++	}
+ 	return 0;
+ }
+ 
+@@ -344,10 +488,31 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
+ static struct cht_acpi_card snd_soc_cards[] = {
+ 	{"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ 	{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
++	{"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
++	{"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+ 	{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
+ };
+ 
+-static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++static char cht_rt5645_codec_aif_name[12]; /*  = "rt5645-aif[1|2]" */
++static char cht_rt5645_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */
++	u64 aif_value;       /* 1: AIF1, 2: AIF2 */
++	u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */
++};
+ 
+ static int snd_cht_mc_probe(struct platform_device *pdev)
+ {
+@@ -358,22 +523,33 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
+ 	struct sst_acpi_mach *mach;
+ 	const char *i2c_name = NULL;
+ 	int dai_index = 0;
++	bool found = false;
++	bool is_bytcr = false;
+ 
+ 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ 	if (!drv)
+ 		return -ENOMEM;
+ 
++	mach = (&pdev->dev)->platform_data;
++
+ 	for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
+-		if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
++		if (acpi_dev_found(snd_soc_cards[i].codec_id) &&
++			(!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {
+ 			dev_dbg(&pdev->dev,
+ 				"found codec %s\n", snd_soc_cards[i].codec_id);
+ 			card = snd_soc_cards[i].soc_card;
+ 			drv->acpi_card = &snd_soc_cards[i];
++			found = true;
+ 			break;
+ 		}
+ 	}
++
++	if (!found) {
++		dev_err(&pdev->dev, "No matching HID found in supported list\n");
++		return -ENODEV;
++	}
++
+ 	card->dev = &pdev->dev;
+-	mach = card->dev->platform_data;
+ 	sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+ 
+ 	/* set correct codec name */
+@@ -386,9 +562,105 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
+ 	/* fixup codec name based on HID */
+ 	i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ 	if (i2c_name != NULL) {
+-		snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
++		snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
+ 			"%s%s", "i2c-", i2c_name);
+-		cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
++		cht_dailink[dai_index].codec_name = cht_rt5645_codec_name;
++	}
++
++	/*
++	 * swap SSP0 if bytcr is detected
++	 * (will be overridden if DMI quirk is detected)
++	 */
++	if (is_valleyview()) {
++		struct sst_platform_info *p_info = mach->pdata;
++		const struct sst_res_info *res_info = p_info->res_info;
++
++		if (res_info->acpi_ipc_irq_index == 0)
++			is_bytcr = true;
++	}
++
++	if (is_bytcr) {
++		/*
++		 * Baytrail CR platforms may have CHAN package in BIOS, try
++		 * to find relevant routing quirk based as done on Windows
++		 * platforms. We have to read the information directly from the
++		 * BIOS, at this stage the card is not created and the links
++		 * with the codec driver/pdata are non-existent
++		 */
++
++		struct acpi_chan_package chan_package;
++
++		/* format specified: 2 64-bit integers */
++		struct acpi_buffer format = {sizeof("NN"), "NN"};
++		struct acpi_buffer state = {0, NULL};
++		struct sst_acpi_package_context pkg_ctx;
++		bool pkg_found = false;
++
++		state.length = sizeof(chan_package);
++		state.pointer = &chan_package;
++
++		pkg_ctx.name = "CHAN";
++		pkg_ctx.length = 2;
++		pkg_ctx.format = &format;
++		pkg_ctx.state = &state;
++		pkg_ctx.data_valid = false;
++
++		pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
++		if (pkg_found) {
++			if (chan_package.aif_value == 1) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
++				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1;
++			} else  if (chan_package.aif_value == 2) {
++				dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
++				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
++			} else {
++				dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
++				pkg_found = false;
++			}
++		}
++
++		if (!pkg_found) {
++			/* no BIOS indications, assume SSP0-AIF2 connection */
++			cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
++		}
++	}
++
++	/* check quirks before creating card */
++	dmi_check_system(cht_rt5645_quirk_table);
++	log_quirks(&pdev->dev);
++
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* fixup codec aif name */
++		snprintf(cht_rt5645_codec_aif_name,
++			sizeof(cht_rt5645_codec_aif_name),
++			"%s", "rt5645-aif2");
++
++		cht_dailink[dai_index].codec_dai_name =
++			cht_rt5645_codec_aif_name;
++	}
++
++	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
++		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
++
++		/* fixup cpu dai name name */
++		snprintf(cht_rt5645_cpu_dai_name,
++			sizeof(cht_rt5645_cpu_dai_name),
++			"%s", "ssp0-port");
++
++		cht_dailink[dai_index].cpu_dai_name =
++			cht_rt5645_cpu_dai_name;
++	}
++
++	if (is_valleyview()) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			return PTR_ERR(drv->mclk);
++		}
+ 	}
+ 
+ 	snd_soc_card_set_drvdata(card, drv);
+diff --git a/sound/soc/intel/boards/cht_cx2072x.c b/sound/soc/intel/boards/cht_cx2072x.c
+new file mode 100644
+index 000000000000..30a97b201c9a
+--- /dev/null
++++ b/sound/soc/intel/boards/cht_cx2072x.c
+@@ -0,0 +1,453 @@
++/*
++ *  cht_cx207x.c - ASoc DPCM Machine driver for CherryTrail w/ CX2072x
++ *
++ *  Copyright (C) 2016 Intel Corp
++ *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
++ *
++ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; version 2 of the License.
++ *
++ *  This program is distributed in the hope that it will be useful, but
++ *  WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ *  General Public License for more details.
++ *
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/acpi.h>
++#include <linux/device.h>
++#include <linux/dmi.h>
++#include <linux/slab.h>
++#include <asm/cpu_device_id.h>
++#include <asm/platform_sst_audio.h>
++#include <linux/clk.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++#include "../../codecs/cx2072x.h"
++#include "../atom/sst-atom-controls.h"
++#include "../common/sst-acpi.h"
++
++
++#define CHT_PLAT_CLK_3_HZ	19200000
++
++enum {
++	CHT_CX_DMIC_MAP,
++};
++
++#define CHT_CX_MAP(quirk)	((quirk) & 0xff)
++#define CHT_CX_MCLK_EN	BIT(16)
++
++
++struct cht_mc_private {
++	struct clk *mclk;
++};
++
++static unsigned long cht_cx_quirk = CHT_CX_MCLK_EN;
++
++#define BYT_CODEC_DAI1	"cx2072x-hifi"
++
++static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
++{
++	struct snd_soc_pcm_runtime *rtd;
++
++	list_for_each_entry(rtd, &card->rtd_list, list) {
++		if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
++			     strlen(BYT_CODEC_DAI1)))
++			return rtd->codec_dai;
++	}
++	return NULL;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++				  struct snd_kcontrol *k, int  event)
++{
++	struct snd_soc_dapm_context *dapm = w->dapm;
++	struct snd_soc_card *card = dapm->card;
++	struct snd_soc_dai *codec_dai;
++	struct cht_mc_private *priv = snd_soc_card_get_drvdata(card);
++	int ret;
++
++	codec_dai = byt_get_codec_dai(card);
++	if (!codec_dai) {
++		dev_err(card->dev,
++			"Codec dai not found; Unable to set platform clock\n");
++		return -EIO;
++	}
++
++	if (SND_SOC_DAPM_EVENT_ON(event)) {
++		if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk) {
++			ret = clk_prepare_enable(priv->mclk);
++			if (ret < 0) {
++				dev_err(card->dev,
++					"could not configure MCLK state");
++				return ret;
++			}
++		}
++		ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++	} else {
++		/*
++		 * Set codec clock source to internal clock before
++		 * turning off the platform clock. Codec needs clock
++		 * for Jack detection and button press
++		 */
++
++		/* FIXME: what is the internal setting, if any? */
++		ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++
++		if (!ret) {
++			if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk)
++				clk_disable_unprepare(priv->mclk);
++		}
++	}
++
++	if (ret < 0) {
++		dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
++	SND_SOC_DAPM_HP("Headphone", NULL),
++	SND_SOC_DAPM_MIC("Headset Mic", NULL),
++	SND_SOC_DAPM_MIC("Int Mic", NULL),
++	SND_SOC_DAPM_SPK("Ext Spk", NULL),
++	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++			platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++			SND_SOC_DAPM_POST_PMD),
++};
++
++static const struct snd_soc_dapm_route cht_audio_map[] = {
++		/* External Speakers: HFL, HFR */
++		{"Headphone", NULL, "PORTA"},
++		{"Ext Spk", NULL, "PORTG"},
++		{"PORTC", NULL, "Int Mic"},
++		/* Headset Mic: Headset Mic with bias */
++		{"PORTD", NULL, "Headset Mic"},
++		/*{"Headset Bias", NULL, "Headset Mic"},*/
++		/*{"PortD Mic Bias", NULL, "Headset Mic"},*/
++
++		/* Headset Stereophone(Headphone): HSOL, HSOR */
++		//{"Headphone", NULL, "HPL"},
++		//{"Headphone", NULL, "HPR"},
++
++		{"Playback", NULL, "ssp2 Tx"},
++		{"ssp2 Tx", NULL, "codec_out0"},
++		{"ssp2 Tx", NULL, "codec_out1"},
++		{"codec_in0", NULL, "ssp2 Rx"},
++		{"codec_in1", NULL, "ssp2 Rx"},
++		{"ssp2 Rx", NULL, "Capture"},
++		{"ssp0 Tx", NULL, "modem_out"},
++		{"modem_in", NULL, "ssp0 Rx"},
++		{"Playback", NULL, "Platform Clock"},
++		{"Capture", NULL, "Platform Clock"},
++};
++
++static const struct snd_kcontrol_new cht_mc_controls[] = {
++	SOC_DAPM_PIN_SWITCH("Headphone"),
++	SOC_DAPM_PIN_SWITCH("Headset Mic"),
++	SOC_DAPM_PIN_SWITCH("Int Mic"),
++	SOC_DAPM_PIN_SWITCH("Ext Spk"),
++};
++
++static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
++			     struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_dai *codec_dai = rtd->codec_dai;
++	int ret;
++	
++	ret = snd_soc_dai_set_sysclk(codec_dai, CX2072X_MCLK_EXTERNAL_PLL,
++				     19200000, SND_SOC_CLOCK_IN);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
++		return ret;
++	}
++
++	/* FIXME: No need PLL for conexant codec */
++#if 0
++	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
++				  CHT_PLAT_CLK_3_HZ, rate * 512);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
++		return ret;
++	}
++#endif
++
++
++	return 0;
++}
++
++static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
++{
++	int ret=0;
++	struct snd_soc_card *card = runtime->card;
++	struct cht_mc_private *priv = snd_soc_card_get_drvdata(runtime->card);
++
++	card->dapm.idle_bias_off = true;
++
++	/* FIXME: is this necessary */
++//      snd_soc_dapm_ignore_suspend(&codec->dapm, "PortD");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Headset Mic");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Headphone");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Ext Spk");
++//	snd_soc_dapm_enable_pin(&card->dapm, "Int Mic");
++
++//	snd_soc_dapm_sync(&card->dapm);
++
++	if ((cht_cx_quirk & CHT_CX_MCLK_EN) && priv->mclk) {
++		/*
++		 * The firmware might enable the clock at
++		 * boot (this information may or may not
++		 * be reflected in the enable clock register).
++		 * To change the rate we must disable the clock
++		 * first to cover these cases. Due to common
++		 * clock framework restrictions that do not allow
++		 * to disable a clock that has not been enabled,
++		 * we need to enable the clock first.
++		 */
++		ret = clk_prepare_enable(priv->mclk);
++		if (!ret)
++			clk_disable_unprepare(priv->mclk);
++
++		ret = clk_set_rate(priv->mclk, 19200000);
++
++		if (ret)
++			dev_err(card->dev, "unable to set MCLK rate\n");
++	}
++
++	return ret;
++}
++
++static const struct snd_soc_pcm_stream cht_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
++			    struct snd_pcm_hw_params *params)
++{
++	struct snd_interval *rate = hw_param_interval(params,
++			SNDRV_PCM_HW_PARAM_RATE);
++	struct snd_interval *channels = hw_param_interval(params,
++						SNDRV_PCM_HW_PARAM_CHANNELS);
++	int ret;
++	
++	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
++	rate->min = rate->max = 48000;
++	channels->min = channels->max = 2;
++
++	/* set SSP2 to 24-bit */
++	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
++	
++	/*
++	 * Default mode for SSP configuration is TDM 4 slot, override config
++	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
++	 * dai_set_tdm_slot() since there is no other API exposed
++	 */
++	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
++				SND_SOC_DAIFMT_I2S     |
++				SND_SOC_DAIFMT_NB_IF   |
++				SND_SOC_DAIFMT_CBS_CFS);
++	if (ret < 0) {
++		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct snd_soc_pcm_stream cht_cx_dai_params = {
++	.formats = SNDRV_PCM_FMTBIT_S24_LE,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++static int cht_aif1_startup(struct snd_pcm_substream *substream)
++{
++	return snd_pcm_hw_constraint_single(substream->runtime,
++					SNDRV_PCM_HW_PARAM_RATE, 48000);
++}
++
++static struct snd_soc_ops cht_aif1_ops = {
++	.startup = cht_aif1_startup,
++};
++
++static struct snd_soc_ops cht_be_ssp2_ops = {
++	.hw_params = cht_aif1_hw_params,
++};
++
++static struct snd_soc_dai_link cht_dailink[] = {
++	[MERR_DPCM_AUDIO] = {
++		.name = "Audio Port",
++		.stream_name = "Audio",
++		.cpu_dai_name = "media-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++		.nonatomic = true,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.ops = &cht_aif1_ops,
++	},
++	[MERR_DPCM_DEEP_BUFFER] = {
++		.name = "Deep-Buffer Audio Port",
++		.stream_name = "Deep-Buffer Audio",
++		.cpu_dai_name = "deepbuffer-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++		.nonatomic = true,
++		.dynamic = 1,
++		.dpcm_playback = 1,
++		.ops = &cht_aif1_ops,
++	},
++	[MERR_DPCM_COMPR] = {
++		.name = "Compressed Port",
++		.stream_name = "Compress",
++		.cpu_dai_name = "compress-cpu-dai",
++		.codec_dai_name = "snd-soc-dummy-dai",
++		.codec_name = "snd-soc-dummy",
++		.platform_name = "sst-mfld-platform",
++	},
++	/* CODEC<->CODEC link */
++	/* back ends */
++	{
++		.name = "SSP2-Codec",
++		.id = 1,
++		.cpu_dai_name = "ssp2-port",
++		.platform_name = "sst-mfld-platform",
++		.no_pcm = 1,
++		.codec_dai_name = "cx2072x-hifi",
++		.codec_name = "i2c-14F10720:00",
++		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF
++		                              | SND_SOC_DAIFMT_CBS_CFS,
++		.init = cht_codec_init,
++		.be_hw_params_fixup = cht_codec_fixup,
++		.nonatomic = true,
++		.dpcm_playback = 1,
++		.dpcm_capture = 1,
++		.ops = &cht_be_ssp2_ops,
++	},
++};
++
++/* SoC card */
++static struct snd_soc_card chtcx2072x_card = {
++	.name = "chtcx2072x",
++	.dai_link = cht_dailink,
++	.num_links = ARRAY_SIZE(cht_dailink),
++	.dapm_widgets = cht_dapm_widgets,
++	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
++	.dapm_routes = cht_audio_map,
++	.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
++	.controls = cht_mc_controls,
++	.num_controls = ARRAY_SIZE(cht_mc_controls),
++};
++
++static char cht_cx_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
++
++static bool is_valleyview(void)
++{
++	static const struct x86_cpu_id cpu_ids[] = {
++		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
++		{}
++	};
++
++	if (!x86_match_cpu(cpu_ids))
++		return false;
++	return true;
++}
++
++static int snd_cht_mc_probe(struct platform_device *pdev)
++{
++	int ret_val = 0;
++	int i;
++	int dai_index;
++	struct cht_mc_private *drv;
++	struct sst_acpi_mach *mach;
++	const char *i2c_name = NULL;
++	
++	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
++	if (!drv) {
++		pr_err("allocation failed\n");
++		return -ENOMEM;
++	}
++
++	/* register the soc card */
++	chtcx2072x_card.dev = &pdev->dev;
++	mach = chtcx2072x_card.dev->platform_data;
++	snd_soc_card_set_drvdata(&chtcx2072x_card, drv);
++	
++	/* fix index of codec dai */
++	dai_index = MERR_DPCM_COMPR + 1;
++	for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
++		if (!strcmp(cht_dailink[i].codec_name, "i2c-14F10720:00")) {
++			dai_index = i;
++			break;
++		}
++	}
++
++	/* fixup codec name based on HID */
++	i2c_name = sst_acpi_find_name_from_hid(mach->id);
++	if (i2c_name != NULL) {
++		snprintf(cht_cx_codec_name, sizeof(cht_cx_codec_name),
++			"%s%s", "i2c-", i2c_name);
++
++		cht_dailink[dai_index].codec_name = cht_cx_codec_name;
++	}
++
++	if ((cht_cx_quirk & CHT_CX_MCLK_EN) && (is_valleyview())) {
++		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
++		if (IS_ERR(drv->mclk)) {
++			dev_err(&pdev->dev,
++				"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
++				PTR_ERR(drv->mclk));
++			
++			return PTR_ERR(drv->mclk);
++		}
++	}
++
++	ret_val = devm_snd_soc_register_card(&pdev->dev, &chtcx2072x_card);
++	
++	if (ret_val) {
++		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
++			ret_val);
++		return ret_val;
++	}
++	platform_set_drvdata(pdev, &chtcx2072x_card);
++	return ret_val;
++}
++
++
++
++static struct platform_driver snd_cht_mc_driver = {
++	.driver = {
++		.name = "cht-cx2072x",
++		.pm = &snd_soc_pm_ops,
++	},
++	.probe = snd_cht_mc_probe,
++};
++module_platform_driver(snd_cht_mc_driver);
++
++MODULE_DESCRIPTION("ASoC Intel(R) Cherrytrail Machine driver");
++MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:cht-cx2072x");
+diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
+index 012742299dd5..214e000667ae 100644
+--- a/sound/soc/intel/common/sst-acpi.h
++++ b/sound/soc/intel/common/sst-acpi.h
+@@ -15,14 +15,29 @@
+ #include <linux/stddef.h>
+ #include <linux/acpi.h>
+ 
+-/* translation fron HID to I2C name, needed for DAI codec_name */
++struct sst_acpi_package_context {
++	char *name;           /* package name */
++	int length;           /* number of elements */
++	struct acpi_buffer *format;
++	struct acpi_buffer *state;
++	bool data_valid;
++};
++
+ #if IS_ENABLED(CONFIG_ACPI)
++/* translation fron HID to I2C name, needed for DAI codec_name */
+ const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
++bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++				    struct sst_acpi_package_context *ctx);
+ #else
+ static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+ {
+ 	return NULL;
+ }
++static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++					   struct sst_acpi_package_context *ctx)
++{
++	return false;
++}
+ #endif
+ 
+ /* acpi match */
+diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c
+index 789843307a49..1070f3ad23e5 100644
+--- a/sound/soc/intel/common/sst-match-acpi.c
++++ b/sound/soc/intel/common/sst-match-acpi.c
+@@ -77,5 +77,62 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
+ }
+ EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
+ 
++static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level,
++					void *context, void **ret)
++{
++	struct acpi_device *adev;
++	acpi_status status = AE_OK;
++	struct sst_acpi_package_context *pkg_ctx = context;
++
++	pkg_ctx->data_valid = false;
++
++	if (acpi_bus_get_device(handle, &adev))
++		return AE_OK;
++
++	if (adev->status.present && adev->status.functional) {
++		struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
++		union acpi_object  *myobj = NULL;
++
++		status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
++						NULL, &buffer,
++						ACPI_TYPE_PACKAGE);
++		if (ACPI_FAILURE(status))
++			return AE_OK;
++
++		myobj = buffer.pointer;
++		if (!myobj || myobj->package.count != pkg_ctx->length) {
++			kfree(buffer.pointer);
++			return AE_OK;
++		}
++
++		status = acpi_extract_package(myobj,
++					pkg_ctx->format, pkg_ctx->state);
++		if (ACPI_FAILURE(status)) {
++			kfree(buffer.pointer);
++			return AE_OK;
++		}
++
++		kfree(buffer.pointer);
++		pkg_ctx->data_valid = true;
++		return AE_CTRL_TERMINATE;
++	}
++
++	return AE_OK;
++}
++
++bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
++				struct sst_acpi_package_context *ctx)
++{
++	acpi_status status;
++
++	status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL);
++
++	if (ACPI_FAILURE(status) || !ctx->data_valid)
++		return false;
++
++	return true;
++}
++EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid);
++
+ MODULE_LICENSE("GPL v2");
+ MODULE_DESCRIPTION("Intel Common ACPI Match module");
diff --git a/modules/hardware/thinkpad.nix b/modules/hardware/thinkpad.nix
new file mode 100644
index 00000000..f94b5934
--- /dev/null
+++ b/modules/hardware/thinkpad.nix
@@ -0,0 +1,31 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.hardware.thinkpad;
+
+in
+{
+  options.vuizvui.hardware.thinkpad = {
+    enable = mkEnableOption "thinkpad support";
+  };
+
+  config = mkIf cfg.enable {
+    # read acpi stats (e.g. battery)
+    environment.systemPackages = [ pkgs.acpi ];
+
+    # for wifi
+    hardware.enableRedistributableFirmware = mkDefault true;
+
+    hardware.trackpoint = mkDefault {
+      enable = true;
+      emulateWheel = true;
+      speed = 250;
+      sensitivity = 140;
+    };
+
+    # TLP Linux Advanced Power Management
+    services.tlp.enable = mkDefault true;
+  };
+}
diff --git a/modules/module-list.nix b/modules/module-list.nix
new file mode 100644
index 00000000..5994dd61
--- /dev/null
+++ b/modules/module-list.nix
@@ -0,0 +1,36 @@
+[
+  ./core/common.nix
+  ./core/licensing.nix
+  ./core/tests.nix
+  ./core/lazy-packages.nix
+  ./hardware/gamecontroller.nix
+  ./hardware/rtl8192cu
+  ./hardware/t100ha
+  ./hardware/thinkpad.nix
+  ./programs/gnupg
+  ./programs/fish/fasd.nix
+  # broken
+  # ./services/multipath-vpn.nix
+  ./services/postfix
+  ./services/starbound.nix
+  ./system/iso.nix
+  ./user/aszlig/profiles/base.nix
+  ./user/aszlig/profiles/managed.nix
+  ./user/aszlig/profiles/workstation
+  ./user/aszlig/programs/gajim
+  ./user/aszlig/programs/git
+  ./user/aszlig/programs/mpv
+  ./user/aszlig/programs/taalo-build
+  ./user/aszlig/programs/taskwarrior
+  ./user/aszlig/programs/vim
+  ./user/aszlig/programs/xpdf
+  ./user/aszlig/programs/zsh
+  ./user/aszlig/services/i3
+  ./user/aszlig/services/slim
+  ./user/aszlig/services/vlock
+  ./user/aszlig/system/kernel.nix
+  ./user/openlab/base.nix
+  ./user/openlab/labtops.nix
+  ./user/openlab/stackenblocken.nix
+  ./user/profpatsch/programs/scanning.nix
+]
diff --git a/modules/programs/fish/fasd.nix b/modules/programs/fish/fasd.nix
new file mode 100644
index 00000000..ce00d320
--- /dev/null
+++ b/modules/programs/fish/fasd.nix
@@ -0,0 +1,30 @@
+{ pkgs, config, lib, ... }:
+
+with lib;
+
+let cfg = config.vuizvui.programs.fish.fasd;
+in
+
+{
+  options.vuizvui.programs.fish.fasd = {
+    enable = mkEnableOption "fasd integration in fish";
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.fasd ];
+
+    programs.fish = {
+      interactiveShellInit = let fasd = "${pkgs.fasd}/bin/fasd"; in ''
+        function _run_fasd -e fish_preexec
+          ${fasd} --proc (${fasd} --sanitize "$argv") > "/dev/null" 2>&1
+        end
+        function z --description "Jump to folder by usage frequency"
+          cd (fasd -d -e 'printf %s' "$argv")
+        end
+        set PATH (dirname ${fasd}) $PATH
+      '';
+    };
+
+  };
+}
diff --git a/modules/programs/gnupg/agent-wrapper.c b/modules/programs/gnupg/agent-wrapper.c
new file mode 100644
index 00000000..d9cb7a0e
--- /dev/null
+++ b/modules/programs/gnupg/agent-wrapper.c
@@ -0,0 +1,313 @@
+#define _GNU_SOURCE
+#include <dlfcn.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <systemd/sd-daemon.h>
+
+#ifndef SUPERVISOR_SUPPORT
+static int main_fd = 0;
+static int scdaemon_fd = 0;
+#endif
+
+static int ssh_fd = 0;
+
+static int gather_sd_fds(void) {
+#ifdef SUPERVISOR_SUPPORT
+    if (ssh_fd == 0) {
+#else
+    if (main_fd == 0 && ssh_fd == 0 && scdaemon_fd == 0) {
+#endif
+        int num_fds;
+        char **fdmap = NULL;
+        void *libsystemd = NULL;
+        int (*_sd_listen_fds_with_names)(int, char ***);
+
+        if ((libsystemd = dlopen(LIBSYSTEMD, RTLD_LAZY)) == NULL) {
+            fprintf(stderr, "dlopen %s\n", dlerror());
+            return -4;
+        }
+
+        _sd_listen_fds_with_names =
+            dlsym(libsystemd, "sd_listen_fds_with_names");
+
+        if (_sd_listen_fds_with_names == NULL) {
+            fprintf(stderr, "dlsym %s\n", dlerror());
+            return -4;
+        }
+
+        num_fds = _sd_listen_fds_with_names(0, &fdmap);
+
+        if (num_fds <= 0) {
+            fputs("No suitable file descriptors in LISTEN_FDS.\n", stderr);
+            if (num_fds == 0)
+                return -3;
+            return -4;
+        }
+
+        if (fdmap != NULL) {
+            for (int i = 0; i < num_fds; i++) {
+                if (strncmp(fdmap[i], "ssh", 4) == 0)
+                    ssh_fd = SD_LISTEN_FDS_START + i;
+#ifndef SUPERVISOR_SUPPORT
+                else if (strncmp(fdmap[i], "main", 5) == 0)
+                    main_fd = SD_LISTEN_FDS_START + i;
+                else if (strncmp(fdmap[i], "scdaemon", 9) == 0)
+                    scdaemon_fd = SD_LISTEN_FDS_START + i;
+#endif
+                free(fdmap[i]);
+            }
+            free(fdmap);
+        }
+
+        if (dlclose(libsystemd) != 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+#ifndef SUPERVISOR_SUPPORT
+
+/* Get a systemd file descriptor corresponding to the specified socket path.
+ *
+ * Return values:
+ *   -1 Socket path not a systemd socket
+ *   -2 Provided socket path is not absolute
+ *   -3 No suitable file descriptors in LISTEN_FDS
+ *   -4 Error while determining LISTEN_FDS
+ */
+static int get_sd_fd(const char *sockpath)
+{
+    int ret;
+
+    if ((ret = gather_sd_fds()) != 0)
+        return ret;
+
+    char *basename = strrchr(sockpath, '/');
+    if (basename == NULL)
+        return -2;
+    else
+        basename++;
+
+    if (strncmp(basename, "S.gpg-agent", 12) == 0)
+        return main_fd;
+    else if (strncmp(basename, "S.gpg-agent.ssh", 16) == 0)
+        return ssh_fd;
+    else if (strncmp(basename, "S.scdaemon", 11) == 0)
+        return scdaemon_fd;
+
+    return -1;
+}
+
+/* Get the systemd file descriptor for a particular sockaddr.
+ * Returns -1 if there is an error or -2 if it is an unnamed socket.
+ */
+static int get_sd_fd_sockaddr(const struct sockaddr_un *addr)
+{
+    int ret;
+
+    if (addr->sun_path == NULL || *(addr->sun_path) == 0)
+        return -2;
+
+    ret = get_sd_fd(addr->sun_path);
+
+    if (ret < 0) {
+        switch (ret) {
+            case -1:
+                fprintf(stderr, "Socket path %s is unknown.\n", addr->sun_path);
+                break;
+            case -2:
+                fprintf(stderr, "Socket path %s is not absolute.\n",
+                        addr->sun_path);
+                break;
+        }
+        errno = EADDRNOTAVAIL;
+        return -1;
+    }
+
+    return ret;
+}
+
+/* Replace the systemd-provided socket FD with the one that is used by the
+ * agent, so that we can later look it up in our accept() wrapper.
+ */
+static void record_sockfd(int sysd_fd, int redir_fd)
+{
+    if (sysd_fd == main_fd)
+        main_fd = redir_fd;
+    else if (sysd_fd == ssh_fd)
+        ssh_fd = redir_fd;
+}
+
+/* systemd is already listening on that socket, so we don't need to. */
+int listen(int sockfd, int backlog)
+{
+    return 0;
+}
+
+/* Don't unlink() the socket, because it breaks systemd socket functionality. */
+int remove(const char *pathname)
+{
+    static int (*_remove)(const char*) = NULL;
+
+    if (get_sd_fd(pathname) > 0)
+        return 0;
+
+    if (_remove == NULL)
+        _remove = dlsym(RTLD_NEXT, "remove");
+
+    return _remove(pathname);
+}
+
+/* Don't close the socket either, because we want to re-use it. */
+int close(int fd)
+{
+    static int (*_close)(int) = NULL;
+
+    if (_close == NULL)
+        _close = dlsym(RTLD_NEXT, "close");
+
+    if (fd <= 0)
+        return _close(fd);
+    else if (fd == main_fd || fd == ssh_fd || fd == scdaemon_fd)
+        return 0;
+
+    return _close(fd);
+}
+
+/* The agent should already have called socket() before and we need to close the
+ * file descriptor that the socket() call has returned and replace it with the
+ * one provided by systemd.
+ */
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+    int new_fd;
+
+    new_fd = get_sd_fd_sockaddr((const struct sockaddr_un *)addr);
+
+    switch (new_fd) {
+        case -1: return -1;
+        /* Unnamed socket, just pretend everything is fine */
+        case -2: return 0;
+    }
+
+    if ((new_fd = get_sd_fd_sockaddr((const struct sockaddr_un *)addr)) == -1)
+        return -1;
+
+    fprintf(stderr, "bind: Redirecting FD %d to systemd-provided FD %d.\n",
+            sockfd, new_fd);
+
+    if (dup2(new_fd, sockfd) == -1)
+        return -1;
+    else
+        record_sockfd(new_fd, sockfd);
+
+    return 0;
+}
+
+/* Avoid forking for the first time so we can properly track the agent using a
+ * systemd service (without the need to set Type="forking").
+ */
+pid_t fork(void)
+{
+    static int first_fork = 1;
+
+    static pid_t (*_fork)(void) = NULL;
+    if (_fork == NULL)
+        _fork = dlsym(RTLD_NEXT, "fork");
+
+    /* Unset the LD_PRELOAD environment variable to make sure we don't propagate
+     * it down to things like the pinentry.
+     */
+    if (unsetenv("LD_PRELOAD") == -1)
+        return -1;
+
+    if (first_fork)
+        return first_fork = 0;
+
+    return _fork();
+}
+
+#endif /* !SUPERVISOR_SUPPORT */
+
+/* Get the PID of the client connected to the given socket FD. */
+static pid_t get_socket_pid(int sockfd)
+{
+    struct ucred pcred;
+    socklen_t pcred_len = sizeof(pcred);
+
+    if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &pcred, &pcred_len) == -1)
+        return -1;
+
+    return pcred.pid;
+}
+
+static pid_t last_pid = 0;
+
+/* For the pinentry to work correctly with SSH, we need to record the process ID
+ * of the process communicating with the agent. That way we can get more
+ * information about the PTS/PTY/TTY the user is on and also know whether a
+ * DISPLAY is set for that process, because we will connect the pinentry's TTY
+ * to the TTY of the process on the other end of the socket.
+ */
+int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+    int retval;
+
+    static int (*_accept)(int, struct sockaddr *, socklen_t *) = NULL;
+    if (_accept == NULL)
+        _accept = dlsym(RTLD_NEXT, "accept");
+
+    retval = _accept(sockfd, addr, addrlen);
+
+    last_pid = 0;
+
+#ifdef SUPERVISOR_SUPPORT
+    if (ssh_fd == 0) gather_sd_fds();
+#endif
+
+    if (retval != -1 && ssh_fd != 0 && sockfd == ssh_fd) {
+        pid_t client_pid = get_socket_pid(retval);
+        if (client_pid == -1) {
+            close(retval);
+            return -1;
+        }
+        last_pid = client_pid;
+        fprintf(stderr, "Socket endpoint PID for accepted socket %d is %d.\n",
+                retval, client_pid);
+    }
+
+    return retval;
+}
+
+/* Wrap the execv() that calls the pinentry program to include a special
+ * _CLIENT_PID environment variable, which contains the PID we gathered during
+ * accept(). Note that this is potentially racy if we have a lot of concurrent
+ * connections, but the worst that could happen is that we end up having a
+ * pinentry running on the wrong TTY/display.
+ */
+int execv(const char *path, char *const argv[])
+{
+    static int (*_execv)(const char *, char *const[]) = NULL;
+    if (_execv == NULL)
+        _execv = dlsym(RTLD_NEXT, "execv");
+
+    if (last_pid != 0 &&
+        strncmp(path, PINENTRY_WRAPPER, sizeof(PINENTRY_WRAPPER) + 1) == 0) {
+        char env_var[40];
+        if (snprintf(env_var, 40, "_CLIENT_PID=%d", last_pid) < 0)
+            return -1;
+        if (putenv(env_var) < 0)
+            return -1;
+    }
+
+    last_pid = 0;
+    return _execv(path, argv);
+}
diff --git a/modules/programs/gnupg/default.nix b/modules/programs/gnupg/default.nix
new file mode 100644
index 00000000..db9bdc6b
--- /dev/null
+++ b/modules/programs/gnupg/default.nix
@@ -0,0 +1,191 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.programs.gnupg;
+
+  hasXdgSupport = versionAtLeast (getVersion cfg.package) "2.1.13";
+  isDefaultHome = cfg.homeDir == ".gnupg";
+
+  hasSupervisorSupport = versionAtLeast (getVersion cfg.package) "2.1.16";
+
+  sockDir = if hasXdgSupport && isDefaultHome
+            then "%t/gnupg"
+            else "%h/${cfg.homeDir}";
+  shellSockDir = if hasXdgSupport && isDefaultHome
+                 then "$XDG_RUNTIME_DIR/gnupg"
+                 else "$HOME/${cfg.homeDir}";
+
+  pinentryWrapper = pkgs.runCommandCC "pinentry-wrapper" {
+    pinentryProgram = cfg.agent.pinentry.program;
+  } ''
+    cc -Wall -std=gnu11 -DPINENTRY_PROGRAM=\"$pinentryProgram\" \
+      "${./pinentry-wrapper.c}" -o "$out"
+  '';
+
+  scdaemonRedirector = pkgs.writeScript "scdaemon-redirector" ''
+    #!${pkgs.stdenv.shell}
+    exec "${pkgs.socat}/bin/socat" - \
+      UNIX-CONNECT:"${shellSockDir}/S.scdaemon"
+  '';
+
+  agentWrapper = withSupervisor: pkgs.runCommandCC "gpg-agent-wrapper" {
+    buildInputs = with pkgs; [ pkgconfig systemd ];
+    inherit pinentryWrapper;
+  } ''
+    cc -Wall -shared -std=c11 \
+      ${optionalString withSupervisor "-DSUPERVISOR_SUPPORT=1"} \
+      -DLIBSYSTEMD=\"${pkgs.systemd.lib}/lib/libsystemd.so\" \
+      -DPINENTRY_WRAPPER=\"$pinentryWrapper\" \
+      $(pkg-config --cflags libsystemd) -ldl \
+      "${./agent-wrapper.c}" -o "$out" -fPIC
+  '';
+
+  agentSocketConfig = name: {
+    FileDescriptorName = name;
+    Service = "gpg-agent.service";
+    SocketMode = "0600";
+    DirectoryMode = "0700";
+  };
+
+in {
+  options.vuizvui.programs.gnupg = {
+    enable = mkEnableOption "support for GnuPG";
+
+    homeDir = mkOption {
+      type = types.addCheck types.str (d: builtins.substring 0 1 d != "/");
+      default = ".gnupg";
+      description = ''
+        The directory where GnuPG keeps its state files and configuration files,
+        relative to the user's home directory.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.gnupg;
+      defaultText = "pkgs.gnupg";
+      example = literalExample "pkgs.gnupg21";
+      description = ''
+        The GnuPG package to use for running the agent and make available in
+        <option>environment.systemPackages</option>.
+      '';
+    };
+
+    agent = {
+      enable = mkEnableOption "support for the GnuPG agent";
+
+      pinentry.program = mkOption {
+        type = types.path;
+        default = "${pkgs.pinentry}/bin/pinentry";
+        defaultText = "\${pkgs.pinentry}/bin/pinentry";
+        example = literalExample "\${pkgs.pinentry_qt5}/bin/pinentry";
+        description = "The pinentry program to use to ask for passphrases.";
+      };
+
+      sshSupport = mkEnableOption "GnuPG agent support for SSH";
+
+      scdaemon = {
+        enable = mkEnableOption "GnuPG agent with Smartcard daemon";
+
+        program = mkOption {
+          type = types.path;
+          default = "${cfg.package}/libexec/scdaemon";
+          defaultText = let
+            configPath = "config.vuizvui.programs.gnupg";
+          in "\${${configPath}.package}/libexec/scdaemon";
+          example = literalExample "\${pkgs.my_shiny_scdaemon}/bin/scdaemon";
+          description = "The program to use for the Smartcard daemon";
+        };
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+      vuizvui.requiresTests = singleton ["vuizvui" "programs" "gnupg"];
+      environment.systemPackages = [ cfg.package ];
+    })
+    (mkIf (cfg.enable && !isDefaultHome) {
+      environment.variables.GNUPGHOME = "~/${cfg.homeDir}";
+    })
+    (mkIf (cfg.enable && cfg.agent.enable) {
+      systemd.user.services.gpg-agent = {
+        description = "GnuPG Agent";
+        environment.LD_PRELOAD = agentWrapper hasSupervisorSupport;
+        environment.GNUPGHOME = "~/${cfg.homeDir}";
+
+        serviceConfig.ExecStart = toString ([
+          "${cfg.package}/bin/gpg-agent"
+          "--pinentry-program=${pinentryWrapper}"
+          (if cfg.agent.scdaemon.enable
+           then "--scdaemon-program=${scdaemonRedirector}"
+           else "--disable-scdaemon")
+          (if hasSupervisorSupport
+           then "--supervised"
+           else "--no-detach --daemon")
+        ] ++ optional cfg.agent.sshSupport "--enable-ssh-support");
+
+        serviceConfig.ExecReload = toString [
+          "${cfg.package}/bin/gpg-connect-agent"
+          "RELOADAGENT"
+          "/bye"
+        ];
+      };
+
+      systemd.user.sockets.gpg-agent-main = {
+        wantedBy = [ "sockets.target" ];
+        description = "Main Socket For GnuPG Agent";
+        listenStreams = singleton "${sockDir}/S.gpg-agent";
+        socketConfig = let
+          sockName = if hasSupervisorSupport then "std" else "main";
+        in agentSocketConfig sockName;
+      };
+    })
+    (mkIf (cfg.enable && cfg.agent.enable && cfg.agent.scdaemon.enable) {
+      systemd.user.sockets.gnupg-scdaemon = {
+        wantedBy = [ "sockets.target" ];
+        description = "GnuPG Smartcard Daemon Socket";
+        listenStreams = singleton "${sockDir}/S.scdaemon";
+        socketConfig = {
+          FileDescriptorName = "scdaemon";
+          SocketMode = "0600";
+          DirectoryMode = "0700";
+        };
+      };
+
+      systemd.user.services.gnupg-scdaemon = {
+        description = "GnuPG Smartcard Daemon";
+        environment.LD_PRELOAD = agentWrapper false;
+        environment.GNUPGHOME = "~/${cfg.homeDir}";
+
+        serviceConfig.ExecStart = toString [
+          "${cfg.agent.scdaemon.program}"
+          "--no-detach"
+          "--daemon"
+        ];
+      };
+    })
+    (mkIf (cfg.enable && cfg.agent.enable && cfg.agent.sshSupport) {
+      environment.variables.SSH_AUTH_SOCK = "${shellSockDir}/S.gpg-agent.ssh";
+
+      systemd.user.sockets.gpg-agent-ssh = {
+        wantedBy = [ "sockets.target" ];
+        description = "SSH Socket For GnuPG Agent";
+        listenStreams = singleton "${sockDir}/S.gpg-agent.ssh";
+        socketConfig = agentSocketConfig "ssh";
+      };
+
+      assertions = singleton {
+        assertion = !config.programs.ssh.startAgent;
+        message = toString [
+          "You cannot use the GnuPG agent with SSH support in addition to the"
+          "SSH agent, please either disable"
+          "`vuizvui.programs.gpg-agent.sshSupport' or disable"
+          "`programs.ssh.startAgent'."
+        ];
+      };
+    })
+  ];
+}
diff --git a/modules/programs/gnupg/pinentry-wrapper.c b/modules/programs/gnupg/pinentry-wrapper.c
new file mode 100644
index 00000000..12710760
--- /dev/null
+++ b/modules/programs/gnupg/pinentry-wrapper.c
@@ -0,0 +1,281 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Get the terminal path of the given PID and FD using the /proc file system. */
+char *get_terminal(pid_t pid, int fd)
+{
+    int term, is_term;
+    ssize_t linklen;
+    char fd_path[50];
+    char term_path[100];
+    struct stat st;
+
+    if (snprintf(fd_path, 50, "/proc/%d/fd/%d", pid, fd) < 0) {
+        perror("snprintf proc fd path");
+        return NULL;
+    }
+
+    if (lstat(fd_path, &st) == -1)
+        return NULL;
+
+    if (!S_ISLNK(st.st_mode))
+        return NULL;
+
+    if ((linklen = readlink(fd_path, term_path, sizeof term_path)) == -1) {
+        perror("readlink term path");
+        return NULL;
+    }
+
+    term_path[linklen] = 0;
+
+    if ((term = open(term_path, O_RDONLY | O_NOCTTY)) == -1)
+        return NULL;
+
+    is_term = isatty(term);
+
+    if (close(term) == -1) {
+        perror("close client tty");
+        return NULL;
+    }
+
+    if (!is_term)
+        return NULL;
+
+    return strdup(term_path);
+}
+
+/* Probes FD 0, 1 and 2 for a connected terminal device and return an allocated
+ * string pointing to the filename.
+ */
+char *detect_terminal(pid_t pid)
+{
+    char *term;
+
+    for (int i = 0; i < 3; ++i) {
+        term = get_terminal(pid, i);
+        if (term == NULL)
+            continue;
+
+        return term;
+    }
+
+    return NULL;
+}
+
+/* Fetch the info from /proc/PID/environ and retorn it as an array. */
+char **fetch_environ(pid_t pid)
+{
+    char environ_path[50], **result = NULL;
+    char buf[2048], *envbuf, *environ = NULL;
+    size_t chunklen, envlen = 0;
+    int env_fd;
+
+    if (snprintf(environ_path, 50, "/proc/%d/environ", pid) < 0) {
+        perror("snprintf proc environ path");
+        return NULL;
+    }
+
+    if ((env_fd = open(environ_path, O_RDONLY)) == -1) {
+        perror("open proc environ");
+        return NULL;
+    }
+
+    while ((chunklen = read(env_fd, buf, sizeof buf)) > 0) {
+        if (environ == NULL) {
+            if ((environ = malloc(envlen + chunklen + 1)) == NULL) {
+                perror("malloc proc environ");
+                return NULL;
+            }
+        } else {
+            if ((environ = realloc(environ, envlen + chunklen + 1)) == NULL) {
+                perror("realloc proc environ");
+                free(environ);
+                return NULL;
+            }
+        }
+        memcpy(environ + envlen, buf, chunklen);
+        envlen += chunklen;
+        environ[envlen + 1] = 0;
+        if (chunklen < sizeof buf)
+            break;
+    }
+
+    if (close(env_fd) == -1) {
+        perror("close proc environ");
+        free(environ);
+        return NULL;
+    }
+
+    envbuf = environ;
+
+    if ((result = malloc(sizeof(char*))) == NULL) {
+        perror("malloc environ array");
+        free(environ);
+        return NULL;
+    }
+    result[0] = NULL;
+
+    for (int i = 0; envbuf - environ < envlen; ++i) {
+        if ((result = realloc(result, sizeof(char*) * (i + 2))) == NULL) {
+            perror("realloc environ array");
+            free(environ);
+            free(result);
+            return NULL;
+        }
+
+        result[i] = strndup(envbuf, envlen - (envbuf - environ));
+        result[i + 1] = NULL;
+        envbuf += strlen(envbuf) + 1;
+    }
+
+    free(environ);
+    return result;
+}
+
+void free_environ(char **environ)
+{
+    char **tmp = environ;
+    if (environ == NULL) return;
+    do free(*tmp);
+    while (*(++tmp) != NULL);
+    free(environ);
+    environ = NULL;
+}
+
+struct proc_info {
+    char **environ;
+    char *term;
+};
+
+/* Gather information for the given process ID, like environment or connected
+ * terminals.
+ */
+struct proc_info *open_proc_info(pid_t pid)
+{
+    struct proc_info *pi = NULL;
+
+    if ((pi = malloc(sizeof(struct proc_info *))) == NULL) {
+        perror("malloc proc_info");
+        return NULL;
+    }
+
+    pi->term = detect_terminal(pid);
+    if ((pi->environ = fetch_environ(pid)) == NULL) {
+        free(pi->term);
+        free(pi);
+        return NULL;
+    }
+
+    return pi;
+}
+
+void close_proc_info(struct proc_info *pi)
+{
+    if (pi->term != NULL) free(pi->term);
+    free_environ(pi->environ);
+    free(pi);
+}
+
+/* Fetch an environment variable from the proc_info structure similar to
+ * getenv() but for remote PIDs.
+ */
+char *proc_info_getenv(struct proc_info *pi, const char *name)
+{
+    char **tmp = pi->environ;
+    size_t namelen = strlen(name);
+    do {
+        if (strncmp(*tmp, name, namelen) == 0 &&
+            *(*tmp + namelen) == '=') {
+            return strdup(*tmp + namelen + 1);
+        }
+    } while (*(++tmp) != NULL);
+    return NULL;
+}
+
+#define MAYBE_EXPAND_ARGV(opt, value) \
+    if ((tmp = value) != NULL) { \
+        new_argv = realloc(new_argv, sizeof(char*) * (new_argc + 3)); \
+        if (new_argv == NULL) { \
+            perror("realloc new argv"); \
+            return EXIT_FAILURE; \
+        } \
+        new_argv[new_argc + 0] = "--" opt; \
+        new_argv[new_argc + 1] = tmp; \
+        new_argv[new_argc + 2] = NULL; \
+        new_argc += 2; \
+    }
+
+/* This is our program main routine whenever we get a _CLIENT_PID environment
+ * variable.
+ */
+int wrap(struct proc_info *pi, int argc, char **argv)
+{
+    char *tmp, **new_argv;
+    int new_argc = 1;
+
+    if ((new_argv = malloc(sizeof(char*) * 2)) == NULL) {
+        perror("malloc new argv");
+        return EXIT_FAILURE;
+    }
+
+    new_argv[0] = PINENTRY_PROGRAM;
+    new_argv[1] = NULL;
+
+    MAYBE_EXPAND_ARGV("display", proc_info_getenv(pi, "DISPLAY"));
+    MAYBE_EXPAND_ARGV("ttyname", strdup(pi->term));
+    MAYBE_EXPAND_ARGV("ttytype", proc_info_getenv(pi, "TERM"));
+    MAYBE_EXPAND_ARGV("lc-ctype", proc_info_getenv(pi, "LC_CTYPE"));
+    MAYBE_EXPAND_ARGV("lc-messages", proc_info_getenv(pi, "LC_MESSAGES"));
+
+    close_proc_info(pi);
+
+    /* No DISPLAY/TTY found, so use the arguments provided by the agent. */
+    if (new_argc == 1) {
+        free(new_argv);
+        new_argv = argv;
+    }
+
+    /* Make sure we don't have DISPLAY already in our environment to avoid
+     * starting a pinentry on X while the user is connected via SSH for example.
+     */
+    if (unsetenv("DISPLAY") == -1)
+        return EXIT_FAILURE;
+
+    if (execv(PINENTRY_PROGRAM, new_argv) == -1) {
+        perror("execv real pinentry");
+        return EXIT_FAILURE;
+    }
+
+    /* Not reached because the process should be substituted in execve(). */
+    return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+    const char *pidstr;
+    struct proc_info *pi = NULL;
+
+    if ((pidstr = getenv("_CLIENT_PID")) != NULL) {
+        if ((pi = open_proc_info(atoi(pidstr))) == NULL)
+            fprintf(stderr, "Client PID %d has vanished before we could"
+                    " retrieve /proc information.\n", atoi(pidstr));
+        else
+            return wrap(pi, argc, argv);
+    }
+
+    argv[0] = PINENTRY_PROGRAM;
+
+    if (execv(PINENTRY_PROGRAM, argv) == -1) {
+        perror("execv real pinentry");
+        return EXIT_FAILURE;
+    }
+
+    /* Not reached because the process should be substituted in execve(). */
+    return EXIT_SUCCESS;
+}
diff --git a/modules/services/multipath-vpn.nix b/modules/services/multipath-vpn.nix
new file mode 100644
index 00000000..c6c318aa
--- /dev/null
+++ b/modules/services/multipath-vpn.nix
@@ -0,0 +1,246 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  deps = with pkgs.perlPackages; rec {
+    IOInterface = buildPerlPackage {
+      name = "IO-Interface-1.09";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/L/LD/LDS/IO-Interface-1.09.tar.gz;
+        sha256 = "0fkizbclng7jaxkwj9cr2wby34r45mazb0yrq87fdq5i5v2q2gp6";
+      };
+      buildInputs = [ ModuleBuild ];
+      preConfigure = "touch Makefile.PL";
+      buildPhase = "perl Build.PL --prefix=$out; ./Build build";
+      installPhase = "./Build install";
+      checkPhase = "./Build test";
+    };
+
+    IOPipely = buildPerlPackage {
+      name = "IO-Pipely-0.005";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/R/RC/RCAPUTO/IO-Pipely-0.005.tar.gz;
+        sha256 = "0x1fkwbkbkhxf0cvz08yj24hm9c775i1xx8khlqfwiibrgsnqfz3";
+      };
+    };
+
+    ModuleBuild = buildPerlPackage {
+      name = "Module-Build-0.4211";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/L/LE/LEONT/Module-Build-0.4211.tar.gz;
+        sha256 = "1c5hfhajr963w4mdjivsc7yz4vf4pz1rrfch5a93fbac1x2mr58h";
+      };
+      doCheck = false;
+    };
+
+    POE = buildPerlPackage {
+      name = "POE-1.366";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/R/RC/RCAPUTO/POE-1.366.tar.gz;
+        sha256 = "08qmb45clkjw2ni9dl5y1fa4ifrinvbvvcgh7r20ls32frw034xl";
+      };
+      buildInputs = [ POETestLoops ];
+      propagatedBuildInputs = [ IOPipely IOTty POETestLoops ];
+    };
+
+    POETestLoops = buildPerlPackage {
+      name = "POE-Test-Loops-1.360";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/R/RC/RCAPUTO/POE-Test-Loops-1.360.tar.gz;
+        sha256 = "0yx4wsljfmdzsiv0ni98x6lw975cm82ahngbwqvzv60wx5pwkl5y";
+      };
+    };
+
+    POEWheelUDP = pkgs.buildPerlPackage {
+      name = "POE-Wheel-UDP-0.02";
+      src = fetchurl {
+        url = mirror://cpan/authors/id/H/HA/HACHI/POE-Wheel-UDP-0.02.tar.gz;
+        sha256 = "0d611cqpmq7svmxq6pbjb59b97x5zh2z4lc11f8zjmci98nag2g6";
+      };
+      propagatedBuildInputs = [ POE ];
+    };
+  };
+
+  linkOptions = { name, ... }: {
+    options = {
+      interface = mkOption {
+        type = types.str;
+        description = ''
+          IP address or interface name to connect to the relay.
+        '';
+      };
+
+      sourcePort = mkOption {
+        type = types.int;
+        default = 11218;
+        description = ''
+          Local UDP port to use for connecting to the other endpoint.
+        '';
+      };
+
+      destAddress = mkOption {
+        type = types.str;
+        description = ''
+          Remote UDP host or IP of the other endpoint.
+        '';
+      };
+
+      destPort = mkOption {
+        type = types.int;
+        default = 11218;
+        description = ''
+          Remote UDP port the other endpoint is listening.
+        '';
+      };
+
+      ratio = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''
+          Defines how many packets the remote endpoint is getting in relation to
+          the other defined links.
+        '';
+      };
+    };
+  };
+
+  commonOptions = {
+    links = mkOption {
+      default = {};
+      type = types.attrsOf (types.submodule linkOptions);
+      description = ''
+        Links used to connect to the remote endpoint (server).
+      '';
+    };
+
+    tun.ip = mkOption {
+      type = types.str;
+      description = ''
+        IP address of the TUN interface used for communicating to/from the
+        outside of the tunnel.
+      '';
+    };
+
+    tun.mask = mkOption {
+      type = types.int;
+      description = ''
+        Network prefix length to use for the TUN interface.
+      '';
+    };
+
+    tun.mtu = mkOption {
+      type = types.int;
+      default = 1500;
+      description = ''
+        Maximum transfer unit for the TUN interface.
+      '';
+    };
+
+    route.network = mkOption {
+      type = types.str;
+      description = ''
+        Network address of the auto-enabled route.
+      '';
+    };
+
+    route.mask = mkOption {
+      type = types.int;
+      description = ''
+        Network prefix length of the auto-enabled route.
+      '';
+    };
+
+    route.gateway = mkOption {
+      type = types.str;
+      description = ''
+        Gateway address of the auto-enabled route.
+      '';
+    };
+  };
+
+  clientOptions = commonOptions // {
+    enable = mkEnableOption "Multipath VPN Client";
+  };
+
+  serverOptions = commonOptions // {
+    enable = mkEnableOption "Multipath VPN Server";
+  };
+
+  genConfig = name: cfg: mkIf cfg.enable (let
+    attrs = if name == "client" then {
+      descName = "Client";
+    } else if name == "server" then {
+      descName = "Server";
+    } else throw "Invalid multipath VPN config mode";
+
+    mpvpn = pkgs.stdenv.mkDerivation rec {
+      name = "multipath-vpn";
+
+      src = pkgs.fetchFromGitHub {
+        owner = "richi235";
+        repo = name;
+        rev = "51729f7bb24b5361c90469c60f67df0c8b4e2371";
+        sha256 = "1p2i1m649nhrylqz2grc5nxwgzqq1rnwkzk7iipdxabx2164ahaq";
+      };
+
+      configFile = pkgs.writeText "mpvpn.conf" ''
+        ${concatStringsSep "\n" (mapAttrsToList (
+          name: attrs: concatStringsSep "\t" [
+            "link" name attrs.interface
+            (toString attrs.sourcePort)
+            attrs.destAddress
+            (toString attrs.destPort)
+            (toString attrs.ratio)
+          ]
+        ) cfg.links)}
+
+        ${concatStringsSep "\t" [
+          "local" cfg.tun.ip (toString cfg.tun.mask) (toString cfg.tun.mtu)
+        ]}
+
+        ${concatStringsSep "\t" [
+          "route" cfg.route.network (toString cfg.route.mask) cfg.route.gateway
+        ]}
+      '';
+
+      buildPhase = "true";
+      buildInputs = [
+        pkgs.makeWrapper pkgs.perl
+        deps.POEWheelUDP deps.IOInterface
+      ];
+      installPhase = ''
+        mkdir -p "$out/bin"
+
+        sed -e "s,/etc/multivpn.cfg,$configFile," \
+            -e 's/detect+handle_local_ip_change/handle_local_ip_change/g' \
+            vpn_client_and_server.pl > "$out/bin/multipath-vpn"
+
+        chmod +x "$out/bin/multipath-vpn"
+        wrapProgram $out/bin/multipath-vpn --set PERL5LIB $PERL5LIB
+      '';
+    };
+  in {
+    systemd.services."multipath-vpn-${name}" = {
+      description = "Multipath VPN ${attrs.descName}";
+      after = [ "network-interfaces.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.iptables pkgs.nettools pkgs.iproute pkgs.bridge-utils ];
+      serviceConfig.ExecStart = "@${mpvpn}/bin/multipath-vpn multipath-vpn";
+    };
+  });
+
+in {
+  options.vuizvui.services.multipath-vpn.client = commonOptions // {
+    enable = mkEnableOption "Multipath VPN Client";
+  };
+
+  options.vuizvui.services.multipath-vpn.server = commonOptions // {
+    enable = mkEnableOption "Multipath VPN Server";
+  };
+
+  config = mkMerge [
+    (genConfig "client" config.vuizvui.services.multipath-vpn.client)
+    (genConfig "server" config.vuizvui.services.multipath-vpn.server)
+  ];
+}
diff --git a/modules/services/postfix/default.nix b/modules/services/postfix/default.nix
new file mode 100644
index 00000000..8a0865b9
--- /dev/null
+++ b/modules/services/postfix/default.nix
@@ -0,0 +1,65 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.services.postfix;
+
+  mkRestriction = name: specificDescription: {
+    option.${name} = mkOption {
+      default = null;
+      type = types.nullOr (types.listOf types.str);
+      description = ''
+        A list of restrictions to apply or <option>null</option> to use the
+        built-in default value from Postfix.
+        ${specificDescription}
+      '';
+    };
+
+    config = let
+      restrictions = cfg.restrictions.${name};
+    in mkIf (restrictions != null) {
+      services.postfix.extraConfig = ''
+        smtpd_${name}_restrictions = ${concatStringsSep ", " restrictions}
+      '';
+    };
+  };
+
+  restrictions = mapAttrsToList mkRestriction {
+    client = ''
+      SMTP server access restrictions in the context of a client SMTP connection
+      request.
+    '';
+    data = ''
+      Access restrictions that the Postfix SMTP server applies in the context of
+      the SMTP DATA command.
+    '';
+    end_of_data = ''
+      Access restrictions that the Postfix SMTP server applies in the context of
+      the SMTP END-OF-DATA command.
+    '';
+    etrn = ''
+      SMTP server access restrictions in the context of a client ETRN request.
+    '';
+    helo = ''
+      Restrictions that the Postfix SMTP server applies in the context of the
+      SMTP HELO command.
+    '';
+    recipient = ''
+      Access restrictions that the Postfix SMTP server applies in the context of
+      the RCPT TO command.
+    '';
+    sender = ''
+      Restrictions that the Postfix SMTP server applies in the context of the
+      MAIL FROM command.
+    '';
+  };
+
+in {
+  options.vuizvui.services.postfix = {
+    enable = mkEnableOption "Vuizvui Postfix";
+    restrictions = fold mergeAttrs {} (catAttrs "option" restrictions);
+  };
+
+  config = mkIf cfg.enable (mkMerge (catAttrs "config" restrictions));
+}
diff --git a/modules/services/starbound.nix b/modules/services/starbound.nix
new file mode 100644
index 00000000..b7275593
--- /dev/null
+++ b/modules/services/starbound.nix
@@ -0,0 +1,351 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.services.starbound;
+
+  mkListenerOptions = what: defaultPort: {
+    bind = mkOption {
+      type = types.str;
+      default = "::";
+      description = ''
+        Host/IP address to listen for incoming connections to the ${what}.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = defaultPort;
+      description = ''
+        Port to listen for incoming connections to the ${what}.
+      '';
+    };
+  };
+
+  serverConfig = {
+    allowAdminCommands = cfg.adminCommands.allow;
+    allowAdminCommandsFromAnyone = cfg.adminCommands.allowFromAnyone;
+
+    allowAnonymousConnections = cfg.anonymousConnections.allow;
+    anonymousConnectionsAreAdmin = cfg.anonymousConnections.adminPrivileges;
+
+    serverUsers = mapAttrs (user: attrs: {
+      inherit (attrs) admin password;
+    }) cfg.users;
+
+    inherit (cfg)
+      allowAssetsMismatch maxPlayers maxTeamSize serverName serverFidelity;
+
+    clearPlayerFiles = false;
+    clearUniverseFiles = false;
+
+    safeScripts = cfg.safeScripts.enable;
+    scriptInstructionLimit = cfg.safeScripts.instructionLimit;
+    scriptInstructionMeasureInterval =
+      cfg.safeScripts.instructionMeasureInterval;
+    scriptProfilingEnabled = cfg.safeScripts.profiling.enable;
+    scriptRecursionLimit = cfg.safeScripts.recursionLimit;
+
+    gameServerBind = cfg.bind;
+    gameServerPort = cfg.port;
+
+    bannedIPs = cfg.bannedIPs;
+    bannedUuids = cfg.bannedUUIDs;
+
+    runRconServer = cfg.rconServer.enable;
+    rconServerBind = cfg.rconServer.bind;
+    rconServerPort = cfg.rconServer.port;
+    rconServerPassword = cfg.rconServer.password;
+    rconServerTimeout = cfg.rconServer.timeout;
+
+    runQueryServer = cfg.queryServer.enable;
+    queryServerBind = cfg.queryServer.bind;
+    queryServerPort = cfg.queryServer.port;
+  } // cfg.extraConfig;
+
+  bootConfig = pkgs.writeText "sbinit.config" (builtins.toJSON {
+    logFileBackups = 0;
+    storageDirectory = cfg.dataDir;
+    assetDirectories = singleton (cfg.package.assets);
+    defaultConfiguration = serverConfig;
+  });
+
+  # Traverse a given path with ../ until we get to the root directory (/).
+  gotoRoot = p: concatStringsSep "/" (map (const "..") (splitString "/" p));
+
+in {
+  options.vuizvui.services.starbound = {
+    enable = mkEnableOption "Starbound game server";
+
+    adminCommands = {
+      allow = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to allow admin commands in general.
+        '';
+        # XXX: Make this dependant on whether an account is defined with enabled
+        # admin.
+      };
+
+      allowFromAnyone = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Allow anyone, even anonymous users to use admin commands.
+        '';
+        # XXX: Check whether this is true!
+      };
+    };
+
+    anonymousConnections = {
+      allow = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to allow anonymous connections to the server.
+
+          Set this to <literal>false</literal> and use
+          <option>serverUsers</option> to only allow specific accounts to
+          connect.
+        '';
+      };
+
+      adminPrivileges = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether all anonymous connections have administrative privileges.
+        '';
+      };
+    };
+
+    users = mkOption {
+      type = types.attrsOf (types.submodule {
+        options.admin = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether this user has admin privileges.
+          '';
+        };
+        options.password = mkOption {
+          type = types.str;
+          example = "supersecure";
+          description = ''
+            The password for the user.
+          '';
+        };
+      });
+      default = {};
+      description = ''
+        User accounts to allow connection to the Starbound server.
+      '';
+    };
+
+    allowAssetsMismatch = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Check whether the assets on the client match the ones from the server
+        and deny connection if they don't match.
+      '';
+    };
+
+    bannedIPs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        IP addresses disallowed for connection to the server.
+      '';
+    };
+
+    bannedUUIDs = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        User IDs disallowed for connection to the server.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/lib/starbound";
+      description = ''
+        The directory where Starbound stores its universe/player files.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.vuizvui.games.humblebundle.starbound;
+      defaultText = "pkgs.vuizvui.games.humblebundle.starbound";
+      description = ''
+        The starbound package to use for running this game server.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      description = ''
+        Extra configuration options to add to the server config.
+      '';
+    };
+
+    rconServer = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run an RCON server which allows to run administrative
+          commands on this game server instance.
+
+          See the <link xlink:href="${
+            "https://developer.valvesoftware.com/wiki/Source_RCON_Protocol"
+          }">RCON protocol documentation</link> for more information about this.
+        '';
+      };
+
+      password = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          The password needed to authorize with the RCON server.
+        '';
+      };
+
+      timeout = mkOption {
+        type = types.int;
+        default = 1000;
+        # XXX: Find out what this timeout is for and whether it's in seconds.
+        description = ''
+          After how many seconds the RCON server drops the connection.
+        '';
+      };
+    } // mkListenerOptions "RCON server" 21026;
+
+    queryServer = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run a query server that shows information such as currently
+          connected players.
+        '';
+      };
+    } // mkListenerOptions "query server" 21025;
+
+    safeScripts = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable certain limitations of LUA scripts.
+        '';
+      };
+
+      instructionLimit = mkOption {
+        type = types.int;
+        default = 10000000;
+        description = ''
+          The maximum amount of instructions a LUA function can have.
+        '';
+      };
+
+      instructionMeasureInterval = mkOption {
+        type = types.int;
+        default = 10000;
+        description = ''
+          The amount of milliseconds to wait between consecutive checks of the
+          <option>instructionLimit</option> on LUA scripts.
+        '';
+      };
+
+      recursionLimit = mkOption {
+        type = types.int;
+        default = 100;
+        description = ''
+          Maximum depth of recursion for LUA scripts.
+        '';
+      };
+
+      profiling.enable = mkEnableOption "LUA script profiling";
+    };
+
+    serverName = mkOption {
+      type = types.str;
+      default = "A Starbound Server";
+      example = "My shiny Starbound Server";
+      description = ''
+        A short description or name of the Starbound server to run.
+      '';
+    };
+
+    serverFidelity = mkOption {
+      type = types.enum [ "automatic" "minimum" "low" "medium" "high" ];
+      default = "automatic";
+      example = "high";
+      description = ''
+        The fidelity profile to use for this server as defined in
+        <path>worldserver.config</path> inside the packed assets.
+
+        If this is set to <literal>automatic</literal> the server will
+        automatically switch between these profiles.
+      '';
+    };
+
+    maxPlayers = mkOption {
+      type = types.int;
+      default = 8;
+      description = ''
+        Maximum amount of players to allow concurrently.
+      '';
+    };
+
+    maxTeamSize = mkOption {
+      type = types.int;
+      default = 4;
+      description = ''
+        Maximum amount of players to allow within a party.
+      '';
+    };
+  } // mkListenerOptions "game server" 21025;
+
+  config = mkIf cfg.enable {
+    users.groups.starbound = {
+      gid = config.ids.gids.starbound;
+    };
+
+    users.users.starbound = {
+      uid = config.ids.uids.starbound;
+      description = "Starbound Game Server User";
+      group = "starbound";
+      home = cfg.dataDir;
+      createHome = true;
+    };
+
+    systemd.services.starbound = {
+      description = "Starbound Server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "fs.target" ];
+
+      serviceConfig = {
+        User = "starbound";
+        Group = "starbound";
+        PrivateTmp = true;
+
+        KillSignal = "SIGINT";
+
+        ExecStart = toString [
+          "${cfg.package}/bin/starbound-server"
+          "-bootconfig \"${bootConfig}\""
+          # Workaround to disable logging to file
+          "-logfile \"${gotoRoot cfg.dataDir}/dev/null\""
+          "-verbose"
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/system/iso.nix b/modules/system/iso.nix
new file mode 100644
index 00000000..893a56e9
--- /dev/null
+++ b/modules/system/iso.nix
@@ -0,0 +1,12 @@
+{ lib, ... }:
+
+{
+  options.vuizvui.createISO = lib.mkOption {
+    default = false;
+    example = true;
+    type = lib.types.bool;
+    description = ''
+      Whether to build an ISO image out of this machine configuration on Hydra.
+    '';
+  };
+}
diff --git a/modules/user/aszlig/profiles/base.nix b/modules/user/aszlig/profiles/base.nix
new file mode 100644
index 00000000..47a64eda
--- /dev/null
+++ b/modules/user/aszlig/profiles/base.nix
@@ -0,0 +1,111 @@
+{ config, pkgs, unfreePkgs, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.profiles.base;
+
+in {
+  options.vuizvui.user.aszlig.profiles.base = {
+    enable = lib.mkEnableOption "Base profile for aszlig";
+  };
+
+  config = lib.mkIf cfg.enable {
+    nix = {
+      useSandbox = true;
+      readOnlyStore = true;
+      buildCores = 0;
+      extraOptions = ''
+        auto-optimise-store = true
+        log-servers = https://headcounter.org/hydra/log
+      '';
+    };
+
+    boot.loader.grub = {
+      enable = true;
+      version = 2;
+    };
+
+    hardware.cpu.intel.updateMicrocode = true;
+
+    users.defaultUserShell = "/var/run/current-system/sw/bin/zsh";
+
+    networking.wireless.enable = false;
+    networking.firewall.enable = false;
+    networking.useNetworkd = true;
+
+    i18n.consoleKeyMap = "dvorak";
+    i18n.consoleFont = "lat9w-16";
+
+    programs.ssh.startAgent = false;
+    programs.ssh.extraConfig = ''
+      ServerAliveInterval 60
+    '';
+
+    vuizvui.user.aszlig.programs.vim.enable = true;
+    vuizvui.user.aszlig.programs.zsh.enable = true;
+    vuizvui.enableGlobalNixpkgsConfig = true;
+
+    services.nixosManual.showManual = false;
+
+    services.journald.extraConfig = ''
+      MaxRetentionSec=3month
+    '';
+
+    environment.systemPackages = with pkgs; [
+      binutils
+      cacert
+      file
+      htop
+      iotop
+      psmisc
+      unfreePkgs.unrar
+      unzip
+      vlock
+      wget
+      xz
+    ];
+
+    nixpkgs.config = {
+      pulseaudio = true;
+      firefox.icedtea = true;
+
+      allowBroken = true;
+
+      packageOverrides = pkgs: {
+        beets = pkgs.beets.override {
+          enableAlternatives = true;
+        };
+        miro = pkgs.miro.override {
+          enableBonjour = true;
+        };
+        netrw = pkgs.netrw.override {
+          checksumType = "mhash";
+        };
+        nix = pkgs.nixUnstable;
+        # XXX: As of edolstra/nix-repl@8a2f5f0, this won't build with
+        #      nixUnstable (version 1.12pre4509_69f28eb).
+        nix-repl = pkgs.nix-repl.overrideDerivation (drv: {
+          src = pkgs.fetchFromGitHub {
+            owner = "edolstra";
+            repo = "nix-repl";
+            rev = "0e49f941205769852846cb8afa228831cf6ae363";
+            sha256 = "0zsxkxypnm8zzzslvcdqips7glbxw1riq9mxn9w23kayl8d1mfpx";
+          };
+          NIX_CFLAGS_COMPILE = "-std=c++1y";
+          postPatch = (drv.postPatch or "") + ''
+            sed -i -e 's/pid\.wait(true)/pid.wait()/g' nix-repl.cc
+          '';
+        });
+        uqm = pkgs.uqm.override {
+          use3DOVideos = true;
+          useRemixPacks = true;
+        };
+        w3m = pkgs.w3m.override {
+          graphicsSupport = true;
+        };
+      };
+    };
+
+    system.fsPackages = with pkgs; [ sshfsFuse ];
+    time.timeZone = "Europe/Berlin";
+  };
+}
diff --git a/modules/user/aszlig/profiles/managed.nix b/modules/user/aszlig/profiles/managed.nix
new file mode 100644
index 00000000..60848daa
--- /dev/null
+++ b/modules/user/aszlig/profiles/managed.nix
@@ -0,0 +1,41 @@
+{ pkgs, unfreeAndNonDistributablePkgs, config, lib, ... }:
+
+let
+  inherit (lib) mkIf mkEnableOption mkOption;
+  cfg = config.vuizvui.user.aszlig.profiles.managed;
+  inherit (cfg) mainUser;
+
+in {
+  options.vuizvui.user.aszlig.profiles.managed = {
+    enable = mkEnableOption "common profile for aszlig's managed machines";
+    mainUser = mkOption {
+      example = "foobar";
+      description = ''
+        Main user account of the managed system.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.simple-scan ];
+
+    # Printing for the most common printers among the managed machines.
+    services.printing.enable = true;
+    services.printing.drivers = [
+      pkgs.gutenprint
+      unfreeAndNonDistributablePkgs.hplipWithPlugin
+    ];
+
+    # And also most common scanners are also HP ones.
+    hardware.sane.enable = true;
+    hardware.sane.extraBackends = [
+      unfreeAndNonDistributablePkgs.hplipWithPlugin
+    ];
+
+    users.users.${mainUser} = {
+      isNormalUser = true;
+      uid = 1000;
+      extraGroups = [ "networkmanager" "scanner" "video" "wheel" ];
+    };
+  };
+}
diff --git a/modules/user/aszlig/profiles/workstation/default.nix b/modules/user/aszlig/profiles/workstation/default.nix
new file mode 100644
index 00000000..793a1727
--- /dev/null
+++ b/modules/user/aszlig/profiles/workstation/default.nix
@@ -0,0 +1,182 @@
+{ pkgs, config, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.profiles.workstation;
+  inherit (config.services.xserver) xrandrHeads;
+
+in {
+  options.vuizvui.user.aszlig.profiles.workstation = {
+    enable = lib.mkEnableOption "Workstation profile for aszlig";
+  };
+
+  config = lib.mkIf cfg.enable {
+    vuizvui.user.aszlig.profiles.base.enable = true;
+
+    boot.kernelParams = [ "zswap.enabled=1" "panic=1800" ];
+    boot.cleanTmpDir = true;
+
+    environment.systemPackages = with lib; let
+      mkRandrConf = acc: rcfg: acc ++ singleton {
+        name = rcfg.output;
+        value = "--output ${lib.escapeShellArg rcfg.output} --preferred"
+              + optionalString rcfg.primary " --primary"
+              + optionalString (acc != []) " --right-of '${(head acc).name}'";
+      };
+      randrConf = map (getAttr "value") (foldl mkRandrConf [] xrandrHeads);
+    in singleton (pkgs.writeScriptBin "xreset" ''
+      #!${pkgs.stdenv.shell}
+      ${pkgs.xorg.xrandr}/bin/xrandr ${concatStringsSep " " randrConf}
+    '') ++ import ./packages.nix pkgs;
+
+    environment.pathsToLink = lib.singleton "/share/chromium/extensions";
+
+    vuizvui.lazyPackages = import ./lazy-packages.nix pkgs;
+
+    hardware = {
+      pulseaudio.enable = true;
+      pulseaudio.package = pkgs.pulseaudioFull;
+      opengl = {
+        driSupport32Bit = true;
+        s3tcSupport = true;
+      };
+    };
+
+    fonts = {
+      enableFontDir = true;
+      enableGhostscriptFonts = true;
+      fonts = [
+        pkgs.dosemu_fonts
+        pkgs.liberation_ttf
+      ];
+    };
+
+    vuizvui.user.aszlig.services.i3.enable = true;
+    vuizvui.user.aszlig.services.slim.enable = true;
+    vuizvui.user.aszlig.services.vlock.enable = true;
+
+    vuizvui.user.aszlig.programs.gajim.enable = true;
+    vuizvui.user.aszlig.programs.mpv.enable = true;
+    vuizvui.user.aszlig.programs.taskwarrior.enable = true;
+    vuizvui.user.aszlig.programs.xpdf.enable = true;
+
+    vuizvui.user.aszlig.programs.git.enable = true;
+    vuizvui.user.aszlig.programs.git.config = {
+      color.ui = "auto";
+      merge.tool = "vimdiff3";
+      user.email = "aszlig@redmoonstudios.org";
+      user.name = "aszlig";
+      user.signingkey = "4DFD43EC834B6901BDA2BAAC1DE8E48E57DB5436";
+      gpg.program = "${pkgs.gnupg}/bin/gpg2";
+      push.default = "current";
+      tar."tar.xz".command = "${pkgs.xz}/bin/xz -c";
+      rebase.autosquash = true;
+      rerere.enabled = true;
+      rerere.autoupdate = true;
+      commit.gpgsign = true;
+
+      alias.backport = let
+        release = "14.04";
+        message = "Merge release ${release} into backports.";
+      in "!git fetch upstream release-${release} &&"
+       + " git merge -m \"${message}\" --log FETCH_HEAD";
+    };
+
+    vuizvui.hardware.gameController."03000000ff1100004133000010010000" = {
+      name = "PS2 Controller";
+      mapping = {
+        a = "b2";
+        b = "b1";
+        x = "b3";
+        y = "b0";
+        back = "b8";
+        start = "b9";
+        leftshoulder = "b6";
+        rightshoulder = "b7";
+        leftstick = "b10";
+        rightstick = "b11";
+        leftx = "a0";
+        lefty = "a1";
+        rightx = "a3";
+        righty = "a2";
+        lefttrigger = "b4";
+        righttrigger = "b5";
+        dpup = "h0.1";
+        dpleft = "h0.8";
+        dpdown = "h0.4";
+        dpright = "h0.2";
+      };
+    };
+
+    vuizvui.programs.gnupg.enable = true;
+    vuizvui.programs.gnupg.agent.enable = true;
+    vuizvui.programs.gnupg.agent.sshSupport = true;
+    vuizvui.programs.gnupg.agent.scdaemon.enable = true;
+
+    vuizvui.user.aszlig.programs.taalo-build.enable = true;
+
+    services = {
+      openssh = {
+        enable = true;
+        permitRootLogin = "without-password";
+      };
+
+      xfs.enable = false;
+
+      gpm = {
+        enable = true;
+        protocol = "exps2";
+      };
+
+      printing.enable = true;
+      printing.drivers = [ pkgs.gutenprint pkgs.hplip ];
+
+      pcscd.enable = true;
+      pcscd.plugins = [ pkgs.ccid pkgs.pcsc-cyberjack ];
+
+      udev.extraRules = ''
+        # aXbo S.P.A.C.
+        SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", \
+          ATTRS{serial}=="0001", OWNER="aszlig", SYMLINK+="axbo"
+        # Enttec DMX device
+        SUBSYSTEM=="usb*|tty", ACTION=="add|change", ATTRS{idVendor}=="0403", \
+          ATTRS{idProduct}=="6001", OWNER="aszlig"
+      '';
+
+      redshift = {
+        enable = true;
+        latitude = "48.428404";
+        longitude = "10.866007";
+        temperature.day = 5500;
+        temperature.night = 3500;
+      };
+
+      xserver = {
+        enable = true;
+        layout = "dvorak";
+
+        displayManager.sessionCommands = ''
+          ${pkgs.xorg.xrdb}/bin/xrdb "${pkgs.writeText "xrdb.config" ''
+            XTerm*font:                vga
+            XTerm*saveLines:           10000
+            XTerm*bellIsUrgent:        true
+            XTerm*background:          black
+            XTerm*foreground:          grey
+
+            XTerm*backarrowKeyIsErase: true
+            XTerm*ptyInitialErase:     true
+          ''}"
+        '';
+
+        desktopManager.default = "none";
+        desktopManager.xterm.enable = false;
+      };
+    };
+
+    users.users.aszlig = {
+      uid = 1000;
+      isNormalUser = true;
+      description = "aszlig";
+      extraGroups = [ "wheel" "video" ];
+    };
+  };
+}
diff --git a/modules/user/aszlig/profiles/workstation/lazy-packages.nix b/modules/user/aszlig/profiles/workstation/lazy-packages.nix
new file mode 100644
index 00000000..17fbcf09
--- /dev/null
+++ b/modules/user/aszlig/profiles/workstation/lazy-packages.nix
@@ -0,0 +1,28 @@
+pkgs: with pkgs; [
+  vuizvui.aszlig.aacolorize
+  aqbanking
+  erlang
+  fbida
+  firefox
+  gimp
+  gwenhywfar
+  gpodder
+  graphviz
+  haskellPackages.cabal2nix
+  haskellPackages.cabal-install
+  haskellPackages.hlint
+  haskellPackages.yesod-bin
+  haxe
+  lastwatch
+  libchipcard
+  lftp
+  mp3info
+  mpg321
+  mumble
+  neko
+  nixpkgs-lint
+  picard
+  rtmpdump
+  rtorrent
+  uqm
+]
diff --git a/modules/user/aszlig/profiles/workstation/packages.nix b/modules/user/aszlig/profiles/workstation/packages.nix
new file mode 100644
index 00000000..5bb1f167
--- /dev/null
+++ b/modules/user/aszlig/profiles/workstation/packages.nix
@@ -0,0 +1,78 @@
+pkgs: with pkgs; [
+  abook
+  acpi
+  apg
+  ascii
+  aspellDicts.de
+  aspellDicts.en
+  vuizvui.aszlig.axbo
+  bc
+  beets
+  chromium
+  dash
+  dos2unix
+  ffmpeg
+  figlet
+  flac
+  gdb
+  ghostscript
+  vuizvui.aszlig.git-detach
+  glxinfo
+  gnumake
+  gnupg1compat
+  hexedit
+  i3
+  i3lock
+  imagemagick
+  jwhois
+  jq
+  keychain
+  ltrace
+  man-pages
+  mmv
+  mosh
+  mtr
+  mutt
+  ncdu
+  netrw
+  nix-prefetch-scripts
+  nix-repl
+  vuizvui.aszlig.nixops
+  nmap
+  openssh
+  openssl
+  p7zip
+  pavucontrol
+  posix_man_pages
+  pulseaudioLight
+  vuizvui.aszlig.pvolctrl
+  pass
+  python
+  python3
+  pythonPackages.hetzner
+  pythonPackages.pep8
+  pythonPackages.polib
+  radare2
+  rlwrap
+  rsync
+  vuizvui.aszlig.santander
+  samplicator
+  screen
+  scrot
+  socat
+  sox
+  sqlite
+  stdmanpages
+  strace
+  surfraw
+  telnet
+  unzip
+  valgrind
+  vbindiff
+  vorbisTools
+  w3m
+  wireshark
+  xorg.xhost
+  youtubeDL
+  zathura
+]
diff --git a/modules/user/aszlig/programs/gajim/config.nix b/modules/user/aszlig/programs/gajim/config.nix
new file mode 100644
index 00000000..86de59ed
--- /dev/null
+++ b/modules/user/aszlig/programs/gajim/config.nix
@@ -0,0 +1,731 @@
+lib: with lib;
+
+let
+  mkConfig = let
+    traverse = path: attrs: let
+      mkVal = name: value: let
+        flatPath = concatStringsSep "." (path ++ [name]);
+      in if isAttrs value then traverse (path ++ [name]) value
+         else if value == true then "${flatPath} = True"
+         else if value == false then "${flatPath} = False"
+         else "${flatPath} = ${value}";
+    in concatStringsSep "\n" (mapAttrsToList mkVal attrs);
+
+    rootTraverse = attrs: (traverse [] attrs) + "\n";
+  in rootTraverse;
+
+in mkConfig {
+  activity_iconset = "default";
+  after_nickname = ">";
+  allow_hide_roster = true;
+  always_english_wikipedia = false;
+  always_english_wiktionary = true;
+  ascii_formatting = true;
+  ask_avatars_on_startup = true;
+  ask_offline_status = true;
+  ask_offline_status_on_connection = false;
+  ask_online_status = false;
+  attach_notifications_to_systray = false;
+  audio_input_device = "pulsesrc device=alsa_input."
+                     + "usb-046d_0804_DD519390-02-U0x46d0x804.analog-mono"
+                     + " ! volume name=gajim_vol";
+  audio_input_volume = "50";
+  audio_output_device = "pulsesink device=alsa_output."
+                      + "pci-0000_00_1b.0.analog-stereo sync=true";
+  audio_output_volume = "50";
+  autoaway = false;
+  autoaway_message = "$S (Away as a result of being idle more than $T min)";
+  autoawaytime = "5";
+  autodetect_browser_mailer = false;
+  autopopup = true;
+  autopopupaway = true;
+  autoxa = false;
+  autoxa_message = "$S (Not available as a result of being"
+                 + " idle more than $T min)";
+  autoxatime = "15";
+  avatar_position_in_roster = "left";
+  before_nickname = "<";
+  change_roster_title = true;
+  change_status_window_timeout = "15";
+  "chat-msgwin-height" = "440";
+  "chat-msgwin-width" = "480";
+  "chat-msgwin-x-position" = "-1";
+  "chat-msgwin-y-position" = "-1";
+  chat_avatar_height = "52";
+  chat_avatar_width = "52";
+  chat_merge_consecutive_nickname = false;
+  chat_merge_consecutive_nickname_indent = "  ";
+  check_idle_every_foo_seconds = "2";
+  check_if_gajim_is_default = true;
+  collapsed_rows = "";
+  compact_view = false;
+  confirm_block = "";
+  confirm_close_muc = true;
+  confirm_close_muc_rooms = "";
+  confirm_close_multiple_tabs = true;
+  confirm_custom_status = "no";
+  confirm_metacontacts = "no";
+  conversation_font = "Liberation Mono 10";
+  ctrl_tab_go_to_next_composing = true;
+  custom_file_manager = "";
+  custombrowser = "chromium";
+  custommailapp = "";
+  dictionary_url = "WIKTIONARY";
+  displayed_chat_state_notifications = "all";
+  emoticons_theme = "";
+  enable_negative_priority = false;
+  escape_key_closes = false;
+  esession_modp = "5,14";
+  file_transfers_port = "28011";
+  ft_add_hosts_to_send = "";
+  "gc-hpaned-position" = "979";
+  "gc-msgwin-height" = "440";
+  "gc-msgwin-width" = "600";
+  "gc-msgwin-x-position" = "-1";
+  "gc-msgwin-y-position" = "-1";
+  gc_nicknames_colors = "#4e9a06:#f57900:#ce5c00:#3465a4:#204a87:#75507b:"
+                      + "#5c3566:#c17d11:#8f5902:#ef2929:#cc0000:#a40000";
+  gc_proposed_nick_char = "_";
+  gc_refer_to_nick_char = ":";
+  global_proxy = "";
+  hide_avatar_of_transport = true;
+  hide_chat_banner = false;
+  hide_groupchat_banner = false;
+  hide_groupchat_occupants_list = false;
+  history_window_height = "1156";
+  history_window_width = "1596";
+  "history_window_x-position" = "0";
+  "history_window_y-position" = "20";
+  iconset = "dcraven";
+  ignore_incoming_xhtml = false;
+  inmsgcolor = "#ff7f50";
+  inmsgfont = "";
+  inmsgtxtcolor = "";
+  inmsgtxtfont = "";
+  just_connected_bg_color = "#adc3c6";
+  just_disconnected_bg_color = "#ab6161";
+  key_up_lines = "25";
+  last_emoticons_dir = "";
+  last_roster_visible = true;
+  last_save_dir = "";
+  last_send_dir = "";
+  last_sounds_dir = "";
+  latex_png_dpi = "108";
+  log_contact_status_changes = true;
+  log_xhtml_messages = false;
+  markedmsgcolor = "#ff8080";
+  max_conversation_lines = "500";
+  mergeaccounts = false;
+  mood_iconset = "default";
+  "msgwin-height" = "1156";
+  "msgwin-max-state" = true;
+  "msgwin-width" = "1336";
+  "msgwin-x-position" = "0";
+  "msgwin-y-position" = "20";
+  muc_autorejoin_on_kick = false;
+  muc_autorejoin_timeout = "1";
+  muc_highlight_words = "DOWN;PROBLEM;CRITICAL;UNREACHABLE";
+  muc_restore_lines = "20";
+  muc_restore_timeout = "60";
+  networkmanager_support = true;
+  noconfirm_close_muc_rooms = "";
+  notification_avatar_height = "48";
+  notification_avatar_width = "48";
+  notification_position_x = "-1";
+  notification_position_y = "-1";
+  notification_preview_message = true;
+  notification_timeout = "5";
+  notify_on_all_muc_messages = false;
+  notify_on_file_complete = true;
+  notify_on_new_gmail_email = true;
+  notify_on_new_gmail_email_command = "";
+  notify_on_new_gmail_email_extra = false;
+  notify_on_new_message = false;
+  notify_on_signin = false;
+  notify_on_signout = false;
+  one_message_window = "always_with_roster";
+  openwith = "xdg-open";
+  outgoing_chat_state_notifications = "composing_only";
+  outmsgcolor = "#add8e6";
+  outmsgfont = "";
+  outmsgtxtcolor = "";
+  outmsgtxtfont = "";
+  plugins.plugin_installer.active = false;
+  print_ichat_every_foo_minutes = "5";
+  print_status_in_chats = true;
+  print_status_in_muc = "in_and_out";
+  print_time = "always";
+  print_time_fuzzy = "0";
+  quit_on_roster_x_button = true;
+  recently_groupchat = "";
+  remote_control = true;
+  restore_lines = "10";
+  restore_timeout = "60";
+  restored_messages_color = "#555753";
+  restored_messages_small = false;
+  roster_avatar_height = "16";
+  roster_avatar_width = "16";
+  roster_height = "1156";
+  roster_theme = "blue";
+  roster_width = "206";
+  roster_window_skip_taskbar = false;
+  "roster_x-position" = "0";
+  "roster_y-position" = "20";
+  rst_formatting_outgoing_messages = false;
+  "save-roster-position" = true;
+  scroll_roster_to_last_message = true;
+  search_engine = "https://www.google.com/search?&q=%s&sourceid=gajim";
+  send_on_ctrl_enter = false;
+  send_sha_in_gc_presence = true;
+  shell_like_completion = true;
+  show_activity_in_roster = true;
+  show_affiliation_in_groupchat = true;
+  show_ascii_formatting_chars = true;
+  show_avatar_in_chat = true;
+  show_avatars_in_roster = true;
+  show_contacts_number = true;
+  show_location_in_roster = true;
+  show_mood_in_roster = true;
+  show_only_chat_and_online = false;
+  show_roster_on_startup = "always";
+  show_self_contact = "when_other_resource";
+  show_status_msgs_in_roster = true;
+  show_transports_group = true;
+  show_tunes_in_roster = true;
+  show_unread_tab_icon = true;
+  showoffline = false;
+  "single-msg-height" = "280";
+  "single-msg-width" = "400";
+  "single-msg-x-position" = "0";
+  "single-msg-y-position" = "0";
+  sort_by_show_in_muc = false;
+  sort_by_show_in_roster = true;
+  sounddnd = false;
+  soundplayer = "aplay -q";
+  sounds_on = false;
+  speller_language = "de.en";
+  statusmsgcolor = "#4e9a06";
+  statusmsgfont = "";
+  stun_server = "";
+  tabs_always_visible = true;
+  tabs_border = false;
+  tabs_close_button = true;
+  tabs_position = "right";
+  time_stamp = "[%H:%M:%S]";
+  tooltip_account_name_color = "#888A85";
+  tooltip_affiliation_administrator_color = "#F57900";
+  tooltip_affiliation_member_color = "#73D216";
+  tooltip_affiliation_none_color = "#555753";
+  tooltip_affiliation_owner_color = "#CC0000";
+  tooltip_avatar_height = "125";
+  tooltip_avatar_width = "125";
+  tooltip_idle_color = "#888A85";
+  tooltip_status_away_color = "#EDD400";
+  tooltip_status_busy_color = "#F57900";
+  tooltip_status_free_for_chat_color = "#3465A4";
+  tooltip_status_na_color = "#CC0000";
+  tooltip_status_offline_color = "#555753";
+  tooltip_status_online_color = "#73D216";
+  trayicon = "never";
+  trayicon_notification_on_events = true;
+  treat_incoming_messages = "";
+  uri_schemes = "aaa:// aaas:// acap:// cap:// cid: crid:// data: dav: "
+              + "dict:// dns: fax: file:/ ftp:// geo: go: gopher:// h323: "
+              + "http:// https:// iax: icap:// im: imap:// info: ipp:// iris: "
+              + "iris.beep: iris.xpc: iris.xpcs: iris.lwz: ldap:// mid: "
+              + "modem: msrp:// msrps:// mtqp:// mupdate:// news: nfs:// "
+              + "nntp:// opaquelocktoken: pop:// pres: prospero:// rtsp:// "
+              + "service: shttp:// sip: sips: sms: snmp:// soap.beep:// "
+              + "soap.beeps:// tag: tel: telnet:// tftp:// thismessage:/ "
+              + "tip:// tv: urn:// vemmi:// xmlrpc.beep:// xmlrpc.beeps:// "
+              + "z39.50r:// z39.50s:// about: apt: cvs:// daap:// ed2k:// "
+              + "feed: fish:// git:// iax2: irc:// ircs:// ldaps:// magnet: "
+              + "mms:// rsync:// ssh:// svn:// sftp:// smb:// webcal://";
+  urlmsgcolor = "#add8e6";
+  use_gnomekeyring = true;
+  use_gpg_agent = true;
+  use_kib_mib = false;
+  use_kwalletcli = true;
+  use_latex = false;
+  use_notif_daemon = true;
+  use_smooth_scrolling = true;
+  use_speller = true;
+  use_stun_server = false;
+  use_transports_iconsets = true;
+  use_urgency_hint = true;
+  vcard_avatar_height = "200";
+  vcard_avatar_width = "200";
+  verbose = false;
+  version = "0.15.4";
+  video_framerate = "";
+  video_input_device = "v4l2src device=/dev/video0";
+  video_output_device = "ximagesink";
+  video_size = "";
+
+  accounts = {
+    Local = {
+      action_when_plaintext_connection = "warn";
+      active = true;
+      adjust_priority_with_status = true;
+      allow_no_log_for = "";
+      anonymous_auth = false;
+      answer_receipts = true;
+      attached_gpg_keys = "";
+      autoauth = false;
+      autoconnect = true;
+      autoconnect_as = "online";
+      autonegotiate_esessions = true;
+      autopriority_away = "40";
+      autopriority_chat = "50";
+      autopriority_dnd = "20";
+      autopriority_invisible = "10";
+      autopriority_online = "50";
+      autopriority_xa = "30";
+      autoreconnect = true;
+      client_cert = "";
+      client_cert_encrypted = false;
+      connection_types = "tls ssl plain";
+      custom_host = "";
+      custom_port = "5298";
+      dont_ack_subscription = false;
+      enable_esessions = true;
+      enable_message_carbons = false;
+      file_transfer_proxies = "proxy.eu.jabber.org, proxy.jabber.ru, "
+                            + "proxy.jabbim.cz";
+      ft_send_local_ips = true;
+      gpg_sign_presence = true;
+      hostname = "mmrnmhrm";
+      http_auth = "ask";
+      ignore_ssl_errors = "";
+      ignore_unknown_contacts = false;
+      is_zeroconf = true;
+      keep_alive_every_foo_secs = "55";
+      keep_alives_enabled = true;
+      keyid = "";
+      keyname = "";
+      last_archiving_time = "1970-01-01T00:00:00Z";
+      last_status = "online";
+      last_status_msg = "";
+      listen_to_network_manager = true;
+      log_encrypted_sessions = true;
+      minimized_gc = "";
+      "msgwin-height" = "440";
+      "msgwin-width" = "480";
+      "msgwin-x-position" = "-1";
+      "msgwin-y-position" = "-1";
+      name = "aszlig";
+      no_log_for = "";
+      password = "zeroconf";
+      ping_alive_every_foo_secs = "120";
+      ping_alives_enabled = true;
+      priority = "5";
+      proxy = "";
+      publish_location = false;
+      publish_tune = false;
+      request_receipt = true;
+      resource = "gajim";
+      restore_last_status = false;
+      roster_version = "";
+      savepass = false;
+      send_idle_time = true;
+      send_os_info = true;
+      send_time_info = true;
+      ssl_fingerprint_sha1 = "";
+      subscribe_activity = true;
+      subscribe_location = true;
+      subscribe_mood = true;
+      subscribe_nick = true;
+      subscribe_tune = true;
+      subscription_request_msg = "";
+      sync_with_global_status = true;
+      test_ft_proxies_on_startup = true;
+      time_for_ping_alive_answer = "60";
+      try_connecting_for_foo_secs = "60";
+      use_custom_host = false;
+      use_env_http_proxy = false;
+      use_ft_proxies = false;
+      use_srv = true;
+      warn_when_insecure_password = true;
+      warn_when_insecure_ssl_connection = true;
+      zeroconf_email = "";
+      zeroconf_first_name = "";
+      zeroconf_jabber_id = "";
+      zeroconf_last_name = "";
+    };
+
+    "aszlig.net" = {
+      action_when_plaintext_connection = "disconnect";
+      active = true;
+      adjust_priority_with_status = true;
+      allow_no_log_for = "";
+      anonymous_auth = false;
+      answer_receipts = true;
+      autoauth = false;
+      autoconnect = false;
+      autoconnect_as = "online";
+      autonegotiate_esessions = true;
+      autopriority_away = "40";
+      autopriority_chat = "50";
+      autopriority_dnd = "20";
+      autopriority_invisible = "10";
+      autopriority_online = "50";
+      autopriority_xa = "30";
+      autoreconnect = true;
+      client_cert = "";
+      client_cert_encrypted = false;
+      connection_types = "tls ssl plain";
+      custom_host = "aszlig.net";
+      custom_port = "5222";
+      dont_ack_subscription = false;
+      enable_esessions = true;
+      enable_message_carbons = false;
+      file_transfer_proxies = "proxy.headcounter.org";
+      ft_send_local_ips = true;
+      gpg_sign_presence = true;
+      hostname = "aszlig.net";
+      http_auth = "ask";
+      ignore_ssl_errors = "";
+      ignore_unknown_contacts = false;
+      is_zeroconf = false;
+      keep_alive_every_foo_secs = "55";
+      keep_alives_enabled = true;
+      keyid = "4DFD43EC834B6901BDA2BAAC1DE8E48E57DB5436";
+      keyname = ''aszlig <"^[0-9]+$"@regexmail.net>'';
+      last_archiving_time = "1970-01-01T00:00:00Z";
+      last_status_msg = "";
+      listen_to_network_manager = true;
+      log_encrypted_sessions = true;
+      minimized_gc = "";
+      "msgwin-height" = "440";
+      "msgwin-width" = "480";
+      "msgwin-x-position" = "-1";
+      "msgwin-y-position" = "-1";
+      name = "aszlig";
+      no_log_for = "";
+      ping_alive_every_foo_secs = "120";
+      ping_alives_enabled = true;
+      priority = "5";
+      proxy = "";
+      publish_location = false;
+      publish_tune = false;
+      request_receipt = true;
+      resource = "redmoon";
+      restore_last_status = false;
+      savepass = true;
+      send_idle_time = true;
+      send_os_info = true;
+      send_time_info = true;
+      ssl_fingerprint_sha1 = "8D:BC:E5:46:AB:B3:53:F7:36:B3:"
+                           + "66:0D:B4:B7:83:32:65:BA:A8:EF";
+      subscribe_activity = true;
+      subscribe_location = true;
+      subscribe_mood = true;
+      subscribe_nick = true;
+      subscribe_tune = true;
+      subscription_request_msg = "";
+      sync_with_global_status = true;
+      test_ft_proxies_on_startup = true;
+      time_for_ping_alive_answer = "60";
+      try_connecting_for_foo_secs = "60";
+      use_custom_host = false;
+      use_env_http_proxy = false;
+      use_ft_proxies = true;
+      use_srv = true;
+      warn_when_insecure_password = true;
+      warn_when_insecure_ssl_connection = true;
+      zeroconf_email = "";
+      zeroconf_first_name = "";
+      zeroconf_jabber_id = "";
+      zeroconf_last_name = "";
+    };
+  };
+
+  defaultstatusmsg = {
+    away = {
+      enabled = false;
+      message = "Be right back.";
+    };
+
+    chat = {
+      enabled = false;
+      message = "I'm free for chat.";
+    };
+
+    dnd = {
+      enabled = false;
+      message = "Do not disturb.";
+    };
+
+    invisible = {
+      enabled = false;
+      message = "Bye!";
+    };
+
+    offline = {
+      enabled = false;
+      message = "Bye!";
+    };
+
+    online = {
+      enabled = false;
+      message = "I'm available.";
+    };
+
+    xa = {
+      enabled = false;
+      message = "I'm not available.";
+    };
+  };
+
+  statusmsg = let
+    defaults = {
+      activity = "";
+      activity_text = "";
+      message = "";
+      mood = "";
+      mood_text = "";
+      subactivity = "";
+    };
+    applyDefaults = const (attrs: defaults // attrs);
+  in mapAttrs applyDefaults {
+    zone.activity = "working";
+    zone.subactivity = "coding";
+    zone.message = "In The Zone[TM]";
+
+    rofa.activity = "working";
+    rofa.activity_text = "Blinded by the lights...";
+    rofa.subactivity = "other";
+    rofa.message = "RoFa";
+
+    kernel.mood = "happy";
+    kernel.message = "Kerneling down for reboot NOW.";
+
+    sleep.activity = "inactive";
+    sleep.subactivity = "sleeping";
+    sleep.mood = "sleepy";
+    sleep.message = "Sleeping the hell out of here...";
+
+    _last_away = {};
+    _last_chat = {};
+    _last_dnd = {};
+    _last_invisible = {};
+    _last_offline = {};
+    _last_online = {};
+    _last_xa = {};
+  };
+
+  soundevents = {
+    contact_connected = {
+      enabled = false;
+      path = "connected.wav";
+    };
+
+    contact_disconnected = {
+      enabled = false;
+      path = "disconnected.wav";
+    };
+
+    first_message_received = {
+      enabled = true;
+      path = "message1.wav";
+    };
+
+    gmail_received = {
+      enabled = false;
+      path = "message1.wav";
+    };
+
+    message_sent = {
+      enabled = false;
+      path = "sent.wav";
+    };
+
+    muc_message_highlight = {
+      enabled = true;
+      path = "gc_message1.wav";
+    };
+
+    muc_message_received = {
+      enabled = false;
+      path = "gc_message2.wav";
+    };
+
+    next_message_received_focused = {
+      enabled = false;
+      path = "message2.wav";
+    };
+
+    next_message_received_unfocused = {
+      enabled = true;
+      path = "message2.wav";
+    };
+  };
+
+  proxies.Tor = {
+    bosh_content = "text/xml; charset=utf-8";
+    bosh_hold = "2";
+    bosh_http_pipelining = false;
+    bosh_uri = "";
+    bosh_useproxy = false;
+    bosh_wait = "30";
+    bosh_wait_for_restart_response = false;
+    host = "localhost";
+    pass = "";
+    port = "9050";
+    type = "socks5";
+    useauth = false;
+    user = "";
+  };
+
+  themes = {
+    blue = {
+      accountbgcolor = "#0c232e";
+      accountfont = "Liberation Mono 8";
+      accountfontattrs = "B";
+      accounttextcolor = "#ffffff";
+      bannerbgcolor = "#0f4864";
+      bannerfont = "Liberation Mono Bold 12";
+      bannerfontattrs = "B";
+      bannertextcolor = "#ffffff";
+      contactbgcolor = "#0c232b";
+      contactfont = "Liberation Mono Bold 8";
+      contactfontattrs = "";
+      contacttextcolor = "#ffffff";
+      groupbgcolor = "#18515f";
+      groupfont = "Liberation Mono Bold 8";
+      groupfontattrs = "I";
+      grouptextcolor = "#ffffff";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+
+    default = {
+      accountbgcolor = "";
+      accountfont = "";
+      accountfontattrs = "B";
+      accounttextcolor = "";
+      bannerbgcolor = "";
+      bannerfont = "";
+      bannerfontattrs = "B";
+      bannertextcolor = "";
+      contactbgcolor = "";
+      contactfont = "";
+      contactfontattrs = "";
+      contacttextcolor = "";
+      groupbgcolor = "";
+      groupfont = "";
+      groupfontattrs = "I";
+      grouptextcolor = "";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+
+    green = {
+      accountbgcolor = "#94aa8c";
+      accountfont = "";
+      accountfontattrs = "B";
+      accounttextcolor = "";
+      bannerbgcolor = "#94aa8c";
+      bannerfont = "";
+      bannerfontattrs = "B";
+      bannertextcolor = "";
+      contactbgcolor = "";
+      contactfont = "";
+      contactfontattrs = "";
+      contacttextcolor = "#000000";
+      groupbgcolor = "#eff3e7";
+      groupfont = "";
+      groupfontattrs = "I";
+      grouptextcolor = "#0000ff";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+
+    grocery = {
+      accountbgcolor = "#6bbe18";
+      accountfont = "";
+      accountfontattrs = "B";
+      accounttextcolor = "";
+      bannerbgcolor = "#108abd";
+      bannerfont = "";
+      bannerfontattrs = "B";
+      bannertextcolor = "";
+      contactbgcolor = "#efb26b";
+      contactfont = "";
+      contactfontattrs = "";
+      contacttextcolor = "#000000";
+      groupbgcolor = "#ceefad";
+      groupfont = "";
+      groupfontattrs = "I";
+      grouptextcolor = "#12125a";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+
+    human = {
+      accountbgcolor = "#996442";
+      accountfont = "";
+      accountfontattrs = "B";
+      accounttextcolor = "";
+      bannerbgcolor = "#996442";
+      bannerfont = "";
+      bannerfontattrs = "B";
+      bannertextcolor = "";
+      contactbgcolor = "";
+      contactfont = "";
+      contactfontattrs = "";
+      contacttextcolor = "#000000";
+      groupbgcolor = "#e3ca94";
+      groupfont = "";
+      groupfontattrs = "I";
+      grouptextcolor = "#ab5920";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+
+    marine = {
+      accountbgcolor = "#918caa";
+      accountfont = "";
+      accountfontattrs = "B";
+      accounttextcolor = "";
+      bannerbgcolor = "#918caa";
+      bannerfont = "";
+      bannerfontattrs = "B";
+      bannertextcolor = "";
+      contactbgcolor = "";
+      contactfont = "";
+      contactfontattrs = "";
+      contacttextcolor = "#000000";
+      groupbgcolor = "#e9e7f3";
+      groupfont = "";
+      groupfontattrs = "I";
+      grouptextcolor = "";
+      state_composing_color = "green4";
+      state_gone_color = "grey";
+      state_inactive_color = "grey62";
+      state_muc_directed_msg_color = "red2";
+      state_muc_msg_color = "mediumblue";
+      state_paused_color = "mediumblue";
+    };
+  };
+}
diff --git a/modules/user/aszlig/programs/gajim/config.patch b/modules/user/aszlig/programs/gajim/config.patch
new file mode 100644
index 00000000..fcfcc371
--- /dev/null
+++ b/modules/user/aszlig/programs/gajim/config.patch
@@ -0,0 +1,80 @@
+diff --git a/src/common/optparser.py b/src/common/optparser.py
+index f84b18a..0078317 100644
+--- a/src/common/optparser.py
++++ b/src/common/optparser.py
+@@ -30,6 +30,7 @@ import os
+ import sys
+ import locale
+ import re
++from itertools import chain
+ from time import time
+ from common import gajim
+ from common import helpers
+@@ -46,19 +47,25 @@ class OptionsParser:
+ 
+     def read(self):
+         try:
+-            fd = open(self.__filename)
++            cfg = nixfd = open("@nix_config@", 'r')
+         except Exception:
+             if os.path.exists(self.__filename):
+                 #we talk about a file
+                 print _('Error: cannot open %s for reading') % self.__filename
+             return False
+ 
++        try:
++            fd = open(self.__filename)
++            cfg = chain(cfg, fd)
++        except Exception:
++            fd = None
++
+         new_version = gajim.config.get('version')
+         new_version = new_version.split('-', 1)[0]
+         seen = set()
+         regex = re.compile(r"(?P<optname>[^.=]+)(?:(?:\.(?P<key>.+))?\.(?P<subname>[^.=]+))?\s=\s(?P<value>.*)")
+ 
+-        for line in fd:
++        for line in cfg:
+             try:
+                 line = line.decode('utf-8')
+             except UnicodeDecodeError:
+@@ -79,10 +86,13 @@ class OptionsParser:
+         self.update_config(old_version, new_version)
+         self.old_values = {} # clean mem
+ 
+-        fd.close()
++        if fd is not None:
++            fd.close()
++
++        nixfd.close()
+         return True
+ 
+-    def write_line(self, fd, opt, parents, value):
++    def write_line(self, (fd, nixcfg), opt, parents, value):
+         if value is None:
+             return
+         value = value[1]
+@@ -102,17 +112,21 @@ class OptionsParser:
+                     p = p.encode('utf-8')
+                 s += p + '.'
+         s += opt
+-        fd.write(s + ' = ' + value + '\n')
++        line = s + ' = ' + value + '\n'
++        if not nixcfg.startswith(line) and not ('\n' + line) in nixcfg:
++            fd.write(line)
+ 
+     def write(self):
+         (base_dir, filename) = os.path.split(self.__filename)
+         self.__tempfile = os.path.join(base_dir, '.' + filename)
++
+         try:
++            nixcfg = open("@nix_config@", 'r').read()
+             f = open(self.__tempfile, 'w')
+         except IOError, e:
+             return str(e)
+         try:
+-            gajim.config.foreach(self.write_line, f)
++            gajim.config.foreach(self.write_line, (f, nixcfg))
+         except IOError, e:
+             return str(e)
+         f.flush()
diff --git a/modules/user/aszlig/programs/gajim/default.nix b/modules/user/aszlig/programs/gajim/default.nix
new file mode 100644
index 00000000..8259eac1
--- /dev/null
+++ b/modules/user/aszlig/programs/gajim/default.nix
@@ -0,0 +1,70 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.gajim;
+
+  gtkTheme = pkgs.writeText "gajim.gtkrc" ''
+    style "default" {
+      fg[NORMAL] = "#d5faff"
+      fg[ACTIVE] = "#fffeff"
+      fg[SELECTED] = "#fffeff"
+      fg[INSENSITIVE] = "#85aaaf"
+      fg[PRELIGHT] = "#d7f2ff"
+
+      text[NORMAL] = "#fffefe"
+      text[ACTIVE] = "#fffeff"
+      text[SELECTED] = "#fffeff"
+      text[INSENSITIVE] = "#85aaaf"
+      text[PRELIGHT] = "#d7f2ff"
+
+      bg[NORMAL] = "#0f4866"
+      bg[ACTIVE] = "#0c232e"
+      bg[SELECTED] = "#005a56"
+      bg[INSENSITIVE] = "#103040"
+      bg[PRELIGHT] = "#1d5875"
+
+      base[NORMAL] = "#0c232e"
+      base[ACTIVE] = "#0f4864"
+      base[SELECTED] = "#005a56"
+      base[INSENSITIVE] = "#103040"
+      base[PRELIGHT] = "#1d5875"
+    }
+
+    class "GtkWidget" style "default"
+
+    gtk-enable-animations = 0
+  '';
+
+  gajimPatched = let
+    o = pkgs.vuizvui.aszlig.gajim.drvAttrs;
+  in pkgs.stdenv.mkDerivation (pkgs.vuizvui.aszlig.gajim.drvAttrs // {
+    patches = (o.patches or []) ++ singleton (pkgs.substituteAll {
+      src = ./config.patch;
+      nix_config = pkgs.writeText "gajim.config" (import ./config.nix lib);
+    });
+    propagatedBuildInputs = (o.propagatedBuildInputs or []) ++ [
+      pkgs.pythonPackages.python-axolotl
+    ];
+    postPatch = (o.postPatch or "") + ''
+      # Disable a few config-related and GUI tests that won't work with our
+      # patches.
+      sed -i -e '/integration\.test_roster/d' \
+             -e '/unit.test_gui_interface/d' \
+             test/runtests.py
+
+      sed -i -e '/^export/i export GTK2_RC_FILES="${gtkTheme}"' \
+        scripts/gajim.in
+    '';
+  });
+
+in {
+  options.vuizvui.user.aszlig.programs.gajim = {
+    enable = mkEnableOption "aszlig's Gajim";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ gajimPatched ];
+  };
+}
diff --git a/modules/user/aszlig/programs/git/default.nix b/modules/user/aszlig/programs/git/default.nix
new file mode 100644
index 00000000..3cfdc742
--- /dev/null
+++ b/modules/user/aszlig/programs/git/default.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.git;
+
+  genConf = attrs: let
+    escStr = s: "\"${escape [ "\"" "\\" ] s}\"";
+    mkVal = v: if isBool v && v  then "true"
+          else if isBool v && !v then "false"
+          else escStr (toString v);
+    mkLine = key: val: "${key} = ${mkVal val}";
+
+    filterNull = filterAttrs (_: v: !(isNull v));
+
+    mkSection = sect: subsect: vals: ''
+      [${sect}${optionalString (subsect != null) " ${escStr subsect}"}]
+      ${concatStringsSep "\n" (mapAttrsToList mkLine (filterNull vals))}
+    '';
+
+    mkConf = sect: content: let
+      subs = filterAttrs (_: isAttrs) content;
+      nonSubs = filterAttrs (_: s: !isAttrs s) content;
+      hasPlain = (attrNames nonSubs) != [];
+      plainSects = singleton (mkSection sect null nonSubs);
+    in mapAttrsToList (mkSection sect) subs ++ optional hasPlain plainSects;
+
+    text = concatStringsSep "\n" (flatten (mapAttrsToList mkConf attrs));
+  in pkgs.writeText "gitconfig" text;
+
+  gitPatched = overrideDerivation pkgs.gitFull (git: {
+    makeFlags = let
+      oldFlags = git.makeFlags or [];
+      newVal = "ETC_GITCONFIG=${cfg.config}";
+    in if isList oldFlags
+       then oldFlags ++ [ newVal ]
+       else "${oldFlags} ${newVal}";
+  });
+in {
+  options.vuizvui.user.aszlig.programs.git = {
+    enable = mkEnableOption "Git";
+
+    config = mkOption {
+      description = "System-wide default config for Git";
+
+      type = with types; let
+        options = attrsOf (either (either bool int) str);
+        subSection = addCheck (attrsOf options) (s: all isAttrs (attrValues s));
+      in attrsOf (either subSection options);
+
+      default = {};
+      example = {
+        color.ui = "auto";
+        merge.tool = "vimdiff";
+        guitool.foobar.noconsole = true;
+      };
+
+      apply = genConf;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      gitPatched
+      pkgs.gitAndTools.git-remote-hg
+      pkgs.gitAndTools.hub
+    ];
+  };
+}
diff --git a/modules/user/aszlig/programs/mpv/default.nix b/modules/user/aszlig/programs/mpv/default.nix
new file mode 100644
index 00000000..7f7f66fa
--- /dev/null
+++ b/modules/user/aszlig/programs/mpv/default.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.mpv;
+
+  patchedMpv = overrideDerivation pkgs.mpv (o: {
+    installPhase = o.installPhase + ''
+      mkdir -p "$out/etc/mpv"
+      cat > "$out/etc/mpv/mpv.conf" <<CONFIG
+      ao=pulse
+      CONFIG
+    '';
+  });
+
+in {
+  options.vuizvui.user.aszlig.programs.mpv = {
+    enable = mkEnableOption "aszlig's MPV";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ patchedMpv ];
+  };
+}
diff --git a/modules/user/aszlig/programs/taalo-build/default.nix b/modules/user/aszlig/programs/taalo-build/default.nix
new file mode 100644
index 00000000..5fb09438
--- /dev/null
+++ b/modules/user/aszlig/programs/taalo-build/default.nix
@@ -0,0 +1,93 @@
+{ config, pkgs, lib, ... }:
+
+let
+  # Make sure we use a Nix version prior to 1.12, because taalo currently uses
+  # the legacy SSH protocol, so we can't use the ssh-ng store backend here.
+  #
+  # Apart from that, even if we would use the new store backend we would break
+  # the taalo-build backend for Nix 1.11.
+  inherit (import (import (../../../../../nixpkgs-path.nix)) {
+    config = {};
+  }) nix;
+
+  backend = pkgs.writeScript "taalo-realize-backend" ''
+    #!${pkgs.perl}/bin/perl -I${nix}/lib/perl5/site_perl
+    use strict;
+    use Nix::CopyClosure;
+    use Nix::SSH;
+    use IPC::Open2;
+
+    binmode STDERR, ":encoding(utf8)";
+
+    my ($from, $to);
+    my $dest = 'nix-remote-build@taalo.headcounter.org';
+    my $cmd = "exec ssh $dest -C -- nix-store --serve --write";
+    my $pid = open2($from, $to, $cmd);
+
+    # Do the handshake.
+    my $magic;
+    eval {
+        my $SERVE_MAGIC_1 = 0x390c9deb; # FIXME
+        my $clientVersion = 0x200;
+        syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion))
+          or die;
+        $magic = readInt($from);
+    };
+
+    die "unable to connect to taalo\n" if $@;
+    die "did not get valid handshake from taalo\n" if $magic != 0x5452eecb;
+
+    my $serverVersion = readInt($from);
+    die "unsupported server version\n"
+      if $serverVersion < 0x200 || $serverVersion >= 0x300;
+
+    Nix::CopyClosure::copyToOpen(
+      $from, $to, "taalo", \@ARGV, 0, 0, 0, 1
+    );
+
+    writeInt(6, $to) or die;
+    writeStrings(\@ARGV, $to);
+    writeInt(0, $to);
+    writeInt(0, $to);
+
+    my $res = readInt($from);
+
+    close $to;
+
+    waitpid($pid, 0);
+    exit $res;
+  '';
+
+  taalo-realize = pkgs.writeScriptBin "taalo-realize" ''
+    #!${pkgs.stdenv.shell}
+    if [ $# -le 0 -o "$1" = "--help" -o "$1" = "-h" ]; then
+      echo "Usage: $0 DERIVATION..." >&2
+      exit 1
+    fi
+
+    exec ${backend} "$@"
+  '';
+
+  taalo-build = pkgs.writeScriptBin "taalo-build" ''
+    #!${pkgs.stdenv.shell}
+    if tmpdir="$("${pkgs.coreutils}/bin/mktemp" -d -t taalo-build.XXXXXX)"; then
+      trap "rm -rf '$tmpdir'" EXIT
+      set -o pipefail
+      drvs="$(nix-instantiate --add-root "$tmpdir/derivation" --indirect "$@" \
+        | cut -d'!' -f1)" || exit 1
+      ${backend} $("${pkgs.coreutils}/bin/readlink" $drvs)
+      exit $?
+    else
+      echo "Unable to create temporary directory for build link!" >&2
+      exit 1
+    fi
+  '';
+
+in {
+  options.vuizvui.user.aszlig.programs.taalo-build = {
+    enable = lib.mkEnableOption "aszlig's build helpers for remote builds";
+  };
+  config = lib.mkIf config.vuizvui.user.aszlig.programs.taalo-build.enable {
+    environment.systemPackages = [ taalo-realize taalo-build ];
+  };
+}
diff --git a/modules/user/aszlig/programs/taskwarrior/config.patch b/modules/user/aszlig/programs/taskwarrior/config.patch
new file mode 100644
index 00000000..4ee4c4ce
--- /dev/null
+++ b/modules/user/aszlig/programs/taskwarrior/config.patch
@@ -0,0 +1,48 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 5558f6b..c8956f8 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -91,6 +91,9 @@ SET (TASK_DOCDIR  share/doc/task CACHE STRING "Installation directory for doc fi
+ SET (TASK_RCDIR "${TASK_DOCDIR}/rc" CACHE STRING "Installation directory for configuration files")
+ SET (TASK_BINDIR  bin            CACHE STRING "Installation directory for the binary")
+ 
++SET (SYSTEM_TASKRC "${CMAKE_INSTALL_PREFIX}/etc/taskrc"
++     CACHE STRING "System-wide taskrc")
++
+ message ("-- Looking for SHA1 references")
+ if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index)
+   set (HAVE_COMMIT true)
+diff --git a/cmake.h.in b/cmake.h.in
+index 0041e6e..f8c1a0e 100644
+--- a/cmake.h.in
++++ b/cmake.h.in
+@@ -16,6 +16,7 @@
+ 
+ /* Installation details */
+ #define TASK_RCDIR "${CMAKE_INSTALL_PREFIX}/${TASK_RCDIR}"
++#define SYSTEM_TASKRC "${SYSTEM_TASKRC}"
+ 
+ /* Localization */
+ #define PACKAGE_LANGUAGE ${PACKAGE_LANGUAGE}
+diff --git a/src/Context.cpp b/src/Context.cpp
+index 8aae74e..ffa5557 100644
+--- a/src/Context.cpp
++++ b/src/Context.cpp
+@@ -121,7 +121,8 @@ int Context::initialize (int argc, const char** argv)
+     }
+ 
+     config.clear ();
+-    config.load (rc_file);
++    config.load (SYSTEM_TASKRC);
++    config.load (rc_file, 2);
+     CLI2::applyOverrides (argc, argv);
+ 
+     ////////////////////////////////////////////////////////////////////////////
+@@ -146,7 +147,6 @@ int Context::initialize (int argc, const char** argv)
+     }
+ 
+     tdb2.set_location (data_dir);
+-    createDefaultConfig ();
+ 
+     ////////////////////////////////////////////////////////////////////////////
+     //
diff --git a/modules/user/aszlig/programs/taskwarrior/default.nix b/modules/user/aszlig/programs/taskwarrior/default.nix
new file mode 100644
index 00000000..99d428de
--- /dev/null
+++ b/modules/user/aszlig/programs/taskwarrior/default.nix
@@ -0,0 +1,36 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.taskwarrior;
+
+  taskrc = pkgs.writeText "taskrc.in" ''
+    data.location=~/.task
+    include @out@/share/doc/task/rc/dark-yellow-green.theme
+
+    color=on
+    dateformat=Y-m-d
+    dateformat.annotation=Y-m-d
+    dateformat.edit=Y-m-d H:N:S
+    dateformat.holiday=YMD
+    dateformat.info=Y-m-d H:N:S
+    dateformat.report=Y-m-d
+    weekstart=Monday
+  '';
+
+  taskwarrior = pkgs.taskwarrior.overrideDerivation (t: {
+    patches = (t.patches or []) ++ [ ./config.patch ];
+    postInstall = (t.postInstall or "") + ''
+      mkdir -p "$out/etc"
+      substituteAll "${taskrc}" "$out/etc/taskrc"
+    '';
+  });
+
+in {
+  options.vuizvui.user.aszlig.programs.taskwarrior = {
+    enable = lib.mkEnableOption "aszlig's TaskWarrior";
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = lib.singleton taskwarrior;
+  };
+}
diff --git a/modules/user/aszlig/programs/vim/default.nix b/modules/user/aszlig/programs/vim/default.nix
new file mode 100644
index 00000000..27f9e98a
--- /dev/null
+++ b/modules/user/aszlig/programs/vim/default.nix
@@ -0,0 +1,386 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.vim;
+
+  fetchVimScript = { srcId, sha256, type, name }: let
+    baseUrl = "http://www.vim.org/scripts/download_script.php";
+    src = pkgs.fetchurl {
+      name = "script${toString srcId}.vim";
+      url = "${baseUrl}?src_id=${toString srcId}";
+      inherit sha256;
+    };
+  in pkgs.stdenv.mkDerivation {
+    name = "vim-${type}-${toString srcId}";
+    buildCommand = ''
+      install -vD -m 0644 "${src}" "$out/${type}/${name}.vim"
+    '';
+  };
+
+  extractSubdir = subdir: src: pkgs.stdenv.mkDerivation {
+    name = "${src.name}-subdir";
+    phases = [ "unpackPhase" "installPhase" ];
+    inherit src;
+    installPhase = ''
+      cp -Rd "${subdir}" "$out"
+    '';
+  };
+
+  mkVimPlugins = plugins: pkgs.buildEnv {
+    name = "vim-plugins";
+    paths = with lib; mapAttrsToList (const id) plugins;
+    ignoreCollisions = true;
+    postBuild = ''
+      find -L "$out" -mindepth 1 -maxdepth 1 -type f -delete
+    '';
+  };
+
+  pluginDeps = {
+    vimAddonMwUtils = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-mw-utils";
+      rev = "0c5612fa31ee434ba055e21c76f456244b3b5109";
+      sha256 = "147s1k4n45d3x281vj35l26sv4waxjlpqdn83z3k9n51556h1d45";
+    };
+
+    vimAddonCompletion = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-completion";
+      rev = "80f717d68df5b0d7b32228229ddfd29c3e86e435";
+      sha256 = "08acffzy847w8b5j8pdw6qsidm2859ki5q351n4r7fkr969p80mi";
+    };
+
+    vimAddonActions = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-actions";
+      rev = "a5d20500fb8812958540cf17862bd73e7af64936";
+      sha256 = "1wfkwr89sn2w97i94d0dqylcg9mr6pirjadi0a4l492nfnsh99bc";
+    };
+
+    vimAddonBackgroundCmd = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-background-cmd";
+      rev = "14df72660a95804a57c02b9ff0ae3198608e2491";
+      sha256 = "09lh6hqbx05gm7njhpqvhqdwig3pianq9rddxmjsr6b1vylgdgg4";
+    };
+
+    vimAddonErrorFormats = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-errorformats";
+      rev = "dcbb203ad5f56e47e75fdee35bc92e2ba69e1d28";
+      sha256 = "159zqm69fxbxcv3b2y99g57bf20qrzsijcvb5rzy2njxah3049m1";
+    };
+
+    vimAddonToggleBuffer = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-toggle-buffer";
+      rev = "a1b38b9c5709cba666ed2d84ef06548f675c6b0b";
+      sha256 = "1xq38kfdm36c34ln66znw841q797w5gm8bpq1x64bsf2h6n3ml03";
+    };
+
+    vimAddonGotoThingAtCursor = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-goto-thing-at-cursor";
+      rev = "f052e094bdb351829bf72ae3435af9042e09a6e4";
+      sha256 = "1ksm2b0j80zn8sz2y227bpcx4jsv76lwgr2gpgy2drlyqhn2vlv0";
+    };
+
+    vimAddonViews = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-views";
+      rev = "d1383ad56d0a07d7350880adbadf9de501729fa8";
+      sha256 = "09gqh7w5rk4lmra706schqaj8dnisf396lpsipm7xv6gy1qbslnv";
+    };
+
+    vimAddonSwfMill = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-swfmill";
+      rev = "726777e02cbe3ad8f82e37421fb37674f446a148";
+      sha256 = "0ablzl5clgfzhzwvzzbaj0cda0b4cyrj3pbv02f26hx7rfnssaqm";
+    };
+
+    vimHaxeSyntax = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-haxe-syntax";
+      rev = "500acc2f2ab92d77ff6cd04fdc7868800c033dfa";
+      sha256 = "1ipm0igplplfmscm3bk95qpf9rw71h133l9shmw54mxr4h0ymnmj";
+    };
+
+    tlib = pkgs.fetchFromGitHub {
+      owner = "tomtom";
+      repo = "tlib_vim";
+      rev = "bc4097bd38c4bc040fe1e74df68dec6c9adfcb6a";
+      sha256 = "19v7bgmkk4k2g1z62bd0kky29xxfq96l7wfrl27wb2zijlhbrnpz";
+    };
+
+    vamStub = pkgs.writeTextFile {
+      name = "vam-stub";
+      destination = "/autoload/vam.vim";
+      text = ''
+        fun! vam#DefineAndBind(local, global, default)
+          return ' if !exists('.string(a:global).') |
+                 \ let '.a:global.' = '.a:default.' |
+                 \ endif | let '.a:local.' = '.a:global
+        endfun
+      '';
+    };
+  };
+
+  plugins = mkVimPlugins (pluginDeps // {
+    vimErl = pkgs.fetchFromGitHub {
+      owner = "jimenezrick";
+      repo = "vimerl";
+      rev = "823bf8cb515bb10396c705cdc017aa9121cc4d12";
+      sha256 = "0sybkx8iy8qhr6nlwn52j7zd5v99rn0b8wbg43d112z2px4yq5x3";
+    };
+
+    vimHaxe = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-haxe";
+      rev = "8efc705db41a01713d67d437f29866a1ff831e8a";
+      sha256 = "15kv13gvpgf23p0566qrlw7gmpir2z7g5dnkfs1knmcwzw45am5d";
+    };
+
+    factor = extractSubdir "misc/vim" (pkgs.fetchFromGitHub {
+      owner = "slavapestov";
+      repo = "factor";
+      rev = "0d6f70cc7cf35cc627ee78886e2932091a651fe6";
+      sha256 = "0lmqzvrmwgmxpcpwgn59y033sf4jybmw3lffbjwww5d7ch90333q";
+    });
+
+    opaLang = extractSubdir "tools/editors/vim" (pkgs.fetchFromGitHub {
+      owner = "MLstate";
+      repo = "opalang";
+      rev = "94e4e6d9d8da9a72214f4f28dd1ffa1a987997eb";
+      sha256 = "0d6b67868cfqakkz63y5ynpz549lbpfzc3c3x7kx3ffsv10xy3bb";
+    });
+
+    lslvim = pkgs.fetchFromGitHub {
+      owner = "sukima";
+      repo = "LSLvim";
+      rev = "f269de39a1c713a43470e90d0ec78208c0f05e0b";
+      sha256 = "1plwx5id3jsj4y6yhshlf3rishxhf1b9k47g2cpzaczvqb5bl40w";
+    };
+
+    vimSyntaxShakespeare = pkgs.fetchFromGitHub {
+      owner = "pbrisbin";
+      repo = "vim-syntax-shakespeare";
+      rev = "29085ae94ee3dbd7f39f2a7705d86692ef5bc365";
+      sha256 = "0kvys81jiwqzwmpbk1lvbciw28yha4shd1xby5saiy4b68l6d8rk";
+    };
+
+    glsl = fetchVimScript {
+      name = "glsl";
+      srcId = 3194;
+      sha256 = "1vqfcpjmfyjc95wns3i84kgd1k5r2lwjjvjcprygi9g9vng7i5xc";
+      type = "syntax";
+    };
+
+    actionScript = fetchVimScript {
+      name = "actionscript";
+      srcId = 1205;
+      sha256 = "0pdzqg678lhn7lmqf3z9icpj6ff2nnghsxy983kxkn8sblnzlhfs";
+      type = "syntax";
+    };
+
+    indentPython = fetchVimScript {
+      name = "python";
+      srcId = 4316;
+      sha256 = "1pgdiaqd1hm0qpspy1asj7i103pq0846lnjrxvl6pk17ymww9pmk";
+      type = "indent";
+    };
+
+    nixAddon = pkgs.stdenv.mkDerivation {
+      name = "vim-nix-support";
+
+      lnl7 = pkgs.fetchFromGitHub {
+        owner = "LnL7";
+        repo = "vim-nix";
+        rev = "9ac8876e5beb824018b9a09d4640f7efc2fbc8ae";
+        sha256 = "0whdf56c63vp4c3b2jfl1x5c0dxxrzwvxkfm5951qzpfy6xwg27x";
+      };
+
+      src = pkgs.fetchFromGitHub {
+        owner = "MarcWeber";
+        repo = "vim-addon-nix";
+        rev = "2aed79ba5d8c5e6abd102de77e55e242f61b17f1";
+        sha256 = "0zx1q9994py6jmm0qbbx6fc1dy5la8zfskkbvqqxssxrl5dx7vvi";
+      };
+
+      phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+      patchPhase = ''
+        for what in indent syntax; do
+          install -vD -m 0644 "$lnl7/$what/nix.vim" "$what/nix.vim"
+        done
+        sed -i -re '/^ *au(group)? /,/^ *au(group)? +end/ {
+          s/^ *au(tocmd)? +((BufRead|BufNewFile),?)+ +[^ ]+ +setl(ocal)?/${
+            "& sw=2 sts=2 et iskeyword+=-"
+          }/
+        }' plugin/vim-addon-nix.vim
+        grep '^setlocal' "$lnl7/ftplugin/nix.vim" >> ftplugin/nix.vim
+      '';
+
+      installPhase = ''
+        cp -Rd . "$out"
+      '';
+    };
+
+    urwebAddon = pkgs.fetchFromGitHub {
+      owner = "MarcWeber";
+      repo = "vim-addon-urweb";
+      rev = "49ea3960a9924a5dd7ff70956d1a7c0479a55773";
+      sha256 = "090ww8nxqsabrwf4r8g7a93kawnp6zwpsx65yxpacwwwlbc73m7s";
+    };
+
+    indentHaskell = fetchVimScript {
+      name = "haskell";
+      srcId = 7407;
+      sha256 = "1lj44jkyihmcnj2kcfckhqzr9gfipda9frbzicix2wrc5728kjsv";
+      type = "indent";
+    };
+
+    fishSyntax = fetchVimScript {
+      name = "fish";
+      srcId = 20242;
+      sha256 = "12gfmyxxf84f19bp8xfmkb9phbfkifn89sjgi8hnv6dn0a5y1zpj";
+      type = "syntax";
+    };
+
+    elmVim = pkgs.fetchFromGitHub {
+      owner = "lambdatoast";
+      repo = "elm.vim";
+      rev = "ad556c97e26072b065825852ceead0fe6a1f7d7c";
+      sha256 = "19k6b6m5ngm5qn2f3p13hzjyvha53fpdgq691z8n0lwfn8831b21";
+    };
+
+    flake8 = pkgs.fetchFromGitHub {
+      owner = "nvie";
+      repo = "vim-flake8";
+      rev = "293613dbe731a2875ce93739e7b64ee504d8bbab";
+      sha256 = "0xmqmbh66g44vhx9769mzs820k6ksbpfnsfvivmbhzlps2hjqpqg";
+    };
+
+    vader = pkgs.fetchFromGitHub {
+      owner = "junegunn";
+      repo = "vader.vim";
+      rev = "ad2c752435baba9e7544d0046f0277c3573439bd";
+      sha256 = "0yvnah4lxk5w5qidc3y5nvl6lpi8rcv26907b3w7vjskqc935b8f";
+    };
+  });
+
+  generic = ''
+    syntax on
+    colorscheme elflord
+
+    " boolean
+    set nocompatible
+    set showcmd
+    set showmatch
+    set ignorecase
+    set smartcase
+    set incsearch
+    set modeline
+    set smarttab
+    set expandtab
+    set smartindent
+    set ruler
+
+    " non-boolean
+    set tabstop=4
+    set softtabstop=4
+    set shiftwidth=4
+    set textwidth=80
+    set termencoding=ascii
+    set backspace=indent,eol,start
+    set background=dark
+    set mouse=
+  '';
+
+  plugin = ''
+    " erlang
+    let erlang_folding = 0
+    let erlang_highlight_bif = 1
+    let erlang_force_use_vimerl_indent = 1
+
+    " python
+    let python_highlight_numbers = 1
+    let python_highlight_builtins = 1
+    let python_highlight_exceptions = 1
+    let g:flake8_cmd = '${pkgs.pythonPackages.flake8}/bin/flake8'
+
+    " all plugins
+    set runtimepath^=${plugins}
+    set runtimepath+=${plugins}/after
+    runtime! ftdetect/*.vim
+  '';
+
+  autocmd = ''
+    " jump to last position
+    au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") |
+                   \ exe "normal! g'\"zz" | endif
+
+    " filetype defaults
+    filetype plugin indent on
+    au BufNewFile,BufRead *.as set ft=actionscript
+    au BufNewFile,BufRead *.tt set ft=tt2html ts=2 sw=2 sts=2 et
+    au BufNewFile,BufRead *.html set ts=2 sw=2 sts=2 et
+    au FileType python set textwidth=79
+    au FileType gitcommit set textwidth=72
+    au FileType docbk set tabstop=2 shiftwidth=2 expandtab
+
+    " highlight unnecessary whitespace
+    highlight ExtraWhitespace ctermbg=red guibg=red
+    match ExtraWhitespace /\s\+$/
+    au BufWinEnter,InsertLeave * match ExtraWhitespace /\s\+$/
+    au InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
+    " prevent colorscheme from overriding these highlights
+    au ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
+
+    " highlight everything exceeding 80 characters
+    au BufWinEnter * let w:m2=matchadd('ErrorMsg', '\%>80v.\+', -1)
+  '';
+
+  misc = ''
+    " ASCII art mode
+    fun! AAMode()
+      highlight clear ExtraWhitespace
+      for m in getmatches()
+        if m.group == 'ErrorMsg' && m.pattern == '\%>80v.\+'
+          call matchdelete(m.id)
+        endif
+      endfor
+    endfun
+
+    command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
+      \ | wincmd p | diffthis
+
+    " flake everything that has been *detected* as python (not just by suffix).
+    autocmd BufWritePost * if &ft ==# 'python' | call Flake8() | endif
+  '';
+
+  vimrc = pkgs.writeText "vimrc" ''
+    ${generic}
+    ${plugin}
+
+    if has("autocmd")
+      ${autocmd}
+    endif
+
+    ${misc}
+  '';
+
+  patchedVim = lib.overrideDerivation pkgs.vim_configurable (o: {
+    postInstall = (o.postInstall or "") + ''
+      ln -sf "${vimrc}" "$out/share/vim/vimrc"
+    '';
+  });
+
+in {
+  options.vuizvui.user.aszlig.programs.vim = {
+    enable = lib.mkEnableOption "aszlig's Vim";
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = lib.singleton patchedVim;
+  };
+}
diff --git a/modules/user/aszlig/programs/xpdf/default.nix b/modules/user/aszlig/programs/xpdf/default.nix
new file mode 100644
index 00000000..e7edd806
--- /dev/null
+++ b/modules/user/aszlig/programs/xpdf/default.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.xpdf;
+
+  xpdf = pkgs.xpdf.overrideDerivation (drv: {
+    postInstall = (drv.postInstall or "") + ''
+      echo 'bind ctrl-o any toggleOutline' >> "$out/etc/xpdfrc"
+    '';
+  });
+
+in {
+  options.vuizvui.user.aszlig.programs.xpdf = {
+    enable = lib.mkEnableOption "aszlig's xpdf";
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = lib.singleton xpdf;
+  };
+}
diff --git a/modules/user/aszlig/programs/zsh/default.nix b/modules/user/aszlig/programs/zsh/default.nix
new file mode 100644
index 00000000..71b1ca09
--- /dev/null
+++ b/modules/user/aszlig/programs/zsh/default.nix
@@ -0,0 +1,129 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.programs.zsh;
+  inherit (cfg) machineColor;
+
+in {
+  options.vuizvui.user.aszlig.programs.zsh = {
+    enable = mkEnableOption "zsh";
+
+    machineColor = mkOption {
+      type = types.enum [
+        "black" "red" "green" "yellow" "blue" "magenta" "cyan" "white"
+      ];
+      default = "red";
+      example = "green";
+      description = ''
+        The color used for coloring the machine name in the prompt.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.shellInit = ''
+      export EDITOR="vim"
+      export EMAIL="aszlig@redmoonstudios.org"
+    '';
+
+    nixpkgs.config.packageOverrides = pkgs: {
+      zsh = overrideDerivation pkgs.zsh (o: {
+        postConfigure = (o.postConfigure or "") + ''
+          sed -i -e '/^name=zsh\/newuser/d' config.modules
+        '';
+      });
+    };
+
+    programs.zsh.enable = true;
+
+    programs.zsh.shellAliases.t = "task";
+
+    programs.zsh.interactiveShellInit = mkAfter ''
+      export HISTFILE=~/.histfile
+      export HISTSIZE=100000
+      export SAVEHIST=100000
+
+      unsetopt SHARE_HISTORY
+
+      setopt extendedglob
+      setopt extendedhistory
+      setopt globcomplete
+      setopt histnostore
+      setopt histreduceblanks
+      setopt correct
+      setopt dvorak
+      setopt interactivecomments
+      setopt autopushd
+      setopt autocd
+      setopt beep
+
+      bindkey -v
+      if [[ "$TERM" = xterm ]]; then
+        bindkey -v '\e[H' vi-beginning-of-line
+        bindkey -v '\e[F' vi-end-of-line
+
+        function set-title() {
+          echo -en "\e]2;$2\a"
+        }
+
+        function reset-title() {
+          echo -en "\e]2;''${(%):-%~}\a\a"
+        }
+
+        autoload -Uz add-zsh-hook
+        add-zsh-hook preexec set-title
+        add-zsh-hook precmd reset-title
+      else
+        bindkey -v '\e[1~' vi-beginning-of-line
+        bindkey -v '\e[4~' vi-end-of-line
+      fi
+
+      bindkey -a '/' history-incremental-pattern-search-backward
+      bindkey -a '?' history-incremental-pattern-search-forward
+      bindkey '\e[A' up-line-or-history
+      bindkey '\e[B' down-line-or-history
+
+      zstyle ':completion:*' completer _expand _complete _ignored _approximate
+      zstyle ':completion:*' expand prefix suffix
+      zstyle ':completion:*' group-name '''
+      zstyle ':completion:*' insert-unambiguous true
+      zstyle ':completion:*' list-colors '''
+      zstyle ':completion:*' list-prompt \
+        %SAt %p: Hit TAB for more, or the character to insert%s
+      zstyle ':completion:*' list-suffixes true
+      zstyle ':completion:*' matcher-list ''' \
+        'm:{[:lower:]}={[:upper:]}' \
+        'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' \
+        'l:|=* r:|=*' \
+        'r:|[._-]=** r:|=**'
+      zstyle ':completion:*' max-errors 2 numeric
+      zstyle ':completion:*' menu select=long
+      zstyle ':completion:*' original true
+      zstyle ':completion:*' preserve-prefix '//[^/]##/'
+      zstyle ':completion:*' prompt \
+        'Hm, did you mistype something? There are %e errors in the completion.'
+      zstyle ':completion:*' select-prompt \
+        %SScrolling active: current selection at %p%s
+      zstyle ':completion:*' use-compctl false
+      zstyle ':completion:*' verbose true
+
+      autoload -Uz compinit
+      compinit
+
+      autoload -Uz zmv
+    '';
+
+    programs.zsh.promptInit = ''
+      autoload -Uz prompt_special_chars
+
+      () {
+          local p_machine='%(!..%B%F{red}%n%b%F{blue}@)%b%F{${machineColor}}%m'
+          local p_path='%B%F{blue}[%F{cyan}%~%B%F{blue}]'
+          local p_exitcode='%F{green}%?%(!.%F{cyan}>.%b%F{green}>)%b%f '
+          PROMPT="$p_machine$p_path$p_exitcode"
+      }
+    '';
+  };
+}
diff --git a/modules/user/aszlig/services/i3/conky.nix b/modules/user/aszlig/services/i3/conky.nix
new file mode 100644
index 00000000..6c5815aa
--- /dev/null
+++ b/modules/user/aszlig/services/i3/conky.nix
@@ -0,0 +1,122 @@
+{ pkgs ? import (import ../../../../../nixpkgs-path.nix) {}
+, lib ? import "${import ../../../../../nixpkgs-path.nix}/lib"
+, timeout ? 300
+}:
+
+with lib;
+
+let
+  baseConfig = pkgs.writeText "conkyrc" ''
+    conky.config = {
+      cpu_avg_samples = 2,
+      net_avg_samples = 2,
+      no_buffers = true,
+      out_to_console = true,
+      out_to_ncurses = false,
+      out_to_stderr = false,
+      out_to_x = false,
+      extra_newline = false,
+      update_interval = 1.0,
+      uppercase = false,
+      use_spacer = 'none',
+      pad_percents = 3,
+      use_spacer = 'left',
+    };
+
+    conky.text = ''';
+  '';
+
+  optexpr = name: expr: "\${${name}_disabled:-\\\${${name} ${expr}\\}}";
+  cexpr = name: args: "${optexpr name (concatStringsSep " " args)}";
+
+  mkNetInfo = iface: let
+    upspeed = cexpr "upspeed" [ iface ];
+    downspeed = cexpr "downspeed" [ iface ];
+  in "${upspeed} ${downspeed}";
+
+  mkDiskFree = path: let
+    used = cexpr "fs_used" [ path ];
+    size = cexpr "fs_size" [ path ];
+  in "${used}/${size}";
+
+  gpuTemp = "${cexpr "hwmon" [ "0" "temp" "1" ]}C";
+
+  weather = (cexpr "weather" [
+    "http://tgftp.nws.noaa.gov/data/observations/metar/stations/"
+    "EDMA"
+    "temperature"
+  ]) + "C";
+
+  mkConky = args: let
+    time = cexpr "time" [ "%a %b %d %T %Z %Y" ];
+    text = concatStringsSep " | " (args ++ singleton time);
+    conky = pkgs.conky.override {
+      weatherMetarSupport = true;
+    };
+  in pkgs.writeScript "conky-run.sh" ''
+    #!${pkgs.stdenv.shell}
+    PATH="${pkgs.coreutils}/bin"
+
+    cpuload() {
+      for i in $(seq 1 $(nproc))
+      do
+        [ $i -eq 1 ] || echo -n ' '
+        echo -n "\''${cpu cpu$i}%"
+      done
+    }
+
+    cputemp_collect() {
+      for i in /sys/bus/platform/devices/coretemp.?/hwmon/hwmon?/temp?_input
+      do
+        [ -e "$i" ] || continue
+        echo "$i" | ${pkgs.gnused}/bin/sed -re \
+          's/^.*hwmon([0-9]+)[^0-9]*([0-9]+).*$/''${hwmon \1 temp \2}/'
+      done
+    }
+
+    cputemp() {
+      echo $(cputemp_collect)
+    }
+
+    tries=0
+    while ! raw_netinfo="$(${
+      "${pkgs.iproute}/sbin/ip route get 8.8.8.8 2> /dev/null"
+    })"; do
+      if [ $tries -ge ${toString timeout} ]; then
+        upspeed_disabled=N/A
+        downspeed_disabled=N/A
+        break
+      fi
+      echo "Waiting for primary network interface to become available..."
+      tries=$(($tries + 1))
+      sleep 1
+    done
+
+    primary_netdev="$(echo "$raw_netinfo" | \
+      ${pkgs.gnused}/bin/sed -nre 's/^.*dev *([^ ]+).*$/\1/p')"
+
+    # FIXME: Log stderr to the journal!
+    ${conky}/bin/conky -c "${baseConfig}" -t "${text}" 2> /dev/null
+  '';
+
+in {
+  left = mkConky [
+    "CPU: $(cpuload) - ${cexpr "cpu" [ "cpu0" ]}%"
+    "MEM: \\$mem/\\$memmax - \\$memperc%"
+    "SWAP: \\$swap/\\$swapmax \\$swapperc%"
+  ];
+
+  right = mkConky [
+    "NET: ${mkNetInfo "$primary_netdev"}"
+    "DF: ${mkDiskFree "/"}"
+    "LAVG: \\$loadavg"
+    "TEMP - CPU: $(cputemp) - GPU: ${gpuTemp} - OUTSIDE: ${weather}"
+  ];
+
+  single = mkConky [
+    "CPU: $(cpuload) - ${cexpr "cpu" [ "cpu0" ]}%"
+    "MEM: \\$mem/\\$memmax - \\$memperc%"
+    "NET: ${mkNetInfo "$primary_netdev"}"
+    "TEMP - CPU: $(cputemp) - OUTSIDE: ${weather}"
+  ];
+}
diff --git a/modules/user/aszlig/services/i3/default.nix b/modules/user/aszlig/services/i3/default.nix
new file mode 100644
index 00000000..34767e38
--- /dev/null
+++ b/modules/user/aszlig/services/i3/default.nix
@@ -0,0 +1,134 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.services.i3;
+  inherit (config.services.xserver) xrandrHeads;
+
+  # The symbols if you press shift and a number key.
+  wsNumberSymbols = [
+    "exclam" "at" "numbersign" "dollar" "percent"
+    "asciicircum" "ampersand" "asterisk" "parenleft" "parenright"
+  ];
+
+  wsCount = length wsNumberSymbols;
+
+  headCount = length xrandrHeads;
+  wsPerHead = wsCount / headCount;
+  excessWs = wsCount - (headCount * wsPerHead);
+  headModifier = if cfg.reverseHeads then reverseList else id;
+  getHeadAt = x: (elemAt (headModifier xrandrHeads) x).output;
+
+  mkSwitchTo = number: "$mod+${if number == 10 then "0" else toString number}";
+
+  mkDefaultWorkspace = number: numberSymbol: {
+    name = toString number;
+    value = {
+      label = mkDefault null;
+      labelPrefix = mkDefault "${toString number}: ";
+      keys.switchTo = mkDefault (mkSwitchTo number);
+      keys.moveTo = mkDefault "$mod+Shift+${numberSymbol}";
+      head = if headCount == 0 then mkDefault null
+             else mkDefault (getHeadAt ((number - (excessWs + 1)) / wsPerHead));
+    };
+  };
+
+  wsCfgList = mapAttrsToList (_: getAttr "config") cfg.workspaces;
+  wsConfig = concatStrings wsCfgList;
+  defaultWorkspaces = listToAttrs (imap mkDefaultWorkspace wsNumberSymbols);
+
+  conky = import ./conky.nix {
+    inherit pkgs lib;
+    timeout = cfg.networkTimeout;
+  };
+
+  mkBar = output: statusCmd: singleton ''
+    bar {
+      ${optionalString (output != null) "output ${output}"}
+      ${optionalString (statusCmd != null) "status_command ${statusCmd}"}
+      colors {
+        focused_workspace  #5c5cff #e5e5e5
+        active_workspace   #ffffff #0000ee
+        inactive_workspace #00cdcd #0000ee
+        urgent_workspace   #ffff00 #cd0000
+      }
+    }
+  '';
+
+  barConfig = let
+    barHeads = map (h: h.output) (headModifier xrandrHeads);
+    bars = if headCount == 0 then mkBar null conky.single
+      else if headCount == 1 then mkBar (head barHeads) conky.single
+      else let inner = take (length barHeads - 2) (tail barHeads);
+           in mkBar (head barHeads) conky.left
+           ++ map (flip mkBar null) inner
+           ++ mkBar (last barHeads) conky.right;
+  in concatStrings (headModifier bars);
+
+in
+{
+  options.vuizvui.user.aszlig.services.i3 = {
+    enable = mkEnableOption "i3";
+
+    workspaces = mkOption {
+      type = types.attrsOf (types.submodule (import ./workspace.nix));
+      description = ''
+        Workspace to monitor assignment.
+
+        Workspaces are by default assigned starting from the leftmost monitor
+        being workspace 1 and the rightmost monitor being workspace 10. The
+        workspaces are divided by the number of available heads, so if you have
+        a dual head system, you'll end up having workspace 1 to 5 on the left
+        monitor and 6 to 10 on the right.
+      '';
+    };
+
+    reverseHeads = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Reverse the order of the heads, so if enabled and you have two heads,
+        you'll end up having workspaces 1 to 5 on the right head and 6 to 10 on
+        the left head.
+      '';
+    };
+
+    networkTimeout = mkOption {
+      type = types.int;
+      default = 300;
+      description = ''
+        Maximum number of seconds to wait for network device detection.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    vuizvui.user.aszlig.services.i3.workspaces = defaultWorkspaces;
+
+    services.xserver.windowManager = {
+      default = "i3";
+
+      i3.enable = true;
+      i3.configFile = pkgs.substituteAll {
+        name = "i3.conf";
+        src = ./i3.conf;
+
+        inherit (pkgs) dmenu xterm;
+        inherit (pkgs.vuizvui.aszlig) pvolctrl;
+        inherit (pkgs.xorg) xsetroot;
+        inherit wsConfig barConfig;
+
+        lockall = pkgs.writeScript "lockvt.sh" ''
+          #!${pkgs.stdenv.shell}
+          "${pkgs.socat}/bin/socat" - UNIX-CONNECT:/run/console-lock.sock \
+            < /dev/null
+        '';
+
+        postInstall = ''
+          ${pkgs.i3}/bin/i3 -c "$target" -C
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/user/aszlig/services/i3/i3.conf b/modules/user/aszlig/services/i3/i3.conf
new file mode 100644
index 00000000..cd14c425
--- /dev/null
+++ b/modules/user/aszlig/services/i3/i3.conf
@@ -0,0 +1,131 @@
+# default modifier key
+set $mod Mod4
+
+# we want to have a VT-style font :-)
+font -dosemu-vga-medium-r-normal--17-160-75-75-c-80-ibm-cp866
+
+# Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $mod
+
+# reasonable defaults!
+default_orientation horizontal
+workspace_layout tabbed
+popup_during_fullscreen ignore
+
+# start a terminal
+bindsym $mod+Shift+Return exec --no-startup-id @xterm@/bin/xterm
+
+# kill focused window
+bindsym $mod+Shift+C kill
+
+# start dmenu (a program launcher)
+bindsym $mod+p exec --no-startup-id @dmenu@/bin/dmenu_run
+
+# start lock screen
+bindsym $mod+Shift+Escape exec --no-startup-id @lockall@
+
+# set background
+exec @xsetroot@/bin/xsetroot -solid black
+
+# audio controls
+bindsym XF86AudioLowerVolume exec @pvolctrl@/bin/pvolctrl -10
+bindsym XF86AudioRaiseVolume exec @pvolctrl@/bin/pvolctrl 10
+bindsym XF86AudioMute exec @pvolctrl@/bin/pvolctrl 0
+
+# change/move focus
+bindsym $mod+Shift+Left move left
+bindsym $mod+Shift+H move left
+bindsym $mod+Shift+Down move down
+bindsym $mod+Shift+T move down
+bindsym $mod+Shift+Up move up
+bindsym $mod+Shift+N move up
+bindsym $mod+Shift+Right move right
+bindsym $mod+Shift+S move right
+
+bindsym $mod+Left focus left
+bindsym $mod+h focus left
+bindsym $mod+Down focus down
+bindsym $mod+t focus down
+bindsym $mod+Up focus up
+bindsym $mod+n focus up
+bindsym $mod+Right focus right
+bindsym $mod+s focus right
+
+# split in horizontal orientation
+bindsym $mod+i split h
+
+# split in vertical orientation
+bindsym $mod+d split v
+
+# enter fullscreen mode for the focused container
+bindsym $mod+f fullscreen
+
+# change container layout (stacked, tabbed, default)
+bindsym $mod+apostrophe layout stacking
+bindsym $mod+comma layout tabbed
+bindsym $mod+period layout default
+
+# toggle tiling / floating
+bindsym $mod+Shift+space floating toggle
+
+# change focus between tiling / floating windows
+bindsym $mod+space focus mode_toggle
+
+# focus the parent container
+bindsym $mod+a focus parent
+
+# focus the child container
+bindsym $mod+semicolon focus child
+
+# reload the configuration file
+bindsym $mod+Shift+L reload
+# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
+bindsym $mod+Shift+R restart
+# exit i3 (logs you out of your X session)
+bindsym $mod+Shift+Q exit
+
+# resize window (you can also use the mouse for that)
+mode "resize" {
+    # These bindings trigger as soon as you enter the resize mode
+
+    # They resize the border in the direction you pressed, e.g.
+    # when pressing left, the window is resized so that it has
+    # more space on its left
+
+    bindsym Left resize shrink left 10 px or 10 ppt
+    bindsym h resize shrink left 10 px or 10 ppt
+    bindsym Down resize shrink down 10 px or 10 ppt
+    bindsym t resize shrink down 10 px or 10 ppt
+    bindsym Up resize shrink up 10 px or 10 ppt
+    bindsym n resize shrink up 10 px or 10 ppt
+    bindsym Right resize shrink right 10 px or 10 ppt
+    bindsym s resize shrink right 10 px or 10 ppt
+
+    bindsym Shift+Left resize grow left 10 px or 10 ppt
+    bindsym Shift+H resize grow left 10 px or 10 ppt
+    bindsym Shift+Down resize grow down 10 px or 10 ppt
+    bindsym Shift+T resize grow down 10 px or 10 ppt
+    bindsym Shift+Up resize grow up 10 px or 10 ppt
+    bindsym Shift+N resize grow up 10 px or 10 ppt
+    bindsym Shift+Right resize grow right 10 px or 10 ppt
+    bindsym Shift+S resize grow right 10 px or 10 ppt
+
+    # back to normal: Enter or Escape
+    bindsym Return mode "default"
+    bindsym Escape mode "default"
+}
+
+bindsym $mod+r mode "resize"
+
+# workspace configuration
+@wsConfig@
+
+# ratmenu should be as unintrusive as possible
+for_window [class="^ratmenu$"] floating enable
+for_window [class="^ratmenu$"] border none
+
+# various app cruft
+for_window [class="^Dia$"] floating enable
+
+# bar configuration
+@barConfig@
diff --git a/modules/user/aszlig/services/i3/workspace.nix b/modules/user/aszlig/services/i3/workspace.nix
new file mode 100644
index 00000000..403ba57d
--- /dev/null
+++ b/modules/user/aszlig/services/i3/workspace.nix
@@ -0,0 +1,107 @@
+{ name, lib, config, ... }:
+
+with lib;
+
+let
+  finalLabel =
+    if config.label == null then name
+    else config.labelPrefix + config.label;
+
+  mkDoc = anchor: "http://i3wm.org/docs/userguide.html#${anchor}";
+in
+{
+  options = {
+    labelPrefix = mkOption {
+      type = types.str;
+      default = "";
+      example = "666: ";
+      description = ''
+        The value that will be put in front of the <option>label</option>.
+        So if you have a label called <replaceable>bar</replaceable> and a
+        <option>labelPrefix</option> called <replaceable>foo</replaceable> the
+        label for the workspace will be <replaceable>foobar</replaceable>.
+      '';
+    };
+
+    label = mkOption {
+      type = types.nullOr types.str;
+      default = name;
+      description = ''
+        The label of this workspace, which is its name by default. If the value
+        is <replaceable>null</replaceable>, the resulting label of the workspace
+        is just its name and no <option>labelPrefix</option> is applied.
+      '';
+    };
+
+    assign = mkOption {
+      type = types.listOf types.attrs;
+      default = [];
+      example = [
+        { class = "^Chromium(?:-browser)?\$"; }
+        { instance = "^gajim\$"; }
+      ];
+      description = let
+        anchor = "_automatically_putting_clients_on_specific_workspaces";
+      in ''
+        Assign windows to this specific workspace using the attribute names
+        described by <link xlink:href="${mkDoc anchor}"/>.
+      '';
+    };
+
+    head = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The XRandR head this workspace will be assigned to.
+      '';
+    };
+
+    keys = let
+      commonDesc = ''
+        The <replaceable>$mod</replaceable> placeholder represents the default
+        modifier key. Details about the syntax of key combinations can be found
+        at <link xlink:href="${mkDoc "keybindings"}"/>.
+      '';
+    in {
+      switchTo = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "$mod+1";
+        description = ''
+          Key combination to switch to this workspace.
+        '' + commonDesc;
+      };
+
+      moveTo = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "$mod+Shift+exclam";
+        description = ''
+          Key combination to move a container to this workspace.
+        '' + commonDesc;
+      };
+    };
+
+    config = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Raw configuration options for this workspace.
+      '';
+    };
+  };
+
+  config.config = let
+    mkAssign = mapAttrsToList (criteria: value: "${criteria}=\"${value}\"");
+    mkSym = sym: rest: optionalString (sym != null) "bindsym ${sym} ${rest}";
+  in ''
+    ${optionalString (config.head != null) ''
+    workspace "${finalLabel}" output ${config.head}
+    ''}
+    ${mkSym config.keys.switchTo "workspace \"${finalLabel}\""}
+    ${mkSym config.keys.moveTo "move workspace \"${finalLabel}\""}
+    ${concatMapStrings (assign: ''
+    assign [${concatStringsSep " " (mkAssign assign)}] ${finalLabel}
+    '') config.assign}
+  '';
+}
diff --git a/modules/user/aszlig/services/slim/default.nix b/modules/user/aszlig/services/slim/default.nix
new file mode 100644
index 00000000..6e61912a
--- /dev/null
+++ b/modules/user/aszlig/services/slim/default.nix
@@ -0,0 +1,45 @@
+{ pkgs, config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.aszlig.services.slim;
+  randrHeads = config.services.xserver.xrandrHeads;
+in {
+  options.vuizvui.user.aszlig.services.slim = {
+    enable = mkEnableOption "Vuizvui SLiM";
+  };
+
+  config.services.xserver.displayManager.slim = mkIf cfg.enable {
+    enable = true;
+    theme = pkgs.stdenv.mkDerivation {
+      name = "nixos-theme-vuizvui";
+      src = pkgs.slimThemes.nixosSlim;
+      phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+      patchPhase = let
+        headFactor = if randrHeads == [] then 1 else lib.length randrHeads;
+        centerLeft = 100 / (headFactor * 2);
+      in ''
+        "${pkgs.imagemagick.out}/bin/mogrify" \
+          -fill '#080010' -draw 'color 0,0 reset' \
+          share/slim/themes/nixos-slim-testing/background.png
+        "${pkgs.imagemagick.out}/bin/mogrify" \
+          -negate -region 100x110+0+0 -negate -fill white -colorize 20% \
+          share/slim/themes/nixos-slim-testing/panel.png
+        sed -i \
+          -e 's/^\([a-z_]\+_x[^0-9]*\)[0-9]\+%/\1${toString centerLeft}%/' \
+          share/slim/themes/nixos-slim-testing/slim.theme
+        cat >> share/slim/themes/nixos-slim-testing/slim.theme <<EOF
+        session_x      ${toString centerLeft}%
+        msg_color      #ffffff
+        username_color #ffffff
+        password_color #ffffff
+        input_color    #ffffff
+        EOF
+      '';
+      installPhase = ''
+        cp -R share/slim/themes/nixos-slim-testing "$out"
+      '';
+    };
+  };
+}
diff --git a/modules/user/aszlig/services/vlock/default.nix b/modules/user/aszlig/services/vlock/default.nix
new file mode 100644
index 00000000..2eced029
--- /dev/null
+++ b/modules/user/aszlig/services/vlock/default.nix
@@ -0,0 +1,57 @@
+{ pkgs, config, lib, ... }:
+
+let
+  cfg = config.vuizvui.user.aszlig.services.vlock;
+
+  messageFile = pkgs.runCommand "message.cat" {} ''
+    echo -en '\e[H\e[2J\e[?25l' > "$out"
+    "${pkgs.vuizvui.aszlig.aacolorize}/bin/aacolorize" \
+      "${./message.cat}" "${./message.colmap}" \
+      >> "$out"
+  '';
+
+  esc = "\\\\033";
+  unlockCSI = "${esc}[16;39H${esc}[?25h${esc}[K";
+
+  vlock = lib.overrideDerivation pkgs.vlock (o: {
+    postPatch = (o.postPatch or "") + ''
+      echo -n '"' > src/message.h
+      sed -e ':nl;N;$!bnl;s/[\\"]/\\&/g;s/\n/\\n/g' "${messageFile}" \
+        >> src/message.h
+      sed -i -e '$s/$/"/' src/message.h
+      sed -i -e 's!getenv("VLOCK_MESSAGE")!\n#include "message.h"\n!' \
+        src/vlock-main.c
+      sed -i -re 's/(fprintf[^"]*")(.*user)/\1${unlockCSI}\2/' \
+        src/auth-pam.c
+    '';
+  });
+in {
+  options.vuizvui.user.aszlig.services.vlock = {
+    enable = lib.mkEnableOption "console lock";
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.sockets.vlock = {
+      description = "Console Lock Socket";
+      wantedBy = [ "sockets.target" ];
+      socketConfig.ListenStream = "/run/console-lock.sock";
+      socketConfig.Accept = true;
+    };
+
+    systemd.services."vlock@" = {
+      description = "Lock All Consoles";
+      serviceConfig.Type = "oneshot";
+
+      #environment.USER = "%i"; XXX
+      environment.USER = "aszlig";
+
+      script = ''
+        retval=0
+        oldvt="$("${pkgs.kbd}/bin/fgconsole")"
+        "${vlock}/bin/vlock" -asn || retval=$?
+        if [ $retval -ne 0 ]; then "${pkgs.kbd}/bin/chvt" "$oldvt"; fi
+        exit $retval
+      '';
+    };
+  };
+}
diff --git a/modules/user/aszlig/services/vlock/message.cat b/modules/user/aszlig/services/vlock/message.cat
new file mode 100644
index 00000000..f079e829
--- /dev/null
+++ b/modules/user/aszlig/services/vlock/message.cat
@@ -0,0 +1,18 @@
+
+                .
+                |
+          -_    |     .           .-.  .-. ..      ,.--., ,===.
+            `-_ |     |           '||\.||' `' ,  , ||  || ;___
+    -_         >:_    |    _-      ||`\||  || `\/' ||  ||     ;
+      `-_   _-'   `-_ | _-'       .'   `|  ;' /'`\ ``=='' ,==='
+         >:'         `:'
+      _-' |           |    _-   ..              ..             ..
+    -'    |           | _-'     ||              ||             ||
+         .|.         _:<        ||  ,---. .---. ||,-. .--.  .--||
+      _-' | `-_   _-'   `-_     ||  ||"|| ||''' |.,'' |"/'  |,";|
+    -'    |    `:<         `-   ||_ ||_|| ||__  |,\\. ||__  ||_,|
+          |     | `-_           `--'`---' `---' '' `' `---' `---'
+          '     |    `-
+                |                     press ENTER to unlock
+                `
+
diff --git a/modules/user/aszlig/services/vlock/message.colmap b/modules/user/aszlig/services/vlock/message.colmap
new file mode 100644
index 00000000..d7e42fb6
--- /dev/null
+++ b/modules/user/aszlig/services/vlock/message.colmap
@@ -0,0 +1,18 @@
+
+                c
+                c
+          cc    c     b           WWW  WWW WW      BccccB cBBBc
+            ccc c     b           WWWWWWWW WW W  W Bc  cB cccc
+    bb         ccc    b    bb      WWWWWW  WW WWWW Bc  cB     c
+      bbb   bbb   ccc b bbb       WW   WW  WW WWWW BcBBcB cBBBc
+         bbb         cbb
+      bbb c           b    cc   rr              rr             rr
+    bb    c           b ccc     rr              rr             rr
+         ccb         ccc        rr  rrrrr rrrrr rrrrr rrrr  rrrrr
+      ccc c bbb   ccc   ccc     rr  rrRrr rrRRR rrrrr rRrr  rrRrr
+    cc    c    bbb         cc   rrr rrrrr rrrr  rrrrr rrrr  rrrrr
+          c     b bbb           rrrrrrrrr rrrrr rr rr rrrrr rrrrr
+          c     b    bb
+                b                     ppppp PPPPP pp pppppp
+                b
+
diff --git a/modules/user/aszlig/system/bfq.patch b/modules/user/aszlig/system/bfq.patch
new file mode 100644
index 00000000..54f82499
--- /dev/null
+++ b/modules/user/aszlig/system/bfq.patch
@@ -0,0 +1,10167 @@
+diff --git a/Documentation/block/00-INDEX b/Documentation/block/00-INDEX
+index e55103ace382..8d55b4bbb5e2 100644
+--- a/Documentation/block/00-INDEX
++++ b/Documentation/block/00-INDEX
+@@ -1,5 +1,7 @@
+ 00-INDEX
+ 	- This file
++bfq-iosched.txt
++	- BFQ IO scheduler and its tunables
+ biodoc.txt
+ 	- Notes on the Generic Block Layer Rewrite in Linux 2.5
+ biovecs.txt
+diff --git a/Documentation/block/bfq-iosched.txt b/Documentation/block/bfq-iosched.txt
+new file mode 100644
+index 000000000000..13b5248eba7e
+--- /dev/null
++++ b/Documentation/block/bfq-iosched.txt
+@@ -0,0 +1,530 @@
++BFQ (Budget Fair Queueing)
++==========================
++
++BFQ is a proportional-share I/O scheduler, with some extra
++low-latency capabilities. In addition to cgroups support (blkio or io
++controllers), BFQ's main features are:
++- BFQ guarantees a high system and application responsiveness, and a
++  low latency for time-sensitive applications, such as audio or video
++  players;
++- BFQ distributes bandwidth, and not just time, among processes or
++  groups (switching back to time distribution when needed to keep
++  throughput high).
++
++On average CPUs, the current version of BFQ can handle devices
++performing at most ~30K IOPS; at most ~50 KIOPS on faster CPUs. As a
++reference, 30-50 KIOPS correspond to very high bandwidths with
++sequential I/O (e.g., 8-12 GB/s if I/O requests are 256 KB large), and
++to 120-200 MB/s with 4KB random I/O.
++
++The table of contents follow. Impatients can just jump to Section 3.
++
++CONTENTS
++
++1. When may BFQ be useful?
++ 1-1 Personal systems
++ 1-2 Server systems
++2. How does BFQ work?
++3. What are BFQ's tunable?
++4. BFQ group scheduling
++ 4-1 Service guarantees provided
++ 4-2 Interface
++
++1. When may BFQ be useful?
++==========================
++
++BFQ provides the following benefits on personal and server systems.
++
++1-1 Personal systems
++--------------------
++
++Low latency for interactive applications
++
++Regardless of the actual background workload, BFQ guarantees that, for
++interactive tasks, the storage device is virtually as responsive as if
++it was idle. For example, even if one or more of the following
++background workloads are being executed:
++- one or more large files are being read, written or copied,
++- a tree of source files is being compiled,
++- one or more virtual machines are performing I/O,
++- a software update is in progress,
++- indexing daemons are scanning filesystems and updating their
++  databases,
++starting an application or loading a file from within an application
++takes about the same time as if the storage device was idle. As a
++comparison, with CFQ, NOOP or DEADLINE, and in the same conditions,
++applications experience high latencies, or even become unresponsive
++until the background workload terminates (also on SSDs).
++
++Low latency for soft real-time applications
++
++Also soft real-time applications, such as audio and video
++players/streamers, enjoy a low latency and a low drop rate, regardless
++of the background I/O workload. As a consequence, these applications
++do not suffer from almost any glitch due to the background workload.
++
++Higher speed for code-development tasks
++
++If some additional workload happens to be executed in parallel, then
++BFQ executes the I/O-related components of typical code-development
++tasks (compilation, checkout, merge, ...) much more quickly than CFQ,
++NOOP or DEADLINE.
++
++High throughput
++
++On hard disks, BFQ achieves up to 30% higher throughput than CFQ, and
++up to 150% higher throughput than DEADLINE and NOOP, with all the
++sequential workloads considered in our tests. With random workloads,
++and with all the workloads on flash-based devices, BFQ achieves,
++instead, about the same throughput as the other schedulers.
++
++Strong fairness, bandwidth and delay guarantees
++
++BFQ distributes the device throughput, and not just the device time,
++among I/O-bound applications in proportion their weights, with any
++workload and regardless of the device parameters. From these bandwidth
++guarantees, it is possible to compute tight per-I/O-request delay
++guarantees by a simple formula. If not configured for strict service
++guarantees, BFQ switches to time-based resource sharing (only) for
++applications that would otherwise cause a throughput loss.
++
++1-2 Server systems
++------------------
++
++Most benefits for server systems follow from the same service
++properties as above. In particular, regardless of whether additional,
++possibly heavy workloads are being served, BFQ guarantees:
++
++. audio and video-streaming with zero or very low jitter and drop
++  rate;
++
++. fast retrieval of WEB pages and embedded objects;
++
++. real-time recording of data in live-dumping applications (e.g.,
++  packet logging);
++
++. responsiveness in local and remote access to a server.
++
++
++2. How does BFQ work?
++=====================
++
++BFQ is a proportional-share I/O scheduler, whose general structure,
++plus a lot of code, are borrowed from CFQ.
++
++- Each process doing I/O on a device is associated with a weight and a
++  (bfq_)queue.
++
++- BFQ grants exclusive access to the device, for a while, to one queue
++  (process) at a time, and implements this service model by
++  associating every queue with a budget, measured in number of
++  sectors.
++
++  - After a queue is granted access to the device, the budget of the
++    queue is decremented, on each request dispatch, by the size of the
++    request.
++
++  - The in-service queue is expired, i.e., its service is suspended,
++    only if one of the following events occurs: 1) the queue finishes
++    its budget, 2) the queue empties, 3) a "budget timeout" fires.
++
++    - The budget timeout prevents processes doing random I/O from
++      holding the device for too long and dramatically reducing
++      throughput.
++
++    - Actually, as in CFQ, a queue associated with a process issuing
++      sync requests may not be expired immediately when it empties. In
++      contrast, BFQ may idle the device for a short time interval,
++      giving the process the chance to go on being served if it issues
++      a new request in time. Device idling typically boosts the
++      throughput on rotational devices, if processes do synchronous
++      and sequential I/O. In addition, under BFQ, device idling is
++      also instrumental in guaranteeing the desired throughput
++      fraction to processes issuing sync requests (see the description
++      of the slice_idle tunable in this document, or [1, 2], for more
++      details).
++
++      - With respect to idling for service guarantees, if several
++	processes are competing for the device at the same time, but
++	all processes (and groups, after the following commit) have
++	the same weight, then BFQ guarantees the expected throughput
++	distribution without ever idling the device. Throughput is
++	thus as high as possible in this common scenario.
++
++  - If low-latency mode is enabled (default configuration), BFQ
++    executes some special heuristics to detect interactive and soft
++    real-time applications (e.g., video or audio players/streamers),
++    and to reduce their latency. The most important action taken to
++    achieve this goal is to give to the queues associated with these
++    applications more than their fair share of the device
++    throughput. For brevity, we call just "weight-raising" the whole
++    sets of actions taken by BFQ to privilege these queues. In
++    particular, BFQ provides a milder form of weight-raising for
++    interactive applications, and a stronger form for soft real-time
++    applications.
++
++  - BFQ automatically deactivates idling for queues born in a burst of
++    queue creations. In fact, these queues are usually associated with
++    the processes of applications and services that benefit mostly
++    from a high throughput. Examples are systemd during boot, or git
++    grep.
++
++  - As CFQ, BFQ merges queues performing interleaved I/O, i.e.,
++    performing random I/O that becomes mostly sequential if
++    merged. Differently from CFQ, BFQ achieves this goal with a more
++    reactive mechanism, called Early Queue Merge (EQM). EQM is so
++    responsive in detecting interleaved I/O (cooperating processes),
++    that it enables BFQ to achieve a high throughput, by queue
++    merging, even for queues for which CFQ needs a different
++    mechanism, preemption, to get a high throughput. As such EQM is a
++    unified mechanism to achieve a high throughput with interleaved
++    I/O.
++
++  - Queues are scheduled according to a variant of WF2Q+, named
++    B-WF2Q+, and implemented using an augmented rb-tree to preserve an
++    O(log N) overall complexity.  See [2] for more details. B-WF2Q+ is
++    also ready for hierarchical scheduling. However, for a cleaner
++    logical breakdown, the code that enables and completes
++    hierarchical support is provided in the next commit, which focuses
++    exactly on this feature.
++
++  - B-WF2Q+ guarantees a tight deviation with respect to an ideal,
++    perfectly fair, and smooth service. In particular, B-WF2Q+
++    guarantees that each queue receives a fraction of the device
++    throughput proportional to its weight, even if the throughput
++    fluctuates, and regardless of: the device parameters, the current
++    workload and the budgets assigned to the queue.
++
++  - The last, budget-independence, property (although probably
++    counterintuitive in the first place) is definitely beneficial, for
++    the following reasons:
++
++    - First, with any proportional-share scheduler, the maximum
++      deviation with respect to an ideal service is proportional to
++      the maximum budget (slice) assigned to queues. As a consequence,
++      BFQ can keep this deviation tight not only because of the
++      accurate service of B-WF2Q+, but also because BFQ *does not*
++      need to assign a larger budget to a queue to let the queue
++      receive a higher fraction of the device throughput.
++
++    - Second, BFQ is free to choose, for every process (queue), the
++      budget that best fits the needs of the process, or best
++      leverages the I/O pattern of the process. In particular, BFQ
++      updates queue budgets with a simple feedback-loop algorithm that
++      allows a high throughput to be achieved, while still providing
++      tight latency guarantees to time-sensitive applications. When
++      the in-service queue expires, this algorithm computes the next
++      budget of the queue so as to:
++
++      - Let large budgets be eventually assigned to the queues
++	associated with I/O-bound applications performing sequential
++	I/O: in fact, the longer these applications are served once
++	got access to the device, the higher the throughput is.
++
++      - Let small budgets be eventually assigned to the queues
++	associated with time-sensitive applications (which typically
++	perform sporadic and short I/O), because, the smaller the
++	budget assigned to a queue waiting for service is, the sooner
++	B-WF2Q+ will serve that queue (Subsec 3.3 in [2]).
++
++- If several processes are competing for the device at the same time,
++  but all processes and groups have the same weight, then BFQ
++  guarantees the expected throughput distribution without ever idling
++  the device. It uses preemption instead. Throughput is then much
++  higher in this common scenario.
++
++- ioprio classes are served in strict priority order, i.e.,
++  lower-priority queues are not served as long as there are
++  higher-priority queues.  Among queues in the same class, the
++  bandwidth is distributed in proportion to the weight of each
++  queue. A very thin extra bandwidth is however guaranteed to
++  the Idle class, to prevent it from starving.
++
++
++3. What are BFQ's tunable?
++==========================
++
++The tunables back_seek-max, back_seek_penalty, fifo_expire_async and
++fifo_expire_sync below are the same as in CFQ. Their description is
++just copied from that for CFQ. Some considerations in the description
++of slice_idle are copied from CFQ too.
++
++per-process ioprio and weight
++-----------------------------
++
++Unless the cgroups interface is used (see "4. BFQ group scheduling"),
++weights can be assigned to processes only indirectly, through I/O
++priorities, and according to the relation:
++weight = (IOPRIO_BE_NR - ioprio) * 10.
++
++Beware that, if low-latency is set, then BFQ automatically raises the
++weight of the queues associated with interactive and soft real-time
++applications. Unset this tunable if you need/want to control weights.
++
++slice_idle
++----------
++
++This parameter specifies how long BFQ should idle for next I/O
++request, when certain sync BFQ queues become empty. By default
++slice_idle is a non-zero value. Idling has a double purpose: boosting
++throughput and making sure that the desired throughput distribution is
++respected (see the description of how BFQ works, and, if needed, the
++papers referred there).
++
++As for throughput, idling can be very helpful on highly seeky media
++like single spindle SATA/SAS disks where we can cut down on overall
++number of seeks and see improved throughput.
++
++Setting slice_idle to 0 will remove all the idling on queues and one
++should see an overall improved throughput on faster storage devices
++like multiple SATA/SAS disks in hardware RAID configuration.
++
++So depending on storage and workload, it might be useful to set
++slice_idle=0.  In general for SATA/SAS disks and software RAID of
++SATA/SAS disks keeping slice_idle enabled should be useful. For any
++configurations where there are multiple spindles behind single LUN
++(Host based hardware RAID controller or for storage arrays), setting
++slice_idle=0 might end up in better throughput and acceptable
++latencies.
++
++Idling is however necessary to have service guarantees enforced in
++case of differentiated weights or differentiated I/O-request lengths.
++To see why, suppose that a given BFQ queue A must get several I/O
++requests served for each request served for another queue B. Idling
++ensures that, if A makes a new I/O request slightly after becoming
++empty, then no request of B is dispatched in the middle, and thus A
++does not lose the possibility to get more than one request dispatched
++before the next request of B is dispatched. Note that idling
++guarantees the desired differentiated treatment of queues only in
++terms of I/O-request dispatches. To guarantee that the actual service
++order then corresponds to the dispatch order, the strict_guarantees
++tunable must be set too.
++
++There is an important flipside for idling: apart from the above cases
++where it is beneficial also for throughput, idling can severely impact
++throughput. One important case is random workload. Because of this
++issue, BFQ tends to avoid idling as much as possible, when it is not
++beneficial also for throughput. As a consequence of this behavior, and
++of further issues described for the strict_guarantees tunable,
++short-term service guarantees may be occasionally violated. And, in
++some cases, these guarantees may be more important than guaranteeing
++maximum throughput. For example, in video playing/streaming, a very
++low drop rate may be more important than maximum throughput. In these
++cases, consider setting the strict_guarantees parameter.
++
++strict_guarantees
++-----------------
++
++If this parameter is set (default: unset), then BFQ
++
++- always performs idling when the in-service queue becomes empty;
++
++- forces the device to serve one I/O request at a time, by dispatching a
++  new request only if there is no outstanding request.
++
++In the presence of differentiated weights or I/O-request sizes, both
++the above conditions are needed to guarantee that every BFQ queue
++receives its allotted share of the bandwidth. The first condition is
++needed for the reasons explained in the description of the slice_idle
++tunable.  The second condition is needed because all modern storage
++devices reorder internally-queued requests, which may trivially break
++the service guarantees enforced by the I/O scheduler.
++
++Setting strict_guarantees may evidently affect throughput.
++
++back_seek_max
++-------------
++
++This specifies, given in Kbytes, the maximum "distance" for backward seeking.
++The distance is the amount of space from the current head location to the
++sectors that are backward in terms of distance.
++
++This parameter allows the scheduler to anticipate requests in the "backward"
++direction and consider them as being the "next" if they are within this
++distance from the current head location.
++
++back_seek_penalty
++-----------------
++
++This parameter is used to compute the cost of backward seeking. If the
++backward distance of request is just 1/back_seek_penalty from a "front"
++request, then the seeking cost of two requests is considered equivalent.
++
++So scheduler will not bias toward one or the other request (otherwise scheduler
++will bias toward front request). Default value of back_seek_penalty is 2.
++
++fifo_expire_async
++-----------------
++
++This parameter is used to set the timeout of asynchronous requests. Default
++value of this is 248ms.
++
++fifo_expire_sync
++----------------
++
++This parameter is used to set the timeout of synchronous requests. Default
++value of this is 124ms. In case to favor synchronous requests over asynchronous
++one, this value should be decreased relative to fifo_expire_async.
++
++low_latency
++-----------
++
++This parameter is used to enable/disable BFQ's low latency mode. By
++default, low latency mode is enabled. If enabled, interactive and soft
++real-time applications are privileged and experience a lower latency,
++as explained in more detail in the description of how BFQ works.
++
++DO NOT enable this mode if you need full control on bandwidth
++distribution. In fact, if it is enabled, then BFQ automatically
++increases the bandwidth share of privileged applications, as the main
++means to guarantee a lower latency to them.
++
++timeout_sync
++------------
++
++Maximum amount of device time that can be given to a task (queue) once
++it has been selected for service. On devices with costly seeks,
++increasing this time usually increases maximum throughput. On the
++opposite end, increasing this time coarsens the granularity of the
++short-term bandwidth and latency guarantees, especially if the
++following parameter is set to zero.
++
++max_budget
++----------
++
++Maximum amount of service, measured in sectors, that can be provided
++to a BFQ queue once it is set in service (of course within the limits
++of the above timeout). According to what said in the description of
++the algorithm, larger values increase the throughput in proportion to
++the percentage of sequential I/O requests issued. The price of larger
++values is that they coarsen the granularity of short-term bandwidth
++and latency guarantees.
++
++The default value is 0, which enables auto-tuning: BFQ sets max_budget
++to the maximum number of sectors that can be served during
++timeout_sync, according to the estimated peak rate.
++
++weights
++-------
++
++Read-only parameter, used to show the weights of the currently active
++BFQ queues.
++
++
++wr_ tunables
++------------
++
++BFQ exports a few parameters to control/tune the behavior of
++low-latency heuristics.
++
++wr_coeff
++
++Factor by which the weight of a weight-raised queue is multiplied. If
++the queue is deemed soft real-time, then the weight is further
++multiplied by an additional, constant factor.
++
++wr_max_time
++
++Maximum duration of a weight-raising period for an interactive task
++(ms). If set to zero (default value), then this value is computed
++automatically, as a function of the peak rate of the device. In any
++case, when the value of this parameter is read, it always reports the
++current duration, regardless of whether it has been set manually or
++computed automatically.
++
++wr_max_softrt_rate
++
++Maximum service rate below which a queue is deemed to be associated
++with a soft real-time application, and is then weight-raised
++accordingly (sectors/sec).
++
++wr_min_idle_time
++
++Minimum idle period after which interactive weight-raising may be
++reactivated for a queue (in ms).
++
++wr_rt_max_time
++
++Maximum weight-raising duration for soft real-time queues (in ms). The
++start time from which this duration is considered is automatically
++moved forward if the queue is detected to be still soft real-time
++before the current soft real-time weight-raising period finishes.
++
++wr_min_inter_arr_async
++
++Minimum period between I/O request arrivals after which weight-raising
++may be reactivated for an already busy async queue (in ms).
++
++
++4. Group scheduling with BFQ
++============================
++
++BFQ supports both cgroups-v1 and cgroups-v2 io controllers, namely
++blkio and io. In particular, BFQ supports weight-based proportional
++share. To activate cgroups support, set BFQ_GROUP_IOSCHED.
++
++4-1 Service guarantees provided
++-------------------------------
++
++With BFQ, proportional share means true proportional share of the
++device bandwidth, according to group weights. For example, a group
++with weight 200 gets twice the bandwidth, and not just twice the time,
++of a group with weight 100.
++
++BFQ supports hierarchies (group trees) of any depth. Bandwidth is
++distributed among groups and processes in the expected way: for each
++group, the children of the group share the whole bandwidth of the
++group in proportion to their weights. In particular, this implies
++that, for each leaf group, every process of the group receives the
++same share of the whole group bandwidth, unless the ioprio of the
++process is modified.
++
++The resource-sharing guarantee for a group may partially or totally
++switch from bandwidth to time, if providing bandwidth guarantees to
++the group lowers the throughput too much. This switch occurs on a
++per-process basis: if a process of a leaf group causes throughput loss
++if served in such a way to receive its share of the bandwidth, then
++BFQ switches back to just time-based proportional share for that
++process.
++
++4-2 Interface
++-------------
++
++To get proportional sharing of bandwidth with BFQ for a given device,
++BFQ must of course be the active scheduler for that device.
++
++Within each group directory, the names of the files associated with
++BFQ-specific cgroup parameters and stats begin with the "bfq."
++prefix. So, with cgroups-v1 or cgroups-v2, the full prefix for
++BFQ-specific files is "blkio.bfq." or "io.bfq." For example, the group
++parameter to set the weight of a group with BFQ is blkio.bfq.weight
++or io.bfq.weight.
++
++Parameters to set
++-----------------
++
++For each group, there is only the following parameter to set.
++
++weight (namely blkio.bfq.weight or io.bfq-weight): the weight of the
++group inside its parent. Available values: 1..10000 (default 100). The
++linear mapping between ioprio and weights, described at the beginning
++of the tunable section, is still valid, but all weights higher than
++IOPRIO_BE_NR*10 are mapped to ioprio 0.
++
++Recall that, if low-latency is set, then BFQ automatically raises the
++weight of the queues associated with interactive and soft real-time
++applications. Unset this tunable if you need/want to control weights.
++
++
++[1] P. Valente, A. Avanzini, "Evolution of the BFQ Storage I/O
++    Scheduler", Proceedings of the First Workshop on Mobile System
++    Technologies (MST-2015), May 2015.
++    http://algogroup.unimore.it/people/paolo/disk_sched/mst-2015.pdf
++
++[2] P. Valente and M. Andreolini, "Improving Application
++    Responsiveness with the BFQ Disk I/O Scheduler", Proceedings of
++    the 5th Annual International Systems and Storage Conference
++    (SYSTOR '12), June 2012.
++    Slightly extended version:
++    http://algogroup.unimore.it/people/paolo/disk_sched/bfq-v1-suite-
++							results.pdf
+diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched
+index 58fc8684788d..99a42261677a 100644
+--- a/block/Kconfig.iosched
++++ b/block/Kconfig.iosched
+@@ -39,6 +39,25 @@ config CFQ_GROUP_IOSCHED
+ 	---help---
+ 	  Enable group IO scheduling in CFQ.
+ 
++config IOSCHED_BFQ
++	tristate "BFQ I/O scheduler"
++	default n
++	---help---
++	The BFQ I/O scheduler distributes bandwidth among all
++	processes according to their weights, regardless of the
++	device parameters and with any workload. It also guarantees
++	a low latency to interactive and soft real-time applications.
++	Details in Documentation/block/bfq-iosched.txt
++
++config BFQ_GROUP_IOSCHED
++	bool "BFQ hierarchical scheduling support"
++	depends on IOSCHED_BFQ && BLK_CGROUP
++	default n
++	---help---
++
++	Enable hierarchical scheduling in BFQ, using the blkio
++	(cgroups-v1) or io (cgroups-v2) controller.
++
+ choice
+ 	prompt "Default I/O scheduler"
+ 	default DEFAULT_CFQ
+@@ -52,6 +71,16 @@ choice
+ 	config DEFAULT_CFQ
+ 		bool "CFQ" if IOSCHED_CFQ=y
+ 
++	config DEFAULT_BFQ
++		bool "BFQ" if IOSCHED_BFQ=y
++		help
++		  Selects BFQ as the default I/O scheduler which will be
++		  used by default for all block devices.
++		  The BFQ I/O scheduler aims at distributing the bandwidth
++		  as desired, independently of the disk parameters and with
++		  any workload. It also tries to guarantee low latency to
++		  interactive and soft real-time applications.
++
+ 	config DEFAULT_NOOP
+ 		bool "No-op"
+ 
+@@ -61,6 +90,7 @@ config DEFAULT_IOSCHED
+ 	string
+ 	default "deadline" if DEFAULT_DEADLINE
+ 	default "cfq" if DEFAULT_CFQ
++	default "bfq" if DEFAULT_BFQ
+ 	default "noop" if DEFAULT_NOOP
+ 
+ config MQ_IOSCHED_DEADLINE
+diff --git a/block/Makefile b/block/Makefile
+index 081bb680789b..91869f2ef2dc 100644
+--- a/block/Makefile
++++ b/block/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_IOSCHED_NOOP)	+= noop-iosched.o
+ obj-$(CONFIG_IOSCHED_DEADLINE)	+= deadline-iosched.o
+ obj-$(CONFIG_IOSCHED_CFQ)	+= cfq-iosched.o
+ obj-$(CONFIG_MQ_IOSCHED_DEADLINE)	+= mq-deadline.o
++obj-$(CONFIG_IOSCHED_BFQ)	+= bfq-iosched.o
+ 
+ obj-$(CONFIG_BLOCK_COMPAT)	+= compat_ioctl.o
+ obj-$(CONFIG_BLK_CMDLINE_PARSER)	+= cmdline-parser.o
+diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
+new file mode 100644
+index 000000000000..39daaf405dc6
+--- /dev/null
++++ b/block/bfq-cgroup.c
+@@ -0,0 +1,1190 @@
++/*
++ * BFQ: CGROUPS support.
++ *
++ * Based on ideas and code from CFQ:
++ * Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
++ *
++ * Copyright (C) 2008 Fabio Checconi <fabio@gandalf.sssup.it>
++ *		      Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2015 Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2016 Paolo Valente <paolo.valente@linaro.org>
++ *
++ * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ
++ * file.
++ */
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++
++/* bfqg stats flags */
++enum bfqg_stats_flags {
++	BFQG_stats_waiting = 0,
++	BFQG_stats_idling,
++	BFQG_stats_empty,
++};
++
++#define BFQG_FLAG_FNS(name)						\
++static void bfqg_stats_mark_##name(struct bfqg_stats *stats)	\
++{									\
++	stats->flags |= (1 << BFQG_stats_##name);			\
++}									\
++static void bfqg_stats_clear_##name(struct bfqg_stats *stats)	\
++{									\
++	stats->flags &= ~(1 << BFQG_stats_##name);			\
++}									\
++static int bfqg_stats_##name(struct bfqg_stats *stats)		\
++{									\
++	return (stats->flags & (1 << BFQG_stats_##name)) != 0;		\
++}									\
++
++BFQG_FLAG_FNS(waiting)
++BFQG_FLAG_FNS(idling)
++BFQG_FLAG_FNS(empty)
++#undef BFQG_FLAG_FNS
++
++/* This should be called with the queue_lock held. */
++static void bfqg_stats_update_group_wait_time(struct bfqg_stats *stats)
++{
++	unsigned long long now;
++
++	if (!bfqg_stats_waiting(stats))
++		return;
++
++	now = sched_clock();
++	if (time_after64(now, stats->start_group_wait_time))
++		blkg_stat_add(&stats->group_wait_time,
++			      now - stats->start_group_wait_time);
++	bfqg_stats_clear_waiting(stats);
++}
++
++/* This should be called with the queue_lock held. */
++static void bfqg_stats_set_start_group_wait_time(struct bfq_group *bfqg,
++						 struct bfq_group *curr_bfqg)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++
++	if (bfqg_stats_waiting(stats))
++		return;
++	if (bfqg == curr_bfqg)
++		return;
++	stats->start_group_wait_time = sched_clock();
++	bfqg_stats_mark_waiting(stats);
++}
++
++/* This should be called with the queue_lock held. */
++static void bfqg_stats_end_empty_time(struct bfqg_stats *stats)
++{
++	unsigned long long now;
++
++	if (!bfqg_stats_empty(stats))
++		return;
++
++	now = sched_clock();
++	if (time_after64(now, stats->start_empty_time))
++		blkg_stat_add(&stats->empty_time,
++			      now - stats->start_empty_time);
++	bfqg_stats_clear_empty(stats);
++}
++
++static void bfqg_stats_update_dequeue(struct bfq_group *bfqg)
++{
++	blkg_stat_add(&bfqg->stats.dequeue, 1);
++}
++
++static void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++
++	if (blkg_rwstat_total(&stats->queued))
++		return;
++
++	/*
++	 * group is already marked empty. This can happen if bfqq got new
++	 * request in parent group and moved to this group while being added
++	 * to service tree. Just ignore the event and move on.
++	 */
++	if (bfqg_stats_empty(stats))
++		return;
++
++	stats->start_empty_time = sched_clock();
++	bfqg_stats_mark_empty(stats);
++}
++
++static void bfqg_stats_update_idle_time(struct bfq_group *bfqg)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++
++	if (bfqg_stats_idling(stats)) {
++		unsigned long long now = sched_clock();
++
++		if (time_after64(now, stats->start_idle_time))
++			blkg_stat_add(&stats->idle_time,
++				      now - stats->start_idle_time);
++		bfqg_stats_clear_idling(stats);
++	}
++}
++
++static void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++
++	stats->start_idle_time = sched_clock();
++	bfqg_stats_mark_idling(stats);
++}
++
++static void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++
++	blkg_stat_add(&stats->avg_queue_size_sum,
++		      blkg_rwstat_total(&stats->queued));
++	blkg_stat_add(&stats->avg_queue_size_samples, 1);
++	bfqg_stats_update_group_wait_time(stats);
++}
++
++static struct blkcg_policy blkcg_policy_bfq;
++
++/*
++ * blk-cgroup policy-related handlers
++ * The following functions help in converting between blk-cgroup
++ * internal structures and BFQ-specific structures.
++ */
++
++static struct bfq_group *pd_to_bfqg(struct blkg_policy_data *pd)
++{
++	return pd ? container_of(pd, struct bfq_group, pd) : NULL;
++}
++
++static struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg)
++{
++	return pd_to_blkg(&bfqg->pd);
++}
++
++static struct bfq_group *blkg_to_bfqg(struct blkcg_gq *blkg)
++{
++	struct blkg_policy_data *pd = blkg_to_pd(blkg, &blkcg_policy_bfq);
++
++	return pd_to_bfqg(pd);
++}
++
++/*
++ * bfq_group handlers
++ * The following functions help in navigating the bfq_group hierarchy
++ * by allowing to find the parent of a bfq_group or the bfq_group
++ * associated to a bfq_queue.
++ */
++
++static struct bfq_group *bfqg_parent(struct bfq_group *bfqg)
++{
++	struct blkcg_gq *pblkg = bfqg_to_blkg(bfqg)->parent;
++
++	return pblkg ? blkg_to_bfqg(pblkg) : NULL;
++}
++
++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq)
++{
++	struct bfq_entity *group_entity = bfqq->entity.parent;
++
++	return group_entity ? container_of(group_entity, struct bfq_group,
++					   entity) :
++			      bfqq->bfqd->root_group;
++}
++
++/*
++ * The following two functions handle get and put of a bfq_group by
++ * wrapping the related blk-cgroup hooks.
++ */
++
++static void bfqg_get(struct bfq_group *bfqg)
++{
++	return blkg_get(bfqg_to_blkg(bfqg));
++}
++
++static void bfqg_put(struct bfq_group *bfqg)
++{
++	return blkg_put(bfqg_to_blkg(bfqg));
++}
++
++static void bfqg_stats_update_io_add(struct bfq_group *bfqg,
++				     struct bfq_queue *bfqq,
++				     unsigned int op)
++{
++	blkg_rwstat_add(&bfqg->stats.queued, op, 1);
++	bfqg_stats_end_empty_time(&bfqg->stats);
++	if (!(bfqq == ((struct bfq_data *)bfqg->bfqd)->in_service_queue))
++		bfqg_stats_set_start_group_wait_time(bfqg, bfqq_group(bfqq));
++}
++
++static void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op)
++{
++	blkg_rwstat_add(&bfqg->stats.queued, op, -1);
++}
++
++static void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op)
++{
++	blkg_rwstat_add(&bfqg->stats.merged, op, 1);
++}
++
++static void bfqg_stats_update_completion(struct bfq_group *bfqg,
++			uint64_t start_time, uint64_t io_start_time,
++			unsigned int op)
++{
++	struct bfqg_stats *stats = &bfqg->stats;
++	unsigned long long now = sched_clock();
++
++	if (time_after64(now, io_start_time))
++		blkg_rwstat_add(&stats->service_time, op,
++				now - io_start_time);
++	if (time_after64(io_start_time, start_time))
++		blkg_rwstat_add(&stats->wait_time, op,
++				io_start_time - start_time);
++}
++
++/* @stats = 0 */
++static void bfqg_stats_reset(struct bfqg_stats *stats)
++{
++	/* queued stats shouldn't be cleared */
++	blkg_rwstat_reset(&stats->merged);
++	blkg_rwstat_reset(&stats->service_time);
++	blkg_rwstat_reset(&stats->wait_time);
++	blkg_stat_reset(&stats->time);
++	blkg_stat_reset(&stats->avg_queue_size_sum);
++	blkg_stat_reset(&stats->avg_queue_size_samples);
++	blkg_stat_reset(&stats->dequeue);
++	blkg_stat_reset(&stats->group_wait_time);
++	blkg_stat_reset(&stats->idle_time);
++	blkg_stat_reset(&stats->empty_time);
++}
++
++/* @to += @from */
++static void bfqg_stats_add_aux(struct bfqg_stats *to, struct bfqg_stats *from)
++{
++	if (!to || !from)
++		return;
++
++	/* queued stats shouldn't be cleared */
++	blkg_rwstat_add_aux(&to->merged, &from->merged);
++	blkg_rwstat_add_aux(&to->service_time, &from->service_time);
++	blkg_rwstat_add_aux(&to->wait_time, &from->wait_time);
++	blkg_stat_add_aux(&from->time, &from->time);
++	blkg_stat_add_aux(&to->avg_queue_size_sum, &from->avg_queue_size_sum);
++	blkg_stat_add_aux(&to->avg_queue_size_samples,
++			  &from->avg_queue_size_samples);
++	blkg_stat_add_aux(&to->dequeue, &from->dequeue);
++	blkg_stat_add_aux(&to->group_wait_time, &from->group_wait_time);
++	blkg_stat_add_aux(&to->idle_time, &from->idle_time);
++	blkg_stat_add_aux(&to->empty_time, &from->empty_time);
++}
++
++/*
++ * Transfer @bfqg's stats to its parent's dead_stats so that the ancestors'
++ * recursive stats can still account for the amount used by this bfqg after
++ * it's gone.
++ */
++static void bfqg_stats_xfer_dead(struct bfq_group *bfqg)
++{
++	struct bfq_group *parent;
++
++	if (!bfqg) /* root_group */
++		return;
++
++	parent = bfqg_parent(bfqg);
++
++	lockdep_assert_held(bfqg_to_blkg(bfqg)->q->queue_lock);
++
++	if (unlikely(!parent))
++		return;
++
++	bfqg_stats_add_aux(&parent->stats, &bfqg->stats);
++	bfqg_stats_reset(&bfqg->stats);
++}
++
++static void bfq_init_entity(struct bfq_entity *entity,
++			    struct bfq_group *bfqg)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	entity->weight = entity->new_weight;
++	entity->orig_weight = entity->new_weight;
++	if (bfqq) {
++		bfqq->ioprio = bfqq->new_ioprio;
++		bfqq->ioprio_class = bfqq->new_ioprio_class;
++		bfqg_get(bfqg);
++	}
++	entity->parent = bfqg->my_entity; /* NULL for root group */
++	entity->sched_data = &bfqg->sched_data;
++}
++
++static void bfqg_stats_exit(struct bfqg_stats *stats)
++{
++	blkg_rwstat_exit(&stats->merged);
++	blkg_rwstat_exit(&stats->service_time);
++	blkg_rwstat_exit(&stats->wait_time);
++	blkg_rwstat_exit(&stats->queued);
++	blkg_stat_exit(&stats->time);
++	blkg_stat_exit(&stats->avg_queue_size_sum);
++	blkg_stat_exit(&stats->avg_queue_size_samples);
++	blkg_stat_exit(&stats->dequeue);
++	blkg_stat_exit(&stats->group_wait_time);
++	blkg_stat_exit(&stats->idle_time);
++	blkg_stat_exit(&stats->empty_time);
++}
++
++static int bfqg_stats_init(struct bfqg_stats *stats, gfp_t gfp)
++{
++	if (blkg_rwstat_init(&stats->merged, gfp) ||
++	    blkg_rwstat_init(&stats->service_time, gfp) ||
++	    blkg_rwstat_init(&stats->wait_time, gfp) ||
++	    blkg_rwstat_init(&stats->queued, gfp) ||
++	    blkg_stat_init(&stats->time, gfp) ||
++	    blkg_stat_init(&stats->avg_queue_size_sum, gfp) ||
++	    blkg_stat_init(&stats->avg_queue_size_samples, gfp) ||
++	    blkg_stat_init(&stats->dequeue, gfp) ||
++	    blkg_stat_init(&stats->group_wait_time, gfp) ||
++	    blkg_stat_init(&stats->idle_time, gfp) ||
++	    blkg_stat_init(&stats->empty_time, gfp)) {
++		bfqg_stats_exit(stats);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++static struct bfq_group_data *cpd_to_bfqgd(struct blkcg_policy_data *cpd)
++{
++	return cpd ? container_of(cpd, struct bfq_group_data, pd) : NULL;
++}
++
++static struct bfq_group_data *blkcg_to_bfqgd(struct blkcg *blkcg)
++{
++	return cpd_to_bfqgd(blkcg_to_cpd(blkcg, &blkcg_policy_bfq));
++}
++
++static struct blkcg_policy_data *bfq_cpd_alloc(gfp_t gfp)
++{
++	struct bfq_group_data *bgd;
++
++	bgd = kzalloc(sizeof(*bgd), gfp);
++	if (!bgd)
++		return NULL;
++	return &bgd->pd;
++}
++
++static void bfq_cpd_init(struct blkcg_policy_data *cpd)
++{
++	struct bfq_group_data *d = cpd_to_bfqgd(cpd);
++
++	d->weight = cgroup_subsys_on_dfl(io_cgrp_subsys) ?
++		CGROUP_WEIGHT_DFL : BFQ_WEIGHT_LEGACY_DFL;
++}
++
++static void bfq_cpd_free(struct blkcg_policy_data *cpd)
++{
++	kfree(cpd_to_bfqgd(cpd));
++}
++
++static struct blkg_policy_data *bfq_pd_alloc(gfp_t gfp, int node)
++{
++	struct bfq_group *bfqg;
++
++	bfqg = kzalloc_node(sizeof(*bfqg), gfp, node);
++	if (!bfqg)
++		return NULL;
++
++	if (bfqg_stats_init(&bfqg->stats, gfp)) {
++		kfree(bfqg);
++		return NULL;
++	}
++
++	return &bfqg->pd;
++}
++
++static void bfq_pd_init(struct blkg_policy_data *pd)
++{
++	struct blkcg_gq *blkg;
++	struct bfq_group *bfqg;
++	struct bfq_data *bfqd;
++	struct bfq_entity *entity;
++	struct bfq_group_data *d;
++
++	blkg = pd_to_blkg(pd);
++	BUG_ON(!blkg);
++	bfqg = blkg_to_bfqg(blkg);
++	bfqd = blkg->q->elevator->elevator_data;
++	entity = &bfqg->entity;
++	d = blkcg_to_bfqgd(blkg->blkcg);
++
++	entity->orig_weight = entity->weight = entity->new_weight = d->weight;
++	entity->my_sched_data = &bfqg->sched_data;
++	bfqg->my_entity = entity; /*
++				   * the root_group's will be set to NULL
++				   * in bfq_init_queue()
++				   */
++	bfqg->bfqd = bfqd;
++	bfqg->active_entities = 0;
++	bfqg->rq_pos_tree = RB_ROOT;
++}
++
++static void bfq_pd_free(struct blkg_policy_data *pd)
++{
++	struct bfq_group *bfqg = pd_to_bfqg(pd);
++
++	bfqg_stats_exit(&bfqg->stats);
++	return kfree(bfqg);
++}
++
++static void bfq_pd_reset_stats(struct blkg_policy_data *pd)
++{
++	struct bfq_group *bfqg = pd_to_bfqg(pd);
++
++	bfqg_stats_reset(&bfqg->stats);
++}
++
++static void bfq_group_set_parent(struct bfq_group *bfqg,
++					struct bfq_group *parent)
++{
++	struct bfq_entity *entity;
++
++	BUG_ON(!parent);
++	BUG_ON(!bfqg);
++	BUG_ON(bfqg == parent);
++
++	entity = &bfqg->entity;
++	entity->parent = parent->my_entity;
++	entity->sched_data = &parent->sched_data;
++}
++
++static struct bfq_group *bfq_lookup_bfqg(struct bfq_data *bfqd,
++					 struct blkcg *blkcg)
++{
++	struct blkcg_gq *blkg;
++
++	blkg = blkg_lookup(blkcg, bfqd->queue);
++	if (likely(blkg))
++		return blkg_to_bfqg(blkg);
++	return NULL;
++}
++
++static struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
++					    struct blkcg *blkcg)
++{
++	struct bfq_group *bfqg, *parent;
++	struct bfq_entity *entity;
++
++	assert_spin_locked(bfqd->queue->queue_lock);
++
++	bfqg = bfq_lookup_bfqg(bfqd, blkcg);
++
++	if (unlikely(!bfqg))
++		return NULL;
++
++	/*
++	 * Update chain of bfq_groups as we might be handling a leaf group
++	 * which, along with some of its relatives, has not been hooked yet
++	 * to the private hierarchy of BFQ.
++	 */
++	entity = &bfqg->entity;
++	for_each_entity(entity) {
++		bfqg = container_of(entity, struct bfq_group, entity);
++		BUG_ON(!bfqg);
++		if (bfqg != bfqd->root_group) {
++			parent = bfqg_parent(bfqg);
++			if (!parent)
++				parent = bfqd->root_group;
++			BUG_ON(!parent);
++			bfq_group_set_parent(bfqg, parent);
++		}
++	}
++
++	return bfqg;
++}
++
++static void bfq_pos_tree_add_move(struct bfq_data *bfqd,
++				  struct bfq_queue *bfqq);
++
++static void bfq_bfqq_expire(struct bfq_data *bfqd,
++			    struct bfq_queue *bfqq,
++			    bool compensate,
++			    enum bfqq_expiration reason);
++
++/**
++ * bfq_bfqq_move - migrate @bfqq to @bfqg.
++ * @bfqd: queue descriptor.
++ * @bfqq: the queue to move.
++ * @bfqg: the group to move to.
++ *
++ * Move @bfqq to @bfqg, deactivating it from its old group and reactivating
++ * it on the new one.  Avoid putting the entity on the old group idle tree.
++ *
++ * Must be called under the queue lock; the cgroup owning @bfqg must
++ * not disappear (by now this just means that we are called under
++ * rcu_read_lock()).
++ */
++static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++			  struct bfq_group *bfqg)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	BUG_ON(!bfq_bfqq_busy(bfqq) && !RB_EMPTY_ROOT(&bfqq->sort_list));
++	BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list) && !entity->on_st);
++	BUG_ON(bfq_bfqq_busy(bfqq) && RB_EMPTY_ROOT(&bfqq->sort_list)
++	       && entity->on_st &&
++	       bfqq != bfqd->in_service_queue);
++	BUG_ON(!bfq_bfqq_busy(bfqq) && bfqq == bfqd->in_service_queue);
++
++	/* If bfqq is empty, then bfq_bfqq_expire also invokes
++	 * bfq_del_bfqq_busy, thereby removing bfqq and its entity
++	 * from data structures related to current group. Otherwise we
++	 * need to remove bfqq explicitly with bfq_deactivate_bfqq, as
++	 * we do below.
++	 */
++	if (bfqq == bfqd->in_service_queue)
++		bfq_bfqq_expire(bfqd, bfqd->in_service_queue,
++				false, BFQ_BFQQ_PREEMPTED);
++
++	BUG_ON(entity->on_st && !bfq_bfqq_busy(bfqq)
++	    && &bfq_entity_service_tree(entity)->idle !=
++	       entity->tree);
++
++	BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_bfqq_busy(bfqq));
++
++	if (bfq_bfqq_busy(bfqq))
++		bfq_deactivate_bfqq(bfqd, bfqq, false, false);
++	else if (entity->on_st) {
++		BUG_ON(&bfq_entity_service_tree(entity)->idle !=
++		       entity->tree);
++		bfq_put_idle_entity(bfq_entity_service_tree(entity), entity);
++	}
++	bfqg_put(bfqq_group(bfqq));
++
++	/*
++	 * Here we use a reference to bfqg.  We don't need a refcounter
++	 * as the cgroup reference will not be dropped, so that its
++	 * destroy() callback will not be invoked.
++	 */
++	entity->parent = bfqg->my_entity;
++	entity->sched_data = &bfqg->sched_data;
++	bfqg_get(bfqg);
++
++	BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_bfqq_busy(bfqq));
++	if (bfq_bfqq_busy(bfqq)) {
++		bfq_pos_tree_add_move(bfqd, bfqq);
++		bfq_activate_bfqq(bfqd, bfqq);
++	}
++
++	if (!bfqd->in_service_queue && !bfqd->rq_in_driver)
++		bfq_schedule_dispatch(bfqd);
++	BUG_ON(entity->on_st && !bfq_bfqq_busy(bfqq)
++	       && &bfq_entity_service_tree(entity)->idle !=
++	       entity->tree);
++}
++
++/**
++ * __bfq_bic_change_cgroup - move @bic to @cgroup.
++ * @bfqd: the queue descriptor.
++ * @bic: the bic to move.
++ * @blkcg: the blk-cgroup to move to.
++ *
++ * Move bic to blkcg, assuming that bfqd->queue is locked; the caller
++ * has to make sure that the reference to cgroup is valid across the call.
++ *
++ * NOTE: an alternative approach might have been to store the current
++ * cgroup in bfqq and getting a reference to it, reducing the lookup
++ * time here, at the price of slightly more complex code.
++ */
++static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd,
++						struct bfq_io_cq *bic,
++						struct blkcg *blkcg)
++{
++	struct bfq_queue *async_bfqq = bic_to_bfqq(bic, 0);
++	struct bfq_queue *sync_bfqq = bic_to_bfqq(bic, 1);
++	struct bfq_group *bfqg;
++	struct bfq_entity *entity;
++
++	lockdep_assert_held(bfqd->queue->queue_lock);
++
++	bfqg = bfq_find_set_group(bfqd, blkcg);
++
++	if (unlikely(!bfqg))
++		bfqg = bfqd->root_group;
++
++	if (async_bfqq) {
++		entity = &async_bfqq->entity;
++
++		if (entity->sched_data != &bfqg->sched_data) {
++			bic_set_bfqq(bic, NULL, 0);
++			bfq_log_bfqq(bfqd, async_bfqq,
++				     "bic_change_group: %p %d",
++				     async_bfqq,
++				     async_bfqq->ref);
++			bfq_put_queue(async_bfqq);
++		}
++	}
++
++	if (sync_bfqq) {
++		entity = &sync_bfqq->entity;
++		if (entity->sched_data != &bfqg->sched_data)
++			bfq_bfqq_move(bfqd, sync_bfqq, bfqg);
++	}
++
++	return bfqg;
++}
++
++static void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio)
++{
++	struct bfq_data *bfqd = bic_to_bfqd(bic);
++	struct bfq_group *bfqg = NULL;
++	uint64_t serial_nr;
++
++	rcu_read_lock();
++	serial_nr = bio_blkcg(bio)->css.serial_nr;
++
++	/*
++	 * Check whether blkcg has changed.  The condition may trigger
++	 * spuriously on a newly created cic but there's no harm.
++	 */
++	if (unlikely(!bfqd) || likely(bic->blkcg_serial_nr == serial_nr))
++		goto out;
++
++	bfqg = __bfq_bic_change_cgroup(bfqd, bic, bio_blkcg(bio));
++	bic->blkcg_serial_nr = serial_nr;
++out:
++	rcu_read_unlock();
++}
++
++/**
++ * bfq_flush_idle_tree - deactivate any entity on the idle tree of @st.
++ * @st: the service tree being flushed.
++ */
++static void bfq_flush_idle_tree(struct bfq_service_tree *st)
++{
++	struct bfq_entity *entity = st->first_idle;
++
++	for (; entity ; entity = st->first_idle)
++		__bfq_deactivate_entity(entity, false);
++}
++
++/**
++ * bfq_reparent_leaf_entity - move leaf entity to the root_group.
++ * @bfqd: the device data structure with the root group.
++ * @entity: the entity to move.
++ */
++static void bfq_reparent_leaf_entity(struct bfq_data *bfqd,
++				     struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	BUG_ON(!bfqq);
++	bfq_bfqq_move(bfqd, bfqq, bfqd->root_group);
++}
++
++/**
++ * bfq_reparent_active_entities - move to the root group all active
++ *                                entities.
++ * @bfqd: the device data structure with the root group.
++ * @bfqg: the group to move from.
++ * @st: the service tree with the entities.
++ *
++ * Needs queue_lock to be taken and reference to be valid over the call.
++ */
++static void bfq_reparent_active_entities(struct bfq_data *bfqd,
++					 struct bfq_group *bfqg,
++					 struct bfq_service_tree *st)
++{
++	struct rb_root *active = &st->active;
++	struct bfq_entity *entity = NULL;
++
++	if (!RB_EMPTY_ROOT(&st->active))
++		entity = bfq_entity_of(rb_first(active));
++
++	for (; entity ; entity = bfq_entity_of(rb_first(active)))
++		bfq_reparent_leaf_entity(bfqd, entity);
++
++	if (bfqg->sched_data.in_service_entity)
++		bfq_reparent_leaf_entity(bfqd,
++			bfqg->sched_data.in_service_entity);
++}
++
++/**
++ * bfq_pd_offline - deactivate the entity associated with @pd,
++ *		    and reparent its children entities.
++ * @pd: descriptor of the policy going offline.
++ *
++ * blkio already grabs the queue_lock for us, so no need to use
++ * RCU-based magic
++ */
++static void bfq_pd_offline(struct blkg_policy_data *pd)
++{
++	struct bfq_service_tree *st;
++	struct bfq_group *bfqg;
++	struct bfq_data *bfqd;
++	struct bfq_entity *entity;
++	int i;
++
++	BUG_ON(!pd);
++	bfqg = pd_to_bfqg(pd);
++	BUG_ON(!bfqg);
++	bfqd = bfqg->bfqd;
++	BUG_ON(bfqd && !bfqd->root_group);
++
++	entity = bfqg->my_entity;
++
++	if (!entity) /* root group */
++		return;
++
++	/*
++	 * Empty all service_trees belonging to this group before
++	 * deactivating the group itself.
++	 */
++	for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) {
++		BUG_ON(!bfqg->sched_data.service_tree);
++		st = bfqg->sched_data.service_tree + i;
++		/*
++		 * The idle tree may still contain bfq_queues belonging
++		 * to exited task because they never migrated to a different
++		 * cgroup from the one being destroyed now.  No one else
++		 * can access them so it's safe to act without any lock.
++		 */
++		bfq_flush_idle_tree(st);
++
++		/*
++		 * It may happen that some queues are still active
++		 * (busy) upon group destruction (if the corresponding
++		 * processes have been forced to terminate). We move
++		 * all the leaf entities corresponding to these queues
++		 * to the root_group.
++		 * Also, it may happen that the group has an entity
++		 * in service, which is disconnected from the active
++		 * tree: it must be moved, too.
++		 * There is no need to put the sync queues, as the
++		 * scheduler has taken no reference.
++		 */
++		bfq_reparent_active_entities(bfqd, bfqg, st);
++		BUG_ON(!RB_EMPTY_ROOT(&st->active));
++		BUG_ON(!RB_EMPTY_ROOT(&st->idle));
++	}
++	BUG_ON(bfqg->sched_data.next_in_service);
++	BUG_ON(bfqg->sched_data.in_service_entity);
++
++	__bfq_deactivate_entity(entity, false);
++	bfq_put_async_queues(bfqd, bfqg);
++
++	/*
++	 * @blkg is going offline and will be ignored by
++	 * blkg_[rw]stat_recursive_sum().  Transfer stats to the parent so
++	 * that they don't get lost.  If IOs complete after this point, the
++	 * stats for them will be lost.  Oh well...
++	 */
++	bfqg_stats_xfer_dead(bfqg);
++}
++
++static void bfq_end_wr_async(struct bfq_data *bfqd)
++{
++	struct blkcg_gq *blkg;
++
++	list_for_each_entry(blkg, &bfqd->queue->blkg_list, q_node) {
++		struct bfq_group *bfqg = blkg_to_bfqg(blkg);
++		BUG_ON(!bfqg);
++
++		bfq_end_wr_async_queues(bfqd, bfqg);
++	}
++	bfq_end_wr_async_queues(bfqd, bfqd->root_group);
++}
++
++static int bfq_io_show_weight(struct seq_file *sf, void *v)
++{
++	struct blkcg *blkcg = css_to_blkcg(seq_css(sf));
++	struct bfq_group_data *bfqgd = blkcg_to_bfqgd(blkcg);
++	unsigned int val = 0;
++
++	if (bfqgd)
++		val = bfqgd->weight;
++
++	seq_printf(sf, "%u\n", val);
++
++	return 0;
++}
++
++static int bfq_io_set_weight_legacy(struct cgroup_subsys_state *css,
++				    struct cftype *cftype,
++				    u64 val)
++{
++	struct blkcg *blkcg = css_to_blkcg(css);
++	struct bfq_group_data *bfqgd = blkcg_to_bfqgd(blkcg);
++	struct blkcg_gq *blkg;
++	int ret = -ERANGE;
++
++	if (val < BFQ_MIN_WEIGHT || val > BFQ_MAX_WEIGHT)
++		return ret;
++
++	ret = 0;
++	spin_lock_irq(&blkcg->lock);
++	bfqgd->weight = (unsigned short)val;
++	hlist_for_each_entry(blkg, &blkcg->blkg_list, blkcg_node) {
++		struct bfq_group *bfqg = blkg_to_bfqg(blkg);
++
++		if (!bfqg)
++			continue;
++		/*
++		 * Setting the prio_changed flag of the entity
++		 * to 1 with new_weight == weight would re-set
++		 * the value of the weight to its ioprio mapping.
++		 * Set the flag only if necessary.
++		 */
++		if ((unsigned short)val != bfqg->entity.new_weight) {
++			bfqg->entity.new_weight = (unsigned short)val;
++			/*
++			 * Make sure that the above new value has been
++			 * stored in bfqg->entity.new_weight before
++			 * setting the prio_changed flag. In fact,
++			 * this flag may be read asynchronously (in
++			 * critical sections protected by a different
++			 * lock than that held here), and finding this
++			 * flag set may cause the execution of the code
++			 * for updating parameters whose value may
++			 * depend also on bfqg->entity.new_weight (in
++			 * __bfq_entity_update_weight_prio).
++			 * This barrier makes sure that the new value
++			 * of bfqg->entity.new_weight is correctly
++			 * seen in that code.
++			 */
++			smp_wmb();
++			bfqg->entity.prio_changed = 1;
++		}
++	}
++	spin_unlock_irq(&blkcg->lock);
++
++	return ret;
++}
++
++static ssize_t bfq_io_set_weight(struct kernfs_open_file *of,
++				 char *buf, size_t nbytes,
++				 loff_t off)
++{
++	u64 weight;
++	/* First unsigned long found in the file is used */
++	int ret = kstrtoull(strim(buf), 0, &weight);
++
++	if (ret)
++		return ret;
++
++	return bfq_io_set_weight_legacy(of_css(of), NULL, weight);
++}
++
++static int bfqg_print_stat(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), blkg_prfill_stat,
++			  &blkcg_policy_bfq, seq_cft(sf)->private, false);
++	return 0;
++}
++
++static int bfqg_print_rwstat(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), blkg_prfill_rwstat,
++			  &blkcg_policy_bfq, seq_cft(sf)->private, true);
++	return 0;
++}
++
++static u64 bfqg_prfill_stat_recursive(struct seq_file *sf,
++				      struct blkg_policy_data *pd, int off)
++{
++	u64 sum = blkg_stat_recursive_sum(pd_to_blkg(pd),
++					  &blkcg_policy_bfq, off);
++	return __blkg_prfill_u64(sf, pd, sum);
++}
++
++static u64 bfqg_prfill_rwstat_recursive(struct seq_file *sf,
++					struct blkg_policy_data *pd, int off)
++{
++	struct blkg_rwstat sum = blkg_rwstat_recursive_sum(pd_to_blkg(pd),
++							   &blkcg_policy_bfq,
++							   off);
++	return __blkg_prfill_rwstat(sf, pd, &sum);
++}
++
++static int bfqg_print_stat_recursive(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)),
++			  bfqg_prfill_stat_recursive, &blkcg_policy_bfq,
++			  seq_cft(sf)->private, false);
++	return 0;
++}
++
++static int bfqg_print_rwstat_recursive(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)),
++			  bfqg_prfill_rwstat_recursive, &blkcg_policy_bfq,
++			  seq_cft(sf)->private, true);
++	return 0;
++}
++
++static u64 bfqg_prfill_sectors(struct seq_file *sf, struct blkg_policy_data *pd,
++			       int off)
++{
++	u64 sum = blkg_rwstat_total(&pd->blkg->stat_bytes);
++
++	return __blkg_prfill_u64(sf, pd, sum >> 9);
++}
++
++static int bfqg_print_stat_sectors(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)),
++			  bfqg_prfill_sectors, &blkcg_policy_bfq, 0, false);
++	return 0;
++}
++
++static u64 bfqg_prfill_sectors_recursive(struct seq_file *sf,
++					 struct blkg_policy_data *pd, int off)
++{
++	struct blkg_rwstat tmp = blkg_rwstat_recursive_sum(pd->blkg, NULL,
++					offsetof(struct blkcg_gq, stat_bytes));
++	u64 sum = atomic64_read(&tmp.aux_cnt[BLKG_RWSTAT_READ]) +
++		atomic64_read(&tmp.aux_cnt[BLKG_RWSTAT_WRITE]);
++
++	return __blkg_prfill_u64(sf, pd, sum >> 9);
++}
++
++static int bfqg_print_stat_sectors_recursive(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)),
++			  bfqg_prfill_sectors_recursive, &blkcg_policy_bfq, 0,
++			  false);
++	return 0;
++}
++
++
++static u64 bfqg_prfill_avg_queue_size(struct seq_file *sf,
++				      struct blkg_policy_data *pd, int off)
++{
++	struct bfq_group *bfqg = pd_to_bfqg(pd);
++	u64 samples = blkg_stat_read(&bfqg->stats.avg_queue_size_samples);
++	u64 v = 0;
++
++	if (samples) {
++		v = blkg_stat_read(&bfqg->stats.avg_queue_size_sum);
++		v = div64_u64(v, samples);
++	}
++	__blkg_prfill_u64(sf, pd, v);
++	return 0;
++}
++
++/* print avg_queue_size */
++static int bfqg_print_avg_queue_size(struct seq_file *sf, void *v)
++{
++	blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)),
++			  bfqg_prfill_avg_queue_size, &blkcg_policy_bfq,
++			  0, false);
++	return 0;
++}
++
++static struct bfq_group *
++bfq_create_group_hierarchy(struct bfq_data *bfqd, int node)
++{
++	int ret;
++
++	ret = blkcg_activate_policy(bfqd->queue, &blkcg_policy_bfq);
++	if (ret)
++		return NULL;
++
++	return blkg_to_bfqg(bfqd->queue->root_blkg);
++}
++
++static struct cftype bfq_blkcg_legacy_files[] = {
++	{
++		.name = "bfq.weight",
++		.flags = CFTYPE_NOT_ON_ROOT,
++		.seq_show = bfq_io_show_weight,
++		.write_u64 = bfq_io_set_weight_legacy,
++	},
++
++	/* statistics, covers only the tasks in the bfqg */
++	{
++		.name = "bfq.time",
++		.private = offsetof(struct bfq_group, stats.time),
++		.seq_show = bfqg_print_stat,
++	},
++	{
++		.name = "bfq.sectors",
++		.seq_show = bfqg_print_stat_sectors,
++	},
++	{
++		.name = "bfq.io_service_bytes",
++		.private = (unsigned long)&blkcg_policy_bfq,
++		.seq_show = blkg_print_stat_bytes,
++	},
++	{
++		.name = "bfq.io_serviced",
++		.private = (unsigned long)&blkcg_policy_bfq,
++		.seq_show = blkg_print_stat_ios,
++	},
++	{
++		.name = "bfq.io_service_time",
++		.private = offsetof(struct bfq_group, stats.service_time),
++		.seq_show = bfqg_print_rwstat,
++	},
++	{
++		.name = "bfq.io_wait_time",
++		.private = offsetof(struct bfq_group, stats.wait_time),
++		.seq_show = bfqg_print_rwstat,
++	},
++	{
++		.name = "bfq.io_merged",
++		.private = offsetof(struct bfq_group, stats.merged),
++		.seq_show = bfqg_print_rwstat,
++	},
++	{
++		.name = "bfq.io_queued",
++		.private = offsetof(struct bfq_group, stats.queued),
++		.seq_show = bfqg_print_rwstat,
++	},
++
++	/* the same statictics which cover the bfqg and its descendants */
++	{
++		.name = "bfq.time_recursive",
++		.private = offsetof(struct bfq_group, stats.time),
++		.seq_show = bfqg_print_stat_recursive,
++	},
++	{
++		.name = "bfq.sectors_recursive",
++		.seq_show = bfqg_print_stat_sectors_recursive,
++	},
++	{
++		.name = "bfq.io_service_bytes_recursive",
++		.private = (unsigned long)&blkcg_policy_bfq,
++		.seq_show = blkg_print_stat_bytes_recursive,
++	},
++	{
++		.name = "bfq.io_serviced_recursive",
++		.private = (unsigned long)&blkcg_policy_bfq,
++		.seq_show = blkg_print_stat_ios_recursive,
++	},
++	{
++		.name = "bfq.io_service_time_recursive",
++		.private = offsetof(struct bfq_group, stats.service_time),
++		.seq_show = bfqg_print_rwstat_recursive,
++	},
++	{
++		.name = "bfq.io_wait_time_recursive",
++		.private = offsetof(struct bfq_group, stats.wait_time),
++		.seq_show = bfqg_print_rwstat_recursive,
++	},
++	{
++		.name = "bfq.io_merged_recursive",
++		.private = offsetof(struct bfq_group, stats.merged),
++		.seq_show = bfqg_print_rwstat_recursive,
++	},
++	{
++		.name = "bfq.io_queued_recursive",
++		.private = offsetof(struct bfq_group, stats.queued),
++		.seq_show = bfqg_print_rwstat_recursive,
++	},
++	{
++		.name = "bfq.avg_queue_size",
++		.seq_show = bfqg_print_avg_queue_size,
++	},
++	{
++		.name = "bfq.group_wait_time",
++		.private = offsetof(struct bfq_group, stats.group_wait_time),
++		.seq_show = bfqg_print_stat,
++	},
++	{
++		.name = "bfq.idle_time",
++		.private = offsetof(struct bfq_group, stats.idle_time),
++		.seq_show = bfqg_print_stat,
++	},
++	{
++		.name = "bfq.empty_time",
++		.private = offsetof(struct bfq_group, stats.empty_time),
++		.seq_show = bfqg_print_stat,
++	},
++	{
++		.name = "bfq.dequeue",
++		.private = offsetof(struct bfq_group, stats.dequeue),
++		.seq_show = bfqg_print_stat,
++	},
++	{ }	/* terminate */
++};
++
++static struct cftype bfq_blkg_files[] = {
++	{
++		.name = "bfq.weight",
++		.flags = CFTYPE_NOT_ON_ROOT,
++		.seq_show = bfq_io_show_weight,
++		.write = bfq_io_set_weight,
++	},
++	{} /* terminate */
++};
++
++#else /* CONFIG_BFQ_GROUP_IOSCHED */
++
++static inline void bfqg_stats_update_io_add(struct bfq_group *bfqg,
++			struct bfq_queue *bfqq, unsigned int op) { }
++static inline void
++bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op) { }
++static inline void
++bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op) { }
++static inline void bfqg_stats_update_completion(struct bfq_group *bfqg,
++			uint64_t start_time, uint64_t io_start_time,
++			unsigned int op) { }
++static inline void
++bfqg_stats_set_start_group_wait_time(struct bfq_group *bfqg,
++				     struct bfq_group *curr_bfqg) { }
++static inline void bfqg_stats_end_empty_time(struct bfqg_stats *stats) { }
++static inline void bfqg_stats_update_dequeue(struct bfq_group *bfqg) { }
++static inline void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg) { }
++static inline void bfqg_stats_update_idle_time(struct bfq_group *bfqg) { }
++static inline void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg) { }
++static inline void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg) { }
++
++static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++			  struct bfq_group *bfqg) {}
++
++static void bfq_init_entity(struct bfq_entity *entity,
++			    struct bfq_group *bfqg)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	entity->weight = entity->new_weight;
++	entity->orig_weight = entity->new_weight;
++	if (bfqq) {
++		bfqq->ioprio = bfqq->new_ioprio;
++		bfqq->ioprio_class = bfqq->new_ioprio_class;
++	}
++	entity->sched_data = &bfqg->sched_data;
++}
++
++static void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio) {}
++
++static void bfq_end_wr_async(struct bfq_data *bfqd)
++{
++	bfq_end_wr_async_queues(bfqd, bfqd->root_group);
++}
++
++static struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
++					    struct blkcg *blkcg)
++{
++	return bfqd->root_group;
++}
++
++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq)
++{
++	return bfqq->bfqd->root_group;
++}
++
++static struct bfq_group *
++bfq_create_group_hierarchy(struct bfq_data *bfqd, int node)
++{
++	struct bfq_group *bfqg;
++	int i;
++
++	bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node);
++	if (!bfqg)
++		return NULL;
++
++	for (i = 0; i < BFQ_IOPRIO_CLASSES; i++)
++		bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT;
++
++	return bfqg;
++}
++#endif
+diff --git a/block/bfq-ioc.c b/block/bfq-ioc.c
+new file mode 100644
+index 000000000000..fb7bb8f08b75
+--- /dev/null
++++ b/block/bfq-ioc.c
+@@ -0,0 +1,36 @@
++/*
++ * BFQ: I/O context handling.
++ *
++ * Based on ideas and code from CFQ:
++ * Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
++ *
++ * Copyright (C) 2008 Fabio Checconi <fabio@gandalf.sssup.it>
++ *		      Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2010 Paolo Valente <paolo.valente@unimore.it>
++ */
++
++/**
++ * icq_to_bic - convert iocontext queue structure to bfq_io_cq.
++ * @icq: the iocontext queue.
++ */
++static struct bfq_io_cq *icq_to_bic(struct io_cq *icq)
++{
++	/* bic->icq is the first member, %NULL will convert to %NULL */
++	return container_of(icq, struct bfq_io_cq, icq);
++}
++
++/**
++ * bfq_bic_lookup - search into @ioc a bic associated to @bfqd.
++ * @bfqd: the lookup key.
++ * @ioc: the io_context of the process doing I/O.
++ *
++ * Queue lock must be held.
++ */
++static struct bfq_io_cq *bfq_bic_lookup(struct bfq_data *bfqd,
++					struct io_context *ioc)
++{
++	if (ioc)
++		return icq_to_bic(ioc_lookup_icq(ioc, bfqd->queue));
++	return NULL;
++}
+diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
+new file mode 100644
+index 000000000000..6d1f54fb9c2b
+--- /dev/null
++++ b/block/bfq-iosched.c
+@@ -0,0 +1,5336 @@
++/*
++ * Budget Fair Queueing (BFQ) I/O scheduler.
++ *
++ * Based on ideas and code from CFQ:
++ * Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
++ *
++ * Copyright (C) 2008 Fabio Checconi <fabio@gandalf.sssup.it>
++ *		      Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2015 Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2017 Paolo Valente <paolo.valente@linaro.org>
++ *
++ * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ
++ * file.
++ *
++ * BFQ is a proportional-share I/O scheduler, with some extra
++ * low-latency capabilities. BFQ also supports full hierarchical
++ * scheduling through cgroups. Next paragraphs provide an introduction
++ * on BFQ inner workings. Details on BFQ benefits and usage can be
++ * found in Documentation/block/bfq-iosched.txt.
++ *
++ * BFQ is a proportional-share storage-I/O scheduling algorithm based
++ * on the slice-by-slice service scheme of CFQ. But BFQ assigns
++ * budgets, measured in number of sectors, to processes instead of
++ * time slices. The device is not granted to the in-service process
++ * for a given time slice, but until it has exhausted its assigned
++ * budget. This change from the time to the service domain enables BFQ
++ * to distribute the device throughput among processes as desired,
++ * without any distortion due to throughput fluctuations, or to device
++ * internal queueing. BFQ uses an ad hoc internal scheduler, called
++ * B-WF2Q+, to schedule processes according to their budgets. More
++ * precisely, BFQ schedules queues associated with processes. Thanks to
++ * the accurate policy of B-WF2Q+, BFQ can afford to assign high
++ * budgets to I/O-bound processes issuing sequential requests (to
++ * boost the throughput), and yet guarantee a low latency to
++ * interactive and soft real-time applications.
++ *
++ * BFQ is described in [1], where also a reference to the initial, more
++ * theoretical paper on BFQ can be found. The interested reader can find
++ * in the latter paper full details on the main algorithm, as well as
++ * formulas of the guarantees and formal proofs of all the properties.
++ * With respect to the version of BFQ presented in these papers, this
++ * implementation adds a few more heuristics, such as the one that
++ * guarantees a low latency to soft real-time applications, and a
++ * hierarchical extension based on H-WF2Q+.
++ *
++ * B-WF2Q+ is based on WF2Q+, that is described in [2], together with
++ * H-WF2Q+, while the augmented tree used to implement B-WF2Q+ with O(log N)
++ * complexity derives from the one introduced with EEVDF in [3].
++ *
++ * [1] P. Valente, A. Avanzini, "Evolution of the BFQ Storage I/O
++ *   Scheduler", Proceedings of the First Workshop on Mobile System
++ *   Technologies (MST-2015), May 2015.
++ *   http://algogroup.unimore.it/people/paolo/disk_sched/mst-2015.pdf
++ *
++ * http://algogroup.unimo.it/people/paolo/disk_sched/bf1-v1-suite-results.pdf
++ *
++ * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing
++ *     Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689,
++ *     Oct 1997.
++ *
++ * http://www.cs.cmu.edu/~hzhang/papers/TON-97-Oct.ps.gz
++ *
++ * [3] I. Stoica and H. Abdel-Wahab, ``Earliest Eligible Virtual Deadline
++ *     First: A Flexible and Accurate Mechanism for Proportional Share
++ *     Resource Allocation,'' technical report.
++ *
++ * http://www.cs.berkeley.edu/~istoica/papers/eevdf-tr-95.pdf
++ */
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/blkdev.h>
++#include <linux/cgroup.h>
++#include <linux/elevator.h>
++#include <linux/jiffies.h>
++#include <linux/rbtree.h>
++#include <linux/ioprio.h>
++#include "blk.h"
++#include "bfq.h"
++
++/* Expiration time of sync (0) and async (1) requests, in ns. */
++static const u64 bfq_fifo_expire[2] = { NSEC_PER_SEC / 4, NSEC_PER_SEC / 8 };
++
++/* Maximum backwards seek, in KiB. */
++static const int bfq_back_max = (16 * 1024);
++
++/* Penalty of a backwards seek, in number of sectors. */
++static const int bfq_back_penalty = 2;
++
++/* Idling period duration, in ns. */
++static u32 bfq_slice_idle = (NSEC_PER_SEC / 125);
++
++/* Minimum number of assigned budgets for which stats are safe to compute. */
++static const int bfq_stats_min_budgets = 194;
++
++/* Default maximum budget values, in sectors and number of requests. */
++static const int bfq_default_max_budget = (16 * 1024);
++
++/*
++ * Async to sync throughput distribution is controlled as follows:
++ * when an async request is served, the entity is charged the number
++ * of sectors of the request, multiplied by the factor below
++ */
++static const int bfq_async_charge_factor = 10;
++
++/* Default timeout values, in jiffies, approximating CFQ defaults. */
++static const int bfq_timeout = (HZ / 8);
++
++static struct kmem_cache *bfq_pool;
++
++/* Below this threshold (in ns), we consider thinktime immediate. */
++#define BFQ_MIN_TT		(2 * NSEC_PER_MSEC)
++
++/* hw_tag detection: parallel requests threshold and min samples needed. */
++#define BFQ_HW_QUEUE_THRESHOLD	4
++#define BFQ_HW_QUEUE_SAMPLES	32
++
++#define BFQQ_SEEK_THR		(sector_t)(8 * 100)
++#define BFQQ_SECT_THR_NONROT	(sector_t)(2 * 32)
++#define BFQQ_CLOSE_THR		(sector_t)(8 * 1024)
++#define BFQQ_SEEKY(bfqq)	(hweight32(bfqq->seek_history) > 32/8)
++
++/* Min number of samples required to perform peak-rate update */
++#define BFQ_RATE_MIN_SAMPLES	32
++/* Min observation time interval required to perform a peak-rate update (ns) */
++#define BFQ_RATE_MIN_INTERVAL	(300*NSEC_PER_MSEC)
++/* Target observation time interval for a peak-rate update (ns) */
++#define BFQ_RATE_REF_INTERVAL	NSEC_PER_SEC
++
++/* Shift used for peak rate fixed precision calculations. */
++#define BFQ_RATE_SHIFT		16
++
++/*
++ * By default, BFQ computes the duration of the weight raising for
++ * interactive applications automatically, using the following formula:
++ * duration = (R / r) * T, where r is the peak rate of the device, and
++ * R and T are two reference parameters.
++ * In particular, R is the peak rate of the reference device (see below),
++ * and T is a reference time: given the systems that are likely to be
++ * installed on the reference device according to its speed class, T is
++ * about the maximum time needed, under BFQ and while reading two files in
++ * parallel, to load typical large applications on these systems.
++ * In practice, the slower/faster the device at hand is, the more/less it
++ * takes to load applications with respect to the reference device.
++ * Accordingly, the longer/shorter BFQ grants weight raising to interactive
++ * applications.
++ *
++ * BFQ uses four different reference pairs (R, T), depending on:
++ * . whether the device is rotational or non-rotational;
++ * . whether the device is slow, such as old or portable HDDs, as well as
++ *   SD cards, or fast, such as newer HDDs and SSDs.
++ *
++ * The device's speed class is dynamically (re)detected in
++ * bfq_update_peak_rate() every time the estimated peak rate is updated.
++ *
++ * In the following definitions, R_slow[0]/R_fast[0] and
++ * T_slow[0]/T_fast[0] are the reference values for a slow/fast
++ * rotational device, whereas R_slow[1]/R_fast[1] and
++ * T_slow[1]/T_fast[1] are the reference values for a slow/fast
++ * non-rotational device. Finally, device_speed_thresh are the
++ * thresholds used to switch between speed classes. The reference
++ * rates are not the actual peak rates of the devices used as a
++ * reference, but slightly lower values. The reason for using these
++ * slightly lower values is that the peak-rate estimator tends to
++ * yield slightly lower values than the actual peak rate (it can yield
++ * the actual peak rate only if there is only one process doing I/O,
++ * and the process does sequential I/O).
++ *
++ * Both the reference peak rates and the thresholds are measured in
++ * sectors/usec, left-shifted by BFQ_RATE_SHIFT.
++ */
++static int R_slow[2] = {1000, 10700};
++static int R_fast[2] = {14000, 33000};
++/*
++ * To improve readability, a conversion function is used to initialize the
++ * following arrays, which entails that they can be initialized only in a
++ * function.
++ */
++static int T_slow[2];
++static int T_fast[2];
++static int device_speed_thresh[2];
++
++#define BFQ_SERVICE_TREE_INIT	((struct bfq_service_tree)		\
++				{ RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 })
++
++#define RQ_BIC(rq)		((struct bfq_io_cq *) (rq)->elv.priv[0])
++#define RQ_BFQQ(rq)		((rq)->elv.priv[1])
++
++static void bfq_schedule_dispatch(struct bfq_data *bfqd);
++
++#include "bfq-ioc.c"
++#include "bfq-sched.c"
++#include "bfq-cgroup.c"
++
++#define bfq_class_idle(bfqq)	((bfqq)->ioprio_class == IOPRIO_CLASS_IDLE)
++#define bfq_class_rt(bfqq)	((bfqq)->ioprio_class == IOPRIO_CLASS_RT)
++
++#define bfq_sample_valid(samples)	((samples) > 80)
++
++/*
++ * Scheduler run of queue, if there are requests pending and no one in the
++ * driver that will restart queueing.
++ */
++static void bfq_schedule_dispatch(struct bfq_data *bfqd)
++{
++	if (bfqd->queued != 0) {
++		bfq_log(bfqd, "schedule dispatch");
++		kblockd_schedule_work(&bfqd->unplug_work);
++	}
++}
++
++/*
++ * Lifted from AS - choose which of rq1 and rq2 that is best served now.
++ * We choose the request that is closesr to the head right now.  Distance
++ * behind the head is penalized and only allowed to a certain extent.
++ */
++static struct request *bfq_choose_req(struct bfq_data *bfqd,
++				      struct request *rq1,
++				      struct request *rq2,
++				      sector_t last)
++{
++	sector_t s1, s2, d1 = 0, d2 = 0;
++	unsigned long back_max;
++#define BFQ_RQ1_WRAP	0x01 /* request 1 wraps */
++#define BFQ_RQ2_WRAP	0x02 /* request 2 wraps */
++	unsigned int wrap = 0; /* bit mask: requests behind the disk head? */
++
++	if (!rq1 || rq1 == rq2)
++		return rq2;
++	if (!rq2)
++		return rq1;
++
++	if (rq_is_sync(rq1) && !rq_is_sync(rq2))
++		return rq1;
++	else if (rq_is_sync(rq2) && !rq_is_sync(rq1))
++		return rq2;
++	if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META))
++		return rq1;
++	else if ((rq2->cmd_flags & REQ_META) && !(rq1->cmd_flags & REQ_META))
++		return rq2;
++
++	s1 = blk_rq_pos(rq1);
++	s2 = blk_rq_pos(rq2);
++
++	/*
++	 * By definition, 1KiB is 2 sectors.
++	 */
++	back_max = bfqd->bfq_back_max * 2;
++
++	/*
++	 * Strict one way elevator _except_ in the case where we allow
++	 * short backward seeks which are biased as twice the cost of a
++	 * similar forward seek.
++	 */
++	if (s1 >= last)
++		d1 = s1 - last;
++	else if (s1 + back_max >= last)
++		d1 = (last - s1) * bfqd->bfq_back_penalty;
++	else
++		wrap |= BFQ_RQ1_WRAP;
++
++	if (s2 >= last)
++		d2 = s2 - last;
++	else if (s2 + back_max >= last)
++		d2 = (last - s2) * bfqd->bfq_back_penalty;
++	else
++		wrap |= BFQ_RQ2_WRAP;
++
++	/* Found required data */
++
++	/*
++	 * By doing switch() on the bit mask "wrap" we avoid having to
++	 * check two variables for all permutations: --> faster!
++	 */
++	switch (wrap) {
++	case 0: /* common case for CFQ: rq1 and rq2 not wrapped */
++		if (d1 < d2)
++			return rq1;
++		else if (d2 < d1)
++			return rq2;
++
++		if (s1 >= s2)
++			return rq1;
++		else
++			return rq2;
++
++	case BFQ_RQ2_WRAP:
++		return rq1;
++	case BFQ_RQ1_WRAP:
++		return rq2;
++	case (BFQ_RQ1_WRAP|BFQ_RQ2_WRAP): /* both rqs wrapped */
++	default:
++		/*
++		 * Since both rqs are wrapped,
++		 * start with the one that's further behind head
++		 * (--> only *one* back seek required),
++		 * since back seek takes more time than forward.
++		 */
++		if (s1 <= s2)
++			return rq1;
++		else
++			return rq2;
++	}
++}
++
++static struct bfq_queue *
++bfq_rq_pos_tree_lookup(struct bfq_data *bfqd, struct rb_root *root,
++		     sector_t sector, struct rb_node **ret_parent,
++		     struct rb_node ***rb_link)
++{
++	struct rb_node **p, *parent;
++	struct bfq_queue *bfqq = NULL;
++
++	parent = NULL;
++	p = &root->rb_node;
++	while (*p) {
++		struct rb_node **n;
++
++		parent = *p;
++		bfqq = rb_entry(parent, struct bfq_queue, pos_node);
++
++		/*
++		 * Sort strictly based on sector. Smallest to the left,
++		 * largest to the right.
++		 */
++		if (sector > blk_rq_pos(bfqq->next_rq))
++			n = &(*p)->rb_right;
++		else if (sector < blk_rq_pos(bfqq->next_rq))
++			n = &(*p)->rb_left;
++		else
++			break;
++		p = n;
++		bfqq = NULL;
++	}
++
++	*ret_parent = parent;
++	if (rb_link)
++		*rb_link = p;
++
++	bfq_log(bfqd, "rq_pos_tree_lookup %llu: returning %d",
++		(unsigned long long) sector,
++		bfqq ? bfqq->pid : 0);
++
++	return bfqq;
++}
++
++static void bfq_pos_tree_add_move(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	struct rb_node **p, *parent;
++	struct bfq_queue *__bfqq;
++
++	if (bfqq->pos_root) {
++		rb_erase(&bfqq->pos_node, bfqq->pos_root);
++		bfqq->pos_root = NULL;
++	}
++
++	if (bfq_class_idle(bfqq))
++		return;
++	if (!bfqq->next_rq)
++		return;
++
++	bfqq->pos_root = &bfq_bfqq_to_bfqg(bfqq)->rq_pos_tree;
++	__bfqq = bfq_rq_pos_tree_lookup(bfqd, bfqq->pos_root,
++			blk_rq_pos(bfqq->next_rq), &parent, &p);
++	if (!__bfqq) {
++		rb_link_node(&bfqq->pos_node, parent, p);
++		rb_insert_color(&bfqq->pos_node, bfqq->pos_root);
++	} else
++		bfqq->pos_root = NULL;
++}
++
++/*
++ * Tell whether there are active queues or groups with differentiated weights.
++ */
++static bool bfq_differentiated_weights(struct bfq_data *bfqd)
++{
++	/*
++	 * For weights to differ, at least one of the trees must contain
++	 * at least two nodes.
++	 */
++	return (!RB_EMPTY_ROOT(&bfqd->queue_weights_tree) &&
++		(bfqd->queue_weights_tree.rb_node->rb_left ||
++		 bfqd->queue_weights_tree.rb_node->rb_right)
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	       ) ||
++	       (!RB_EMPTY_ROOT(&bfqd->group_weights_tree) &&
++		(bfqd->group_weights_tree.rb_node->rb_left ||
++		 bfqd->group_weights_tree.rb_node->rb_right)
++#endif
++	       );
++}
++
++/*
++ * The following function returns true if every queue must receive the
++ * same share of the throughput (this condition is used when deciding
++ * whether idling may be disabled, see the comments in the function
++ * bfq_bfqq_may_idle()).
++ *
++ * Such a scenario occurs when:
++ * 1) all active queues have the same weight,
++ * 2) all active groups at the same level in the groups tree have the same
++ *    weight,
++ * 3) all active groups at the same level in the groups tree have the same
++ *    number of children.
++ *
++ * Unfortunately, keeping the necessary state for evaluating exactly the
++ * above symmetry conditions would be quite complex and time-consuming.
++ * Therefore this function evaluates, instead, the following stronger
++ * sub-conditions, for which it is much easier to maintain the needed
++ * state:
++ * 1) all active queues have the same weight,
++ * 2) all active groups have the same weight,
++ * 3) all active groups have at most one active child each.
++ * In particular, the last two conditions are always true if hierarchical
++ * support and the cgroups interface are not enabled, thus no state needs
++ * to be maintained in this case.
++ */
++static bool bfq_symmetric_scenario(struct bfq_data *bfqd)
++{
++	return !bfq_differentiated_weights(bfqd);
++}
++
++/*
++ * If the weight-counter tree passed as input contains no counter for
++ * the weight of the input entity, then add that counter; otherwise just
++ * increment the existing counter.
++ *
++ * Note that weight-counter trees contain few nodes in mostly symmetric
++ * scenarios. For example, if all queues have the same weight, then the
++ * weight-counter tree for the queues may contain at most one node.
++ * This holds even if low_latency is on, because weight-raised queues
++ * are not inserted in the tree.
++ * In most scenarios, the rate at which nodes are created/destroyed
++ * should be low too.
++ */
++static void bfq_weights_tree_add(struct bfq_data *bfqd,
++				 struct bfq_entity *entity,
++				 struct rb_root *root)
++{
++	struct rb_node **new = &(root->rb_node), *parent = NULL;
++
++	/*
++	 * Do not insert if the entity is already associated with a
++	 * counter, which happens if:
++	 *   1) the entity is associated with a queue,
++	 *   2) a request arrival has caused the queue to become both
++	 *      non-weight-raised, and hence change its weight, and
++	 *      backlogged; in this respect, each of the two events
++	 *      causes an invocation of this function,
++	 *   3) this is the invocation of this function caused by the
++	 *      second event. This second invocation is actually useless,
++	 *      and we handle this fact by exiting immediately. More
++	 *      efficient or clearer solutions might possibly be adopted.
++	 */
++	if (entity->weight_counter)
++		return;
++
++	while (*new) {
++		struct bfq_weight_counter *__counter = container_of(*new,
++						struct bfq_weight_counter,
++						weights_node);
++		parent = *new;
++
++		if (entity->weight == __counter->weight) {
++			entity->weight_counter = __counter;
++			goto inc_counter;
++		}
++		if (entity->weight < __counter->weight)
++			new = &((*new)->rb_left);
++		else
++			new = &((*new)->rb_right);
++	}
++
++	entity->weight_counter = kzalloc(sizeof(struct bfq_weight_counter),
++					 GFP_ATOMIC);
++
++	/*
++	 * In the unlucky event of an allocation failure, we just
++	 * exit. This will cause the weight of entity to not be
++	 * considered in bfq_differentiated_weights, which, in its
++	 * turn, causes the scenario to be deemed wrongly symmetric in
++	 * case entity's weight would have been the only weight making
++	 * the scenario asymmetric. On the bright side, no unbalance
++	 * will however occur when entity becomes inactive again (the
++	 * invocation of this function is triggered by an activation
++	 * of entity). In fact, bfq_weights_tree_remove does nothing
++	 * if !entity->weight_counter.
++	 */
++	if (unlikely(!entity->weight_counter))
++		return;
++
++	entity->weight_counter->weight = entity->weight;
++	rb_link_node(&entity->weight_counter->weights_node, parent, new);
++	rb_insert_color(&entity->weight_counter->weights_node, root);
++
++inc_counter:
++	entity->weight_counter->num_active++;
++}
++
++/*
++ * Decrement the weight counter associated with the entity, and, if the
++ * counter reaches 0, remove the counter from the tree.
++ * See the comments to the function bfq_weights_tree_add() for considerations
++ * about overhead.
++ */
++static void bfq_weights_tree_remove(struct bfq_data *bfqd,
++				    struct bfq_entity *entity,
++				    struct rb_root *root)
++{
++	if (!entity->weight_counter)
++		return;
++
++	BUG_ON(RB_EMPTY_ROOT(root));
++	BUG_ON(entity->weight_counter->weight != entity->weight);
++
++	BUG_ON(!entity->weight_counter->num_active);
++	entity->weight_counter->num_active--;
++	if (entity->weight_counter->num_active > 0)
++		goto reset_entity_pointer;
++
++	rb_erase(&entity->weight_counter->weights_node, root);
++	kfree(entity->weight_counter);
++
++reset_entity_pointer:
++	entity->weight_counter = NULL;
++}
++
++/*
++ * Return expired entry, or NULL to just start from scratch in rbtree.
++ */
++static struct request *bfq_check_fifo(struct bfq_queue *bfqq,
++				      struct request *last)
++{
++	struct request *rq;
++
++	if (bfq_bfqq_fifo_expire(bfqq))
++		return NULL;
++
++	bfq_mark_bfqq_fifo_expire(bfqq);
++
++	rq = rq_entry_fifo(bfqq->fifo.next);
++
++	if (rq == last || ktime_get_ns() < rq->fifo_time)
++		return NULL;
++
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "check_fifo: returned %p", rq);
++	BUG_ON(RB_EMPTY_NODE(&rq->rb_node));
++	return rq;
++}
++
++static struct request *bfq_find_next_rq(struct bfq_data *bfqd,
++					struct bfq_queue *bfqq,
++					struct request *last)
++{
++	struct rb_node *rbnext = rb_next(&last->rb_node);
++	struct rb_node *rbprev = rb_prev(&last->rb_node);
++	struct request *next, *prev = NULL;
++
++	BUG_ON(list_empty(&bfqq->fifo));
++
++	/* Follow expired path, else get first next available. */
++	next = bfq_check_fifo(bfqq, last);
++	if (next) {
++		BUG_ON(next == last);
++		return next;
++	}
++
++	BUG_ON(RB_EMPTY_NODE(&last->rb_node));
++
++	if (rbprev)
++		prev = rb_entry_rq(rbprev);
++
++	if (rbnext)
++		next = rb_entry_rq(rbnext);
++	else {
++		rbnext = rb_first(&bfqq->sort_list);
++		if (rbnext && rbnext != &last->rb_node)
++			next = rb_entry_rq(rbnext);
++	}
++
++	return bfq_choose_req(bfqd, next, prev, blk_rq_pos(last));
++}
++
++/* see the definition of bfq_async_charge_factor for details */
++static unsigned long bfq_serv_to_charge(struct request *rq,
++					struct bfq_queue *bfqq)
++{
++	if (bfq_bfqq_sync(bfqq) || bfqq->wr_coeff > 1)
++		return blk_rq_sectors(rq);
++
++	/*
++	 * If there are no weight-raised queues, then amplify service
++	 * by just the async charge factor; otherwise amplify service
++	 * by twice the async charge factor, to further reduce latency
++	 * for weight-raised queues.
++	 */
++	if (bfqq->bfqd->wr_busy_queues == 0)
++		return blk_rq_sectors(rq) * bfq_async_charge_factor;
++
++	return blk_rq_sectors(rq) * 2 * bfq_async_charge_factor;
++}
++
++/**
++ * bfq_updated_next_req - update the queue after a new next_rq selection.
++ * @bfqd: the device data the queue belongs to.
++ * @bfqq: the queue to update.
++ *
++ * If the first request of a queue changes we make sure that the queue
++ * has enough budget to serve at least its first request (if the
++ * request has grown).  We do this because if the queue has not enough
++ * budget for its first request, it has to go through two dispatch
++ * rounds to actually get it dispatched.
++ */
++static void bfq_updated_next_req(struct bfq_data *bfqd,
++				 struct bfq_queue *bfqq)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++	struct request *next_rq = bfqq->next_rq;
++	unsigned long new_budget;
++
++	if (!next_rq)
++		return;
++
++	if (bfqq == bfqd->in_service_queue)
++		/*
++		 * In order not to break guarantees, budgets cannot be
++		 * changed after an entity has been selected.
++		 */
++		return;
++
++	BUG_ON(entity->tree != &st->active);
++	BUG_ON(entity == entity->sched_data->in_service_entity);
++
++	new_budget = max_t(unsigned long, bfqq->max_budget,
++			   bfq_serv_to_charge(next_rq, bfqq));
++	if (entity->budget != new_budget) {
++		entity->budget = new_budget;
++		bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu",
++					 new_budget);
++		bfq_requeue_bfqq(bfqd, bfqq);
++	}
++}
++
++static unsigned int bfq_wr_duration(struct bfq_data *bfqd)
++{
++	u64 dur;
++
++	if (bfqd->bfq_wr_max_time > 0)
++		return bfqd->bfq_wr_max_time;
++
++	dur = bfqd->RT_prod;
++	do_div(dur, bfqd->peak_rate);
++
++	/*
++	 * Limit duration between 3 and 13 seconds. Tests show that
++	 * higher values than 13 seconds often yield the opposite of
++	 * the desired result, i.e., worsen responsiveness by letting
++	 * non-interactive and non-soft-real-time applications
++	 * preserve weight raising for a too long time interval.
++	 *
++	 * On the other end, lower values than 3 seconds make it
++	 * difficult for most interactive tasks to complete their jobs
++	 * before weight-raising finishes.
++	 */
++	if (dur > msecs_to_jiffies(13000))
++		dur = msecs_to_jiffies(13000);
++	else if (dur < msecs_to_jiffies(3000))
++		dur = msecs_to_jiffies(3000);
++
++	return dur;
++}
++
++static void
++bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_io_cq *bic)
++{
++	if (bic->saved_idle_window)
++		bfq_mark_bfqq_idle_window(bfqq);
++	else
++		bfq_clear_bfqq_idle_window(bfqq);
++
++	if (bic->saved_IO_bound)
++		bfq_mark_bfqq_IO_bound(bfqq);
++	else
++		bfq_clear_bfqq_IO_bound(bfqq);
++
++	bfqq->wr_coeff = bic->saved_wr_coeff;
++	bfqq->wr_start_at_switch_to_srt = bic->saved_wr_start_at_switch_to_srt;
++	BUG_ON(time_is_after_jiffies(bfqq->wr_start_at_switch_to_srt));
++	bfqq->last_wr_start_finish = bic->saved_last_wr_start_finish;
++	bfqq->wr_cur_max_time = bic->saved_wr_cur_max_time;
++	BUG_ON(time_is_after_jiffies(bfqq->last_wr_start_finish));
++
++	if (bfqq->wr_coeff > 1 && (bfq_bfqq_in_large_burst(bfqq) ||
++	    time_is_before_jiffies(bfqq->last_wr_start_finish +
++				   bfqq->wr_cur_max_time))) {
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			     "resume state: switching off wr (%lu + %lu < %lu)",
++			     bfqq->last_wr_start_finish, bfqq->wr_cur_max_time,
++			     jiffies);
++
++		bfqq->wr_coeff = 1;
++	}
++	/* make sure weight will be updated, however we got here */
++	bfqq->entity.prio_changed = 1;
++}
++
++static int bfqq_process_refs(struct bfq_queue *bfqq)
++{
++	int process_refs, io_refs;
++
++	lockdep_assert_held(bfqq->bfqd->queue->queue_lock);
++
++	io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE];
++	process_refs = bfqq->ref - io_refs - bfqq->entity.on_st;
++	BUG_ON(process_refs < 0);
++	return process_refs;
++}
++
++/* Empty burst list and add just bfqq (see comments to bfq_handle_burst) */
++static void bfq_reset_burst_list(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	struct bfq_queue *item;
++	struct hlist_node *n;
++
++	hlist_for_each_entry_safe(item, n, &bfqd->burst_list, burst_list_node)
++		hlist_del_init(&item->burst_list_node);
++	hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list);
++	bfqd->burst_size = 1;
++	bfqd->burst_parent_entity = bfqq->entity.parent;
++}
++
++/* Add bfqq to the list of queues in current burst (see bfq_handle_burst) */
++static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	/* Increment burst size to take into account also bfqq */
++	bfqd->burst_size++;
++
++	bfq_log_bfqq(bfqd, bfqq, "add_to_burst %d", bfqd->burst_size);
++
++	BUG_ON(bfqd->burst_size > bfqd->bfq_large_burst_thresh);
++
++	if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) {
++		struct bfq_queue *pos, *bfqq_item;
++		struct hlist_node *n;
++
++		/*
++		 * Enough queues have been activated shortly after each
++		 * other to consider this burst as large.
++		 */
++		bfqd->large_burst = true;
++		bfq_log_bfqq(bfqd, bfqq, "add_to_burst: large burst started");
++
++		/*
++		 * We can now mark all queues in the burst list as
++		 * belonging to a large burst.
++		 */
++		hlist_for_each_entry(bfqq_item, &bfqd->burst_list,
++				     burst_list_node) {
++			bfq_mark_bfqq_in_large_burst(bfqq_item);
++			bfq_log_bfqq(bfqd, bfqq_item, "marked in large burst");
++		}
++		bfq_mark_bfqq_in_large_burst(bfqq);
++		bfq_log_bfqq(bfqd, bfqq, "marked in large burst");
++
++		/*
++		 * From now on, and until the current burst finishes, any
++		 * new queue being activated shortly after the last queue
++		 * was inserted in the burst can be immediately marked as
++		 * belonging to a large burst. So the burst list is not
++		 * needed any more. Remove it.
++		 */
++		hlist_for_each_entry_safe(pos, n, &bfqd->burst_list,
++					  burst_list_node)
++			hlist_del_init(&pos->burst_list_node);
++	} else /*
++		* Burst not yet large: add bfqq to the burst list. Do
++		* not increment the ref counter for bfqq, because bfqq
++		* is removed from the burst list before freeing bfqq
++		* in put_queue.
++		*/
++		hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list);
++}
++
++/*
++ * If many queues belonging to the same group happen to be created
++ * shortly after each other, then the processes associated with these
++ * queues have typically a common goal. In particular, bursts of queue
++ * creations are usually caused by services or applications that spawn
++ * many parallel threads/processes. Examples are systemd during boot,
++ * or git grep. To help these processes get their job done as soon as
++ * possible, it is usually better to not grant either weight-raising
++ * or device idling to their queues.
++ *
++ * In this comment we describe, firstly, the reasons why this fact
++ * holds, and, secondly, the next function, which implements the main
++ * steps needed to properly mark these queues so that they can then be
++ * treated in a different way.
++ *
++ * The above services or applications benefit mostly from a high
++ * throughput: the quicker the requests of the activated queues are
++ * cumulatively served, the sooner the target job of these queues gets
++ * completed. As a consequence, weight-raising any of these queues,
++ * which also implies idling the device for it, is almost always
++ * counterproductive. In most cases it just lowers throughput.
++ *
++ * On the other hand, a burst of queue creations may be caused also by
++ * the start of an application that does not consist of a lot of
++ * parallel I/O-bound threads. In fact, with a complex application,
++ * several short processes may need to be executed to start-up the
++ * application. In this respect, to start an application as quickly as
++ * possible, the best thing to do is in any case to privilege the I/O
++ * related to the application with respect to all other
++ * I/O. Therefore, the best strategy to start as quickly as possible
++ * an application that causes a burst of queue creations is to
++ * weight-raise all the queues created during the burst. This is the
++ * exact opposite of the best strategy for the other type of bursts.
++ *
++ * In the end, to take the best action for each of the two cases, the
++ * two types of bursts need to be distinguished. Fortunately, this
++ * seems relatively easy, by looking at the sizes of the bursts. In
++ * particular, we found a threshold such that only bursts with a
++ * larger size than that threshold are apparently caused by
++ * services or commands such as systemd or git grep. For brevity,
++ * hereafter we call just 'large' these bursts. BFQ *does not*
++ * weight-raise queues whose creation occurs in a large burst. In
++ * addition, for each of these queues BFQ performs or does not perform
++ * idling depending on which choice boosts the throughput more. The
++ * exact choice depends on the device and request pattern at
++ * hand.
++ *
++ * Unfortunately, false positives may occur while an interactive task
++ * is starting (e.g., an application is being started). The
++ * consequence is that the queues associated with the task do not
++ * enjoy weight raising as expected. Fortunately these false positives
++ * are very rare. They typically occur if some service happens to
++ * start doing I/O exactly when the interactive task starts.
++ *
++ * Turning back to the next function, it implements all the steps
++ * needed to detect the occurrence of a large burst and to properly
++ * mark all the queues belonging to it (so that they can then be
++ * treated in a different way). This goal is achieved by maintaining a
++ * "burst list" that holds, temporarily, the queues that belong to the
++ * burst in progress. The list is then used to mark these queues as
++ * belonging to a large burst if the burst does become large. The main
++ * steps are the following.
++ *
++ * . when the very first queue is created, the queue is inserted into the
++ *   list (as it could be the first queue in a possible burst)
++ *
++ * . if the current burst has not yet become large, and a queue Q that does
++ *   not yet belong to the burst is activated shortly after the last time
++ *   at which a new queue entered the burst list, then the function appends
++ *   Q to the burst list
++ *
++ * . if, as a consequence of the previous step, the burst size reaches
++ *   the large-burst threshold, then
++ *
++ *     . all the queues in the burst list are marked as belonging to a
++ *       large burst
++ *
++ *     . the burst list is deleted; in fact, the burst list already served
++ *       its purpose (keeping temporarily track of the queues in a burst,
++ *       so as to be able to mark them as belonging to a large burst in the
++ *       previous sub-step), and now is not needed any more
++ *
++ *     . the device enters a large-burst mode
++ *
++ * . if a queue Q that does not belong to the burst is created while
++ *   the device is in large-burst mode and shortly after the last time
++ *   at which a queue either entered the burst list or was marked as
++ *   belonging to the current large burst, then Q is immediately marked
++ *   as belonging to a large burst.
++ *
++ * . if a queue Q that does not belong to the burst is created a while
++ *   later, i.e., not shortly after, than the last time at which a queue
++ *   either entered the burst list or was marked as belonging to the
++ *   current large burst, then the current burst is deemed as finished and:
++ *
++ *        . the large-burst mode is reset if set
++ *
++ *        . the burst list is emptied
++ *
++ *        . Q is inserted in the burst list, as Q may be the first queue
++ *          in a possible new burst (then the burst list contains just Q
++ *          after this step).
++ */
++static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	/*
++	 * If bfqq is already in the burst list or is part of a large
++	 * burst, or finally has just been split, then there is
++	 * nothing else to do.
++	 */
++	if (!hlist_unhashed(&bfqq->burst_list_node) ||
++	    bfq_bfqq_in_large_burst(bfqq) ||
++	    time_is_after_eq_jiffies(bfqq->split_time +
++				     msecs_to_jiffies(10)))
++		return;
++
++	/*
++	 * If bfqq's creation happens late enough, or bfqq belongs to
++	 * a different group than the burst group, then the current
++	 * burst is finished, and related data structures must be
++	 * reset.
++	 *
++	 * In this respect, consider the special case where bfqq is
++	 * the very first queue created after BFQ is selected for this
++	 * device. In this case, last_ins_in_burst and
++	 * burst_parent_entity are not yet significant when we get
++	 * here. But it is easy to verify that, whether or not the
++	 * following condition is true, bfqq will end up being
++	 * inserted into the burst list. In particular the list will
++	 * happen to contain only bfqq. And this is exactly what has
++	 * to happen, as bfqq may be the first queue of the first
++	 * burst.
++	 */
++	if (time_is_before_jiffies(bfqd->last_ins_in_burst +
++	    bfqd->bfq_burst_interval) ||
++	    bfqq->entity.parent != bfqd->burst_parent_entity) {
++		bfqd->large_burst = false;
++		bfq_reset_burst_list(bfqd, bfqq);
++		bfq_log_bfqq(bfqd, bfqq,
++			"handle_burst: late activation or different group");
++		goto end;
++	}
++
++	/*
++	 * If we get here, then bfqq is being activated shortly after the
++	 * last queue. So, if the current burst is also large, we can mark
++	 * bfqq as belonging to this large burst immediately.
++	 */
++	if (bfqd->large_burst) {
++		bfq_log_bfqq(bfqd, bfqq, "handle_burst: marked in burst");
++		bfq_mark_bfqq_in_large_burst(bfqq);
++		goto end;
++	}
++
++	/*
++	 * If we get here, then a large-burst state has not yet been
++	 * reached, but bfqq is being activated shortly after the last
++	 * queue. Then we add bfqq to the burst.
++	 */
++	bfq_add_to_burst(bfqd, bfqq);
++end:
++	/*
++	 * At this point, bfqq either has been added to the current
++	 * burst or has caused the current burst to terminate and a
++	 * possible new burst to start. In particular, in the second
++	 * case, bfqq has become the first queue in the possible new
++	 * burst.  In both cases last_ins_in_burst needs to be moved
++	 * forward.
++	 */
++	bfqd->last_ins_in_burst = jiffies;
++
++}
++
++static int bfq_bfqq_budget_left(struct bfq_queue *bfqq)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	return entity->budget - entity->service;
++}
++
++/*
++ * If enough samples have been computed, return the current max budget
++ * stored in bfqd, which is dynamically updated according to the
++ * estimated disk peak rate; otherwise return the default max budget
++ */
++static int bfq_max_budget(struct bfq_data *bfqd)
++{
++	if (bfqd->budgets_assigned < bfq_stats_min_budgets)
++		return bfq_default_max_budget;
++	else
++		return bfqd->bfq_max_budget;
++}
++
++/*
++ * Return min budget, which is a fraction of the current or default
++ * max budget (trying with 1/32)
++ */
++static int bfq_min_budget(struct bfq_data *bfqd)
++{
++	if (bfqd->budgets_assigned < bfq_stats_min_budgets)
++		return bfq_default_max_budget / 32;
++	else
++		return bfqd->bfq_max_budget / 32;
++}
++
++static void bfq_bfqq_expire(struct bfq_data *bfqd,
++			    struct bfq_queue *bfqq,
++			    bool compensate,
++			    enum bfqq_expiration reason);
++
++/*
++ * The next function, invoked after the input queue bfqq switches from
++ * idle to busy, updates the budget of bfqq. The function also tells
++ * whether the in-service queue should be expired, by returning
++ * true. The purpose of expiring the in-service queue is to give bfqq
++ * the chance to possibly preempt the in-service queue, and the reason
++ * for preempting the in-service queue is to achieve one of the two
++ * goals below.
++ *
++ * 1. Guarantee to bfqq its reserved bandwidth even if bfqq has
++ * expired because it has remained idle. In particular, bfqq may have
++ * expired for one of the following two reasons:
++ *
++ * - BFQ_BFQQ_NO_MORE_REQUEST bfqq did not enjoy any device idling and
++ *   did not make it to issue a new request before its last request
++ *   was served;
++ *
++ * - BFQ_BFQQ_TOO_IDLE bfqq did enjoy device idling, but did not issue
++ *   a new request before the expiration of the idling-time.
++ *
++ * Even if bfqq has expired for one of the above reasons, the process
++ * associated with the queue may be however issuing requests greedily,
++ * and thus be sensitive to the bandwidth it receives (bfqq may have
++ * remained idle for other reasons: CPU high load, bfqq not enjoying
++ * idling, I/O throttling somewhere in the path from the process to
++ * the I/O scheduler, ...). But if, after every expiration for one of
++ * the above two reasons, bfqq has to wait for the service of at least
++ * one full budget of another queue before being served again, then
++ * bfqq is likely to get a much lower bandwidth or resource time than
++ * its reserved ones. To address this issue, two countermeasures need
++ * to be taken.
++ *
++ * First, the budget and the timestamps of bfqq need to be updated in
++ * a special way on bfqq reactivation: they need to be updated as if
++ * bfqq did not remain idle and did not expire. In fact, if they are
++ * computed as if bfqq expired and remained idle until reactivation,
++ * then the process associated with bfqq is treated as if, instead of
++ * being greedy, it stopped issuing requests when bfqq remained idle,
++ * and restarts issuing requests only on this reactivation. In other
++ * words, the scheduler does not help the process recover the "service
++ * hole" between bfqq expiration and reactivation. As a consequence,
++ * the process receives a lower bandwidth than its reserved one. In
++ * contrast, to recover this hole, the budget must be updated as if
++ * bfqq was not expired at all before this reactivation, i.e., it must
++ * be set to the value of the remaining budget when bfqq was
++ * expired. Along the same line, timestamps need to be assigned the
++ * value they had the last time bfqq was selected for service, i.e.,
++ * before last expiration. Thus timestamps need to be back-shifted
++ * with respect to their normal computation (see [1] for more details
++ * on this tricky aspect).
++ *
++ * Secondly, to allow the process to recover the hole, the in-service
++ * queue must be expired too, to give bfqq the chance to preempt it
++ * immediately. In fact, if bfqq has to wait for a full budget of the
++ * in-service queue to be completed, then it may become impossible to
++ * let the process recover the hole, even if the back-shifted
++ * timestamps of bfqq are lower than those of the in-service queue. If
++ * this happens for most or all of the holes, then the process may not
++ * receive its reserved bandwidth. In this respect, it is worth noting
++ * that, being the service of outstanding requests unpreemptible, a
++ * little fraction of the holes may however be unrecoverable, thereby
++ * causing a little loss of bandwidth.
++ *
++ * The last important point is detecting whether bfqq does need this
++ * bandwidth recovery. In this respect, the next function deems the
++ * process associated with bfqq greedy, and thus allows it to recover
++ * the hole, if: 1) the process is waiting for the arrival of a new
++ * request (which implies that bfqq expired for one of the above two
++ * reasons), and 2) such a request has arrived soon. The first
++ * condition is controlled through the flag non_blocking_wait_rq,
++ * while the second through the flag arrived_in_time. If both
++ * conditions hold, then the function computes the budget in the
++ * above-described special way, and signals that the in-service queue
++ * should be expired. Timestamp back-shifting is done later in
++ * __bfq_activate_entity.
++ *
++ * 2. Reduce latency. Even if timestamps are not backshifted to let
++ * the process associated with bfqq recover a service hole, bfqq may
++ * however happen to have, after being (re)activated, a lower finish
++ * timestamp than the in-service queue.  That is, the next budget of
++ * bfqq may have to be completed before the one of the in-service
++ * queue. If this is the case, then preempting the in-service queue
++ * allows this goal to be achieved, apart from the unpreemptible,
++ * outstanding requests mentioned above.
++ *
++ * Unfortunately, regardless of which of the above two goals one wants
++ * to achieve, service trees need first to be updated to know whether
++ * the in-service queue must be preempted. To have service trees
++ * correctly updated, the in-service queue must be expired and
++ * rescheduled, and bfqq must be scheduled too. This is one of the
++ * most costly operations (in future versions, the scheduling
++ * mechanism may be re-designed in such a way to make it possible to
++ * know whether preemption is needed without needing to update service
++ * trees). In addition, queue preemptions almost always cause random
++ * I/O, and thus loss of throughput. Because of these facts, the next
++ * function adopts the following simple scheme to avoid both costly
++ * operations and too frequent preemptions: it requests the expiration
++ * of the in-service queue (unconditionally) only for queues that need
++ * to recover a hole, or that either are weight-raised or deserve to
++ * be weight-raised.
++ */
++static bool bfq_bfqq_update_budg_for_activation(struct bfq_data *bfqd,
++						struct bfq_queue *bfqq,
++						bool arrived_in_time,
++						bool wr_or_deserves_wr)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	if (bfq_bfqq_non_blocking_wait_rq(bfqq) && arrived_in_time) {
++		/*
++		 * We do not clear the flag non_blocking_wait_rq here, as
++		 * the latter is used in bfq_activate_bfqq to signal
++		 * that timestamps need to be back-shifted (and is
++		 * cleared right after).
++		 */
++
++		/*
++		 * In next assignment we rely on that either
++		 * entity->service or entity->budget are not updated
++		 * on expiration if bfqq is empty (see
++		 * __bfq_bfqq_recalc_budget). Thus both quantities
++		 * remain unchanged after such an expiration, and the
++		 * following statement therefore assigns to
++		 * entity->budget the remaining budget on such an
++		 * expiration. For clarity, entity->service is not
++		 * updated on expiration in any case, and, in normal
++		 * operation, is reset only when bfqq is selected for
++		 * service (see bfq_get_next_queue).
++		 */
++		BUG_ON(bfqq->max_budget < 0);
++		entity->budget = min_t(unsigned long,
++				       bfq_bfqq_budget_left(bfqq),
++				       bfqq->max_budget);
++
++		BUG_ON(entity->budget < 0);
++		return true;
++	}
++
++	BUG_ON(bfqq->max_budget < 0);
++	entity->budget = max_t(unsigned long, bfqq->max_budget,
++			       bfq_serv_to_charge(bfqq->next_rq, bfqq));
++	BUG_ON(entity->budget < 0);
++
++	bfq_clear_bfqq_non_blocking_wait_rq(bfqq);
++	return wr_or_deserves_wr;
++}
++
++static void bfq_update_bfqq_wr_on_rq_arrival(struct bfq_data *bfqd,
++					     struct bfq_queue *bfqq,
++					     unsigned int old_wr_coeff,
++					     bool wr_or_deserves_wr,
++					     bool interactive,
++					     bool in_burst,
++					     bool soft_rt)
++{
++	if (old_wr_coeff == 1 && wr_or_deserves_wr) {
++		/* start a weight-raising period */
++		if (interactive) {
++			bfqq->wr_coeff = bfqd->bfq_wr_coeff;
++			bfqq->wr_cur_max_time = bfq_wr_duration(bfqd);
++		} else {
++			bfqq->wr_start_at_switch_to_srt = jiffies;
++			bfqq->wr_coeff = bfqd->bfq_wr_coeff *
++				BFQ_SOFTRT_WEIGHT_FACTOR;
++			bfqq->wr_cur_max_time =
++				bfqd->bfq_wr_rt_max_time;
++		}
++		/*
++		 * If needed, further reduce budget to make sure it is
++		 * close to bfqq's backlog, so as to reduce the
++		 * scheduling-error component due to a too large
++		 * budget. Do not care about throughput consequences,
++		 * but only about latency. Finally, do not assign a
++		 * too small budget either, to avoid increasing
++		 * latency by causing too frequent expirations.
++		 */
++		bfqq->entity.budget = min_t(unsigned long,
++					    bfqq->entity.budget,
++					    2 * bfq_min_budget(bfqd));
++
++		bfq_log_bfqq(bfqd, bfqq,
++			     "wrais starting at %lu, rais_max_time %u",
++			     jiffies,
++			     jiffies_to_msecs(bfqq->wr_cur_max_time));
++	} else if (old_wr_coeff > 1) {
++		if (interactive) { /* update wr coeff and duration */
++			bfqq->wr_coeff = bfqd->bfq_wr_coeff;
++			bfqq->wr_cur_max_time = bfq_wr_duration(bfqd);
++		} else if (in_burst) {
++			bfqq->wr_coeff = 1;
++			bfq_log_bfqq(bfqd, bfqq,
++				     "wrais ending at %lu, rais_max_time %u",
++				     jiffies,
++				     jiffies_to_msecs(bfqq->
++						      wr_cur_max_time));
++		} else if (soft_rt) {
++			/*
++			 * The application is now or still meeting the
++			 * requirements for being deemed soft rt.  We
++			 * can then correctly and safely (re)charge
++			 * the weight-raising duration for the
++			 * application with the weight-raising
++			 * duration for soft rt applications.
++			 *
++			 * In particular, doing this recharge now, i.e.,
++			 * before the weight-raising period for the
++			 * application finishes, reduces the probability
++			 * of the following negative scenario:
++			 * 1) the weight of a soft rt application is
++			 *    raised at startup (as for any newly
++			 *    created application),
++			 * 2) since the application is not interactive,
++			 *    at a certain time weight-raising is
++			 *    stopped for the application,
++			 * 3) at that time the application happens to
++			 *    still have pending requests, and hence
++			 *    is destined to not have a chance to be
++			 *    deemed soft rt before these requests are
++			 *    completed (see the comments to the
++			 *    function bfq_bfqq_softrt_next_start()
++			 *    for details on soft rt detection),
++			 * 4) these pending requests experience a high
++			 *    latency because the application is not
++			 *    weight-raised while they are pending.
++			 */
++			if (bfqq->wr_cur_max_time !=
++				bfqd->bfq_wr_rt_max_time) {
++				bfqq->wr_start_at_switch_to_srt =
++					bfqq->last_wr_start_finish;
++                BUG_ON(time_is_after_jiffies(bfqq->last_wr_start_finish));
++
++				bfqq->wr_cur_max_time =
++					bfqd->bfq_wr_rt_max_time;
++				bfqq->wr_coeff = bfqd->bfq_wr_coeff *
++					BFQ_SOFTRT_WEIGHT_FACTOR;
++				bfq_log_bfqq(bfqd, bfqq,
++					     "switching to soft_rt wr");
++			} else
++				bfq_log_bfqq(bfqd, bfqq,
++					"moving forward soft_rt wr duration");
++			bfqq->last_wr_start_finish = jiffies;
++		}
++	}
++}
++
++static bool bfq_bfqq_idle_for_long_time(struct bfq_data *bfqd,
++					struct bfq_queue *bfqq)
++{
++	return bfqq->dispatched == 0 &&
++		time_is_before_jiffies(
++			bfqq->budget_timeout +
++			bfqd->bfq_wr_min_idle_time);
++}
++
++static void bfq_bfqq_handle_idle_busy_switch(struct bfq_data *bfqd,
++					     struct bfq_queue *bfqq,
++					     int old_wr_coeff,
++					     struct request *rq,
++					     bool *interactive)
++{
++	bool soft_rt, in_burst,	wr_or_deserves_wr,
++		bfqq_wants_to_preempt,
++		idle_for_long_time = bfq_bfqq_idle_for_long_time(bfqd, bfqq),
++		/*
++		 * See the comments on
++		 * bfq_bfqq_update_budg_for_activation for
++		 * details on the usage of the next variable.
++		 */
++		arrived_in_time =  ktime_get_ns() <=
++			RQ_BIC(rq)->ttime.last_end_request +
++			bfqd->bfq_slice_idle * 3;
++
++	bfq_log_bfqq(bfqd, bfqq,
++		     "bfq_add_request non-busy: "
++		     "jiffies %lu, in_time %d, idle_long %d busyw %d "
++		     "wr_coeff %u",
++		     jiffies, arrived_in_time,
++		     idle_for_long_time,
++		     bfq_bfqq_non_blocking_wait_rq(bfqq),
++		     old_wr_coeff);
++
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++
++	BUG_ON(bfqq == bfqd->in_service_queue);
++	bfqg_stats_update_io_add(bfqq_group(RQ_BFQQ(rq)), bfqq, rq->cmd_flags);
++
++	/*
++	 * bfqq deserves to be weight-raised if:
++	 * - it is sync,
++	 * - it does not belong to a large burst,
++	 * - it has been idle for enough time or is soft real-time,
++	 * - is linked to a bfq_io_cq (it is not shared in any sense)
++	 */
++	in_burst = bfq_bfqq_in_large_burst(bfqq);
++	soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 &&
++		!in_burst &&
++		time_is_before_jiffies(bfqq->soft_rt_next_start);
++	*interactive =
++		!in_burst &&
++		idle_for_long_time;
++	wr_or_deserves_wr = bfqd->low_latency &&
++		(bfqq->wr_coeff > 1 ||
++		 (bfq_bfqq_sync(bfqq) &&
++		  bfqq->bic && (*interactive || soft_rt)));
++
++	bfq_log_bfqq(bfqd, bfqq,
++		     "bfq_add_request: "
++		     "in_burst %d, "
++		     "soft_rt %d (next %lu), inter %d, bic %p",
++		     bfq_bfqq_in_large_burst(bfqq), soft_rt,
++		     bfqq->soft_rt_next_start,
++		     *interactive,
++		     bfqq->bic);
++
++	/*
++	 * Using the last flag, update budget and check whether bfqq
++	 * may want to preempt the in-service queue.
++	 */
++	bfqq_wants_to_preempt =
++		bfq_bfqq_update_budg_for_activation(bfqd, bfqq,
++						    arrived_in_time,
++						    wr_or_deserves_wr);
++
++	/*
++	 * If bfqq happened to be activated in a burst, but has been
++	 * idle for much more than an interactive queue, then we
++	 * assume that, in the overall I/O initiated in the burst, the
++	 * I/O associated with bfqq is finished. So bfqq does not need
++	 * to be treated as a queue belonging to a burst
++	 * anymore. Accordingly, we reset bfqq's in_large_burst flag
++	 * if set, and remove bfqq from the burst list if it's
++	 * there. We do not decrement burst_size, because the fact
++	 * that bfqq does not need to belong to the burst list any
++	 * more does not invalidate the fact that bfqq was created in
++	 * a burst.
++	 */
++	if (likely(!bfq_bfqq_just_created(bfqq)) &&
++	    idle_for_long_time &&
++	    time_is_before_jiffies(
++		    bfqq->budget_timeout +
++		    msecs_to_jiffies(10000))) {
++		hlist_del_init(&bfqq->burst_list_node);
++		bfq_clear_bfqq_in_large_burst(bfqq);
++	}
++
++	bfq_clear_bfqq_just_created(bfqq);
++
++	if (!bfq_bfqq_IO_bound(bfqq)) {
++		if (arrived_in_time) {
++			bfqq->requests_within_timer++;
++			if (bfqq->requests_within_timer >=
++			    bfqd->bfq_requests_within_timer)
++				bfq_mark_bfqq_IO_bound(bfqq);
++		} else
++			bfqq->requests_within_timer = 0;
++		bfq_log_bfqq(bfqd, bfqq, "requests in time %d",
++			     bfqq->requests_within_timer);
++	}
++
++	if (bfqd->low_latency) {
++		if (unlikely(time_is_after_jiffies(bfqq->split_time)))
++			/* wraparound */
++			bfqq->split_time =
++				jiffies - bfqd->bfq_wr_min_idle_time - 1;
++
++		if (time_is_before_jiffies(bfqq->split_time +
++					   bfqd->bfq_wr_min_idle_time)) {
++			bfq_update_bfqq_wr_on_rq_arrival(bfqd, bfqq,
++							 old_wr_coeff,
++							 wr_or_deserves_wr,
++							 *interactive,
++							 in_burst,
++							 soft_rt);
++
++			if (old_wr_coeff != bfqq->wr_coeff)
++				bfqq->entity.prio_changed = 1;
++		}
++	}
++
++	bfqq->last_idle_bklogged = jiffies;
++	bfqq->service_from_backlogged = 0;
++	bfq_clear_bfqq_softrt_update(bfqq);
++
++	bfq_add_bfqq_busy(bfqd, bfqq);
++
++	/*
++	 * Expire in-service queue only if preemption may be needed
++	 * for guarantees. In this respect, the function
++	 * next_queue_may_preempt just checks a simple, necessary
++	 * condition, and not a sufficient condition based on
++	 * timestamps. In fact, for the latter condition to be
++	 * evaluated, timestamps would need first to be updated, and
++	 * this operation is quite costly (see the comments on the
++	 * function bfq_bfqq_update_budg_for_activation).
++	 */
++	if (bfqd->in_service_queue && bfqq_wants_to_preempt &&
++	    bfqd->in_service_queue->wr_coeff < bfqq->wr_coeff &&
++	    next_queue_may_preempt(bfqd)) {
++		struct bfq_queue *in_serv =
++			bfqd->in_service_queue;
++		BUG_ON(in_serv == bfqq);
++
++		bfq_bfqq_expire(bfqd, bfqd->in_service_queue,
++				false, BFQ_BFQQ_PREEMPTED);
++	}
++}
++
++static void bfq_add_request(struct request *rq)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq);
++	struct bfq_data *bfqd = bfqq->bfqd;
++	struct request *next_rq, *prev;
++	unsigned int old_wr_coeff = bfqq->wr_coeff;
++	bool interactive = false;
++
++	bfq_log_bfqq(bfqd, bfqq, "add_request: size %u %s",
++		     blk_rq_sectors(rq), rq_is_sync(rq) ? "S" : "A");
++
++	if (bfqq->wr_coeff > 1) /* queue is being weight-raised */
++		bfq_log_bfqq(bfqd, bfqq,
++			"raising period dur %u/%u msec, old coeff %u, w %d(%d)",
++			jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish),
++			jiffies_to_msecs(bfqq->wr_cur_max_time),
++			bfqq->wr_coeff,
++			bfqq->entity.weight, bfqq->entity.orig_weight);
++
++	bfqq->queued[rq_is_sync(rq)]++;
++	bfqd->queued++;
++
++	elv_rb_add(&bfqq->sort_list, rq);
++
++	/*
++	 * Check if this request is a better next-to-serve candidate.
++	 */
++	prev = bfqq->next_rq;
++	next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position);
++	BUG_ON(!next_rq);
++	bfqq->next_rq = next_rq;
++
++	/*
++	 * Adjust priority tree position, if next_rq changes.
++	 */
++	if (prev != bfqq->next_rq)
++		bfq_pos_tree_add_move(bfqd, bfqq);
++
++	if (!bfq_bfqq_busy(bfqq)) /* switching to busy ... */
++		bfq_bfqq_handle_idle_busy_switch(bfqd, bfqq, old_wr_coeff,
++						 rq, &interactive);
++	else {
++		if (bfqd->low_latency && old_wr_coeff == 1 && !rq_is_sync(rq) &&
++		    time_is_before_jiffies(
++				bfqq->last_wr_start_finish +
++				bfqd->bfq_wr_min_inter_arr_async)) {
++			bfqq->wr_coeff = bfqd->bfq_wr_coeff;
++			bfqq->wr_cur_max_time = bfq_wr_duration(bfqd);
++
++			bfqd->wr_busy_queues++;
++			bfqq->entity.prio_changed = 1;
++			bfq_log_bfqq(bfqd, bfqq,
++				     "non-idle wrais starting, "
++				     "wr_max_time %u wr_busy %d",
++				     jiffies_to_msecs(bfqq->wr_cur_max_time),
++				     bfqd->wr_busy_queues);
++		}
++		if (prev != bfqq->next_rq)
++			bfq_updated_next_req(bfqd, bfqq);
++	}
++
++	/*
++	 * Assign jiffies to last_wr_start_finish in the following
++	 * cases:
++	 *
++	 * . if bfqq is not going to be weight-raised, because, for
++	 *   non weight-raised queues, last_wr_start_finish stores the
++	 *   arrival time of the last request; as of now, this piece
++	 *   of information is used only for deciding whether to
++	 *   weight-raise async queues
++	 *
++	 * . if bfqq is not weight-raised, because, if bfqq is now
++	 *   switching to weight-raised, then last_wr_start_finish
++	 *   stores the time when weight-raising starts
++	 *
++	 * . if bfqq is interactive, because, regardless of whether
++	 *   bfqq is currently weight-raised, the weight-raising
++	 *   period must start or restart (this case is considered
++	 *   separately because it is not detected by the above
++	 *   conditions, if bfqq is already weight-raised)
++	 *
++	 * last_wr_start_finish has to be updated also if bfqq is soft
++	 * real-time, because the weight-raising period is constantly
++	 * restarted on idle-to-busy transitions for these queues, but
++	 * this is already done in bfq_bfqq_handle_idle_busy_switch if
++	 * needed.
++	 */
++	if (bfqd->low_latency &&
++		(old_wr_coeff == 1 || bfqq->wr_coeff == 1 || interactive))
++		bfqq->last_wr_start_finish = jiffies;
++}
++
++static struct request *bfq_find_rq_fmerge(struct bfq_data *bfqd,
++					  struct bio *bio)
++{
++	struct task_struct *tsk = current;
++	struct bfq_io_cq *bic;
++	struct bfq_queue *bfqq;
++
++	bic = bfq_bic_lookup(bfqd, tsk->io_context);
++	if (!bic)
++		return NULL;
++
++	bfqq = bic_to_bfqq(bic, op_is_sync(bio->bi_opf));
++	if (bfqq)
++		return elv_rb_find(&bfqq->sort_list, bio_end_sector(bio));
++
++	return NULL;
++}
++
++static sector_t get_sdist(sector_t last_pos, struct request *rq)
++{
++	sector_t sdist = 0;
++
++	if (last_pos) {
++		if (last_pos < blk_rq_pos(rq))
++			sdist = blk_rq_pos(rq) - last_pos;
++		else
++			sdist = last_pos - blk_rq_pos(rq);
++	}
++
++	return sdist;
++}
++
++static void bfq_activate_request(struct request_queue *q, struct request *rq)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	bfqd->rq_in_driver++;
++}
++
++static void bfq_deactivate_request(struct request_queue *q, struct request *rq)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++
++	BUG_ON(bfqd->rq_in_driver == 0);
++	bfqd->rq_in_driver--;
++}
++
++static void bfq_remove_request(struct request *rq)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq);
++	struct bfq_data *bfqd = bfqq->bfqd;
++	const int sync = rq_is_sync(rq);
++
++	BUG_ON(bfqq->entity.service > bfqq->entity.budget &&
++	       bfqq == bfqd->in_service_queue);
++
++	if (bfqq->next_rq == rq) {
++		bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq);
++		bfq_updated_next_req(bfqd, bfqq);
++	}
++
++	if (rq->queuelist.prev != &rq->queuelist)
++		list_del_init(&rq->queuelist);
++	BUG_ON(bfqq->queued[sync] == 0);
++	bfqq->queued[sync]--;
++	bfqd->queued--;
++	elv_rb_del(&bfqq->sort_list, rq);
++
++	if (RB_EMPTY_ROOT(&bfqq->sort_list)) {
++		bfqq->next_rq = NULL;
++
++		BUG_ON(bfqq->entity.budget < 0);
++
++		if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) {
++			BUG_ON(bfqq->ref < 2); /* referred by rq and on tree */
++			bfq_del_bfqq_busy(bfqd, bfqq, false);
++			/*
++			 * bfqq emptied. In normal operation, when
++			 * bfqq is empty, bfqq->entity.service and
++			 * bfqq->entity.budget must contain,
++			 * respectively, the service received and the
++			 * budget used last time bfqq emptied. These
++			 * facts do not hold in this case, as at least
++			 * this last removal occurred while bfqq is
++			 * not in service. To avoid inconsistencies,
++			 * reset both bfqq->entity.service and
++			 * bfqq->entity.budget, if bfqq has still a
++			 * process that may issue I/O requests to it.
++			 */
++			bfqq->entity.budget = bfqq->entity.service = 0;
++		}
++
++		/*
++		 * Remove queue from request-position tree as it is empty.
++		 */
++		if (bfqq->pos_root) {
++			rb_erase(&bfqq->pos_node, bfqq->pos_root);
++			bfqq->pos_root = NULL;
++		}
++	}
++
++	if (rq->cmd_flags & REQ_META) {
++		BUG_ON(bfqq->meta_pending == 0);
++		bfqq->meta_pending--;
++	}
++	bfqg_stats_update_io_remove(bfqq_group(bfqq), rq->cmd_flags);
++}
++
++static enum elv_merge bfq_merge(struct request_queue *q, struct request **req,
++				struct bio *bio)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	struct request *__rq;
++
++	__rq = bfq_find_rq_fmerge(bfqd, bio);
++	if (__rq && elv_bio_merge_ok(__rq, bio)) {
++		*req = __rq;
++		return ELEVATOR_FRONT_MERGE;
++	}
++
++	return ELEVATOR_NO_MERGE;
++}
++
++static void bfq_merged_request(struct request_queue *q, struct request *req,
++			       enum elv_merge type)
++{
++	if (type == ELEVATOR_FRONT_MERGE &&
++	    rb_prev(&req->rb_node) &&
++	    blk_rq_pos(req) <
++	    blk_rq_pos(container_of(rb_prev(&req->rb_node),
++				    struct request, rb_node))) {
++		struct bfq_queue *bfqq = RQ_BFQQ(req);
++		struct bfq_data *bfqd = bfqq->bfqd;
++		struct request *prev, *next_rq;
++
++		/* Reposition request in its sort_list */
++		elv_rb_del(&bfqq->sort_list, req);
++		elv_rb_add(&bfqq->sort_list, req);
++		/* Choose next request to be served for bfqq */
++		prev = bfqq->next_rq;
++		next_rq = bfq_choose_req(bfqd, bfqq->next_rq, req,
++					 bfqd->last_position);
++		BUG_ON(!next_rq);
++		bfqq->next_rq = next_rq;
++		/*
++		 * If next_rq changes, update both the queue's budget to
++		 * fit the new request and the queue's position in its
++		 * rq_pos_tree.
++		 */
++		if (prev != bfqq->next_rq) {
++			bfq_updated_next_req(bfqd, bfqq);
++			bfq_pos_tree_add_move(bfqd, bfqq);
++		}
++	}
++}
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static void bfq_bio_merged(struct request_queue *q, struct request *req,
++			   struct bio *bio)
++{
++	bfqg_stats_update_io_merged(bfqq_group(RQ_BFQQ(req)), bio->bi_opf);
++}
++#endif
++
++static void bfq_merged_requests(struct request_queue *q, struct request *rq,
++				struct request *next)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq), *next_bfqq = RQ_BFQQ(next);
++
++	/*
++	 * If next and rq belong to the same bfq_queue and next is older
++	 * than rq, then reposition rq in the fifo (by substituting next
++	 * with rq). Otherwise, if next and rq belong to different
++	 * bfq_queues, never reposition rq: in fact, we would have to
++	 * reposition it with respect to next's position in its own fifo,
++	 * which would most certainly be too expensive with respect to
++	 * the benefits.
++	 */
++	if (bfqq == next_bfqq &&
++	    !list_empty(&rq->queuelist) && !list_empty(&next->queuelist) &&
++	    next->fifo_time < rq->fifo_time) {
++		list_del_init(&rq->queuelist);
++		list_replace_init(&next->queuelist, &rq->queuelist);
++		rq->fifo_time = next->fifo_time;
++	}
++
++	if (bfqq->next_rq == next)
++		bfqq->next_rq = rq;
++
++	bfq_remove_request(next);
++	bfqg_stats_update_io_merged(bfqq_group(bfqq), next->cmd_flags);
++}
++
++/* Must be called with bfqq != NULL */
++static void bfq_bfqq_end_wr(struct bfq_queue *bfqq)
++{
++	BUG_ON(!bfqq);
++
++	if (bfq_bfqq_busy(bfqq))
++		bfqq->bfqd->wr_busy_queues--;
++	bfqq->wr_coeff = 1;
++	bfqq->wr_cur_max_time = 0;
++	bfqq->last_wr_start_finish = jiffies;
++	/*
++	 * Trigger a weight change on the next invocation of
++	 * __bfq_entity_update_weight_prio.
++	 */
++	bfqq->entity.prio_changed = 1;
++	bfq_log_bfqq(bfqq->bfqd, bfqq,
++		     "end_wr: wrais ending at %lu, rais_max_time %u",
++		     bfqq->last_wr_start_finish,
++		     jiffies_to_msecs(bfqq->wr_cur_max_time));
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "end_wr: wr_busy %d",
++		     bfqq->bfqd->wr_busy_queues);
++}
++
++static void bfq_end_wr_async_queues(struct bfq_data *bfqd,
++				    struct bfq_group *bfqg)
++{
++	int i, j;
++
++	for (i = 0; i < 2; i++)
++		for (j = 0; j < IOPRIO_BE_NR; j++)
++			if (bfqg->async_bfqq[i][j])
++				bfq_bfqq_end_wr(bfqg->async_bfqq[i][j]);
++	if (bfqg->async_idle_bfqq)
++		bfq_bfqq_end_wr(bfqg->async_idle_bfqq);
++}
++
++static void bfq_end_wr(struct bfq_data *bfqd)
++{
++	struct bfq_queue *bfqq;
++
++	spin_lock_irq(bfqd->queue->queue_lock);
++
++	list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list)
++		bfq_bfqq_end_wr(bfqq);
++	list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list)
++		bfq_bfqq_end_wr(bfqq);
++	bfq_end_wr_async(bfqd);
++
++	spin_unlock_irq(bfqd->queue->queue_lock);
++}
++
++static sector_t bfq_io_struct_pos(void *io_struct, bool request)
++{
++	if (request)
++		return blk_rq_pos(io_struct);
++	else
++		return ((struct bio *)io_struct)->bi_iter.bi_sector;
++}
++
++static int bfq_rq_close_to_sector(void *io_struct, bool request,
++				  sector_t sector)
++{
++	return abs(bfq_io_struct_pos(io_struct, request) - sector) <=
++	       BFQQ_CLOSE_THR;
++}
++
++static struct bfq_queue *bfqq_find_close(struct bfq_data *bfqd,
++					 struct bfq_queue *bfqq,
++					 sector_t sector)
++{
++	struct rb_root *root = &bfq_bfqq_to_bfqg(bfqq)->rq_pos_tree;
++	struct rb_node *parent, *node;
++	struct bfq_queue *__bfqq;
++
++	if (RB_EMPTY_ROOT(root))
++		return NULL;
++
++	/*
++	 * First, if we find a request starting at the end of the last
++	 * request, choose it.
++	 */
++	__bfqq = bfq_rq_pos_tree_lookup(bfqd, root, sector, &parent, NULL);
++	if (__bfqq)
++		return __bfqq;
++
++	/*
++	 * If the exact sector wasn't found, the parent of the NULL leaf
++	 * will contain the closest sector (rq_pos_tree sorted by
++	 * next_request position).
++	 */
++	__bfqq = rb_entry(parent, struct bfq_queue, pos_node);
++	if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector))
++		return __bfqq;
++
++	if (blk_rq_pos(__bfqq->next_rq) < sector)
++		node = rb_next(&__bfqq->pos_node);
++	else
++		node = rb_prev(&__bfqq->pos_node);
++	if (!node)
++		return NULL;
++
++	__bfqq = rb_entry(node, struct bfq_queue, pos_node);
++	if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector))
++		return __bfqq;
++
++	return NULL;
++}
++
++static struct bfq_queue *bfq_find_close_cooperator(struct bfq_data *bfqd,
++						   struct bfq_queue *cur_bfqq,
++						   sector_t sector)
++{
++	struct bfq_queue *bfqq;
++
++	/*
++	 * We shall notice if some of the queues are cooperating,
++	 * e.g., working closely on the same area of the device. In
++	 * that case, we can group them together and: 1) don't waste
++	 * time idling, and 2) serve the union of their requests in
++	 * the best possible order for throughput.
++	 */
++	bfqq = bfqq_find_close(bfqd, cur_bfqq, sector);
++	if (!bfqq || bfqq == cur_bfqq)
++		return NULL;
++
++	return bfqq;
++}
++
++static struct bfq_queue *
++bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq)
++{
++	int process_refs, new_process_refs;
++	struct bfq_queue *__bfqq;
++
++	/*
++	 * If there are no process references on the new_bfqq, then it is
++	 * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain
++	 * may have dropped their last reference (not just their last process
++	 * reference).
++	 */
++	if (!bfqq_process_refs(new_bfqq))
++		return NULL;
++
++	/* Avoid a circular list and skip interim queue merges. */
++	while ((__bfqq = new_bfqq->new_bfqq)) {
++		if (__bfqq == bfqq)
++			return NULL;
++		new_bfqq = __bfqq;
++	}
++
++	process_refs = bfqq_process_refs(bfqq);
++	new_process_refs = bfqq_process_refs(new_bfqq);
++	/*
++	 * If the process for the bfqq has gone away, there is no
++	 * sense in merging the queues.
++	 */
++	if (process_refs == 0 || new_process_refs == 0)
++		return NULL;
++
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d",
++		new_bfqq->pid);
++
++	/*
++	 * Merging is just a redirection: the requests of the process
++	 * owning one of the two queues are redirected to the other queue.
++	 * The latter queue, in its turn, is set as shared if this is the
++	 * first time that the requests of some process are redirected to
++	 * it.
++	 *
++	 * We redirect bfqq to new_bfqq and not the opposite, because we
++	 * are in the context of the process owning bfqq, hence we have
++	 * the io_cq of this process. So we can immediately configure this
++	 * io_cq to redirect the requests of the process to new_bfqq.
++	 *
++	 * NOTE, even if new_bfqq coincides with the in-service queue, the
++	 * io_cq of new_bfqq is not available, because, if the in-service
++	 * queue is shared, bfqd->in_service_bic may not point to the
++	 * io_cq of the in-service queue.
++	 * Redirecting the requests of the process owning bfqq to the
++	 * currently in-service queue is in any case the best option, as
++	 * we feed the in-service queue with new requests close to the
++	 * last request served and, by doing so, hopefully increase the
++	 * throughput.
++	 */
++	bfqq->new_bfqq = new_bfqq;
++	new_bfqq->ref += process_refs;
++	return new_bfqq;
++}
++
++static bool bfq_may_be_close_cooperator(struct bfq_queue *bfqq,
++					struct bfq_queue *new_bfqq)
++{
++	if (bfq_class_idle(bfqq) || bfq_class_idle(new_bfqq) ||
++	    (bfqq->ioprio_class != new_bfqq->ioprio_class))
++		return false;
++
++	/*
++	 * If either of the queues has already been detected as seeky,
++	 * then merging it with the other queue is unlikely to lead to
++	 * sequential I/O.
++	 */
++	if (BFQQ_SEEKY(bfqq) || BFQQ_SEEKY(new_bfqq))
++		return false;
++
++	/*
++	 * Interleaved I/O is known to be done by (some) applications
++	 * only for reads, so it does not make sense to merge async
++	 * queues.
++	 */
++	if (!bfq_bfqq_sync(bfqq) || !bfq_bfqq_sync(new_bfqq))
++		return false;
++
++	return true;
++}
++
++/*
++ * If this function returns true, then bfqq cannot be merged. The idea
++ * is that true cooperation happens very early after processes start
++ * to do I/O. Usually, late cooperations are just accidental false
++ * positives. In case bfqq is weight-raised, such false positives
++ * would evidently degrade latency guarantees for bfqq.
++ */
++static bool wr_from_too_long(struct bfq_queue *bfqq)
++{
++	return bfqq->wr_coeff > 1 &&
++		time_is_before_jiffies(bfqq->last_wr_start_finish +
++				       msecs_to_jiffies(100));
++}
++
++/*
++ * Attempt to schedule a merge of bfqq with the currently in-service
++ * queue or with a close queue among the scheduled queues.  Return
++ * NULL if no merge was scheduled, a pointer to the shared bfq_queue
++ * structure otherwise.
++ *
++ * The OOM queue is not allowed to participate to cooperation: in fact, since
++ * the requests temporarily redirected to the OOM queue could be redirected
++ * again to dedicated queues at any time, the state needed to correctly
++ * handle merging with the OOM queue would be quite complex and expensive
++ * to maintain. Besides, in such a critical condition as an out of memory,
++ * the benefits of queue merging may be little relevant, or even negligible.
++ *
++ * Weight-raised queues can be merged only if their weight-raising
++ * period has just started. In fact cooperating processes are usually
++ * started together. Thus, with this filter we avoid false positives
++ * that would jeopardize low-latency guarantees.
++ *
++ * WARNING: queue merging may impair fairness among non-weight raised
++ * queues, for at least two reasons: 1) the original weight of a
++ * merged queue may change during the merged state, 2) even being the
++ * weight the same, a merged queue may be bloated with many more
++ * requests than the ones produced by its originally-associated
++ * process.
++ */
++static struct bfq_queue *
++bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++		     void *io_struct, bool request)
++{
++	struct bfq_queue *in_service_bfqq, *new_bfqq;
++
++	if (bfqq->new_bfqq)
++		return bfqq->new_bfqq;
++
++	if (io_struct && wr_from_too_long(bfqq) &&
++	    likely(bfqq != &bfqd->oom_bfqq))
++		bfq_log_bfqq(bfqd, bfqq,
++			     "would have looked for coop, but bfq%d wr",
++			bfqq->pid);
++
++	if (!io_struct ||
++	    wr_from_too_long(bfqq) ||
++	    unlikely(bfqq == &bfqd->oom_bfqq))
++		return NULL;
++
++	/* If there is only one backlogged queue, don't search. */
++	if (bfqd->busy_queues == 1)
++		return NULL;
++
++	in_service_bfqq = bfqd->in_service_queue;
++
++	if (in_service_bfqq && in_service_bfqq != bfqq &&
++	    bfqd->in_service_bic && wr_from_too_long(in_service_bfqq)
++	    && likely(in_service_bfqq == &bfqd->oom_bfqq))
++		bfq_log_bfqq(bfqd, bfqq,
++		"would have tried merge with in-service-queue, but wr");
++
++	if (!in_service_bfqq || in_service_bfqq == bfqq ||
++	    !bfqd->in_service_bic || wr_from_too_long(in_service_bfqq) ||
++	    unlikely(in_service_bfqq == &bfqd->oom_bfqq))
++		goto check_scheduled;
++
++	if (bfq_rq_close_to_sector(io_struct, request, bfqd->last_position) &&
++	    bfqq->entity.parent == in_service_bfqq->entity.parent &&
++	    bfq_may_be_close_cooperator(bfqq, in_service_bfqq)) {
++		new_bfqq = bfq_setup_merge(bfqq, in_service_bfqq);
++		if (new_bfqq)
++			return new_bfqq;
++	}
++	/*
++	 * Check whether there is a cooperator among currently scheduled
++	 * queues. The only thing we need is that the bio/request is not
++	 * NULL, as we need it to establish whether a cooperator exists.
++	 */
++check_scheduled:
++	new_bfqq = bfq_find_close_cooperator(bfqd, bfqq,
++			bfq_io_struct_pos(io_struct, request));
++
++	BUG_ON(new_bfqq && bfqq->entity.parent != new_bfqq->entity.parent);
++
++	if (new_bfqq && wr_from_too_long(new_bfqq) &&
++	    likely(new_bfqq != &bfqd->oom_bfqq) &&
++	    bfq_may_be_close_cooperator(bfqq, new_bfqq))
++		bfq_log_bfqq(bfqd, bfqq,
++			     "would have merged with bfq%d, but wr",
++			     new_bfqq->pid);
++
++	if (new_bfqq && !wr_from_too_long(new_bfqq) &&
++	    likely(new_bfqq != &bfqd->oom_bfqq) &&
++	    bfq_may_be_close_cooperator(bfqq, new_bfqq))
++		return bfq_setup_merge(bfqq, new_bfqq);
++
++	return NULL;
++}
++
++static void bfq_bfqq_save_state(struct bfq_queue *bfqq)
++{
++	struct bfq_io_cq *bic = bfqq->bic;
++
++	/*
++	 * If !bfqq->bic, the queue is already shared or its requests
++	 * have already been redirected to a shared queue; both idle window
++	 * and weight raising state have already been saved. Do nothing.
++	 */
++	if (!bic)
++		return;
++
++	bic->saved_idle_window = bfq_bfqq_idle_window(bfqq);
++	bic->saved_IO_bound = bfq_bfqq_IO_bound(bfqq);
++	bic->saved_in_large_burst = bfq_bfqq_in_large_burst(bfqq);
++	bic->was_in_burst_list = !hlist_unhashed(&bfqq->burst_list_node);
++	bic->saved_wr_coeff = bfqq->wr_coeff;
++	bic->saved_wr_start_at_switch_to_srt = bfqq->wr_start_at_switch_to_srt;
++	bic->saved_last_wr_start_finish = bfqq->last_wr_start_finish;
++	bic->saved_wr_cur_max_time = bfqq->wr_cur_max_time;
++	BUG_ON(time_is_after_jiffies(bfqq->last_wr_start_finish));
++}
++
++static void bfq_get_bic_reference(struct bfq_queue *bfqq)
++{
++	/*
++	 * If bfqq->bic has a non-NULL value, the bic to which it belongs
++	 * is about to begin using a shared bfq_queue.
++	 */
++	if (bfqq->bic)
++		atomic_long_inc(&bfqq->bic->icq.ioc->refcount);
++}
++
++static void
++bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic,
++		struct bfq_queue *bfqq, struct bfq_queue *new_bfqq)
++{
++	bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu",
++		     (unsigned long) new_bfqq->pid);
++	/* Save weight raising and idle window of the merged queues */
++	bfq_bfqq_save_state(bfqq);
++	bfq_bfqq_save_state(new_bfqq);
++	if (bfq_bfqq_IO_bound(bfqq))
++		bfq_mark_bfqq_IO_bound(new_bfqq);
++	bfq_clear_bfqq_IO_bound(bfqq);
++
++	/*
++	 * If bfqq is weight-raised, then let new_bfqq inherit
++	 * weight-raising. To reduce false positives, neglect the case
++	 * where bfqq has just been created, but has not yet made it
++	 * to be weight-raised (which may happen because EQM may merge
++	 * bfqq even before bfq_add_request is executed for the first
++	 * time for bfqq). Handling this case would however be very
++	 * easy, thanks to the flag just_created.
++	 */
++	if (new_bfqq->wr_coeff == 1 && bfqq->wr_coeff > 1) {
++		new_bfqq->wr_coeff = bfqq->wr_coeff;
++		new_bfqq->wr_cur_max_time = bfqq->wr_cur_max_time;
++		new_bfqq->last_wr_start_finish = bfqq->last_wr_start_finish;
++		new_bfqq->wr_start_at_switch_to_srt =
++			bfqq->wr_start_at_switch_to_srt;
++		if (bfq_bfqq_busy(new_bfqq))
++			bfqd->wr_busy_queues++;
++		new_bfqq->entity.prio_changed = 1;
++		bfq_log_bfqq(bfqd, new_bfqq,
++			     "wr start after merge with %d, rais_max_time %u",
++			     bfqq->pid,
++			     jiffies_to_msecs(bfqq->wr_cur_max_time));
++	}
++
++	if (bfqq->wr_coeff > 1) { /* bfqq has given its wr to new_bfqq */
++		bfqq->wr_coeff = 1;
++		bfqq->entity.prio_changed = 1;
++		if (bfq_bfqq_busy(bfqq))
++			bfqd->wr_busy_queues--;
++	}
++
++	bfq_log_bfqq(bfqd, new_bfqq, "merge_bfqqs: wr_busy %d",
++		     bfqd->wr_busy_queues);
++
++	/*
++	 * Grab a reference to the bic, to prevent it from being destroyed
++	 * before being possibly touched by a bfq_split_bfqq().
++	 */
++	bfq_get_bic_reference(bfqq);
++	bfq_get_bic_reference(new_bfqq);
++	/*
++	 * Merge queues (that is, let bic redirect its requests to new_bfqq)
++	 */
++	bic_set_bfqq(bic, new_bfqq, 1);
++	bfq_mark_bfqq_coop(new_bfqq);
++	/*
++	 * new_bfqq now belongs to at least two bics (it is a shared queue):
++	 * set new_bfqq->bic to NULL. bfqq either:
++	 * - does not belong to any bic any more, and hence bfqq->bic must
++	 *   be set to NULL, or
++	 * - is a queue whose owning bics have already been redirected to a
++	 *   different queue, hence the queue is destined to not belong to
++	 *   any bic soon and bfqq->bic is already NULL (therefore the next
++	 *   assignment causes no harm).
++	 */
++	new_bfqq->bic = NULL;
++	bfqq->bic = NULL;
++	/* release process reference to bfqq */
++	bfq_put_queue(bfqq);
++}
++
++static int bfq_allow_bio_merge(struct request_queue *q, struct request *rq,
++			       struct bio *bio)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	bool is_sync = op_is_sync(bio->bi_opf);
++	struct bfq_io_cq *bic;
++	struct bfq_queue *bfqq, *new_bfqq;
++
++	/*
++	 * Disallow merge of a sync bio into an async request.
++	 */
++	if (is_sync && !rq_is_sync(rq))
++		return false;
++
++	/*
++	 * Lookup the bfqq that this bio will be queued with. Allow
++	 * merge only if rq is queued there.
++	 * Queue lock is held here.
++	 */
++	bic = bfq_bic_lookup(bfqd, current->io_context);
++	if (!bic)
++		return false;
++
++	bfqq = bic_to_bfqq(bic, is_sync);
++	/*
++	 * We take advantage of this function to perform an early merge
++	 * of the queues of possible cooperating processes.
++	 */
++	if (bfqq) {
++		new_bfqq = bfq_setup_cooperator(bfqd, bfqq, bio, false);
++		if (new_bfqq) {
++			bfq_merge_bfqqs(bfqd, bic, bfqq, new_bfqq);
++			/*
++			 * If we get here, the bio will be queued in the
++			 * shared queue, i.e., new_bfqq, so use new_bfqq
++			 * to decide whether bio and rq can be merged.
++			 */
++			bfqq = new_bfqq;
++		}
++	}
++
++	return bfqq == RQ_BFQQ(rq);
++}
++
++static int bfq_allow_rq_merge(struct request_queue *q, struct request *rq,
++			      struct request *next)
++{
++	return RQ_BFQQ(rq) == RQ_BFQQ(next);
++}
++
++/*
++ * Set the maximum time for the in-service queue to consume its
++ * budget. This prevents seeky processes from lowering the throughput.
++ * In practice, a time-slice service scheme is used with seeky
++ * processes.
++ */
++static void bfq_set_budget_timeout(struct bfq_data *bfqd,
++				   struct bfq_queue *bfqq)
++{
++	unsigned int timeout_coeff;
++
++	if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time)
++		timeout_coeff = 1;
++	else
++		timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight;
++
++	bfqd->last_budget_start = ktime_get();
++
++	bfqq->budget_timeout = jiffies +
++		bfqd->bfq_timeout * timeout_coeff;
++
++	bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u",
++		jiffies_to_msecs(bfqd->bfq_timeout * timeout_coeff));
++}
++
++static void __bfq_set_in_service_queue(struct bfq_data *bfqd,
++				       struct bfq_queue *bfqq)
++{
++	if (bfqq) {
++		bfqg_stats_update_avg_queue_size(bfqq_group(bfqq));
++		bfq_mark_bfqq_must_alloc(bfqq);
++		bfq_clear_bfqq_fifo_expire(bfqq);
++
++		bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8;
++
++		BUG_ON(bfqq == bfqd->in_service_queue);
++		BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list));
++
++		if (time_is_before_jiffies(bfqq->last_wr_start_finish) &&
++		    bfqq->wr_coeff > 1 &&
++		    bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time &&
++		    time_is_before_jiffies(bfqq->budget_timeout)) {
++			/*
++			 * For soft real-time queues, move the start
++			 * of the weight-raising period forward by the
++			 * time the queue has not received any
++			 * service. Otherwise, a relatively long
++			 * service delay is likely to cause the
++			 * weight-raising period of the queue to end,
++			 * because of the short duration of the
++			 * weight-raising period of a soft real-time
++			 * queue.  It is worth noting that this move
++			 * is not so dangerous for the other queues,
++			 * because soft real-time queues are not
++			 * greedy.
++			 *
++			 * To not add a further variable, we use the
++			 * overloaded field budget_timeout to
++			 * determine for how long the queue has not
++			 * received service, i.e., how much time has
++			 * elapsed since the queue expired. However,
++			 * this is a little imprecise, because
++			 * budget_timeout is set to jiffies if bfqq
++			 * not only expires, but also remains with no
++			 * request.
++			 */
++			if (time_after(bfqq->budget_timeout,
++				       bfqq->last_wr_start_finish))
++				bfqq->last_wr_start_finish +=
++					jiffies - bfqq->budget_timeout;
++			else
++				bfqq->last_wr_start_finish = jiffies;
++
++			if (time_is_after_jiffies(bfqq->last_wr_start_finish)) {
++			       pr_crit(
++			       "BFQ WARNING:last %lu budget %lu jiffies %lu",
++			       bfqq->last_wr_start_finish,
++			       bfqq->budget_timeout,
++			       jiffies);
++			       pr_crit("diff %lu", jiffies -
++				       max_t(unsigned long,
++					     bfqq->last_wr_start_finish,
++					     bfqq->budget_timeout));
++			       bfqq->last_wr_start_finish = jiffies;
++			}
++		}
++
++		bfq_set_budget_timeout(bfqd, bfqq);
++		bfq_log_bfqq(bfqd, bfqq,
++			     "set_in_service_queue, cur-budget = %d",
++			     bfqq->entity.budget);
++	} else
++		bfq_log(bfqd, "set_in_service_queue: NULL");
++
++	bfqd->in_service_queue = bfqq;
++}
++
++/*
++ * Get and set a new queue for service.
++ */
++static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd)
++{
++	struct bfq_queue *bfqq = bfq_get_next_queue(bfqd);
++
++	__bfq_set_in_service_queue(bfqd, bfqq);
++	return bfqq;
++}
++
++static void bfq_arm_slice_timer(struct bfq_data *bfqd)
++{
++	struct bfq_queue *bfqq = bfqd->in_service_queue;
++	struct bfq_io_cq *bic;
++	u32 sl;
++
++	BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list));
++
++	/* Processes have exited, don't wait. */
++	bic = bfqd->in_service_bic;
++	if (!bic || atomic_read(&bic->icq.ioc->active_ref) == 0)
++		return;
++
++	bfq_mark_bfqq_wait_request(bfqq);
++
++	/*
++	 * We don't want to idle for seeks, but we do want to allow
++	 * fair distribution of slice time for a process doing back-to-back
++	 * seeks. So allow a little bit of time for him to submit a new rq.
++	 *
++	 * To prevent processes with (partly) seeky workloads from
++	 * being too ill-treated, grant them a small fraction of the
++	 * assigned budget before reducing the waiting time to
++	 * BFQ_MIN_TT. This happened to help reduce latency.
++	 */
++	sl = bfqd->bfq_slice_idle;
++	/*
++	 * Unless the queue is being weight-raised or the scenario is
++	 * asymmetric, grant only minimum idle time if the queue
++	 * is seeky. A long idling is preserved for a weight-raised
++	 * queue, or, more in general, in an asymemtric scenario,
++	 * because a long idling is needed for guaranteeing to a queue
++	 * its reserved share of the throughput (in particular, it is
++	 * needed if the queue has a higher weight than some other
++	 * queue).
++	 */
++	if (BFQQ_SEEKY(bfqq) && bfqq->wr_coeff == 1 &&
++	    bfq_symmetric_scenario(bfqd))
++		sl = min_t(u32, sl, BFQ_MIN_TT);
++
++	bfqd->last_idling_start = ktime_get();
++	hrtimer_start(&bfqd->idle_slice_timer, ns_to_ktime(sl),
++		      HRTIMER_MODE_REL);
++	bfqg_stats_set_start_idle_time(bfqq_group(bfqq));
++	bfq_log(bfqd, "arm idle: %ld/%ld ms",
++		sl / NSEC_PER_MSEC, bfqd->bfq_slice_idle / NSEC_PER_MSEC);
++}
++
++/*
++ * In autotuning mode, max_budget is dynamically recomputed as the
++ * amount of sectors transferred in timeout at the estimated peak
++ * rate. This enables BFQ to utilize a full timeslice with a full
++ * budget, even if the in-service queue is served at peak rate. And
++ * this maximises throughput with sequential workloads.
++ */
++static unsigned long bfq_calc_max_budget(struct bfq_data *bfqd)
++{
++	return (u64)bfqd->peak_rate * USEC_PER_MSEC *
++		jiffies_to_msecs(bfqd->bfq_timeout)>>BFQ_RATE_SHIFT;
++}
++
++/*
++ * Update parameters related to throughput and responsiveness, as a
++ * function of the estimated peak rate. See comments on
++ * bfq_calc_max_budget(), and on T_slow and T_fast arrays.
++ */
++static void update_thr_responsiveness_params(struct bfq_data *bfqd)
++{
++	int dev_type = blk_queue_nonrot(bfqd->queue);
++
++	if (bfqd->bfq_user_max_budget == 0) {
++		bfqd->bfq_max_budget =
++			bfq_calc_max_budget(bfqd);
++		BUG_ON(bfqd->bfq_max_budget < 0);
++		bfq_log(bfqd, "new max_budget = %d",
++			bfqd->bfq_max_budget);
++	}
++
++	if (bfqd->device_speed == BFQ_BFQD_FAST &&
++	    bfqd->peak_rate < device_speed_thresh[dev_type]) {
++		bfqd->device_speed = BFQ_BFQD_SLOW;
++		bfqd->RT_prod = R_slow[dev_type] *
++			T_slow[dev_type];
++	} else if (bfqd->device_speed == BFQ_BFQD_SLOW &&
++		   bfqd->peak_rate > device_speed_thresh[dev_type]) {
++		bfqd->device_speed = BFQ_BFQD_FAST;
++		bfqd->RT_prod = R_fast[dev_type] *
++			T_fast[dev_type];
++	}
++
++	bfq_log(bfqd,
++"dev_type %s dev_speed_class = %s (%llu sects/sec), thresh %llu setcs/sec",
++		dev_type == 0 ? "ROT" : "NONROT",
++		bfqd->device_speed == BFQ_BFQD_FAST ? "FAST" : "SLOW",
++		bfqd->device_speed == BFQ_BFQD_FAST ?
++		(USEC_PER_SEC*(u64)R_fast[dev_type])>>BFQ_RATE_SHIFT :
++		(USEC_PER_SEC*(u64)R_slow[dev_type])>>BFQ_RATE_SHIFT,
++		(USEC_PER_SEC*(u64)device_speed_thresh[dev_type])>>
++		BFQ_RATE_SHIFT);
++}
++
++static void bfq_reset_rate_computation(struct bfq_data *bfqd, struct request *rq)
++{
++	if (rq != NULL) { /* new rq dispatch now, reset accordingly */
++		bfqd->last_dispatch = bfqd->first_dispatch = ktime_get_ns() ;
++		bfqd->peak_rate_samples = 1;
++		bfqd->sequential_samples = 0;
++		bfqd->tot_sectors_dispatched = bfqd->last_rq_max_size =
++			blk_rq_sectors(rq);
++	} else /* no new rq dispatched, just reset the number of samples */
++		bfqd->peak_rate_samples = 0; /* full re-init on next disp. */
++
++	bfq_log(bfqd,
++		"reset_rate_computation at end, sample %u/%u tot_sects %llu",
++		bfqd->peak_rate_samples, bfqd->sequential_samples,
++		bfqd->tot_sectors_dispatched);
++}
++
++static void bfq_update_rate_reset(struct bfq_data *bfqd, struct request *rq)
++{
++	u32 rate, weight, divisor;
++
++	/*
++	 * For the convergence property to hold (see comments on
++	 * bfq_update_peak_rate()) and for the assessment to be
++	 * reliable, a minimum number of samples must be present, and
++	 * a minimum amount of time must have elapsed. If not so, do
++	 * not compute new rate. Just reset parameters, to get ready
++	 * for a new evaluation attempt.
++	 */
++	if (bfqd->peak_rate_samples < BFQ_RATE_MIN_SAMPLES ||
++	    bfqd->delta_from_first < BFQ_RATE_MIN_INTERVAL) {
++		bfq_log(bfqd,
++	"update_rate_reset: only resetting, delta_first %lluus samples %d",
++			bfqd->delta_from_first>>10, bfqd->peak_rate_samples);
++		goto reset_computation;
++	}
++
++	/*
++	 * If a new request completion has occurred after last
++	 * dispatch, then, to approximate the rate at which requests
++	 * have been served by the device, it is more precise to
++	 * extend the observation interval to the last completion.
++	 */
++	bfqd->delta_from_first =
++		max_t(u64, bfqd->delta_from_first,
++		      bfqd->last_completion - bfqd->first_dispatch);
++
++	BUG_ON(bfqd->delta_from_first == 0);
++	/*
++	 * Rate computed in sects/usec, and not sects/nsec, for
++	 * precision issues.
++	 */
++	rate = div64_ul(bfqd->tot_sectors_dispatched<<BFQ_RATE_SHIFT,
++			div_u64(bfqd->delta_from_first, NSEC_PER_USEC));
++
++	bfq_log(bfqd,
++"update_rate_reset: tot_sects %llu delta_first %lluus rate %llu sects/s (%d)",
++		bfqd->tot_sectors_dispatched, bfqd->delta_from_first>>10,
++		((USEC_PER_SEC*(u64)rate)>>BFQ_RATE_SHIFT),
++		rate > 20<<BFQ_RATE_SHIFT);
++
++	/*
++	 * Peak rate not updated if:
++	 * - the percentage of sequential dispatches is below 3/4 of the
++	 *   total, and rate is below the current estimated peak rate
++	 * - rate is unreasonably high (> 20M sectors/sec)
++	 */
++	if ((bfqd->sequential_samples < (3 * bfqd->peak_rate_samples)>>2 &&
++	     rate <= bfqd->peak_rate) ||
++		rate > 20<<BFQ_RATE_SHIFT) {
++		bfq_log(bfqd,
++		"update_rate_reset: goto reset, samples %u/%u rate/peak %llu/%llu",
++		bfqd->peak_rate_samples, bfqd->sequential_samples,
++		((USEC_PER_SEC*(u64)rate)>>BFQ_RATE_SHIFT),
++		((USEC_PER_SEC*(u64)bfqd->peak_rate)>>BFQ_RATE_SHIFT));
++		goto reset_computation;
++	} else {
++		bfq_log(bfqd,
++		"update_rate_reset: do update, samples %u/%u rate/peak %llu/%llu",
++		bfqd->peak_rate_samples, bfqd->sequential_samples,
++		((USEC_PER_SEC*(u64)rate)>>BFQ_RATE_SHIFT),
++		((USEC_PER_SEC*(u64)bfqd->peak_rate)>>BFQ_RATE_SHIFT));
++	}
++
++	/*
++	 * We have to update the peak rate, at last! To this purpose,
++	 * we use a low-pass filter. We compute the smoothing constant
++	 * of the filter as a function of the 'weight' of the new
++	 * measured rate.
++	 *
++	 * As can be seen in next formulas, we define this weight as a
++	 * quantity proportional to how sequential the workload is,
++	 * and to how long the observation time interval is.
++	 *
++	 * The weight runs from 0 to 8. The maximum value of the
++	 * weight, 8, yields the minimum value for the smoothing
++	 * constant. At this minimum value for the smoothing constant,
++	 * the measured rate contributes for half of the next value of
++	 * the estimated peak rate.
++	 *
++	 * So, the first step is to compute the weight as a function
++	 * of how sequential the workload is. Note that the weight
++	 * cannot reach 9, because bfqd->sequential_samples cannot
++	 * become equal to bfqd->peak_rate_samples, which, in its
++	 * turn, holds true because bfqd->sequential_samples is not
++	 * incremented for the first sample.
++	 */
++	weight = (9 * bfqd->sequential_samples) / bfqd->peak_rate_samples;
++
++	/*
++	 * Second step: further refine the weight as a function of the
++	 * duration of the observation interval.
++	 */
++	weight = min_t(u32, 8,
++		       div_u64(weight * bfqd->delta_from_first,
++			       BFQ_RATE_REF_INTERVAL));
++
++	/*
++	 * Divisor ranging from 10, for minimum weight, to 2, for
++	 * maximum weight.
++	 */
++	divisor = 10 - weight;
++	BUG_ON(divisor == 0);
++
++	/*
++	 * Finally, update peak rate:
++	 *
++	 * peak_rate = peak_rate * (divisor-1) / divisor  +  rate / divisor
++	 */
++	bfqd->peak_rate *= divisor-1;
++	bfqd->peak_rate /= divisor;
++	rate /= divisor; /* smoothing constant alpha = 1/divisor */
++
++	bfq_log(bfqd,
++		"update_rate_reset: divisor %d tmp_peak_rate %llu tmp_rate %u",
++		divisor,
++		((USEC_PER_SEC*(u64)bfqd->peak_rate)>>BFQ_RATE_SHIFT),
++		(u32)((USEC_PER_SEC*(u64)rate)>>BFQ_RATE_SHIFT));
++
++	BUG_ON(bfqd->peak_rate == 0);
++	BUG_ON(bfqd->peak_rate > 20<<BFQ_RATE_SHIFT);
++
++	bfqd->peak_rate += rate;
++	update_thr_responsiveness_params(bfqd);
++	BUG_ON(bfqd->peak_rate > 20<<BFQ_RATE_SHIFT);
++
++reset_computation:
++	bfq_reset_rate_computation(bfqd, rq);
++}
++
++/*
++ * Update the read/write peak rate (the main quantity used for
++ * auto-tuning, see update_thr_responsiveness_params()).
++ *
++ * It is not trivial to estimate the peak rate (correctly): because of
++ * the presence of sw and hw queues between the scheduler and the
++ * device components that finally serve I/O requests, it is hard to
++ * say exactly when a given dispatched request is served inside the
++ * device, and for how long. As a consequence, it is hard to know
++ * precisely at what rate a given set of requests is actually served
++ * by the device.
++ *
++ * On the opposite end, the dispatch time of any request is trivially
++ * available, and, from this piece of information, the "dispatch rate"
++ * of requests can be immediately computed. So, the idea in the next
++ * function is to use what is known, namely request dispatch times
++ * (plus, when useful, request completion times), to estimate what is
++ * unknown, namely in-device request service rate.
++ *
++ * The main issue is that, because of the above facts, the rate at
++ * which a certain set of requests is dispatched over a certain time
++ * interval can vary greatly with respect to the rate at which the
++ * same requests are then served. But, since the size of any
++ * intermediate queue is limited, and the service scheme is lossless
++ * (no request is silently dropped), the following obvious convergence
++ * property holds: the number of requests dispatched MUST become
++ * closer and closer to the number of requests completed as the
++ * observation interval grows. This is the key property used in
++ * the next function to estimate the peak service rate as a function
++ * of the observed dispatch rate. The function assumes to be invoked
++ * on every request dispatch.
++ */
++static void bfq_update_peak_rate(struct bfq_data *bfqd, struct request *rq)
++{
++	u64 now_ns = ktime_get_ns();
++
++	if (bfqd->peak_rate_samples == 0) { /* first dispatch */
++		bfq_log(bfqd,
++		"update_peak_rate: goto reset, samples %d",
++				bfqd->peak_rate_samples) ;
++		bfq_reset_rate_computation(bfqd, rq);
++		goto update_last_values; /* will add one sample */
++	}
++
++	/*
++	 * Device idle for very long: the observation interval lasting
++	 * up to this dispatch cannot be a valid observation interval
++	 * for computing a new peak rate (similarly to the late-
++	 * completion event in bfq_completed_request()). Go to
++	 * update_rate_and_reset to have the following three steps
++	 * taken:
++	 * - close the observation interval at the last (previous)
++	 *   request dispatch or completion
++	 * - compute rate, if possible, for that observation interval
++	 * - start a new observation interval with this dispatch
++	 */
++	if (now_ns - bfqd->last_dispatch > 100*NSEC_PER_MSEC &&
++	    bfqd->rq_in_driver == 0) {
++		bfq_log(bfqd,
++"update_peak_rate: jumping to updating&resetting delta_last %lluus samples %d",
++			(now_ns - bfqd->last_dispatch)>>10,
++			bfqd->peak_rate_samples) ;
++		goto update_rate_and_reset;
++	}
++
++	/* Update sampling information */
++	bfqd->peak_rate_samples++;
++
++	if ((bfqd->rq_in_driver > 0 ||
++		now_ns - bfqd->last_completion < BFQ_MIN_TT)
++	     && get_sdist(bfqd->last_position, rq) < BFQQ_SEEK_THR)
++		bfqd->sequential_samples++;
++
++	bfqd->tot_sectors_dispatched += blk_rq_sectors(rq);
++
++	/* Reset max observed rq size every 32 dispatches */
++	if (likely(bfqd->peak_rate_samples % 32))
++		bfqd->last_rq_max_size =
++			max_t(u32, blk_rq_sectors(rq), bfqd->last_rq_max_size);
++	else
++		bfqd->last_rq_max_size = blk_rq_sectors(rq);
++
++	bfqd->delta_from_first = now_ns - bfqd->first_dispatch;
++
++	bfq_log(bfqd,
++	"update_peak_rate: added samples %u/%u tot_sects %llu delta_first %lluus",
++		bfqd->peak_rate_samples, bfqd->sequential_samples,
++		bfqd->tot_sectors_dispatched,
++		bfqd->delta_from_first>>10);
++
++	/* Target observation interval not yet reached, go on sampling */
++	if (bfqd->delta_from_first < BFQ_RATE_REF_INTERVAL)
++		goto update_last_values;
++
++update_rate_and_reset:
++	bfq_update_rate_reset(bfqd, rq);
++update_last_values:
++	bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq);
++	bfqd->last_dispatch = now_ns;
++
++	bfq_log(bfqd,
++	"update_peak_rate: delta_first %lluus last_pos %llu peak_rate %llu",
++		(now_ns - bfqd->first_dispatch)>>10,
++		(unsigned long long) bfqd->last_position,
++		((USEC_PER_SEC*(u64)bfqd->peak_rate)>>BFQ_RATE_SHIFT));
++	bfq_log(bfqd,
++	"update_peak_rate: samples at end %d", bfqd->peak_rate_samples);
++}
++
++/*
++ * Move request from internal lists to the dispatch list of the request queue
++ */
++static void bfq_dispatch_insert(struct request_queue *q, struct request *rq)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq);
++
++	/*
++	 * For consistency, the next instruction should have been executed
++	 * after removing the request from the queue and dispatching it.
++	 * We execute instead this instruction before bfq_remove_request()
++	 * (and hence introduce a temporary inconsistency), for efficiency.
++	 * In fact, in a forced_dispatch, this prevents two counters related
++	 * to bfqq->dispatched to risk to be uselessly decremented if bfqq
++	 * is not in service, and then to be incremented again after
++	 * incrementing bfqq->dispatched.
++	 */
++	bfqq->dispatched++;
++	bfq_update_peak_rate(q->elevator->elevator_data, rq);
++
++	bfq_remove_request(rq);
++	elv_dispatch_sort(q, rq);
++}
++
++static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	BUG_ON(bfqq != bfqd->in_service_queue);
++
++	/*
++	 * If this bfqq is shared between multiple processes, check
++	 * to make sure that those processes are still issuing I/Os
++	 * within the mean seek distance. If not, it may be time to
++	 * break the queues apart again.
++	 */
++	if (bfq_bfqq_coop(bfqq) && BFQQ_SEEKY(bfqq))
++		bfq_mark_bfqq_split_coop(bfqq);
++
++	if (RB_EMPTY_ROOT(&bfqq->sort_list)) {
++		if (bfqq->dispatched == 0)
++			/*
++			 * Overloading budget_timeout field to store
++			 * the time at which the queue remains with no
++			 * backlog and no outstanding request; used by
++			 * the weight-raising mechanism.
++			 */
++			bfqq->budget_timeout = jiffies;
++
++		bfq_del_bfqq_busy(bfqd, bfqq, true);
++	} else {
++		bfq_requeue_bfqq(bfqd, bfqq);
++		/*
++		 * Resort priority tree of potential close cooperators.
++		 */
++		bfq_pos_tree_add_move(bfqd, bfqq);
++	}
++
++	/*
++	 * All in-service entities must have been properly deactivated
++	 * or requeued before executing the next function, which
++	 * resets all in-service entites as no more in service.
++	 */
++	__bfq_bfqd_reset_in_service(bfqd);
++}
++
++/**
++ * __bfq_bfqq_recalc_budget - try to adapt the budget to the @bfqq behavior.
++ * @bfqd: device data.
++ * @bfqq: queue to update.
++ * @reason: reason for expiration.
++ *
++ * Handle the feedback on @bfqq budget at queue expiration.
++ * See the body for detailed comments.
++ */
++static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd,
++				     struct bfq_queue *bfqq,
++				     enum bfqq_expiration reason)
++{
++	struct request *next_rq;
++	int budget, min_budget;
++
++	BUG_ON(bfqq != bfqd->in_service_queue);
++
++	min_budget = bfq_min_budget(bfqd);
++
++	if (bfqq->wr_coeff == 1)
++		budget = bfqq->max_budget;
++	else /*
++	      * Use a constant, low budget for weight-raised queues,
++	      * to help achieve a low latency. Keep it slightly higher
++	      * than the minimum possible budget, to cause a little
++	      * bit fewer expirations.
++	      */
++		budget = 2 * min_budget;
++
++	bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %d, budg left %d",
++		bfqq->entity.budget, bfq_bfqq_budget_left(bfqq));
++	bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last max_budg %d, min budg %d",
++		budget, bfq_min_budget(bfqd));
++	bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d",
++		bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->in_service_queue));
++
++	if (bfq_bfqq_sync(bfqq) && bfqq->wr_coeff == 1) {
++		switch (reason) {
++		/*
++		 * Caveat: in all the following cases we trade latency
++		 * for throughput.
++		 */
++		case BFQ_BFQQ_TOO_IDLE:
++			/*
++			 * This is the only case where we may reduce
++			 * the budget: if there is no request of the
++			 * process still waiting for completion, then
++			 * we assume (tentatively) that the timer has
++			 * expired because the batch of requests of
++			 * the process could have been served with a
++			 * smaller budget.  Hence, betting that
++			 * process will behave in the same way when it
++			 * becomes backlogged again, we reduce its
++			 * next budget.  As long as we guess right,
++			 * this budget cut reduces the latency
++			 * experienced by the process.
++			 *
++			 * However, if there are still outstanding
++			 * requests, then the process may have not yet
++			 * issued its next request just because it is
++			 * still waiting for the completion of some of
++			 * the still outstanding ones.  So in this
++			 * subcase we do not reduce its budget, on the
++			 * contrary we increase it to possibly boost
++			 * the throughput, as discussed in the
++			 * comments to the BUDGET_TIMEOUT case.
++			 */
++			if (bfqq->dispatched > 0) /* still outstanding reqs */
++				budget = min(budget * 2, bfqd->bfq_max_budget);
++			else {
++				if (budget > 5 * min_budget)
++					budget -= 4 * min_budget;
++				else
++					budget = min_budget;
++			}
++			break;
++		case BFQ_BFQQ_BUDGET_TIMEOUT:
++			/*
++			 * We double the budget here because it gives
++			 * the chance to boost the throughput if this
++			 * is not a seeky process (and has bumped into
++			 * this timeout because of, e.g., ZBR).
++			 */
++			budget = min(budget * 2, bfqd->bfq_max_budget);
++			break;
++		case BFQ_BFQQ_BUDGET_EXHAUSTED:
++			/*
++			 * The process still has backlog, and did not
++			 * let either the budget timeout or the disk
++			 * idling timeout expire. Hence it is not
++			 * seeky, has a short thinktime and may be
++			 * happy with a higher budget too. So
++			 * definitely increase the budget of this good
++			 * candidate to boost the disk throughput.
++			 */
++			budget = min(budget * 4, bfqd->bfq_max_budget);
++			break;
++		case BFQ_BFQQ_NO_MORE_REQUESTS:
++			/*
++			 * For queues that expire for this reason, it
++			 * is particularly important to keep the
++			 * budget close to the actual service they
++			 * need. Doing so reduces the timestamp
++			 * misalignment problem described in the
++			 * comments in the body of
++			 * __bfq_activate_entity. In fact, suppose
++			 * that a queue systematically expires for
++			 * BFQ_BFQQ_NO_MORE_REQUESTS and presents a
++			 * new request in time to enjoy timestamp
++			 * back-shifting. The larger the budget of the
++			 * queue is with respect to the service the
++			 * queue actually requests in each service
++			 * slot, the more times the queue can be
++			 * reactivated with the same virtual finish
++			 * time. It follows that, even if this finish
++			 * time is pushed to the system virtual time
++			 * to reduce the consequent timestamp
++			 * misalignment, the queue unjustly enjoys for
++			 * many re-activations a lower finish time
++			 * than all newly activated queues.
++			 *
++			 * The service needed by bfqq is measured
++			 * quite precisely by bfqq->entity.service.
++			 * Since bfqq does not enjoy device idling,
++			 * bfqq->entity.service is equal to the number
++			 * of sectors that the process associated with
++			 * bfqq requested to read/write before waiting
++			 * for request completions, or blocking for
++			 * other reasons.
++			 */
++			budget = max_t(int, bfqq->entity.service, min_budget);
++			break;
++		default:
++			return;
++		}
++	} else if (!bfq_bfqq_sync(bfqq))
++		/*
++		 * Async queues get always the maximum possible
++		 * budget, as for them we do not care about latency
++		 * (in addition, their ability to dispatch is limited
++		 * by the charging factor).
++		 */
++		budget = bfqd->bfq_max_budget;
++
++	bfqq->max_budget = budget;
++
++	if (bfqd->budgets_assigned >= bfq_stats_min_budgets &&
++	    !bfqd->bfq_user_max_budget)
++		bfqq->max_budget = min(bfqq->max_budget, bfqd->bfq_max_budget);
++
++	/*
++	 * If there is still backlog, then assign a new budget, making
++	 * sure that it is large enough for the next request.  Since
++	 * the finish time of bfqq must be kept in sync with the
++	 * budget, be sure to call __bfq_bfqq_expire() *after* this
++	 * update.
++	 *
++	 * If there is no backlog, then no need to update the budget;
++	 * it will be updated on the arrival of a new request.
++	 */
++	next_rq = bfqq->next_rq;
++	if (next_rq) {
++		BUG_ON(reason == BFQ_BFQQ_TOO_IDLE ||
++		       reason == BFQ_BFQQ_NO_MORE_REQUESTS);
++		bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget,
++					    bfq_serv_to_charge(next_rq, bfqq));
++		BUG_ON(!bfq_bfqq_busy(bfqq));
++		BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list));
++	}
++
++	bfq_log_bfqq(bfqd, bfqq, "head sect: %u, new budget %d",
++			next_rq ? blk_rq_sectors(next_rq) : 0,
++			bfqq->entity.budget);
++}
++
++/*
++ * Return true if the process associated with bfqq is "slow". The slow
++ * flag is used, in addition to the budget timeout, to reduce the
++ * amount of service provided to seeky processes, and thus reduce
++ * their chances to lower the throughput. More details in the comments
++ * on the function bfq_bfqq_expire().
++ *
++ * An important observation is in order: as discussed in the comments
++ * on the function bfq_update_peak_rate(), with devices with internal
++ * queues, it is hard if ever possible to know when and for how long
++ * an I/O request is processed by the device (apart from the trivial
++ * I/O pattern where a new request is dispatched only after the
++ * previous one has been completed). This makes it hard to evaluate
++ * the real rate at which the I/O requests of each bfq_queue are
++ * served.  In fact, for an I/O scheduler like BFQ, serving a
++ * bfq_queue means just dispatching its requests during its service
++ * slot (i.e., until the budget of the queue is exhausted, or the
++ * queue remains idle, or, finally, a timeout fires). But, during the
++ * service slot of a bfq_queue, around 100 ms at most, the device may
++ * be even still processing requests of bfq_queues served in previous
++ * service slots. On the opposite end, the requests of the in-service
++ * bfq_queue may be completed after the service slot of the queue
++ * finishes.
++ *
++ * Anyway, unless more sophisticated solutions are used
++ * (where possible), the sum of the sizes of the requests dispatched
++ * during the service slot of a bfq_queue is probably the only
++ * approximation available for the service received by the bfq_queue
++ * during its service slot. And this sum is the quantity used in this
++ * function to evaluate the I/O speed of a process.
++ */
++static bool bfq_bfqq_is_slow(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++				 bool compensate, enum bfqq_expiration reason,
++				 unsigned long *delta_ms)
++{
++	ktime_t delta_ktime;
++	u32 delta_usecs;
++	bool slow = BFQQ_SEEKY(bfqq); /* if delta too short, use seekyness */
++
++	if (!bfq_bfqq_sync(bfqq))
++		return false;
++
++	if (compensate)
++		delta_ktime = bfqd->last_idling_start;
++	else
++		delta_ktime = ktime_get();
++	delta_ktime = ktime_sub(delta_ktime, bfqd->last_budget_start);
++	delta_usecs = ktime_to_us(delta_ktime);
++
++	/* don't use too short time intervals */
++	if (delta_usecs < 1000) {
++		if (blk_queue_nonrot(bfqd->queue))
++			 /*
++			  * give same worst-case guarantees as idling
++			  * for seeky
++			  */
++			*delta_ms = BFQ_MIN_TT / NSEC_PER_MSEC;
++		else /* charge at least one seek */
++			*delta_ms = bfq_slice_idle / NSEC_PER_MSEC;
++
++		bfq_log(bfqd, "bfq_bfqq_is_slow: too short %u", delta_usecs);
++
++		return slow;
++	}
++
++	*delta_ms = delta_usecs / USEC_PER_MSEC;
++
++	/*
++	 * Use only long (> 20ms) intervals to filter out excessive
++	 * spikes in service rate estimation.
++	 */
++	if (delta_usecs > 20000) {
++		/*
++		 * Caveat for rotational devices: processes doing I/O
++		 * in the slower disk zones tend to be slow(er) even
++		 * if not seeky. In this respect, the estimated peak
++		 * rate is likely to be an average over the disk
++		 * surface. Accordingly, to not be too harsh with
++		 * unlucky processes, a process is deemed slow only if
++		 * its rate has been lower than half of the estimated
++		 * peak rate.
++		 */
++		slow = bfqq->entity.service < bfqd->bfq_max_budget / 2;
++		bfq_log(bfqd, "bfq_bfqq_is_slow: relative rate %d/%d",
++			bfqq->entity.service, bfqd->bfq_max_budget);
++	}
++
++	bfq_log_bfqq(bfqd, bfqq, "bfq_bfqq_is_slow: slow %d", slow);
++
++	return slow;
++}
++
++/*
++ * To be deemed as soft real-time, an application must meet two
++ * requirements. First, the application must not require an average
++ * bandwidth higher than the approximate bandwidth required to playback or
++ * record a compressed high-definition video.
++ * The next function is invoked on the completion of the last request of a
++ * batch, to compute the next-start time instant, soft_rt_next_start, such
++ * that, if the next request of the application does not arrive before
++ * soft_rt_next_start, then the above requirement on the bandwidth is met.
++ *
++ * The second requirement is that the request pattern of the application is
++ * isochronous, i.e., that, after issuing a request or a batch of requests,
++ * the application stops issuing new requests until all its pending requests
++ * have been completed. After that, the application may issue a new batch,
++ * and so on.
++ * For this reason the next function is invoked to compute
++ * soft_rt_next_start only for applications that meet this requirement,
++ * whereas soft_rt_next_start is set to infinity for applications that do
++ * not.
++ *
++ * Unfortunately, even a greedy application may happen to behave in an
++ * isochronous way if the CPU load is high. In fact, the application may
++ * stop issuing requests while the CPUs are busy serving other processes,
++ * then restart, then stop again for a while, and so on. In addition, if
++ * the disk achieves a low enough throughput with the request pattern
++ * issued by the application (e.g., because the request pattern is random
++ * and/or the device is slow), then the application may meet the above
++ * bandwidth requirement too. To prevent such a greedy application to be
++ * deemed as soft real-time, a further rule is used in the computation of
++ * soft_rt_next_start: soft_rt_next_start must be higher than the current
++ * time plus the maximum time for which the arrival of a request is waited
++ * for when a sync queue becomes idle, namely bfqd->bfq_slice_idle.
++ * This filters out greedy applications, as the latter issue instead their
++ * next request as soon as possible after the last one has been completed
++ * (in contrast, when a batch of requests is completed, a soft real-time
++ * application spends some time processing data).
++ *
++ * Unfortunately, the last filter may easily generate false positives if
++ * only bfqd->bfq_slice_idle is used as a reference time interval and one
++ * or both the following cases occur:
++ * 1) HZ is so low that the duration of a jiffy is comparable to or higher
++ *    than bfqd->bfq_slice_idle. This happens, e.g., on slow devices with
++ *    HZ=100.
++ * 2) jiffies, instead of increasing at a constant rate, may stop increasing
++ *    for a while, then suddenly 'jump' by several units to recover the lost
++ *    increments. This seems to happen, e.g., inside virtual machines.
++ * To address this issue, we do not use as a reference time interval just
++ * bfqd->bfq_slice_idle, but bfqd->bfq_slice_idle plus a few jiffies. In
++ * particular we add the minimum number of jiffies for which the filter
++ * seems to be quite precise also in embedded systems and KVM/QEMU virtual
++ * machines.
++ */
++static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd,
++						struct bfq_queue *bfqq)
++{
++	bfq_log_bfqq(bfqd, bfqq,
++"softrt_next_start: service_blkg %lu soft_rate %u sects/sec interval %u",
++		     bfqq->service_from_backlogged,
++		     bfqd->bfq_wr_max_softrt_rate,
++		     jiffies_to_msecs(HZ * bfqq->service_from_backlogged /
++				      bfqd->bfq_wr_max_softrt_rate));
++
++	return max(bfqq->last_idle_bklogged +
++		   HZ * bfqq->service_from_backlogged /
++		   bfqd->bfq_wr_max_softrt_rate,
++		   jiffies + nsecs_to_jiffies(bfqq->bfqd->bfq_slice_idle) + 4);
++}
++
++/*
++ * Return the farthest future time instant according to jiffies
++ * macros.
++ */
++static unsigned long bfq_greatest_from_now(void)
++{
++	return jiffies + MAX_JIFFY_OFFSET;
++}
++
++/*
++ * Return the farthest past time instant according to jiffies
++ * macros.
++ */
++static unsigned long bfq_smallest_from_now(void)
++{
++	return jiffies - MAX_JIFFY_OFFSET;
++}
++
++/**
++ * bfq_bfqq_expire - expire a queue.
++ * @bfqd: device owning the queue.
++ * @bfqq: the queue to expire.
++ * @compensate: if true, compensate for the time spent idling.
++ * @reason: the reason causing the expiration.
++ *
++ * If the process associated with bfqq does slow I/O (e.g., because it
++ * issues random requests), we charge bfqq with the time it has been
++ * in service instead of the service it has received (see
++ * bfq_bfqq_charge_time for details on how this goal is achieved). As
++ * a consequence, bfqq will typically get higher timestamps upon
++ * reactivation, and hence it will be rescheduled as if it had
++ * received more service than what it has actually received. In the
++ * end, bfqq receives less service in proportion to how slowly its
++ * associated process consumes its budgets (and hence how seriously it
++ * tends to lower the throughput). In addition, this time-charging
++ * strategy guarantees time fairness among slow processes. In
++ * contrast, if the process associated with bfqq is not slow, we
++ * charge bfqq exactly with the service it has received.
++ *
++ * Charging time to the first type of queues and the exact service to
++ * the other has the effect of using the WF2Q+ policy to schedule the
++ * former on a timeslice basis, without violating service domain
++ * guarantees among the latter.
++ */
++static void bfq_bfqq_expire(struct bfq_data *bfqd,
++			    struct bfq_queue *bfqq,
++			    bool compensate,
++			    enum bfqq_expiration reason)
++{
++	bool slow;
++	unsigned long delta = 0;
++	struct bfq_entity *entity = &bfqq->entity;
++	int ref;
++
++	BUG_ON(bfqq != bfqd->in_service_queue);
++
++	/*
++	 * Check whether the process is slow (see bfq_bfqq_is_slow).
++	 */
++	slow = bfq_bfqq_is_slow(bfqd, bfqq, compensate, reason, &delta);
++
++	/*
++	 * Increase service_from_backlogged before next statement,
++	 * because the possible next invocation of
++	 * bfq_bfqq_charge_time would likely inflate
++	 * entity->service. In contrast, service_from_backlogged must
++	 * contain real service, to enable the soft real-time
++	 * heuristic to correctly compute the bandwidth consumed by
++	 * bfqq.
++	 */
++	bfqq->service_from_backlogged += entity->service;
++
++	/*
++	 * As above explained, charge slow (typically seeky) and
++	 * timed-out queues with the time and not the service
++	 * received, to favor sequential workloads.
++	 *
++	 * Processes doing I/O in the slower disk zones will tend to
++	 * be slow(er) even if not seeky. Therefore, since the
++	 * estimated peak rate is actually an average over the disk
++	 * surface, these processes may timeout just for bad luck. To
++	 * avoid punishing them, do not charge time to processes that
++	 * succeeded in consuming at least 2/3 of their budget. This
++	 * allows BFQ to preserve enough elasticity to still perform
++	 * bandwidth, and not time, distribution with little unlucky
++	 * or quasi-sequential processes.
++	 */
++	if (bfqq->wr_coeff == 1 &&
++	    (slow ||
++	     (reason == BFQ_BFQQ_BUDGET_TIMEOUT &&
++	      bfq_bfqq_budget_left(bfqq) >=  entity->budget / 3)))
++		bfq_bfqq_charge_time(bfqd, bfqq, delta);
++
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++
++	if (reason == BFQ_BFQQ_TOO_IDLE &&
++	    entity->service <= 2 * entity->budget / 10)
++		bfq_clear_bfqq_IO_bound(bfqq);
++
++	if (bfqd->low_latency && bfqq->wr_coeff == 1)
++		bfqq->last_wr_start_finish = jiffies;
++
++	if (bfqd->low_latency && bfqd->bfq_wr_max_softrt_rate > 0 &&
++	    RB_EMPTY_ROOT(&bfqq->sort_list)) {
++		/*
++		 * If we get here, and there are no outstanding
++		 * requests, then the request pattern is isochronous
++		 * (see the comments on the function
++		 * bfq_bfqq_softrt_next_start()). Thus we can compute
++		 * soft_rt_next_start. If, instead, the queue still
++		 * has outstanding requests, then we have to wait for
++		 * the completion of all the outstanding requests to
++		 * discover whether the request pattern is actually
++		 * isochronous.
++		 */
++		BUG_ON(bfqd->busy_queues < 1);
++		if (bfqq->dispatched == 0) {
++			bfqq->soft_rt_next_start =
++				bfq_bfqq_softrt_next_start(bfqd, bfqq);
++			bfq_log_bfqq(bfqd, bfqq, "new soft_rt_next %lu",
++				     bfqq->soft_rt_next_start);
++		} else {
++			/*
++			 * The application is still waiting for the
++			 * completion of one or more requests:
++			 * prevent it from possibly being incorrectly
++			 * deemed as soft real-time by setting its
++			 * soft_rt_next_start to infinity. In fact,
++			 * without this assignment, the application
++			 * would be incorrectly deemed as soft
++			 * real-time if:
++			 * 1) it issued a new request before the
++			 *    completion of all its in-flight
++			 *    requests, and
++			 * 2) at that time, its soft_rt_next_start
++			 *    happened to be in the past.
++			 */
++			bfqq->soft_rt_next_start =
++				bfq_greatest_from_now();
++			/*
++			 * Schedule an update of soft_rt_next_start to when
++			 * the task may be discovered to be isochronous.
++			 */
++			bfq_mark_bfqq_softrt_update(bfqq);
++		}
++	}
++
++	bfq_log_bfqq(bfqd, bfqq,
++		"expire (%d, slow %d, num_disp %d, idle_win %d, weight %d)",
++		     reason, slow, bfqq->dispatched,
++		     bfq_bfqq_idle_window(bfqq), entity->weight);
++
++	/*
++	 * Increase, decrease or leave budget unchanged according to
++	 * reason.
++	 */
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++	__bfq_bfqq_recalc_budget(bfqd, bfqq, reason);
++	BUG_ON(bfqq->next_rq == NULL &&
++	       bfqq->entity.budget < bfqq->entity.service);
++	ref = bfqq->ref;
++	__bfq_bfqq_expire(bfqd, bfqq);
++
++	BUG_ON(ref > 1 &&
++	       !bfq_bfqq_busy(bfqq) && reason == BFQ_BFQQ_BUDGET_EXHAUSTED &&
++		!bfq_class_idle(bfqq));
++
++	/* mark bfqq as waiting a request only if a bic still points to it */
++	if (ref > 1 && !bfq_bfqq_busy(bfqq) &&
++	    reason != BFQ_BFQQ_BUDGET_TIMEOUT &&
++	    reason != BFQ_BFQQ_BUDGET_EXHAUSTED)
++		bfq_mark_bfqq_non_blocking_wait_rq(bfqq);
++}
++
++/*
++ * Budget timeout is not implemented through a dedicated timer, but
++ * just checked on request arrivals and completions, as well as on
++ * idle timer expirations.
++ */
++static bool bfq_bfqq_budget_timeout(struct bfq_queue *bfqq)
++{
++	return time_is_before_eq_jiffies(bfqq->budget_timeout);
++}
++
++/*
++ * If we expire a queue that is actively waiting (i.e., with the
++ * device idled) for the arrival of a new request, then we may incur
++ * the timestamp misalignment problem described in the body of the
++ * function __bfq_activate_entity. Hence we return true only if this
++ * condition does not hold, or if the queue is slow enough to deserve
++ * only to be kicked off for preserving a high throughput.
++ */
++static bool bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq)
++{
++	bfq_log_bfqq(bfqq->bfqd, bfqq,
++		"may_budget_timeout: wait_request %d left %d timeout %d",
++		bfq_bfqq_wait_request(bfqq),
++			bfq_bfqq_budget_left(bfqq) >=  bfqq->entity.budget / 3,
++		bfq_bfqq_budget_timeout(bfqq));
++
++	return (!bfq_bfqq_wait_request(bfqq) ||
++		bfq_bfqq_budget_left(bfqq) >=  bfqq->entity.budget / 3)
++		&&
++		bfq_bfqq_budget_timeout(bfqq);
++}
++
++/*
++ * For a queue that becomes empty, device idling is allowed only if
++ * this function returns true for that queue. As a consequence, since
++ * device idling plays a critical role for both throughput boosting
++ * and service guarantees, the return value of this function plays a
++ * critical role as well.
++ *
++ * In a nutshell, this function returns true only if idling is
++ * beneficial for throughput or, even if detrimental for throughput,
++ * idling is however necessary to preserve service guarantees (low
++ * latency, desired throughput distribution, ...). In particular, on
++ * NCQ-capable devices, this function tries to return false, so as to
++ * help keep the drives' internal queues full, whenever this helps the
++ * device boost the throughput without causing any service-guarantee
++ * issue.
++ *
++ * In more detail, the return value of this function is obtained by,
++ * first, computing a number of boolean variables that take into
++ * account throughput and service-guarantee issues, and, then,
++ * combining these variables in a logical expression. Most of the
++ * issues taken into account are not trivial. We discuss these issues
++ * while introducing the variables.
++ */
++static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq)
++{
++	struct bfq_data *bfqd = bfqq->bfqd;
++	bool idling_boosts_thr, idling_boosts_thr_without_issues,
++		idling_needed_for_service_guarantees,
++		asymmetric_scenario;
++
++	if (bfqd->strict_guarantees)
++		return true;
++
++	/*
++	 * The next variable takes into account the cases where idling
++	 * boosts the throughput.
++	 *
++	 * The value of the variable is computed considering, first, that
++	 * idling is virtually always beneficial for the throughput if:
++	 * (a) the device is not NCQ-capable, or
++	 * (b) regardless of the presence of NCQ, the device is rotational
++	 *     and the request pattern for bfqq is I/O-bound and sequential.
++	 *
++	 * Secondly, and in contrast to the above item (b), idling an
++	 * NCQ-capable flash-based device would not boost the
++	 * throughput even with sequential I/O; rather it would lower
++	 * the throughput in proportion to how fast the device
++	 * is. Accordingly, the next variable is true if any of the
++	 * above conditions (a) and (b) is true, and, in particular,
++	 * happens to be false if bfqd is an NCQ-capable flash-based
++	 * device.
++	 */
++	idling_boosts_thr = !bfqd->hw_tag ||
++		(!blk_queue_nonrot(bfqd->queue) && bfq_bfqq_IO_bound(bfqq) &&
++		 bfq_bfqq_idle_window(bfqq));
++
++	/*
++	 * The value of the next variable,
++	 * idling_boosts_thr_without_issues, is equal to that of
++	 * idling_boosts_thr, unless a special case holds. In this
++	 * special case, described below, idling may cause problems to
++	 * weight-raised queues.
++	 *
++	 * When the request pool is saturated (e.g., in the presence
++	 * of write hogs), if the processes associated with
++	 * non-weight-raised queues ask for requests at a lower rate,
++	 * then processes associated with weight-raised queues have a
++	 * higher probability to get a request from the pool
++	 * immediately (or at least soon) when they need one. Thus
++	 * they have a higher probability to actually get a fraction
++	 * of the device throughput proportional to their high
++	 * weight. This is especially true with NCQ-capable drives,
++	 * which enqueue several requests in advance, and further
++	 * reorder internally-queued requests.
++	 *
++	 * For this reason, we force to false the value of
++	 * idling_boosts_thr_without_issues if there are weight-raised
++	 * busy queues. In this case, and if bfqq is not weight-raised,
++	 * this guarantees that the device is not idled for bfqq (if,
++	 * instead, bfqq is weight-raised, then idling will be
++	 * guaranteed by another variable, see below). Combined with
++	 * the timestamping rules of BFQ (see [1] for details), this
++	 * behavior causes bfqq, and hence any sync non-weight-raised
++	 * queue, to get a lower number of requests served, and thus
++	 * to ask for a lower number of requests from the request
++	 * pool, before the busy weight-raised queues get served
++	 * again. This often mitigates starvation problems in the
++	 * presence of heavy write workloads and NCQ, thereby
++	 * guaranteeing a higher application and system responsiveness
++	 * in these hostile scenarios.
++	 */
++	idling_boosts_thr_without_issues = idling_boosts_thr &&
++		bfqd->wr_busy_queues == 0;
++
++	/*
++	 * There is then a case where idling must be performed not
++	 * for throughput concerns, but to preserve service
++	 * guarantees.
++	 *
++	 * To introduce this case, we can note that allowing the drive
++	 * to enqueue more than one request at a time, and hence
++	 * delegating de facto final scheduling decisions to the
++	 * drive's internal scheduler, entails loss of control on the
++	 * actual request service order. In particular, the critical
++	 * situation is when requests from different processes happen
++	 * to be present, at the same time, in the internal queue(s)
++	 * of the drive. In such a situation, the drive, by deciding
++	 * the service order of the internally-queued requests, does
++	 * determine also the actual throughput distribution among
++	 * these processes. But the drive typically has no notion or
++	 * concern about per-process throughput distribution, and
++	 * makes its decisions only on a per-request basis. Therefore,
++	 * the service distribution enforced by the drive's internal
++	 * scheduler is likely to coincide with the desired
++	 * device-throughput distribution only in a completely
++	 * symmetric scenario where:
++	 * (i)  each of these processes must get the same throughput as
++	 *      the others;
++	 * (ii) all these processes have the same I/O pattern
++	 *      (either sequential or random).
++	 * In fact, in such a scenario, the drive will tend to treat
++	 * the requests of each of these processes in about the same
++	 * way as the requests of the others, and thus to provide
++	 * each of these processes with about the same throughput
++	 * (which is exactly the desired throughput distribution). In
++	 * contrast, in any asymmetric scenario, device idling is
++	 * certainly needed to guarantee that bfqq receives its
++	 * assigned fraction of the device throughput (see [1] for
++	 * details).
++	 *
++	 * We address this issue by controlling, actually, only the
++	 * symmetry sub-condition (i), i.e., provided that
++	 * sub-condition (i) holds, idling is not performed,
++	 * regardless of whether sub-condition (ii) holds. In other
++	 * words, only if sub-condition (i) holds, then idling is
++	 * allowed, and the device tends to be prevented from queueing
++	 * many requests, possibly of several processes. The reason
++	 * for not controlling also sub-condition (ii) is that we
++	 * exploit preemption to preserve guarantees in case of
++	 * symmetric scenarios, even if (ii) does not hold, as
++	 * explained in the next two paragraphs.
++	 *
++	 * Even if a queue, say Q, is expired when it remains idle, Q
++	 * can still preempt the new in-service queue if the next
++	 * request of Q arrives soon (see the comments on
++	 * bfq_bfqq_update_budg_for_activation). If all queues and
++	 * groups have the same weight, this form of preemption,
++	 * combined with the hole-recovery heuristic described in the
++	 * comments on function bfq_bfqq_update_budg_for_activation,
++	 * are enough to preserve a correct bandwidth distribution in
++	 * the mid term, even without idling. In fact, even if not
++	 * idling allows the internal queues of the device to contain
++	 * many requests, and thus to reorder requests, we can rather
++	 * safely assume that the internal scheduler still preserves a
++	 * minimum of mid-term fairness. The motivation for using
++	 * preemption instead of idling is that, by not idling,
++	 * service guarantees are preserved without minimally
++	 * sacrificing throughput. In other words, both a high
++	 * throughput and its desired distribution are obtained.
++	 *
++	 * More precisely, this preemption-based, idleless approach
++	 * provides fairness in terms of IOPS, and not sectors per
++	 * second. This can be seen with a simple example. Suppose
++	 * that there are two queues with the same weight, but that
++	 * the first queue receives requests of 8 sectors, while the
++	 * second queue receives requests of 1024 sectors. In
++	 * addition, suppose that each of the two queues contains at
++	 * most one request at a time, which implies that each queue
++	 * always remains idle after it is served. Finally, after
++	 * remaining idle, each queue receives very quickly a new
++	 * request. It follows that the two queues are served
++	 * alternatively, preempting each other if needed. This
++	 * implies that, although both queues have the same weight,
++	 * the queue with large requests receives a service that is
++	 * 1024/8 times as high as the service received by the other
++	 * queue.
++	 *
++	 * On the other hand, device idling is performed, and thus
++	 * pure sector-domain guarantees are provided, for the
++	 * following queues, which are likely to need stronger
++	 * throughput guarantees: weight-raised queues, and queues
++	 * with a higher weight than other queues. When such queues
++	 * are active, sub-condition (i) is false, which triggers
++	 * device idling.
++	 *
++	 * According to the above considerations, the next variable is
++	 * true (only) if sub-condition (i) holds. To compute the
++	 * value of this variable, we not only use the return value of
++	 * the function bfq_symmetric_scenario(), but also check
++	 * whether bfqq is being weight-raised, because
++	 * bfq_symmetric_scenario() does not take into account also
++	 * weight-raised queues (see comments on
++	 * bfq_weights_tree_add()).
++	 *
++	 * As a side note, it is worth considering that the above
++	 * device-idling countermeasures may however fail in the
++	 * following unlucky scenario: if idling is (correctly)
++	 * disabled in a time period during which all symmetry
++	 * sub-conditions hold, and hence the device is allowed to
++	 * enqueue many requests, but at some later point in time some
++	 * sub-condition stops to hold, then it may become impossible
++	 * to let requests be served in the desired order until all
++	 * the requests already queued in the device have been served.
++	 */
++	asymmetric_scenario = bfqq->wr_coeff > 1 ||
++		!bfq_symmetric_scenario(bfqd);
++
++	/*
++	 * Finally, there is a case where maximizing throughput is the
++	 * best choice even if it may cause unfairness toward
++	 * bfqq. Such a case is when bfqq became active in a burst of
++	 * queue activations. Queues that became active during a large
++	 * burst benefit only from throughput, as discussed in the
++	 * comments on bfq_handle_burst. Thus, if bfqq became active
++	 * in a burst and not idling the device maximizes throughput,
++	 * then the device must no be idled, because not idling the
++	 * device provides bfqq and all other queues in the burst with
++	 * maximum benefit. Combining this and the above case, we can
++	 * now establish when idling is actually needed to preserve
++	 * service guarantees.
++	 */
++	idling_needed_for_service_guarantees =
++		asymmetric_scenario && !bfq_bfqq_in_large_burst(bfqq);
++
++	/*
++	 * We have now all the components we need to compute the return
++	 * value of the function, which is true only if both the following
++	 * conditions hold:
++	 * 1) bfqq is sync, because idling make sense only for sync queues;
++	 * 2) idling either boosts the throughput (without issues), or
++	 *    is necessary to preserve service guarantees.
++	 */
++	bfq_log_bfqq(bfqd, bfqq, "may_idle: sync %d idling_boosts_thr %d",
++		     bfq_bfqq_sync(bfqq), idling_boosts_thr);
++
++	bfq_log_bfqq(bfqd, bfqq,
++		     "may_idle: wr_busy %d boosts %d IO-bound %d guar %d",
++		     bfqd->wr_busy_queues,
++		     idling_boosts_thr_without_issues,
++		     bfq_bfqq_IO_bound(bfqq),
++		     idling_needed_for_service_guarantees);
++
++	return bfq_bfqq_sync(bfqq) &&
++		(idling_boosts_thr_without_issues ||
++		 idling_needed_for_service_guarantees);
++}
++
++/*
++ * If the in-service queue is empty but the function bfq_bfqq_may_idle
++ * returns true, then:
++ * 1) the queue must remain in service and cannot be expired, and
++ * 2) the device must be idled to wait for the possible arrival of a new
++ *    request for the queue.
++ * See the comments on the function bfq_bfqq_may_idle for the reasons
++ * why performing device idling is the best choice to boost the throughput
++ * and preserve service guarantees when bfq_bfqq_may_idle itself
++ * returns true.
++ */
++static bool bfq_bfqq_must_idle(struct bfq_queue *bfqq)
++{
++	struct bfq_data *bfqd = bfqq->bfqd;
++
++	return RB_EMPTY_ROOT(&bfqq->sort_list) && bfqd->bfq_slice_idle != 0 &&
++	       bfq_bfqq_may_idle(bfqq);
++}
++
++/*
++ * Select a queue for service.  If we have a current queue in service,
++ * check whether to continue servicing it, or retrieve and set a new one.
++ */
++static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd)
++{
++	struct bfq_queue *bfqq;
++	struct request *next_rq;
++	enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT;
++
++	bfqq = bfqd->in_service_queue;
++	if (!bfqq)
++		goto new_queue;
++
++	bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue");
++
++	if (bfq_may_expire_for_budg_timeout(bfqq) &&
++	    !hrtimer_active(&bfqd->idle_slice_timer) &&
++	    !bfq_bfqq_must_idle(bfqq))
++		goto expire;
++
++check_queue:
++	/*
++	 * This loop is rarely executed more than once. Even when it
++	 * happens, it is much more convenient to re-execute this loop
++	 * than to return NULL and trigger a new dispatch to get a
++	 * request served.
++	 */
++	next_rq = bfqq->next_rq;
++	/*
++	 * If bfqq has requests queued and it has enough budget left to
++	 * serve them, keep the queue, otherwise expire it.
++	 */
++	if (next_rq) {
++		BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list));
++
++		if (bfq_serv_to_charge(next_rq, bfqq) >
++			bfq_bfqq_budget_left(bfqq)) {
++			/*
++			 * Expire the queue for budget exhaustion,
++			 * which makes sure that the next budget is
++			 * enough to serve the next request, even if
++			 * it comes from the fifo expired path.
++			 */
++			reason = BFQ_BFQQ_BUDGET_EXHAUSTED;
++			goto expire;
++		} else {
++			/*
++			 * The idle timer may be pending because we may
++			 * not disable disk idling even when a new request
++			 * arrives.
++			 */
++			if (bfq_bfqq_wait_request(bfqq)) {
++				BUG_ON(!hrtimer_active(&bfqd->idle_slice_timer));
++				/*
++				 * If we get here: 1) at least a new request
++				 * has arrived but we have not disabled the
++				 * timer because the request was too small,
++				 * 2) then the block layer has unplugged
++				 * the device, causing the dispatch to be
++				 * invoked.
++				 *
++				 * Since the device is unplugged, now the
++				 * requests are probably large enough to
++				 * provide a reasonable throughput.
++				 * So we disable idling.
++				 */
++				bfq_clear_bfqq_wait_request(bfqq);
++				hrtimer_try_to_cancel(&bfqd->idle_slice_timer);
++				bfqg_stats_update_idle_time(bfqq_group(bfqq));
++			}
++			goto keep_queue;
++		}
++	}
++
++	/*
++	 * No requests pending. However, if the in-service queue is idling
++	 * for a new request, or has requests waiting for a completion and
++	 * may idle after their completion, then keep it anyway.
++	 */
++	if (hrtimer_active(&bfqd->idle_slice_timer) ||
++	    (bfqq->dispatched != 0 && bfq_bfqq_may_idle(bfqq))) {
++		bfqq = NULL;
++		goto keep_queue;
++	}
++
++	reason = BFQ_BFQQ_NO_MORE_REQUESTS;
++expire:
++	bfq_bfqq_expire(bfqd, bfqq, false, reason);
++new_queue:
++	bfqq = bfq_set_in_service_queue(bfqd);
++	if (bfqq) {
++		bfq_log_bfqq(bfqd, bfqq, "select_queue: checking new queue");
++		goto check_queue;
++	}
++keep_queue:
++	if (bfqq)
++		bfq_log_bfqq(bfqd, bfqq, "select_queue: returned this queue");
++	else
++		bfq_log(bfqd, "select_queue: no queue returned");
++
++	return bfqq;
++}
++
++static void bfq_update_wr_data(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	if (bfqq->wr_coeff > 1) { /* queue is being weight-raised */
++		BUG_ON(bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time &&
++		       time_is_after_jiffies(bfqq->last_wr_start_finish));
++
++		bfq_log_bfqq(bfqd, bfqq,
++			"raising period dur %u/%u msec, old coeff %u, w %d(%d)",
++			jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish),
++			jiffies_to_msecs(bfqq->wr_cur_max_time),
++			bfqq->wr_coeff,
++			bfqq->entity.weight, bfqq->entity.orig_weight);
++
++		BUG_ON(bfqq != bfqd->in_service_queue && entity->weight !=
++		       entity->orig_weight * bfqq->wr_coeff);
++		if (entity->prio_changed)
++			bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change");
++
++		/*
++		 * If the queue was activated in a burst, or too much
++		 * time has elapsed from the beginning of this
++		 * weight-raising period, then end weight raising.
++		 */
++		if (bfq_bfqq_in_large_burst(bfqq))
++			bfq_bfqq_end_wr(bfqq);
++		else if (time_is_before_jiffies(bfqq->last_wr_start_finish +
++					   bfqq->wr_cur_max_time)) {
++			if (bfqq->wr_cur_max_time != bfqd->bfq_wr_rt_max_time ||
++			time_is_before_jiffies(bfqq->wr_start_at_switch_to_srt +
++					bfq_wr_duration(bfqd)))
++				bfq_bfqq_end_wr(bfqq);
++			else {
++				/* switch back to interactive wr */
++				bfqq->wr_coeff = bfqd->bfq_wr_coeff;
++				bfqq->wr_cur_max_time = bfq_wr_duration(bfqd);
++				bfqq->last_wr_start_finish =
++					bfqq->wr_start_at_switch_to_srt;
++				BUG_ON(time_is_after_jiffies(
++					       bfqq->last_wr_start_finish));
++				bfqq->entity.prio_changed = 1;
++				bfq_log_bfqq(bfqd, bfqq,
++					"back to interactive wr");
++			}
++		}
++	}
++	/* Update weight both if it must be raised and if it must be lowered */
++	if ((entity->weight > entity->orig_weight) != (bfqq->wr_coeff > 1))
++		__bfq_entity_update_weight_prio(
++			bfq_entity_service_tree(entity),
++			entity);
++}
++
++/*
++ * Dispatch one request from bfqq, moving it to the request queue
++ * dispatch list.
++ */
++static int bfq_dispatch_request(struct bfq_data *bfqd,
++				struct bfq_queue *bfqq)
++{
++	int dispatched = 0;
++	struct request *rq = bfqq->next_rq;
++	unsigned long service_to_charge;
++
++	BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list));
++	BUG_ON(!rq);
++	service_to_charge = bfq_serv_to_charge(rq, bfqq);
++
++	BUG_ON(service_to_charge > bfq_bfqq_budget_left(bfqq));
++
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++
++	bfq_bfqq_served(bfqq, service_to_charge);
++
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++
++	bfq_dispatch_insert(bfqd->queue, rq);
++
++	/*
++	 * If weight raising has to terminate for bfqq, then next
++	 * function causes an immediate update of bfqq's weight,
++	 * without waiting for next activation. As a consequence, on
++	 * expiration, bfqq will be timestamped as if has never been
++	 * weight-raised during this service slot, even if it has
++	 * received part or even most of the service as a
++	 * weight-raised queue. This inflates bfqq's timestamps, which
++	 * is beneficial, as bfqq is then more willing to leave the
++	 * device immediately to possible other weight-raised queues.
++	 */
++	bfq_update_wr_data(bfqd, bfqq);
++
++	bfq_log_bfqq(bfqd, bfqq,
++			"dispatched %u sec req (%llu), budg left %d",
++			blk_rq_sectors(rq),
++			(unsigned long long) blk_rq_pos(rq),
++			bfq_bfqq_budget_left(bfqq));
++
++	dispatched++;
++
++	if (!bfqd->in_service_bic) {
++		atomic_long_inc(&RQ_BIC(rq)->icq.ioc->refcount);
++		bfqd->in_service_bic = RQ_BIC(rq);
++	}
++
++	if (bfqd->busy_queues > 1 && bfq_class_idle(bfqq))
++		goto expire;
++
++	return dispatched;
++
++expire:
++	bfq_bfqq_expire(bfqd, bfqq, false, BFQ_BFQQ_BUDGET_EXHAUSTED);
++	return dispatched;
++}
++
++static int __bfq_forced_dispatch_bfqq(struct bfq_queue *bfqq)
++{
++	int dispatched = 0;
++
++	while (bfqq->next_rq) {
++		bfq_dispatch_insert(bfqq->bfqd->queue, bfqq->next_rq);
++		dispatched++;
++	}
++
++	BUG_ON(!list_empty(&bfqq->fifo));
++	return dispatched;
++}
++
++/*
++ * Drain our current requests.
++ * Used for barriers and when switching io schedulers on-the-fly.
++ */
++static int bfq_forced_dispatch(struct bfq_data *bfqd)
++{
++	struct bfq_queue *bfqq, *n;
++	struct bfq_service_tree *st;
++	int dispatched = 0;
++
++	bfqq = bfqd->in_service_queue;
++	if (bfqq)
++		__bfq_bfqq_expire(bfqd, bfqq);
++
++	/*
++	 * Loop through classes, and be careful to leave the scheduler
++	 * in a consistent state, as feedback mechanisms and vtime
++	 * updates cannot be disabled during the process.
++	 */
++	list_for_each_entry_safe(bfqq, n, &bfqd->active_list, bfqq_list) {
++		st = bfq_entity_service_tree(&bfqq->entity);
++
++		dispatched += __bfq_forced_dispatch_bfqq(bfqq);
++
++		bfqq->max_budget = bfq_max_budget(bfqd);
++		bfq_forget_idle(st);
++	}
++
++	BUG_ON(bfqd->busy_queues != 0);
++
++	return dispatched;
++}
++
++static int bfq_dispatch_requests(struct request_queue *q, int force)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	struct bfq_queue *bfqq;
++
++	bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues);
++
++	if (bfqd->busy_queues == 0)
++		return 0;
++
++	if (unlikely(force))
++		return bfq_forced_dispatch(bfqd);
++
++	/*
++	 * Force device to serve one request at a time if
++	 * strict_guarantees is true. Forcing this service scheme is
++	 * currently the ONLY way to guarantee that the request
++	 * service order enforced by the scheduler is respected by a
++	 * queueing device. Otherwise the device is free even to make
++	 * some unlucky request wait for as long as the device
++	 * wishes.
++	 *
++	 * Of course, serving one request at at time may cause loss of
++	 * throughput.
++	 */
++	if (bfqd->strict_guarantees && bfqd->rq_in_driver > 0)
++		return 0;
++
++	bfqq = bfq_select_queue(bfqd);
++	if (!bfqq)
++		return 0;
++
++	BUG_ON(bfqq->entity.budget < bfqq->entity.service);
++
++	BUG_ON(bfq_bfqq_wait_request(bfqq));
++
++	if (!bfq_dispatch_request(bfqd, bfqq))
++		return 0;
++
++	bfq_log_bfqq(bfqd, bfqq, "dispatched %s request",
++			bfq_bfqq_sync(bfqq) ? "sync" : "async");
++
++	BUG_ON(bfqq->next_rq == NULL &&
++	       bfqq->entity.budget < bfqq->entity.service);
++	return 1;
++}
++
++/*
++ * Task holds one reference to the queue, dropped when task exits.  Each rq
++ * in-flight on this queue also holds a reference, dropped when rq is freed.
++ *
++ * Queue lock must be held here. Recall not to use bfqq after calling
++ * this function on it.
++ */
++static void bfq_put_queue(struct bfq_queue *bfqq)
++{
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	struct bfq_group *bfqg = bfqq_group(bfqq);
++#endif
++
++	BUG_ON(bfqq->ref <= 0);
++
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p %d", bfqq, bfqq->ref);
++	bfqq->ref--;
++	if (bfqq->ref)
++		return;
++
++	BUG_ON(rb_first(&bfqq->sort_list));
++	BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0);
++	BUG_ON(bfqq->entity.tree);
++	BUG_ON(bfq_bfqq_busy(bfqq));
++
++	if (bfq_bfqq_sync(bfqq))
++		/*
++		 * The fact that this queue is being destroyed does not
++		 * invalidate the fact that this queue may have been
++		 * activated during the current burst. As a consequence,
++		 * although the queue does not exist anymore, and hence
++		 * needs to be removed from the burst list if there,
++		 * the burst size has not to be decremented.
++		 */
++		hlist_del_init(&bfqq->burst_list_node);
++
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p freed", bfqq);
++
++	kmem_cache_free(bfq_pool, bfqq);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	bfqg_put(bfqg);
++#endif
++}
++
++static void bfq_put_cooperator(struct bfq_queue *bfqq)
++{
++	struct bfq_queue *__bfqq, *next;
++
++	/*
++	 * If this queue was scheduled to merge with another queue, be
++	 * sure to drop the reference taken on that queue (and others in
++	 * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs.
++	 */
++	__bfqq = bfqq->new_bfqq;
++	while (__bfqq) {
++		if (__bfqq == bfqq)
++			break;
++		next = __bfqq->new_bfqq;
++		bfq_put_queue(__bfqq);
++		__bfqq = next;
++	}
++}
++
++static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	if (bfqq == bfqd->in_service_queue) {
++		__bfq_bfqq_expire(bfqd, bfqq);
++		bfq_schedule_dispatch(bfqd);
++	}
++
++	bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, bfqq->ref);
++
++	bfq_put_cooperator(bfqq);
++
++	bfq_put_queue(bfqq); /* release process reference */
++}
++
++static void bfq_init_icq(struct io_cq *icq)
++{
++	icq_to_bic(icq)->ttime.last_end_request = ktime_get_ns() - (1ULL<<32);
++}
++
++static void bfq_exit_icq(struct io_cq *icq)
++{
++	struct bfq_io_cq *bic = icq_to_bic(icq);
++	struct bfq_data *bfqd = bic_to_bfqd(bic);
++
++	if (bic_to_bfqq(bic, false)) {
++		bfq_exit_bfqq(bfqd, bic_to_bfqq(bic, false));
++		bic_set_bfqq(bic, NULL, false);
++	}
++
++	if (bic_to_bfqq(bic, true)) {
++		/*
++		 * If the bic is using a shared queue, put the reference
++		 * taken on the io_context when the bic started using a
++		 * shared bfq_queue.
++		 */
++		if (bfq_bfqq_coop(bic_to_bfqq(bic, true)))
++			put_io_context(icq->ioc);
++		bfq_exit_bfqq(bfqd, bic_to_bfqq(bic, true));
++		bic_set_bfqq(bic, NULL, true);
++	}
++}
++
++/*
++ * Update the entity prio values; note that the new values will not
++ * be used until the next (re)activation.
++ */
++static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq,
++				     struct bfq_io_cq *bic)
++{
++	struct task_struct *tsk = current;
++	int ioprio_class;
++
++	ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio);
++	switch (ioprio_class) {
++	default:
++		dev_err(bfqq->bfqd->queue->backing_dev_info->dev,
++			"bfq: bad prio class %d\n", ioprio_class);
++	case IOPRIO_CLASS_NONE:
++		/*
++		 * No prio set, inherit CPU scheduling settings.
++		 */
++		bfqq->new_ioprio = task_nice_ioprio(tsk);
++		bfqq->new_ioprio_class = task_nice_ioclass(tsk);
++		break;
++	case IOPRIO_CLASS_RT:
++		bfqq->new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio);
++		bfqq->new_ioprio_class = IOPRIO_CLASS_RT;
++		break;
++	case IOPRIO_CLASS_BE:
++		bfqq->new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio);
++		bfqq->new_ioprio_class = IOPRIO_CLASS_BE;
++		break;
++	case IOPRIO_CLASS_IDLE:
++		bfqq->new_ioprio_class = IOPRIO_CLASS_IDLE;
++		bfqq->new_ioprio = 7;
++		bfq_clear_bfqq_idle_window(bfqq);
++		break;
++	}
++
++	if (bfqq->new_ioprio >= IOPRIO_BE_NR) {
++		pr_crit("bfq_set_next_ioprio_data: new_ioprio %d\n",
++			bfqq->new_ioprio);
++		BUG();
++	}
++
++	bfqq->entity.new_weight = bfq_ioprio_to_weight(bfqq->new_ioprio);
++	bfqq->entity.prio_changed = 1;
++	bfq_log_bfqq(bfqq->bfqd, bfqq,
++		     "set_next_ioprio_data: bic_class %d prio %d class %d",
++		     ioprio_class, bfqq->new_ioprio, bfqq->new_ioprio_class);
++}
++
++static void bfq_check_ioprio_change(struct bfq_io_cq *bic, struct bio *bio)
++{
++	struct bfq_data *bfqd = bic_to_bfqd(bic);
++	struct bfq_queue *bfqq;
++	unsigned long uninitialized_var(flags);
++	int ioprio = bic->icq.ioc->ioprio;
++
++	/*
++	 * This condition may trigger on a newly created bic, be sure to
++	 * drop the lock before returning.
++	 */
++	if (unlikely(!bfqd) || likely(bic->ioprio == ioprio))
++		return;
++
++	bic->ioprio = ioprio;
++
++	bfqq = bic_to_bfqq(bic, false);
++	if (bfqq) {
++		/* release process reference on this queue */
++		bfq_put_queue(bfqq);
++		bfqq = bfq_get_queue(bfqd, bio, BLK_RW_ASYNC, bic);
++		bic_set_bfqq(bic, bfqq, false);
++		bfq_log_bfqq(bfqd, bfqq,
++			     "check_ioprio_change: bfqq %p %d",
++			     bfqq, bfqq->ref);
++	}
++
++	bfqq = bic_to_bfqq(bic, true);
++	if (bfqq)
++		bfq_set_next_ioprio_data(bfqq, bic);
++}
++
++static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++			  struct bfq_io_cq *bic, pid_t pid, int is_sync)
++{
++	RB_CLEAR_NODE(&bfqq->entity.rb_node);
++	INIT_LIST_HEAD(&bfqq->fifo);
++	INIT_HLIST_NODE(&bfqq->burst_list_node);
++	BUG_ON(!hlist_unhashed(&bfqq->burst_list_node));
++
++	bfqq->ref = 0;
++	bfqq->bfqd = bfqd;
++
++	if (bic)
++		bfq_set_next_ioprio_data(bfqq, bic);
++
++	if (is_sync) {
++		if (!bfq_class_idle(bfqq))
++			bfq_mark_bfqq_idle_window(bfqq);
++		bfq_mark_bfqq_sync(bfqq);
++		bfq_mark_bfqq_just_created(bfqq);
++	} else
++		bfq_clear_bfqq_sync(bfqq);
++	bfq_mark_bfqq_IO_bound(bfqq);
++
++	/* Tentative initial value to trade off between thr and lat */
++	bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3;
++	bfqq->pid = pid;
++
++	bfqq->wr_coeff = 1;
++	bfqq->last_wr_start_finish = jiffies;
++	bfqq->wr_start_at_switch_to_srt = bfq_smallest_from_now();
++	bfqq->budget_timeout = bfq_smallest_from_now();
++	bfqq->split_time = bfq_smallest_from_now();
++
++	/*
++	 * Set to the value for which bfqq will not be deemed as
++	 * soft rt when it becomes backlogged.
++	 */
++	bfqq->soft_rt_next_start = bfq_greatest_from_now();
++
++	/* first request is almost certainly seeky */
++	bfqq->seek_history = 1;
++}
++
++static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd,
++					       struct bfq_group *bfqg,
++					       int ioprio_class, int ioprio)
++{
++	switch (ioprio_class) {
++	case IOPRIO_CLASS_RT:
++		return &bfqg->async_bfqq[0][ioprio];
++	case IOPRIO_CLASS_NONE:
++		ioprio = IOPRIO_NORM;
++		/* fall through */
++	case IOPRIO_CLASS_BE:
++		return &bfqg->async_bfqq[1][ioprio];
++	case IOPRIO_CLASS_IDLE:
++		return &bfqg->async_idle_bfqq;
++	default:
++		BUG();
++	}
++}
++
++static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd,
++				       struct bio *bio, bool is_sync,
++				       struct bfq_io_cq *bic)
++{
++	const int ioprio = IOPRIO_PRIO_DATA(bic->ioprio);
++	const int ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio);
++	struct bfq_queue **async_bfqq = NULL;
++	struct bfq_queue *bfqq;
++	struct bfq_group *bfqg;
++
++	rcu_read_lock();
++
++	bfqg = bfq_find_set_group(bfqd, bio_blkcg(bio));
++	if (!bfqg) {
++		bfqq = &bfqd->oom_bfqq;
++		goto out;
++	}
++
++	if (!is_sync) {
++		async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class,
++						  ioprio);
++		bfqq = *async_bfqq;
++		if (bfqq)
++			goto out;
++	}
++
++	bfqq = kmem_cache_alloc_node(bfq_pool,
++				     GFP_NOWAIT | __GFP_ZERO | __GFP_NOWARN,
++				     bfqd->queue->node);
++
++	if (bfqq) {
++		bfq_init_bfqq(bfqd, bfqq, bic, current->pid,
++			      is_sync);
++		bfq_init_entity(&bfqq->entity, bfqg);
++		bfq_log_bfqq(bfqd, bfqq, "allocated");
++	} else {
++		bfqq = &bfqd->oom_bfqq;
++		bfq_log_bfqq(bfqd, bfqq, "using oom bfqq");
++		goto out;
++	}
++
++	/*
++	 * Pin the queue now that it's allocated, scheduler exit will
++	 * prune it.
++	 */
++	if (async_bfqq) {
++		bfqq->ref++; /*
++			      * Extra group reference, w.r.t. sync
++			      * queue. This extra reference is removed
++			      * only if bfqq->bfqg disappears, to
++			      * guarantee that this queue is not freed
++			      * until its group goes away.
++			      */
++		bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d",
++			     bfqq, bfqq->ref);
++		*async_bfqq = bfqq;
++	}
++
++out:
++	bfqq->ref++; /* get a process reference to this queue */
++	bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, bfqq->ref);
++	rcu_read_unlock();
++	return bfqq;
++}
++
++static void bfq_update_io_thinktime(struct bfq_data *bfqd,
++				    struct bfq_io_cq *bic)
++{
++	struct bfq_ttime *ttime = &bic->ttime;
++	u64 elapsed = ktime_get_ns() - bic->ttime.last_end_request;
++
++	elapsed = min_t(u64, elapsed, 2 * bfqd->bfq_slice_idle);
++
++	ttime->ttime_samples = (7*bic->ttime.ttime_samples + 256) / 8;
++	ttime->ttime_total = div_u64(7*ttime->ttime_total + 256*elapsed,  8);
++	ttime->ttime_mean = div64_ul(ttime->ttime_total + 128,
++				     ttime->ttime_samples);
++}
++
++static void
++bfq_update_io_seektime(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++		       struct request *rq)
++{
++	bfqq->seek_history <<= 1;
++	bfqq->seek_history |=
++		get_sdist(bfqq->last_request_pos, rq) > BFQQ_SEEK_THR &&
++		(!blk_queue_nonrot(bfqd->queue) ||
++		 blk_rq_sectors(rq) < BFQQ_SECT_THR_NONROT);
++}
++
++/*
++ * Disable idle window if the process thinks too long or seeks so much that
++ * it doesn't matter.
++ */
++static void bfq_update_idle_window(struct bfq_data *bfqd,
++				   struct bfq_queue *bfqq,
++				   struct bfq_io_cq *bic)
++{
++	int enable_idle;
++
++	/* Don't idle for async or idle io prio class. */
++	if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq))
++		return;
++
++	/* Idle window just restored, statistics are meaningless. */
++	if (time_is_after_eq_jiffies(bfqq->split_time +
++				     bfqd->bfq_wr_min_idle_time))
++		return;
++
++	enable_idle = bfq_bfqq_idle_window(bfqq);
++
++	if (atomic_read(&bic->icq.ioc->active_ref) == 0 ||
++	    bfqd->bfq_slice_idle == 0 ||
++		(bfqd->hw_tag && BFQQ_SEEKY(bfqq) &&
++			bfqq->wr_coeff == 1))
++		enable_idle = 0;
++	else if (bfq_sample_valid(bic->ttime.ttime_samples)) {
++		if (bic->ttime.ttime_mean > bfqd->bfq_slice_idle &&
++			bfqq->wr_coeff == 1)
++			enable_idle = 0;
++		else
++			enable_idle = 1;
++	}
++	bfq_log_bfqq(bfqd, bfqq, "update_idle_window: enable_idle %d",
++		enable_idle);
++
++	if (enable_idle)
++		bfq_mark_bfqq_idle_window(bfqq);
++	else
++		bfq_clear_bfqq_idle_window(bfqq);
++}
++
++/*
++ * Called when a new fs request (rq) is added to bfqq.  Check if there's
++ * something we should do about it.
++ */
++static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++			    struct request *rq)
++{
++	struct bfq_io_cq *bic = RQ_BIC(rq);
++
++	if (rq->cmd_flags & REQ_META)
++		bfqq->meta_pending++;
++
++	bfq_update_io_thinktime(bfqd, bic);
++	bfq_update_io_seektime(bfqd, bfqq, rq);
++	if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 ||
++	    !BFQQ_SEEKY(bfqq))
++		bfq_update_idle_window(bfqd, bfqq, bic);
++
++	bfq_log_bfqq(bfqd, bfqq,
++		     "rq_enqueued: idle_window=%d (seeky %d)",
++		     bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq));
++
++	bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq);
++
++	if (bfqq == bfqd->in_service_queue && bfq_bfqq_wait_request(bfqq)) {
++		bool small_req = bfqq->queued[rq_is_sync(rq)] == 1 &&
++				 blk_rq_sectors(rq) < 32;
++		bool budget_timeout = bfq_bfqq_budget_timeout(bfqq);
++
++		/*
++		 * There is just this request queued: if the request
++		 * is small and the queue is not to be expired, then
++		 * just exit.
++		 *
++		 * In this way, if the device is being idled to wait
++		 * for a new request from the in-service queue, we
++		 * avoid unplugging the device and committing the
++		 * device to serve just a small request. On the
++		 * contrary, we wait for the block layer to decide
++		 * when to unplug the device: hopefully, new requests
++		 * will be merged to this one quickly, then the device
++		 * will be unplugged and larger requests will be
++		 * dispatched.
++		 */
++		if (small_req && !budget_timeout)
++			return;
++
++		/*
++		 * A large enough request arrived, or the queue is to
++		 * be expired: in both cases disk idling is to be
++		 * stopped, so clear wait_request flag and reset
++		 * timer.
++		 */
++		bfq_clear_bfqq_wait_request(bfqq);
++		hrtimer_try_to_cancel(&bfqd->idle_slice_timer);
++		bfqg_stats_update_idle_time(bfqq_group(bfqq));
++
++		/*
++		 * The queue is not empty, because a new request just
++		 * arrived. Hence we can safely expire the queue, in
++		 * case of budget timeout, without risking that the
++		 * timestamps of the queue are not updated correctly.
++		 * See [1] for more details.
++		 */
++		if (budget_timeout)
++			bfq_bfqq_expire(bfqd, bfqq, false,
++					BFQ_BFQQ_BUDGET_TIMEOUT);
++
++		/*
++		 * Let the request rip immediately, or let a new queue be
++		 * selected if bfqq has just been expired.
++		 */
++		__blk_run_queue(bfqd->queue);
++	}
++}
++
++static void bfq_insert_request(struct request_queue *q, struct request *rq)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	struct bfq_queue *bfqq = RQ_BFQQ(rq), *new_bfqq;
++
++	assert_spin_locked(bfqd->queue->queue_lock);
++
++	/*
++	 * An unplug may trigger a requeue of a request from the device
++	 * driver: make sure we are in process context while trying to
++	 * merge two bfq_queues.
++	 */
++	if (!in_interrupt()) {
++		new_bfqq = bfq_setup_cooperator(bfqd, bfqq, rq, true);
++		if (new_bfqq) {
++			if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq)
++				new_bfqq = bic_to_bfqq(RQ_BIC(rq), 1);
++			/*
++			 * Release the request's reference to the old bfqq
++			 * and make sure one is taken to the shared queue.
++			 */
++			new_bfqq->allocated[rq_data_dir(rq)]++;
++			bfqq->allocated[rq_data_dir(rq)]--;
++			new_bfqq->ref++;
++			bfq_clear_bfqq_just_created(bfqq);
++			if (bic_to_bfqq(RQ_BIC(rq), 1) == bfqq)
++				bfq_merge_bfqqs(bfqd, RQ_BIC(rq),
++						bfqq, new_bfqq);
++			/*
++			 * rq is about to be enqueued into new_bfqq,
++			 * release rq reference on bfqq
++			 */
++			bfq_put_queue(bfqq);
++			rq->elv.priv[1] = new_bfqq;
++			bfqq = new_bfqq;
++		}
++	}
++
++	bfq_add_request(rq);
++
++	rq->fifo_time = ktime_get_ns() + bfqd->bfq_fifo_expire[rq_is_sync(rq)];
++	list_add_tail(&rq->queuelist, &bfqq->fifo);
++
++	bfq_rq_enqueued(bfqd, bfqq, rq);
++}
++
++static void bfq_update_hw_tag(struct bfq_data *bfqd)
++{
++	bfqd->max_rq_in_driver = max_t(int, bfqd->max_rq_in_driver,
++				       bfqd->rq_in_driver);
++
++	if (bfqd->hw_tag == 1)
++		return;
++
++	/*
++	 * This sample is valid if the number of outstanding requests
++	 * is large enough to allow a queueing behavior.  Note that the
++	 * sum is not exact, as it's not taking into account deactivated
++	 * requests.
++	 */
++	if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD)
++		return;
++
++	if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES)
++		return;
++
++	bfqd->hw_tag = bfqd->max_rq_in_driver > BFQ_HW_QUEUE_THRESHOLD;
++	bfqd->max_rq_in_driver = 0;
++	bfqd->hw_tag_samples = 0;
++}
++
++static void bfq_completed_request(struct request_queue *q, struct request *rq)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq);
++	struct bfq_data *bfqd = bfqq->bfqd;
++	u64 now_ns;
++	u32 delta_us;
++
++	bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left",
++		     blk_rq_sectors(rq));
++
++	assert_spin_locked(bfqd->queue->queue_lock);
++	bfq_update_hw_tag(bfqd);
++
++	BUG_ON(!bfqd->rq_in_driver);
++	BUG_ON(!bfqq->dispatched);
++	bfqd->rq_in_driver--;
++	bfqq->dispatched--;
++	bfqg_stats_update_completion(bfqq_group(bfqq),
++				     rq_start_time_ns(rq),
++				     rq_io_start_time_ns(rq),
++				     rq->cmd_flags);
++
++	if (!bfqq->dispatched && !bfq_bfqq_busy(bfqq)) {
++		BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list));
++		/*
++		 * Set budget_timeout (which we overload to store the
++		 * time at which the queue remains with no backlog and
++		 * no outstanding request; used by the weight-raising
++		 * mechanism).
++		 */
++		bfqq->budget_timeout = jiffies;
++
++		bfq_weights_tree_remove(bfqd, &bfqq->entity,
++					&bfqd->queue_weights_tree);
++	}
++
++	now_ns = ktime_get_ns();
++
++	RQ_BIC(rq)->ttime.last_end_request = now_ns;
++
++	/*
++	 * Using us instead of ns, to get a reasonable precision in
++	 * computing rate in next check.
++	 */
++	delta_us = div_u64(now_ns - bfqd->last_completion, NSEC_PER_USEC);
++
++	bfq_log(bfqd, "rq_completed: delta %uus/%luus max_size %u rate %llu/%llu",
++		delta_us, BFQ_MIN_TT/NSEC_PER_USEC, bfqd->last_rq_max_size,
++		(USEC_PER_SEC*
++		(u64)((bfqd->last_rq_max_size<<BFQ_RATE_SHIFT)/delta_us))
++			>>BFQ_RATE_SHIFT,
++		(USEC_PER_SEC*(u64)(1UL<<(BFQ_RATE_SHIFT-10)))>>BFQ_RATE_SHIFT);
++
++	/*
++	 * If the request took rather long to complete, and, according
++	 * to the maximum request size recorded, this completion latency
++	 * implies that the request was certainly served at a very low
++	 * rate (less than 1M sectors/sec), then the whole observation
++	 * interval that lasts up to this time instant cannot be a
++	 * valid time interval for computing a new peak rate.  Invoke
++	 * bfq_update_rate_reset to have the following three steps
++	 * taken:
++	 * - close the observation interval at the last (previous)
++	 *   request dispatch or completion
++	 * - compute rate, if possible, for that observation interval
++	 * - reset to zero samples, which will trigger a proper
++	 *   re-initialization of the observation interval on next
++	 *   dispatch
++	 */
++	if (delta_us > BFQ_MIN_TT/NSEC_PER_USEC &&
++	   (bfqd->last_rq_max_size<<BFQ_RATE_SHIFT)/delta_us <
++			1UL<<(BFQ_RATE_SHIFT - 10))
++		bfq_update_rate_reset(bfqd, NULL);
++	bfqd->last_completion = now_ns;
++
++	/*
++	 * If we are waiting to discover whether the request pattern
++	 * of the task associated with the queue is actually
++	 * isochronous, and both requisites for this condition to hold
++	 * are now satisfied, then compute soft_rt_next_start (see the
++	 * comments on the function bfq_bfqq_softrt_next_start()). We
++	 * schedule this delayed check when bfqq expires, if it still
++	 * has in-flight requests.
++	 */
++	if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 &&
++	    RB_EMPTY_ROOT(&bfqq->sort_list))
++		bfqq->soft_rt_next_start =
++			bfq_bfqq_softrt_next_start(bfqd, bfqq);
++
++	/*
++	 * If this is the in-service queue, check if it needs to be expired,
++	 * or if we want to idle in case it has no pending requests.
++	 */
++	if (bfqd->in_service_queue == bfqq) {
++		if (bfqq->dispatched == 0 && bfq_bfqq_must_idle(bfqq)) {
++			bfq_arm_slice_timer(bfqd);
++			goto out;
++		} else if (bfq_may_expire_for_budg_timeout(bfqq))
++			bfq_bfqq_expire(bfqd, bfqq, false,
++					BFQ_BFQQ_BUDGET_TIMEOUT);
++		else if (RB_EMPTY_ROOT(&bfqq->sort_list) &&
++			 (bfqq->dispatched == 0 ||
++			  !bfq_bfqq_may_idle(bfqq)))
++			bfq_bfqq_expire(bfqd, bfqq, false,
++					BFQ_BFQQ_NO_MORE_REQUESTS);
++	}
++
++	if (!bfqd->rq_in_driver)
++		bfq_schedule_dispatch(bfqd);
++
++out:
++	return;
++}
++
++static int __bfq_may_queue(struct bfq_queue *bfqq)
++{
++	if (bfq_bfqq_wait_request(bfqq) && bfq_bfqq_must_alloc(bfqq)) {
++		bfq_clear_bfqq_must_alloc(bfqq);
++		return ELV_MQUEUE_MUST;
++	}
++
++	return ELV_MQUEUE_MAY;
++}
++
++static int bfq_may_queue(struct request_queue *q, unsigned int op)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	struct task_struct *tsk = current;
++	struct bfq_io_cq *bic;
++	struct bfq_queue *bfqq;
++
++	/*
++	 * Don't force setup of a queue from here, as a call to may_queue
++	 * does not necessarily imply that a request actually will be
++	 * queued. So just lookup a possibly existing queue, or return
++	 * 'may queue' if that fails.
++	 */
++	bic = bfq_bic_lookup(bfqd, tsk->io_context);
++	if (!bic)
++		return ELV_MQUEUE_MAY;
++
++	bfqq = bic_to_bfqq(bic, op_is_sync(op));
++	if (bfqq)
++		return __bfq_may_queue(bfqq);
++
++	return ELV_MQUEUE_MAY;
++}
++
++/*
++ * Queue lock held here.
++ */
++static void bfq_put_request(struct request *rq)
++{
++	struct bfq_queue *bfqq = RQ_BFQQ(rq);
++
++	if (bfqq) {
++		const int rw = rq_data_dir(rq);
++
++		BUG_ON(!bfqq->allocated[rw]);
++		bfqq->allocated[rw]--;
++
++		rq->elv.priv[0] = NULL;
++		rq->elv.priv[1] = NULL;
++
++		bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d",
++			     bfqq, bfqq->ref);
++		bfq_put_queue(bfqq);
++	}
++}
++
++/*
++ * Returns NULL if a new bfqq should be allocated, or the old bfqq if this
++ * was the last process referring to that bfqq.
++ */
++static struct bfq_queue *
++bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq)
++{
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue");
++
++	put_io_context(bic->icq.ioc);
++
++	if (bfqq_process_refs(bfqq) == 1) {
++		bfqq->pid = current->pid;
++		bfq_clear_bfqq_coop(bfqq);
++		bfq_clear_bfqq_split_coop(bfqq);
++		return bfqq;
++	}
++
++	bic_set_bfqq(bic, NULL, 1);
++
++	bfq_put_cooperator(bfqq);
++
++	bfq_put_queue(bfqq);
++	return NULL;
++}
++
++/*
++ * Allocate bfq data structures associated with this request.
++ */
++static int bfq_set_request(struct request_queue *q, struct request *rq,
++			   struct bio *bio, gfp_t gfp_mask)
++{
++	struct bfq_data *bfqd = q->elevator->elevator_data;
++	struct bfq_io_cq *bic = icq_to_bic(rq->elv.icq);
++	const int rw = rq_data_dir(rq);
++	const int is_sync = rq_is_sync(rq);
++	struct bfq_queue *bfqq;
++	unsigned long flags;
++	bool split = false;
++
++	spin_lock_irqsave(q->queue_lock, flags);
++	bfq_check_ioprio_change(bic, bio);
++
++	if (!bic)
++		goto queue_fail;
++
++	bfq_bic_update_cgroup(bic, bio);
++
++new_queue:
++	bfqq = bic_to_bfqq(bic, is_sync);
++	if (!bfqq || bfqq == &bfqd->oom_bfqq) {
++		if (bfqq)
++			bfq_put_queue(bfqq);
++		bfqq = bfq_get_queue(bfqd, bio, is_sync, bic);
++		BUG_ON(!hlist_unhashed(&bfqq->burst_list_node));
++
++		bic_set_bfqq(bic, bfqq, is_sync);
++		if (split && is_sync) {
++			bfq_log_bfqq(bfqd, bfqq,
++				     "set_request: was_in_list %d "
++				     "was_in_large_burst %d "
++				     "large burst in progress %d",
++				     bic->was_in_burst_list,
++				     bic->saved_in_large_burst,
++				     bfqd->large_burst);
++
++			if ((bic->was_in_burst_list && bfqd->large_burst) ||
++			    bic->saved_in_large_burst) {
++				bfq_log_bfqq(bfqd, bfqq,
++					     "set_request: marking in "
++					     "large burst");
++				bfq_mark_bfqq_in_large_burst(bfqq);
++			} else {
++				bfq_log_bfqq(bfqd, bfqq,
++					     "set_request: clearing in "
++					     "large burst");
++				bfq_clear_bfqq_in_large_burst(bfqq);
++				if (bic->was_in_burst_list)
++					hlist_add_head(&bfqq->burst_list_node,
++						       &bfqd->burst_list);
++			}
++			bfqq->split_time = jiffies;
++		}
++	} else {
++		/* If the queue was seeky for too long, break it apart. */
++		if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) {
++			bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq");
++
++			/* Update bic before losing reference to bfqq */
++			if (bfq_bfqq_in_large_burst(bfqq))
++				bic->saved_in_large_burst = true;
++
++			bfqq = bfq_split_bfqq(bic, bfqq);
++			split = true;
++			if (!bfqq)
++				goto new_queue;
++		}
++	}
++
++	bfqq->allocated[rw]++;
++	bfqq->ref++;
++	bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, bfqq->ref);
++
++	rq->elv.priv[0] = bic;
++	rq->elv.priv[1] = bfqq;
++
++	/*
++	 * If a bfq_queue has only one process reference, it is owned
++	 * by only one bfq_io_cq: we can set the bic field of the
++	 * bfq_queue to the address of that structure. Also, if the
++	 * queue has just been split, mark a flag so that the
++	 * information is available to the other scheduler hooks.
++	 */
++	if (likely(bfqq != &bfqd->oom_bfqq) && bfqq_process_refs(bfqq) == 1) {
++		bfqq->bic = bic;
++		if (split) {
++			/*
++			 * If the queue has just been split from a shared
++			 * queue, restore the idle window and the possible
++			 * weight raising period.
++			 */
++			bfq_bfqq_resume_state(bfqq, bic);
++		}
++	}
++
++	if (unlikely(bfq_bfqq_just_created(bfqq)))
++		bfq_handle_burst(bfqd, bfqq);
++
++	spin_unlock_irqrestore(q->queue_lock, flags);
++
++	return 0;
++
++queue_fail:
++	bfq_schedule_dispatch(bfqd);
++	spin_unlock_irqrestore(q->queue_lock, flags);
++
++	return 1;
++}
++
++static void bfq_kick_queue(struct work_struct *work)
++{
++	struct bfq_data *bfqd =
++		container_of(work, struct bfq_data, unplug_work);
++	struct request_queue *q = bfqd->queue;
++
++	spin_lock_irq(q->queue_lock);
++	__blk_run_queue(q);
++	spin_unlock_irq(q->queue_lock);
++}
++
++/*
++ * Handler of the expiration of the timer running if the in-service queue
++ * is idling inside its time slice.
++ */
++static enum hrtimer_restart bfq_idle_slice_timer(struct hrtimer *timer)
++{
++	struct bfq_data *bfqd = container_of(timer, struct bfq_data,
++					     idle_slice_timer);
++	struct bfq_queue *bfqq;
++	unsigned long flags;
++	enum bfqq_expiration reason;
++
++	spin_lock_irqsave(bfqd->queue->queue_lock, flags);
++
++	bfqq = bfqd->in_service_queue;
++	/*
++	 * Theoretical race here: the in-service queue can be NULL or
++	 * different from the queue that was idling if the timer handler
++	 * spins on the queue_lock and a new request arrives for the
++	 * current queue and there is a full dispatch cycle that changes
++	 * the in-service queue.  This can hardly happen, but in the worst
++	 * case we just expire a queue too early.
++	 */
++	if (bfqq) {
++		bfq_log_bfqq(bfqd, bfqq, "slice_timer expired");
++		bfq_clear_bfqq_wait_request(bfqq);
++
++		if (bfq_bfqq_budget_timeout(bfqq))
++			/*
++			 * Also here the queue can be safely expired
++			 * for budget timeout without wasting
++			 * guarantees
++			 */
++			reason = BFQ_BFQQ_BUDGET_TIMEOUT;
++		else if (bfqq->queued[0] == 0 && bfqq->queued[1] == 0)
++			/*
++			 * The queue may not be empty upon timer expiration,
++			 * because we may not disable the timer when the
++			 * first request of the in-service queue arrives
++			 * during disk idling.
++			 */
++			reason = BFQ_BFQQ_TOO_IDLE;
++		else
++			goto schedule_dispatch;
++
++		bfq_bfqq_expire(bfqd, bfqq, true, reason);
++	}
++
++schedule_dispatch:
++	bfq_schedule_dispatch(bfqd);
++
++	spin_unlock_irqrestore(bfqd->queue->queue_lock, flags);
++	return HRTIMER_NORESTART;
++}
++
++static void bfq_shutdown_timer_wq(struct bfq_data *bfqd)
++{
++	hrtimer_cancel(&bfqd->idle_slice_timer);
++	cancel_work_sync(&bfqd->unplug_work);
++}
++
++static void __bfq_put_async_bfqq(struct bfq_data *bfqd,
++				 struct bfq_queue **bfqq_ptr)
++{
++	struct bfq_group *root_group = bfqd->root_group;
++	struct bfq_queue *bfqq = *bfqq_ptr;
++
++	bfq_log(bfqd, "put_async_bfqq: %p", bfqq);
++	if (bfqq) {
++		bfq_bfqq_move(bfqd, bfqq, root_group);
++		bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d",
++			     bfqq, bfqq->ref);
++		bfq_put_queue(bfqq);
++		*bfqq_ptr = NULL;
++	}
++}
++
++/*
++ * Release all the bfqg references to its async queues.  If we are
++ * deallocating the group these queues may still contain requests, so
++ * we reparent them to the root cgroup (i.e., the only one that will
++ * exist for sure until all the requests on a device are gone).
++ */
++static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg)
++{
++	int i, j;
++
++	for (i = 0; i < 2; i++)
++		for (j = 0; j < IOPRIO_BE_NR; j++)
++			__bfq_put_async_bfqq(bfqd, &bfqg->async_bfqq[i][j]);
++
++	__bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq);
++}
++
++static void bfq_exit_queue(struct elevator_queue *e)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++	struct request_queue *q = bfqd->queue;
++	struct bfq_queue *bfqq, *n;
++
++	bfq_shutdown_timer_wq(bfqd);
++
++	spin_lock_irq(q->queue_lock);
++
++	BUG_ON(bfqd->in_service_queue);
++	list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list)
++		bfq_deactivate_bfqq(bfqd, bfqq, false, false);
++
++	spin_unlock_irq(q->queue_lock);
++
++	bfq_shutdown_timer_wq(bfqd);
++
++	BUG_ON(hrtimer_active(&bfqd->idle_slice_timer));
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	blkcg_deactivate_policy(q, &blkcg_policy_bfq);
++#else
++	bfq_put_async_queues(bfqd, bfqd->root_group);
++	kfree(bfqd->root_group);
++#endif
++
++	kfree(bfqd);
++}
++
++static void bfq_init_root_group(struct bfq_group *root_group,
++				struct bfq_data *bfqd)
++{
++	int i;
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	root_group->entity.parent = NULL;
++	root_group->my_entity = NULL;
++	root_group->bfqd = bfqd;
++#endif
++	root_group->rq_pos_tree = RB_ROOT;
++	for (i = 0; i < BFQ_IOPRIO_CLASSES; i++)
++		root_group->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT;
++	root_group->sched_data.bfq_class_idle_last_service = jiffies;
++}
++
++static int bfq_init_queue(struct request_queue *q, struct elevator_type *e)
++{
++	struct bfq_data *bfqd;
++	struct elevator_queue *eq;
++
++	eq = elevator_alloc(q, e);
++	if (!eq)
++		return -ENOMEM;
++
++	bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node);
++	if (!bfqd) {
++		kobject_put(&eq->kobj);
++		return -ENOMEM;
++	}
++	eq->elevator_data = bfqd;
++
++	/*
++	 * Our fallback bfqq if bfq_find_alloc_queue() runs into OOM issues.
++	 * Grab a permanent reference to it, so that the normal code flow
++	 * will not attempt to free it.
++	 */
++	bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, NULL, 1, 0);
++	bfqd->oom_bfqq.ref++;
++	bfqd->oom_bfqq.new_ioprio = BFQ_DEFAULT_QUEUE_IOPRIO;
++	bfqd->oom_bfqq.new_ioprio_class = IOPRIO_CLASS_BE;
++	bfqd->oom_bfqq.entity.new_weight =
++		bfq_ioprio_to_weight(bfqd->oom_bfqq.new_ioprio);
++
++	/* oom_bfqq does not participate to bursts */
++	bfq_clear_bfqq_just_created(&bfqd->oom_bfqq);
++	/*
++	 * Trigger weight initialization, according to ioprio, at the
++	 * oom_bfqq's first activation. The oom_bfqq's ioprio and ioprio
++	 * class won't be changed any more.
++	 */
++	bfqd->oom_bfqq.entity.prio_changed = 1;
++
++	bfqd->queue = q;
++
++	spin_lock_irq(q->queue_lock);
++	q->elevator = eq;
++	spin_unlock_irq(q->queue_lock);
++
++	bfqd->root_group = bfq_create_group_hierarchy(bfqd, q->node);
++	if (!bfqd->root_group)
++		goto out_free;
++	bfq_init_root_group(bfqd->root_group, bfqd);
++	bfq_init_entity(&bfqd->oom_bfqq.entity, bfqd->root_group);
++
++	hrtimer_init(&bfqd->idle_slice_timer, CLOCK_MONOTONIC,
++		     HRTIMER_MODE_REL);
++	bfqd->idle_slice_timer.function = bfq_idle_slice_timer;
++
++	bfqd->queue_weights_tree = RB_ROOT;
++	bfqd->group_weights_tree = RB_ROOT;
++
++	INIT_WORK(&bfqd->unplug_work, bfq_kick_queue);
++
++	INIT_LIST_HEAD(&bfqd->active_list);
++	INIT_LIST_HEAD(&bfqd->idle_list);
++	INIT_HLIST_HEAD(&bfqd->burst_list);
++
++	bfqd->hw_tag = -1;
++
++	bfqd->bfq_max_budget = bfq_default_max_budget;
++
++	bfqd->bfq_fifo_expire[0] = bfq_fifo_expire[0];
++	bfqd->bfq_fifo_expire[1] = bfq_fifo_expire[1];
++	bfqd->bfq_back_max = bfq_back_max;
++	bfqd->bfq_back_penalty = bfq_back_penalty;
++	bfqd->bfq_slice_idle = bfq_slice_idle;
++	bfqd->bfq_timeout = bfq_timeout;
++
++	bfqd->bfq_requests_within_timer = 120;
++
++	bfqd->bfq_large_burst_thresh = 8;
++	bfqd->bfq_burst_interval = msecs_to_jiffies(180);
++
++	bfqd->low_latency = true;
++
++	/*
++	 * Trade-off between responsiveness and fairness.
++	 */
++	bfqd->bfq_wr_coeff = 30;
++	bfqd->bfq_wr_rt_max_time = msecs_to_jiffies(300);
++	bfqd->bfq_wr_max_time = 0;
++	bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000);
++	bfqd->bfq_wr_min_inter_arr_async = msecs_to_jiffies(500);
++	bfqd->bfq_wr_max_softrt_rate = 7000; /*
++					      * Approximate rate required
++					      * to playback or record a
++					      * high-definition compressed
++					      * video.
++					      */
++	bfqd->wr_busy_queues = 0;
++
++	/*
++	 * Begin by assuming, optimistically, that the device is a
++	 * high-speed one, and that its peak rate is equal to 2/3 of
++	 * the highest reference rate.
++	 */
++	bfqd->RT_prod = R_fast[blk_queue_nonrot(bfqd->queue)] *
++			T_fast[blk_queue_nonrot(bfqd->queue)];
++	bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)] * 2 / 3;
++	bfqd->device_speed = BFQ_BFQD_FAST;
++
++	return 0;
++
++out_free:
++	kfree(bfqd);
++	kobject_put(&eq->kobj);
++	return -ENOMEM;
++}
++
++static void bfq_slab_kill(void)
++{
++	kmem_cache_destroy(bfq_pool);
++}
++
++static int __init bfq_slab_setup(void)
++{
++	bfq_pool = KMEM_CACHE(bfq_queue, 0);
++	if (!bfq_pool)
++		return -ENOMEM;
++	return 0;
++}
++
++static ssize_t bfq_var_show(unsigned int var, char *page)
++{
++	return sprintf(page, "%u\n", var);
++}
++
++static ssize_t bfq_var_store(unsigned long *var, const char *page,
++			     size_t count)
++{
++	unsigned long new_val;
++	int ret = kstrtoul(page, 10, &new_val);
++
++	if (ret == 0)
++		*var = new_val;
++
++	return count;
++}
++
++static ssize_t bfq_wr_max_time_show(struct elevator_queue *e, char *page)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++
++	return sprintf(page, "%d\n", bfqd->bfq_wr_max_time > 0 ?
++		       jiffies_to_msecs(bfqd->bfq_wr_max_time) :
++		       jiffies_to_msecs(bfq_wr_duration(bfqd)));
++}
++
++static ssize_t bfq_weights_show(struct elevator_queue *e, char *page)
++{
++	struct bfq_queue *bfqq;
++	struct bfq_data *bfqd = e->elevator_data;
++	ssize_t num_char = 0;
++
++	num_char += sprintf(page + num_char, "Tot reqs queued %d\n\n",
++			    bfqd->queued);
++
++	spin_lock_irq(bfqd->queue->queue_lock);
++
++	num_char += sprintf(page + num_char, "Active:\n");
++	list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) {
++		num_char += sprintf(page + num_char,
++				    "pid%d: weight %hu, nr_queued %d %d, ",
++				    bfqq->pid,
++				    bfqq->entity.weight,
++				    bfqq->queued[0],
++				    bfqq->queued[1]);
++		num_char += sprintf(page + num_char,
++				    "dur %d/%u\n",
++				    jiffies_to_msecs(
++					    jiffies -
++					    bfqq->last_wr_start_finish),
++				    jiffies_to_msecs(bfqq->wr_cur_max_time));
++	}
++
++	num_char += sprintf(page + num_char, "Idle:\n");
++	list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) {
++		num_char += sprintf(page + num_char,
++				    "pid%d: weight %hu, dur %d/%u\n",
++				    bfqq->pid,
++				    bfqq->entity.weight,
++				    jiffies_to_msecs(jiffies -
++						     bfqq->last_wr_start_finish),
++				    jiffies_to_msecs(bfqq->wr_cur_max_time));
++	}
++
++	spin_unlock_irq(bfqd->queue->queue_lock);
++
++	return num_char;
++}
++
++#define SHOW_FUNCTION(__FUNC, __VAR, __CONV)				\
++static ssize_t __FUNC(struct elevator_queue *e, char *page)		\
++{									\
++	struct bfq_data *bfqd = e->elevator_data;			\
++	u64 __data = __VAR;						\
++	if (__CONV == 1)						\
++		__data = jiffies_to_msecs(__data);			\
++	else if (__CONV == 2)						\
++		__data = div_u64(__data, NSEC_PER_MSEC);		\
++	return bfq_var_show(__data, (page));				\
++}
++SHOW_FUNCTION(bfq_fifo_expire_sync_show, bfqd->bfq_fifo_expire[1], 2);
++SHOW_FUNCTION(bfq_fifo_expire_async_show, bfqd->bfq_fifo_expire[0], 2);
++SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0);
++SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0);
++SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 2);
++SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0);
++SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout, 1);
++SHOW_FUNCTION(bfq_strict_guarantees_show, bfqd->strict_guarantees, 0);
++SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0);
++SHOW_FUNCTION(bfq_wr_coeff_show, bfqd->bfq_wr_coeff, 0);
++SHOW_FUNCTION(bfq_wr_rt_max_time_show, bfqd->bfq_wr_rt_max_time, 1);
++SHOW_FUNCTION(bfq_wr_min_idle_time_show, bfqd->bfq_wr_min_idle_time, 1);
++SHOW_FUNCTION(bfq_wr_min_inter_arr_async_show, bfqd->bfq_wr_min_inter_arr_async,
++	1);
++SHOW_FUNCTION(bfq_wr_max_softrt_rate_show, bfqd->bfq_wr_max_softrt_rate, 0);
++#undef SHOW_FUNCTION
++
++#define USEC_SHOW_FUNCTION(__FUNC, __VAR)				\
++static ssize_t __FUNC(struct elevator_queue *e, char *page)		\
++{									\
++	struct bfq_data *bfqd = e->elevator_data;			\
++	u64 __data = __VAR;						\
++	__data = div_u64(__data, NSEC_PER_USEC);			\
++	return bfq_var_show(__data, (page));				\
++}
++USEC_SHOW_FUNCTION(bfq_slice_idle_us_show, bfqd->bfq_slice_idle);
++#undef USEC_SHOW_FUNCTION
++
++#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV)			\
++static ssize_t								\
++__FUNC(struct elevator_queue *e, const char *page, size_t count)	\
++{									\
++	struct bfq_data *bfqd = e->elevator_data;			\
++	unsigned long uninitialized_var(__data);			\
++	int ret = bfq_var_store(&__data, (page), count);		\
++	if (__data < (MIN))						\
++		__data = (MIN);						\
++	else if (__data > (MAX))					\
++		__data = (MAX);						\
++	if (__CONV == 1)						\
++		*(__PTR) = msecs_to_jiffies(__data);			\
++	else if (__CONV == 2)						\
++		*(__PTR) = (u64)__data * NSEC_PER_MSEC;			\
++	else								\
++		*(__PTR) = __data;					\
++	return ret;							\
++}
++STORE_FUNCTION(bfq_fifo_expire_sync_store, &bfqd->bfq_fifo_expire[1], 1,
++		INT_MAX, 2);
++STORE_FUNCTION(bfq_fifo_expire_async_store, &bfqd->bfq_fifo_expire[0], 1,
++		INT_MAX, 2);
++STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0);
++STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1,
++		INT_MAX, 0);
++STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 2);
++STORE_FUNCTION(bfq_wr_coeff_store, &bfqd->bfq_wr_coeff, 1, INT_MAX, 0);
++STORE_FUNCTION(bfq_wr_max_time_store, &bfqd->bfq_wr_max_time, 0, INT_MAX, 1);
++STORE_FUNCTION(bfq_wr_rt_max_time_store, &bfqd->bfq_wr_rt_max_time, 0, INT_MAX,
++		1);
++STORE_FUNCTION(bfq_wr_min_idle_time_store, &bfqd->bfq_wr_min_idle_time, 0,
++		INT_MAX, 1);
++STORE_FUNCTION(bfq_wr_min_inter_arr_async_store,
++		&bfqd->bfq_wr_min_inter_arr_async, 0, INT_MAX, 1);
++STORE_FUNCTION(bfq_wr_max_softrt_rate_store, &bfqd->bfq_wr_max_softrt_rate, 0,
++		INT_MAX, 0);
++#undef STORE_FUNCTION
++
++#define USEC_STORE_FUNCTION(__FUNC, __PTR, MIN, MAX)			\
++static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count)\
++{									\
++	struct bfq_data *bfqd = e->elevator_data;			\
++	unsigned long uninitialized_var(__data);			\
++	int ret = bfq_var_store(&__data, (page), count);		\
++	if (__data < (MIN))						\
++		__data = (MIN);						\
++	else if (__data > (MAX))					\
++		__data = (MAX);						\
++	*(__PTR) = (u64)__data * NSEC_PER_USEC;				\
++	return ret;							\
++}
++USEC_STORE_FUNCTION(bfq_slice_idle_us_store, &bfqd->bfq_slice_idle, 0,
++		    UINT_MAX);
++#undef USEC_STORE_FUNCTION
++
++/* do nothing for the moment */
++static ssize_t bfq_weights_store(struct elevator_queue *e,
++				    const char *page, size_t count)
++{
++	return count;
++}
++
++static ssize_t bfq_max_budget_store(struct elevator_queue *e,
++				    const char *page, size_t count)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++	unsigned long uninitialized_var(__data);
++	int ret = bfq_var_store(&__data, (page), count);
++
++	if (__data == 0)
++		bfqd->bfq_max_budget = bfq_calc_max_budget(bfqd);
++	else {
++		if (__data > INT_MAX)
++			__data = INT_MAX;
++		bfqd->bfq_max_budget = __data;
++	}
++
++	bfqd->bfq_user_max_budget = __data;
++
++	return ret;
++}
++
++/*
++ * Leaving this name to preserve name compatibility with cfq
++ * parameters, but this timeout is used for both sync and async.
++ */
++static ssize_t bfq_timeout_sync_store(struct elevator_queue *e,
++				      const char *page, size_t count)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++	unsigned long uninitialized_var(__data);
++	int ret = bfq_var_store(&__data, (page), count);
++
++	if (__data < 1)
++		__data = 1;
++	else if (__data > INT_MAX)
++		__data = INT_MAX;
++
++	bfqd->bfq_timeout = msecs_to_jiffies(__data);
++	if (bfqd->bfq_user_max_budget == 0)
++		bfqd->bfq_max_budget = bfq_calc_max_budget(bfqd);
++
++	return ret;
++}
++
++static ssize_t bfq_strict_guarantees_store(struct elevator_queue *e,
++				     const char *page, size_t count)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++	unsigned long uninitialized_var(__data);
++	int ret = bfq_var_store(&__data, (page), count);
++
++	if (__data > 1)
++		__data = 1;
++	if (!bfqd->strict_guarantees && __data == 1
++	    && bfqd->bfq_slice_idle < 8 * NSEC_PER_MSEC)
++		bfqd->bfq_slice_idle = 8 * NSEC_PER_MSEC;
++
++	bfqd->strict_guarantees = __data;
++
++	return ret;
++}
++
++static ssize_t bfq_low_latency_store(struct elevator_queue *e,
++				     const char *page, size_t count)
++{
++	struct bfq_data *bfqd = e->elevator_data;
++	unsigned long uninitialized_var(__data);
++	int ret = bfq_var_store(&__data, (page), count);
++
++	if (__data > 1)
++		__data = 1;
++	if (__data == 0 && bfqd->low_latency != 0)
++		bfq_end_wr(bfqd);
++	bfqd->low_latency = __data;
++
++	return ret;
++}
++
++#define BFQ_ATTR(name) \
++	__ATTR(name, S_IRUGO|S_IWUSR, bfq_##name##_show, bfq_##name##_store)
++
++static struct elv_fs_entry bfq_attrs[] = {
++	BFQ_ATTR(fifo_expire_sync),
++	BFQ_ATTR(fifo_expire_async),
++	BFQ_ATTR(back_seek_max),
++	BFQ_ATTR(back_seek_penalty),
++	BFQ_ATTR(slice_idle),
++	BFQ_ATTR(slice_idle_us),
++	BFQ_ATTR(max_budget),
++	BFQ_ATTR(timeout_sync),
++	BFQ_ATTR(strict_guarantees),
++	BFQ_ATTR(low_latency),
++	BFQ_ATTR(wr_coeff),
++	BFQ_ATTR(wr_max_time),
++	BFQ_ATTR(wr_rt_max_time),
++	BFQ_ATTR(wr_min_idle_time),
++	BFQ_ATTR(wr_min_inter_arr_async),
++	BFQ_ATTR(wr_max_softrt_rate),
++	BFQ_ATTR(weights),
++	__ATTR_NULL
++};
++
++static struct elevator_type iosched_bfq = {
++	.ops.sq = {
++		.elevator_merge_fn =		bfq_merge,
++		.elevator_merged_fn =		bfq_merged_request,
++		.elevator_merge_req_fn =	bfq_merged_requests,
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		.elevator_bio_merged_fn =	bfq_bio_merged,
++#endif
++		.elevator_allow_bio_merge_fn =	bfq_allow_bio_merge,
++		.elevator_allow_rq_merge_fn =	bfq_allow_rq_merge,
++		.elevator_dispatch_fn =		bfq_dispatch_requests,
++		.elevator_add_req_fn =		bfq_insert_request,
++		.elevator_activate_req_fn =	bfq_activate_request,
++		.elevator_deactivate_req_fn =	bfq_deactivate_request,
++		.elevator_completed_req_fn =	bfq_completed_request,
++		.elevator_former_req_fn =	elv_rb_former_request,
++		.elevator_latter_req_fn =	elv_rb_latter_request,
++		.elevator_init_icq_fn =		bfq_init_icq,
++		.elevator_exit_icq_fn =		bfq_exit_icq,
++		.elevator_set_req_fn =		bfq_set_request,
++		.elevator_put_req_fn =		bfq_put_request,
++		.elevator_may_queue_fn =	bfq_may_queue,
++		.elevator_init_fn =		bfq_init_queue,
++		.elevator_exit_fn =		bfq_exit_queue,
++	},
++	.icq_size =		sizeof(struct bfq_io_cq),
++	.icq_align =		__alignof__(struct bfq_io_cq),
++	.elevator_attrs =	bfq_attrs,
++	.elevator_name =	"bfq",
++	.elevator_owner =	THIS_MODULE,
++};
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static struct blkcg_policy blkcg_policy_bfq = {
++	.dfl_cftypes		= bfq_blkg_files,
++	.legacy_cftypes		= bfq_blkcg_legacy_files,
++
++	.cpd_alloc_fn		= bfq_cpd_alloc,
++	.cpd_init_fn		= bfq_cpd_init,
++	.cpd_bind_fn	        = bfq_cpd_init,
++	.cpd_free_fn		= bfq_cpd_free,
++
++	.pd_alloc_fn		= bfq_pd_alloc,
++	.pd_init_fn		= bfq_pd_init,
++	.pd_offline_fn		= bfq_pd_offline,
++	.pd_free_fn		= bfq_pd_free,
++	.pd_reset_stats_fn	= bfq_pd_reset_stats,
++};
++#endif
++
++static int __init bfq_init(void)
++{
++	int ret;
++	char msg[60] = "BFQ I/O-scheduler: v8r10-rc1";
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	ret = blkcg_policy_register(&blkcg_policy_bfq);
++	if (ret)
++		return ret;
++#endif
++
++	ret = -ENOMEM;
++	if (bfq_slab_setup())
++		goto err_pol_unreg;
++
++	/*
++	 * Times to load large popular applications for the typical
++	 * systems installed on the reference devices (see the
++	 * comments before the definitions of the next two
++	 * arrays). Actually, we use slightly slower values, as the
++	 * estimated peak rate tends to be smaller than the actual
++	 * peak rate.  The reason for this last fact is that estimates
++	 * are computed over much shorter time intervals than the long
++	 * intervals typically used for benchmarking. Why? First, to
++	 * adapt more quickly to variations. Second, because an I/O
++	 * scheduler cannot rely on a peak-rate-evaluation workload to
++	 * be run for a long time.
++	 */
++	T_slow[0] = msecs_to_jiffies(3500); /* actually 4 sec */
++	T_slow[1] = msecs_to_jiffies(6000); /* actually 6.5 sec */
++	T_fast[0] = msecs_to_jiffies(7000); /* actually 8 sec */
++	T_fast[1] = msecs_to_jiffies(2500); /* actually 3 sec */
++
++	/*
++	 * Thresholds that determine the switch between speed classes
++	 * (see the comments before the definition of the array
++	 * device_speed_thresh). These thresholds are biased towards
++	 * transitions to the fast class. This is safer than the
++	 * opposite bias. In fact, a wrong transition to the slow
++	 * class results in short weight-raising periods, because the
++	 * speed of the device then tends to be higher that the
++	 * reference peak rate. On the opposite end, a wrong
++	 * transition to the fast class tends to increase
++	 * weight-raising periods, because of the opposite reason.
++	 */
++	device_speed_thresh[0] = (4 * R_slow[0]) / 3;
++	device_speed_thresh[1] = (4 * R_slow[1]) / 3;
++
++	ret = elv_register(&iosched_bfq);
++	if (ret)
++		goto err_pol_unreg;
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	strcat(msg, " (with cgroups support)");
++#endif
++	pr_info("%s", msg);
++
++	return 0;
++
++err_pol_unreg:
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	blkcg_policy_unregister(&blkcg_policy_bfq);
++#endif
++	return ret;
++}
++
++static void __exit bfq_exit(void)
++{
++	elv_unregister(&iosched_bfq);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	blkcg_policy_unregister(&blkcg_policy_bfq);
++#endif
++	bfq_slab_kill();
++}
++
++module_init(bfq_init);
++module_exit(bfq_exit);
++
++MODULE_AUTHOR("Arianna Avanzini, Fabio Checconi, Paolo Valente");
++MODULE_LICENSE("GPL");
+diff --git a/block/bfq-sched.c b/block/bfq-sched.c
+new file mode 100644
+index 000000000000..6ab75b6bfd96
+--- /dev/null
++++ b/block/bfq-sched.c
+@@ -0,0 +1,2014 @@
++/*
++ * BFQ: Hierarchical B-WF2Q+ scheduler.
++ *
++ * Based on ideas and code from CFQ:
++ * Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
++ *
++ * Copyright (C) 2008 Fabio Checconi <fabio@gandalf.sssup.it>
++ *		      Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2015 Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2016 Paolo Valente <paolo.valente@linaro.org>
++ */
++
++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
++
++/**
++ * bfq_gt - compare two timestamps.
++ * @a: first ts.
++ * @b: second ts.
++ *
++ * Return @a > @b, dealing with wrapping correctly.
++ */
++static int bfq_gt(u64 a, u64 b)
++{
++	return (s64)(a - b) > 0;
++}
++
++static struct bfq_entity *bfq_root_active_entity(struct rb_root *tree)
++{
++	struct rb_node *node = tree->rb_node;
++
++	return rb_entry(node, struct bfq_entity, rb_node);
++}
++
++static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd);
++
++static bool bfq_update_parent_budget(struct bfq_entity *next_in_service);
++
++/**
++ * bfq_update_next_in_service - update sd->next_in_service
++ * @sd: sched_data for which to perform the update.
++ * @new_entity: if not NULL, pointer to the entity whose activation,
++ *		requeueing or repositionig triggered the invocation of
++ *		this function.
++ *
++ * This function is called to update sd->next_in_service, which, in
++ * its turn, may change as a consequence of the insertion or
++ * extraction of an entity into/from one of the active trees of
++ * sd. These insertions/extractions occur as a consequence of
++ * activations/deactivations of entities, with some activations being
++ * 'true' activations, and other activations being requeueings (i.e.,
++ * implementing the second, requeueing phase of the mechanism used to
++ * reposition an entity in its active tree; see comments on
++ * __bfq_activate_entity and __bfq_requeue_entity for details). In
++ * both the last two activation sub-cases, new_entity points to the
++ * just activated or requeued entity.
++ *
++ * Returns true if sd->next_in_service changes in such a way that
++ * entity->parent may become the next_in_service for its parent
++ * entity.
++ */
++static bool bfq_update_next_in_service(struct bfq_sched_data *sd,
++				       struct bfq_entity *new_entity)
++{
++	struct bfq_entity *next_in_service = sd->next_in_service;
++	struct bfq_queue *bfqq;
++	bool parent_sched_may_change = false;
++
++	/*
++	 * If this update is triggered by the activation, requeueing
++	 * or repositiong of an entity that does not coincide with
++	 * sd->next_in_service, then a full lookup in the active tree
++	 * can be avoided. In fact, it is enough to check whether the
++	 * just-modified entity has a higher priority than
++	 * sd->next_in_service, or, even if it has the same priority
++	 * as sd->next_in_service, is eligible and has a lower virtual
++	 * finish time than sd->next_in_service. If this compound
++	 * condition holds, then the new entity becomes the new
++	 * next_in_service. Otherwise no change is needed.
++	 */
++	if (new_entity && new_entity != sd->next_in_service) {
++		/*
++		 * Flag used to decide whether to replace
++		 * sd->next_in_service with new_entity. Tentatively
++		 * set to true, and left as true if
++		 * sd->next_in_service is NULL.
++		 */
++		bool replace_next = true;
++
++		/*
++		 * If there is already a next_in_service candidate
++		 * entity, then compare class priorities or timestamps
++		 * to decide whether to replace sd->service_tree with
++		 * new_entity.
++		 */
++		if (next_in_service) {
++			unsigned int new_entity_class_idx =
++				bfq_class_idx(new_entity);
++			struct bfq_service_tree *st =
++				sd->service_tree + new_entity_class_idx;
++
++			/*
++			 * For efficiency, evaluate the most likely
++			 * sub-condition first.
++			 */
++			replace_next =
++				(new_entity_class_idx ==
++				 bfq_class_idx(next_in_service)
++				 &&
++				 !bfq_gt(new_entity->start, st->vtime)
++				 &&
++				 bfq_gt(next_in_service->finish,
++					new_entity->finish))
++				||
++				new_entity_class_idx <
++				bfq_class_idx(next_in_service);
++		}
++
++		if (replace_next)
++			next_in_service = new_entity;
++	} else /* invoked because of a deactivation: lookup needed */
++		next_in_service = bfq_lookup_next_entity(sd);
++
++	if (next_in_service) {
++		parent_sched_may_change = !sd->next_in_service ||
++			bfq_update_parent_budget(next_in_service);
++	} else
++		parent_sched_may_change = sd->next_in_service;
++
++	sd->next_in_service = next_in_service;
++
++	if (!next_in_service)
++		return parent_sched_may_change;
++
++	bfqq = bfq_entity_to_bfqq(next_in_service);
++	if (bfqq)
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			     "update_next_in_service: chosen this queue");
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else {
++		struct bfq_group *bfqg =
++			container_of(next_in_service,
++				     struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			     "update_next_in_service: chosen this entity");
++	}
++#endif
++	return parent_sched_may_change;
++}
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++/* both next loops stop at one of the child entities of the root group */
++#define for_each_entity(entity)				\
++	for (; entity ; entity = entity->parent)
++
++/*
++ * For each iteration, compute parent in advance, so as to be safe if
++ * entity is deallocated during the iteration. Such a deallocation may
++ * happen as a consequence of a bfq_put_queue that frees the bfq_queue
++ * containing entity.
++ */
++#define for_each_entity_safe(entity, parent)				\
++	for (; entity && ({ parent = entity->parent; 1; }); entity = parent)
++
++/*
++ * Returns true if this budget changes may let next_in_service->parent
++ * become the next_in_service entity for its parent entity.
++ */
++static bool bfq_update_parent_budget(struct bfq_entity *next_in_service)
++{
++	struct bfq_entity *bfqg_entity;
++	struct bfq_group *bfqg;
++	struct bfq_sched_data *group_sd;
++	bool ret = false;
++
++	BUG_ON(!next_in_service);
++
++	group_sd = next_in_service->sched_data;
++
++	bfqg = container_of(group_sd, struct bfq_group, sched_data);
++	/*
++	 * bfq_group's my_entity field is not NULL only if the group
++	 * is not the root group. We must not touch the root entity
++	 * as it must never become an in-service entity.
++	 */
++	bfqg_entity = bfqg->my_entity;
++	if (bfqg_entity) {
++		if (bfqg_entity->budget > next_in_service->budget)
++			ret = true;
++		bfqg_entity->budget = next_in_service->budget;
++	}
++
++	return ret;
++}
++
++/*
++ * This function tells whether entity stops being a candidate for next
++ * service, according to the following logic.
++ *
++ * This function is invoked for an entity that is about to be set in
++ * service. If such an entity is a queue, then the entity is no longer
++ * a candidate for next service (i.e, a candidate entity to serve
++ * after the in-service entity is expired). The function then returns
++ * true.
++ *
++ * In contrast, the entity could stil be a candidate for next service
++ * if it is not a queue, and has more than one child. In fact, even if
++ * one of its children is about to be set in service, other children
++ * may still be the next to serve. As a consequence, a non-queue
++ * entity is not a candidate for next-service only if it has only one
++ * child. And only if this condition holds, then the function returns
++ * true for a non-queue entity.
++ */
++static bool bfq_no_longer_next_in_service(struct bfq_entity *entity)
++{
++	struct bfq_group *bfqg;
++
++	if (bfq_entity_to_bfqq(entity))
++		return true;
++
++	bfqg = container_of(entity, struct bfq_group, entity);
++
++	BUG_ON(bfqg == ((struct bfq_data *)(bfqg->bfqd))->root_group);
++	BUG_ON(bfqg->active_entities == 0);
++	if (bfqg->active_entities == 1)
++		return true;
++
++	return false;
++}
++
++#else /* CONFIG_BFQ_GROUP_IOSCHED */
++#define for_each_entity(entity)	\
++	for (; entity ; entity = NULL)
++
++#define for_each_entity_safe(entity, parent) \
++	for (parent = NULL; entity ; entity = parent)
++
++static bool bfq_update_parent_budget(struct bfq_entity *next_in_service)
++{
++	return false;
++}
++
++static bool bfq_no_longer_next_in_service(struct bfq_entity *entity)
++{
++	return true;
++}
++
++#endif /* CONFIG_BFQ_GROUP_IOSCHED */
++
++/*
++ * Shift for timestamp calculations.  This actually limits the maximum
++ * service allowed in one timestamp delta (small shift values increase it),
++ * the maximum total weight that can be used for the queues in the system
++ * (big shift values increase it), and the period of virtual time
++ * wraparounds.
++ */
++#define WFQ_SERVICE_SHIFT	22
++
++static struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = NULL;
++
++	BUG_ON(!entity);
++
++	if (!entity->my_sched_data)
++		bfqq = container_of(entity, struct bfq_queue, entity);
++
++	return bfqq;
++}
++
++
++/**
++ * bfq_delta - map service into the virtual time domain.
++ * @service: amount of service.
++ * @weight: scale factor (weight of an entity or weight sum).
++ */
++static u64 bfq_delta(unsigned long service, unsigned long weight)
++{
++	u64 d = (u64)service << WFQ_SERVICE_SHIFT;
++
++	do_div(d, weight);
++	return d;
++}
++
++/**
++ * bfq_calc_finish - assign the finish time to an entity.
++ * @entity: the entity to act upon.
++ * @service: the service to be charged to the entity.
++ */
++static void bfq_calc_finish(struct bfq_entity *entity, unsigned long service)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	unsigned long long start, finish, delta;
++
++	BUG_ON(entity->weight == 0);
++
++	entity->finish = entity->start +
++		bfq_delta(service, entity->weight);
++
++	start = ((entity->start>>10)*1000)>>12;
++	finish = ((entity->finish>>10)*1000)>>12;
++	delta = ((bfq_delta(service, entity->weight)>>10)*1000)>>12;
++
++	if (bfqq) {
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			"calc_finish: serv %lu, w %d",
++			service, entity->weight);
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			"calc_finish: start %llu, finish %llu, delta %llu",
++			start, finish, delta);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	} else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			"calc_finish group: serv %lu, w %d",
++			     service, entity->weight);
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			"calc_finish group: start %llu, finish %llu, delta %llu",
++			start, finish, delta);
++#endif
++	}
++}
++
++/**
++ * bfq_entity_of - get an entity from a node.
++ * @node: the node field of the entity.
++ *
++ * Convert a node pointer to the relative entity.  This is used only
++ * to simplify the logic of some functions and not as the generic
++ * conversion mechanism because, e.g., in the tree walking functions,
++ * the check for a %NULL value would be redundant.
++ */
++static struct bfq_entity *bfq_entity_of(struct rb_node *node)
++{
++	struct bfq_entity *entity = NULL;
++
++	if (node)
++		entity = rb_entry(node, struct bfq_entity, rb_node);
++
++	return entity;
++}
++
++/**
++ * bfq_extract - remove an entity from a tree.
++ * @root: the tree root.
++ * @entity: the entity to remove.
++ */
++static void bfq_extract(struct rb_root *root, struct bfq_entity *entity)
++{
++	BUG_ON(entity->tree != root);
++
++	entity->tree = NULL;
++	rb_erase(&entity->rb_node, root);
++}
++
++/**
++ * bfq_idle_extract - extract an entity from the idle tree.
++ * @st: the service tree of the owning @entity.
++ * @entity: the entity being removed.
++ */
++static void bfq_idle_extract(struct bfq_service_tree *st,
++			     struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	struct rb_node *next;
++
++	BUG_ON(entity->tree != &st->idle);
++
++	if (entity == st->first_idle) {
++		next = rb_next(&entity->rb_node);
++		st->first_idle = bfq_entity_of(next);
++	}
++
++	if (entity == st->last_idle) {
++		next = rb_prev(&entity->rb_node);
++		st->last_idle = bfq_entity_of(next);
++	}
++
++	bfq_extract(&st->idle, entity);
++
++	if (bfqq)
++		list_del(&bfqq->bfqq_list);
++}
++
++/**
++ * bfq_insert - generic tree insertion.
++ * @root: tree root.
++ * @entity: entity to insert.
++ *
++ * This is used for the idle and the active tree, since they are both
++ * ordered by finish time.
++ */
++static void bfq_insert(struct rb_root *root, struct bfq_entity *entity)
++{
++	struct bfq_entity *entry;
++	struct rb_node **node = &root->rb_node;
++	struct rb_node *parent = NULL;
++
++	BUG_ON(entity->tree);
++
++	while (*node) {
++		parent = *node;
++		entry = rb_entry(parent, struct bfq_entity, rb_node);
++
++		if (bfq_gt(entry->finish, entity->finish))
++			node = &parent->rb_left;
++		else
++			node = &parent->rb_right;
++	}
++
++	rb_link_node(&entity->rb_node, parent, node);
++	rb_insert_color(&entity->rb_node, root);
++
++	entity->tree = root;
++}
++
++/**
++ * bfq_update_min - update the min_start field of a entity.
++ * @entity: the entity to update.
++ * @node: one of its children.
++ *
++ * This function is called when @entity may store an invalid value for
++ * min_start due to updates to the active tree.  The function  assumes
++ * that the subtree rooted at @node (which may be its left or its right
++ * child) has a valid min_start value.
++ */
++static void bfq_update_min(struct bfq_entity *entity, struct rb_node *node)
++{
++	struct bfq_entity *child;
++
++	if (node) {
++		child = rb_entry(node, struct bfq_entity, rb_node);
++		if (bfq_gt(entity->min_start, child->min_start))
++			entity->min_start = child->min_start;
++	}
++}
++
++/**
++ * bfq_update_active_node - recalculate min_start.
++ * @node: the node to update.
++ *
++ * @node may have changed position or one of its children may have moved,
++ * this function updates its min_start value.  The left and right subtrees
++ * are assumed to hold a correct min_start value.
++ */
++static void bfq_update_active_node(struct rb_node *node)
++{
++	struct bfq_entity *entity = rb_entry(node, struct bfq_entity, rb_node);
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	entity->min_start = entity->start;
++	bfq_update_min(entity, node->rb_right);
++	bfq_update_min(entity, node->rb_left);
++
++	if (bfqq) {
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			     "update_active_node: new min_start %llu",
++			     ((entity->min_start>>10)*1000)>>12);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	} else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			     "update_active_node: new min_start %llu",
++			     ((entity->min_start>>10)*1000)>>12);
++#endif
++	}
++}
++
++/**
++ * bfq_update_active_tree - update min_start for the whole active tree.
++ * @node: the starting node.
++ *
++ * @node must be the deepest modified node after an update.  This function
++ * updates its min_start using the values held by its children, assuming
++ * that they did not change, and then updates all the nodes that may have
++ * changed in the path to the root.  The only nodes that may have changed
++ * are the ones in the path or their siblings.
++ */
++static void bfq_update_active_tree(struct rb_node *node)
++{
++	struct rb_node *parent;
++
++up:
++	bfq_update_active_node(node);
++
++	parent = rb_parent(node);
++	if (!parent)
++		return;
++
++	if (node == parent->rb_left && parent->rb_right)
++		bfq_update_active_node(parent->rb_right);
++	else if (parent->rb_left)
++		bfq_update_active_node(parent->rb_left);
++
++	node = parent;
++	goto up;
++}
++
++static void bfq_weights_tree_add(struct bfq_data *bfqd,
++				 struct bfq_entity *entity,
++				 struct rb_root *root);
++
++static void bfq_weights_tree_remove(struct bfq_data *bfqd,
++				    struct bfq_entity *entity,
++				    struct rb_root *root);
++
++
++/**
++ * bfq_active_insert - insert an entity in the active tree of its
++ *                     group/device.
++ * @st: the service tree of the entity.
++ * @entity: the entity being inserted.
++ *
++ * The active tree is ordered by finish time, but an extra key is kept
++ * per each node, containing the minimum value for the start times of
++ * its children (and the node itself), so it's possible to search for
++ * the eligible node with the lowest finish time in logarithmic time.
++ */
++static void bfq_active_insert(struct bfq_service_tree *st,
++			      struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	struct rb_node *node = &entity->rb_node;
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	struct bfq_sched_data *sd = NULL;
++	struct bfq_group *bfqg = NULL;
++	struct bfq_data *bfqd = NULL;
++#endif
++
++	bfq_insert(&st->active, entity);
++
++	if (node->rb_left)
++		node = node->rb_left;
++	else if (node->rb_right)
++		node = node->rb_right;
++
++	bfq_update_active_tree(node);
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	sd = entity->sched_data;
++	bfqg = container_of(sd, struct bfq_group, sched_data);
++	BUG_ON(!bfqg);
++	bfqd = (struct bfq_data *)bfqg->bfqd;
++#endif
++	if (bfqq)
++		list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else { /* bfq_group */
++		BUG_ON(!bfqd);
++		bfq_weights_tree_add(bfqd, entity, &bfqd->group_weights_tree);
++	}
++	if (bfqg != bfqd->root_group) {
++		BUG_ON(!bfqg);
++		BUG_ON(!bfqd);
++		bfqg->active_entities++;
++	}
++#endif
++}
++
++/**
++ * bfq_ioprio_to_weight - calc a weight from an ioprio.
++ * @ioprio: the ioprio value to convert.
++ */
++static unsigned short bfq_ioprio_to_weight(int ioprio)
++{
++	BUG_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR);
++	return (IOPRIO_BE_NR - ioprio) * BFQ_WEIGHT_CONVERSION_COEFF;
++}
++
++/**
++ * bfq_weight_to_ioprio - calc an ioprio from a weight.
++ * @weight: the weight value to convert.
++ *
++ * To preserve as much as possible the old only-ioprio user interface,
++ * 0 is used as an escape ioprio value for weights (numerically) equal or
++ * larger than IOPRIO_BE_NR * BFQ_WEIGHT_CONVERSION_COEFF.
++ */
++static unsigned short bfq_weight_to_ioprio(int weight)
++{
++	BUG_ON(weight < BFQ_MIN_WEIGHT || weight > BFQ_MAX_WEIGHT);
++	return IOPRIO_BE_NR * BFQ_WEIGHT_CONVERSION_COEFF - weight < 0 ?
++		0 : IOPRIO_BE_NR * BFQ_WEIGHT_CONVERSION_COEFF - weight;
++}
++
++static void bfq_get_entity(struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	if (bfqq) {
++		bfqq->ref++;
++		bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d",
++			     bfqq, bfqq->ref);
++	}
++}
++
++/**
++ * bfq_find_deepest - find the deepest node that an extraction can modify.
++ * @node: the node being removed.
++ *
++ * Do the first step of an extraction in an rb tree, looking for the
++ * node that will replace @node, and returning the deepest node that
++ * the following modifications to the tree can touch.  If @node is the
++ * last node in the tree return %NULL.
++ */
++static struct rb_node *bfq_find_deepest(struct rb_node *node)
++{
++	struct rb_node *deepest;
++
++	if (!node->rb_right && !node->rb_left)
++		deepest = rb_parent(node);
++	else if (!node->rb_right)
++		deepest = node->rb_left;
++	else if (!node->rb_left)
++		deepest = node->rb_right;
++	else {
++		deepest = rb_next(node);
++		if (deepest->rb_right)
++			deepest = deepest->rb_right;
++		else if (rb_parent(deepest) != node)
++			deepest = rb_parent(deepest);
++	}
++
++	return deepest;
++}
++
++/**
++ * bfq_active_extract - remove an entity from the active tree.
++ * @st: the service_tree containing the tree.
++ * @entity: the entity being removed.
++ */
++static void bfq_active_extract(struct bfq_service_tree *st,
++			       struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	struct rb_node *node;
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	struct bfq_sched_data *sd = NULL;
++	struct bfq_group *bfqg = NULL;
++	struct bfq_data *bfqd = NULL;
++#endif
++
++	node = bfq_find_deepest(&entity->rb_node);
++	bfq_extract(&st->active, entity);
++
++	if (node)
++		bfq_update_active_tree(node);
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	sd = entity->sched_data;
++	bfqg = container_of(sd, struct bfq_group, sched_data);
++	BUG_ON(!bfqg);
++	bfqd = (struct bfq_data *)bfqg->bfqd;
++#endif
++	if (bfqq)
++		list_del(&bfqq->bfqq_list);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else { /* bfq_group */
++		BUG_ON(!bfqd);
++		bfq_weights_tree_remove(bfqd, entity,
++					&bfqd->group_weights_tree);
++	}
++	if (bfqg != bfqd->root_group) {
++		BUG_ON(!bfqg);
++		BUG_ON(!bfqd);
++		BUG_ON(!bfqg->active_entities);
++		bfqg->active_entities--;
++	}
++#endif
++}
++
++/**
++ * bfq_idle_insert - insert an entity into the idle tree.
++ * @st: the service tree containing the tree.
++ * @entity: the entity to insert.
++ */
++static void bfq_idle_insert(struct bfq_service_tree *st,
++			    struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	struct bfq_entity *first_idle = st->first_idle;
++	struct bfq_entity *last_idle = st->last_idle;
++
++	if (!first_idle || bfq_gt(first_idle->finish, entity->finish))
++		st->first_idle = entity;
++	if (!last_idle || bfq_gt(entity->finish, last_idle->finish))
++		st->last_idle = entity;
++
++	bfq_insert(&st->idle, entity);
++
++	if (bfqq)
++		list_add(&bfqq->bfqq_list, &bfqq->bfqd->idle_list);
++}
++
++/**
++ * bfq_forget_entity - do not consider entity any longer for scheduling
++ * @st: the service tree.
++ * @entity: the entity being removed.
++ * @is_in_service: true if entity is currently the in-service entity.
++ *
++ * Forget everything about @entity. In addition, if entity represents
++ * a queue, and the latter is not in service, then release the service
++ * reference to the queue (the one taken through bfq_get_entity). In
++ * fact, in this case, there is really no more service reference to
++ * the queue, as the latter is also outside any service tree. If,
++ * instead, the queue is in service, then __bfq_bfqd_reset_in_service
++ * will take care of putting the reference when the queue finally
++ * stops being served.
++ */
++static void bfq_forget_entity(struct bfq_service_tree *st,
++			      struct bfq_entity *entity,
++			      bool is_in_service)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	BUG_ON(!entity->on_st);
++
++	entity->on_st = false;
++	st->wsum -= entity->weight;
++	if (bfqq && !is_in_service) {
++		bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity (before): %p %d",
++			     bfqq, bfqq->ref);
++		bfq_put_queue(bfqq);
++	}
++}
++
++/**
++ * bfq_put_idle_entity - release the idle tree ref of an entity.
++ * @st: service tree for the entity.
++ * @entity: the entity being released.
++ */
++static void bfq_put_idle_entity(struct bfq_service_tree *st,
++				struct bfq_entity *entity)
++{
++	bfq_idle_extract(st, entity);
++	bfq_forget_entity(st, entity,
++			  entity == entity->sched_data->in_service_entity);
++}
++
++/**
++ * bfq_forget_idle - update the idle tree if necessary.
++ * @st: the service tree to act upon.
++ *
++ * To preserve the global O(log N) complexity we only remove one entry here;
++ * as the idle tree will not grow indefinitely this can be done safely.
++ */
++static void bfq_forget_idle(struct bfq_service_tree *st)
++{
++	struct bfq_entity *first_idle = st->first_idle;
++	struct bfq_entity *last_idle = st->last_idle;
++
++	if (RB_EMPTY_ROOT(&st->active) && last_idle &&
++	    !bfq_gt(last_idle->finish, st->vtime)) {
++		/*
++		 * Forget the whole idle tree, increasing the vtime past
++		 * the last finish time of idle entities.
++		 */
++		st->vtime = last_idle->finish;
++	}
++
++	if (first_idle && !bfq_gt(first_idle->finish, st->vtime))
++		bfq_put_idle_entity(st, first_idle);
++}
++
++static struct bfq_service_tree *
++__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st,
++			 struct bfq_entity *entity)
++{
++	struct bfq_service_tree *new_st = old_st;
++
++	if (entity->prio_changed) {
++		struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++		unsigned int prev_weight, new_weight;
++		struct bfq_data *bfqd = NULL;
++		struct rb_root *root;
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		struct bfq_sched_data *sd;
++		struct bfq_group *bfqg;
++#endif
++
++		if (bfqq)
++			bfqd = bfqq->bfqd;
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		else {
++			sd = entity->my_sched_data;
++			bfqg = container_of(sd, struct bfq_group, sched_data);
++			BUG_ON(!bfqg);
++			bfqd = (struct bfq_data *)bfqg->bfqd;
++			BUG_ON(!bfqd);
++		}
++#endif
++
++		BUG_ON(old_st->wsum < entity->weight);
++		old_st->wsum -= entity->weight;
++
++		if (entity->new_weight != entity->orig_weight) {
++			if (entity->new_weight < BFQ_MIN_WEIGHT ||
++			    entity->new_weight > BFQ_MAX_WEIGHT) {
++				pr_crit("update_weight_prio: new_weight %d\n",
++					entity->new_weight);
++				if (entity->new_weight < BFQ_MIN_WEIGHT)
++					entity->new_weight = BFQ_MIN_WEIGHT;
++				else
++					entity->new_weight = BFQ_MAX_WEIGHT;
++			}
++			entity->orig_weight = entity->new_weight;
++			if (bfqq)
++				bfqq->ioprio =
++				  bfq_weight_to_ioprio(entity->orig_weight);
++		}
++
++		if (bfqq)
++			bfqq->ioprio_class = bfqq->new_ioprio_class;
++		entity->prio_changed = 0;
++
++		/*
++		 * NOTE: here we may be changing the weight too early,
++		 * this will cause unfairness.  The correct approach
++		 * would have required additional complexity to defer
++		 * weight changes to the proper time instants (i.e.,
++		 * when entity->finish <= old_st->vtime).
++		 */
++		new_st = bfq_entity_service_tree(entity);
++
++		prev_weight = entity->weight;
++		new_weight = entity->orig_weight *
++			     (bfqq ? bfqq->wr_coeff : 1);
++		/*
++		 * If the weight of the entity changes, remove the entity
++		 * from its old weight counter (if there is a counter
++		 * associated with the entity), and add it to the counter
++		 * associated with its new weight.
++		 */
++		if (prev_weight != new_weight) {
++			if (bfqq)
++				bfq_log_bfqq(bfqq->bfqd, bfqq,
++					     "weight changed %d %d(%d %d)",
++					     prev_weight, new_weight,
++					     entity->orig_weight,
++					     bfqq->wr_coeff);
++
++			root = bfqq ? &bfqd->queue_weights_tree :
++				      &bfqd->group_weights_tree;
++			bfq_weights_tree_remove(bfqd, entity, root);
++		}
++		entity->weight = new_weight;
++		/*
++		 * Add the entity to its weights tree only if it is
++		 * not associated with a weight-raised queue.
++		 */
++		if (prev_weight != new_weight &&
++		    (bfqq ? bfqq->wr_coeff == 1 : 1))
++			/* If we get here, root has been initialized. */
++			bfq_weights_tree_add(bfqd, entity, root);
++
++		new_st->wsum += entity->weight;
++
++		if (new_st != old_st)
++			entity->start = new_st->vtime;
++	}
++
++	return new_st;
++}
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg);
++#endif
++
++/**
++ * bfq_bfqq_served - update the scheduler status after selection for
++ *                   service.
++ * @bfqq: the queue being served.
++ * @served: bytes to transfer.
++ *
++ * NOTE: this can be optimized, as the timestamps of upper level entities
++ * are synchronized every time a new bfqq is selected for service.  By now,
++ * we keep it to better check consistency.
++ */
++static void bfq_bfqq_served(struct bfq_queue *bfqq, int served)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++	struct bfq_service_tree *st;
++
++	for_each_entity(entity) {
++		st = bfq_entity_service_tree(entity);
++
++		entity->service += served;
++
++		BUG_ON(st->wsum == 0);
++
++		st->vtime += bfq_delta(served, st->wsum);
++		bfq_forget_idle(st);
++	}
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	bfqg_stats_set_start_empty_time(bfqq_group(bfqq));
++#endif
++	st = bfq_entity_service_tree(&bfqq->entity);
++	bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %d secs, vtime %llu on %p",
++		     served,  ((st->vtime>>10)*1000)>>12, st);
++}
++
++/**
++ * bfq_bfqq_charge_time - charge an amount of service equivalent to the length
++ *			  of the time interval during which bfqq has been in
++ *			  service.
++ * @bfqd: the device
++ * @bfqq: the queue that needs a service update.
++ * @time_ms: the amount of time during which the queue has received service
++ *
++ * If a queue does not consume its budget fast enough, then providing
++ * the queue with service fairness may impair throughput, more or less
++ * severely. For this reason, queues that consume their budget slowly
++ * are provided with time fairness instead of service fairness. This
++ * goal is achieved through the BFQ scheduling engine, even if such an
++ * engine works in the service, and not in the time domain. The trick
++ * is charging these queues with an inflated amount of service, equal
++ * to the amount of service that they would have received during their
++ * service slot if they had been fast, i.e., if their requests had
++ * been dispatched at a rate equal to the estimated peak rate.
++ *
++ * It is worth noting that time fairness can cause important
++ * distortions in terms of bandwidth distribution, on devices with
++ * internal queueing. The reason is that I/O requests dispatched
++ * during the service slot of a queue may be served after that service
++ * slot is finished, and may have a total processing time loosely
++ * correlated with the duration of the service slot. This is
++ * especially true for short service slots.
++ */
++static void bfq_bfqq_charge_time(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++				 unsigned long time_ms)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++	int tot_serv_to_charge = entity->service;
++	unsigned int timeout_ms = jiffies_to_msecs(bfq_timeout);
++
++	if (time_ms > 0 && time_ms < timeout_ms)
++		tot_serv_to_charge =
++			(bfqd->bfq_max_budget * time_ms) / timeout_ms;
++
++	if (tot_serv_to_charge < entity->service)
++		tot_serv_to_charge = entity->service;
++
++	bfq_log_bfqq(bfqq->bfqd, bfqq,
++		     "charge_time: %lu/%u ms, %d/%d/%d sectors",
++		     time_ms, timeout_ms, entity->service,
++		     tot_serv_to_charge, entity->budget);
++
++	/* Increase budget to avoid inconsistencies */
++	if (tot_serv_to_charge > entity->budget)
++		entity->budget = tot_serv_to_charge;
++
++	bfq_bfqq_served(bfqq,
++			max_t(int, 0, tot_serv_to_charge - entity->service));
++}
++
++static void bfq_update_fin_time_enqueue(struct bfq_entity *entity,
++					struct bfq_service_tree *st,
++					bool backshifted)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	struct bfq_sched_data *sd = entity->sched_data;
++
++	st = __bfq_entity_update_weight_prio(st, entity);
++	bfq_calc_finish(entity, entity->budget);
++
++	/*
++	 * If some queues enjoy backshifting for a while, then their
++	 * (virtual) finish timestamps may happen to become lower and
++	 * lower than the system virtual time.  In particular, if
++	 * these queues often happen to be idle for short time
++	 * periods, and during such time periods other queues with
++	 * higher timestamps happen to be busy, then the backshifted
++	 * timestamps of the former queues can become much lower than
++	 * the system virtual time. In fact, to serve the queues with
++	 * higher timestamps while the ones with lower timestamps are
++	 * idle, the system virtual time may be pushed-up to much
++	 * higher values than the finish timestamps of the idle
++	 * queues. As a consequence, the finish timestamps of all new
++	 * or newly activated queues may end up being much larger than
++	 * those of lucky queues with backshifted timestamps. The
++	 * latter queues may then monopolize the device for a lot of
++	 * time. This would simply break service guarantees.
++	 *
++	 * To reduce this problem, push up a little bit the
++	 * backshifted timestamps of the queue associated with this
++	 * entity (only a queue can happen to have the backshifted
++	 * flag set): just enough to let the finish timestamp of the
++	 * queue be equal to the current value of the system virtual
++	 * time. This may introduce a little unfairness among queues
++	 * with backshifted timestamps, but it does not break
++	 * worst-case fairness guarantees.
++	 *
++	 * As a special case, if bfqq is weight-raised, push up
++	 * timestamps much less, to keep very low the probability that
++	 * this push up causes the backshifted finish timestamps of
++	 * weight-raised queues to become higher than the backshifted
++	 * finish timestamps of non weight-raised queues.
++	 */
++	if (backshifted && bfq_gt(st->vtime, entity->finish)) {
++		unsigned long delta = st->vtime - entity->finish;
++
++		if (bfqq)
++			delta /= bfqq->wr_coeff;
++
++		entity->start += delta;
++		entity->finish += delta;
++
++		if (bfqq) {
++			bfq_log_bfqq(bfqq->bfqd, bfqq,
++				     "__activate_entity: new queue finish %llu",
++				     ((entity->finish>>10)*1000)>>12);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		} else {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group, entity);
++
++			bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++				     "__activate_entity: new group finish %llu",
++				     ((entity->finish>>10)*1000)>>12);
++#endif
++		}
++	}
++
++	bfq_active_insert(st, entity);
++
++	if (bfqq) {
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			"__activate_entity: queue %seligible in st %p",
++			     entity->start <= st->vtime ? "" : "non ", st);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	} else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			"__activate_entity: group %seligible in st %p",
++			     entity->start <= st->vtime ? "" : "non ", st);
++#endif
++	}
++	BUG_ON(RB_EMPTY_ROOT(&st->active));
++	BUG_ON(&st->active != &sd->service_tree->active &&
++	       &st->active != &(sd->service_tree+1)->active &&
++	       &st->active != &(sd->service_tree+2)->active);
++}
++
++/**
++ * __bfq_activate_entity - handle activation of entity.
++ * @entity: the entity being activated.
++ * @non_blocking_wait_rq: true if entity was waiting for a request
++ *
++ * Called for a 'true' activation, i.e., if entity is not active and
++ * one of its children receives a new request.
++ *
++ * Basically, this function updates the timestamps of entity and
++ * inserts entity into its active tree, ater possible extracting it
++ * from its idle tree.
++ */
++static void __bfq_activate_entity(struct bfq_entity *entity,
++				  bool non_blocking_wait_rq)
++{
++	struct bfq_sched_data *sd = entity->sched_data;
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	bool backshifted = false;
++	unsigned long long min_vstart;
++
++	BUG_ON(!sd);
++	BUG_ON(!st);
++
++	/* See comments on bfq_fqq_update_budg_for_activation */
++	if (non_blocking_wait_rq && bfq_gt(st->vtime, entity->finish)) {
++		backshifted = true;
++		min_vstart = entity->finish;
++	} else
++		min_vstart = st->vtime;
++
++	if (entity->tree == &st->idle) {
++		/*
++		 * Must be on the idle tree, bfq_idle_extract() will
++		 * check for that.
++		 */
++		bfq_idle_extract(st, entity);
++		entity->start = bfq_gt(min_vstart, entity->finish) ?
++			min_vstart : entity->finish;
++	} else {
++		/*
++		 * The finish time of the entity may be invalid, and
++		 * it is in the past for sure, otherwise the queue
++		 * would have been on the idle tree.
++		 */
++		entity->start = min_vstart;
++		st->wsum += entity->weight;
++		/*
++		 * entity is about to be inserted into a service tree,
++		 * and then set in service: get a reference to make
++		 * sure entity does not disappear until it is no
++		 * longer in service or scheduled for service.
++		 */
++		bfq_get_entity(entity);
++
++		BUG_ON(entity->on_st && bfqq);
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		if (entity->on_st && !bfqq) {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group,
++					     entity);
++
++			bfq_log_bfqg((struct bfq_data *)bfqg->bfqd,
++				     bfqg,
++				     "activate bug, class %d in_service %p",
++				     bfq_class_idx(entity), sd->in_service_entity);
++		}
++#endif
++		BUG_ON(entity->on_st && !bfqq);
++		entity->on_st = true;
++	}
++
++	bfq_update_fin_time_enqueue(entity, st, backshifted);
++}
++
++/**
++ * __bfq_requeue_entity - handle requeueing or repositioning of an entity.
++ * @entity: the entity being requeued or repositioned.
++ *
++ * Requeueing is needed if this entity stops being served, which
++ * happens if a leaf descendant entity has expired. On the other hand,
++ * repositioning is needed if the next_inservice_entity for the child
++ * entity has changed. See the comments inside the function for
++ * details.
++ *
++ * Basically, this function: 1) removes entity from its active tree if
++ * present there, 2) updates the timestamps of entity and 3) inserts
++ * entity back into its active tree (in the new, right position for
++ * the new values of the timestamps).
++ */
++static void __bfq_requeue_entity(struct bfq_entity *entity)
++{
++	struct bfq_sched_data *sd = entity->sched_data;
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++
++	BUG_ON(!sd);
++	BUG_ON(!st);
++
++	BUG_ON(entity != sd->in_service_entity &&
++	       entity->tree != &st->active);
++
++	if (entity == sd->in_service_entity) {
++		/*
++		 * We are requeueing the current in-service entity,
++		 * which may have to be done for one of the following
++		 * reasons:
++		 * - entity represents the in-service queue, and the
++		 *   in-service queue is being requeued after an
++		 *   expiration;
++		 * - entity represents a group, and its budget has
++		 *   changed because one of its child entities has
++		 *   just been either activated or requeued for some
++		 *   reason; the timestamps of the entity need then to
++		 *   be updated, and the entity needs to be enqueued
++		 *   or repositioned accordingly.
++		 *
++		 * In particular, before requeueing, the start time of
++		 * the entity must be moved forward to account for the
++		 * service that the entity has received while in
++		 * service. This is done by the next instructions. The
++		 * finish time will then be updated according to this
++		 * new value of the start time, and to the budget of
++		 * the entity.
++		 */
++		bfq_calc_finish(entity, entity->service);
++		entity->start = entity->finish;
++		BUG_ON(entity->tree && entity->tree != &st->active);
++		/*
++		 * In addition, if the entity had more than one child
++		 * when set in service, then was not extracted from
++		 * the active tree. This implies that the position of
++		 * the entity in the active tree may need to be
++		 * changed now, because we have just updated the start
++		 * time of the entity, and we will update its finish
++		 * time in a moment (the requeueing is then, more
++		 * precisely, a repositioning in this case). To
++		 * implement this repositioning, we: 1) dequeue the
++		 * entity here, 2) update the finish time and
++		 * requeue the entity according to the new
++		 * timestamps below.
++		 */
++		if (entity->tree)
++			bfq_active_extract(st, entity);
++	} else { /* The entity is already active, and not in service */
++		/*
++		 * In this case, this function gets called only if the
++		 * next_in_service entity below this entity has
++		 * changed, and this change has caused the budget of
++		 * this entity to change, which, finally implies that
++		 * the finish time of this entity must be
++		 * updated. Such an update may cause the scheduling,
++		 * i.e., the position in the active tree, of this
++		 * entity to change. We handle this change by: 1)
++		 * dequeueing the entity here, 2) updating the finish
++		 * time and requeueing the entity according to the new
++		 * timestamps below. This is the same approach as the
++		 * non-extracted-entity sub-case above.
++		 */
++		bfq_active_extract(st, entity);
++	}
++
++	bfq_update_fin_time_enqueue(entity, st, false);
++}
++
++static void __bfq_activate_requeue_entity(struct bfq_entity *entity,
++					  struct bfq_sched_data *sd,
++					  bool non_blocking_wait_rq)
++{
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++
++	if (sd->in_service_entity == entity || entity->tree == &st->active)
++		 /*
++		  * in service or already queued on the active tree,
++		  * requeue or reposition
++		  */
++		__bfq_requeue_entity(entity);
++	else
++		/*
++		 * Not in service and not queued on its active tree:
++		 * the activity is idle and this is a true activation.
++		 */
++		__bfq_activate_entity(entity, non_blocking_wait_rq);
++}
++
++
++/**
++ * bfq_activate_entity - activate or requeue an entity representing a bfq_queue,
++ *			 and activate, requeue or reposition all ancestors
++ *			 for which such an update becomes necessary.
++ * @entity: the entity to activate.
++ * @non_blocking_wait_rq: true if this entity was waiting for a request
++ * @requeue: true if this is a requeue, which implies that bfqq is
++ *	     being expired; thus ALL its ancestors stop being served and must
++ *	     therefore be requeued
++ */
++static void bfq_activate_requeue_entity(struct bfq_entity *entity,
++					bool non_blocking_wait_rq,
++					bool requeue)
++{
++	struct bfq_sched_data *sd;
++
++	for_each_entity(entity) {
++		BUG_ON(!entity);
++		sd = entity->sched_data;
++		__bfq_activate_requeue_entity(entity, sd, non_blocking_wait_rq);
++
++		BUG_ON(RB_EMPTY_ROOT(&sd->service_tree->active) &&
++		       RB_EMPTY_ROOT(&(sd->service_tree+1)->active) &&
++		       RB_EMPTY_ROOT(&(sd->service_tree+2)->active));
++
++		if (!bfq_update_next_in_service(sd, entity) && !requeue) {
++			BUG_ON(!sd->next_in_service);
++			break;
++		}
++		BUG_ON(!sd->next_in_service);
++	}
++}
++
++/**
++ * __bfq_deactivate_entity - deactivate an entity from its service tree.
++ * @entity: the entity to deactivate.
++ * @ins_into_idle_tree: if false, the entity will not be put into the
++ *			idle tree.
++ *
++ * Deactivates an entity, independently from its previous state.  Must
++ * be invoked only if entity is on a service tree. Extracts the entity
++ * from that tree, and if necessary and allowed, puts it on the idle
++ * tree.
++ */
++static bool __bfq_deactivate_entity(struct bfq_entity *entity,
++				    bool ins_into_idle_tree)
++{
++	struct bfq_sched_data *sd = entity->sched_data;
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++	bool is_in_service = entity == sd->in_service_entity;
++
++	if (!entity->on_st) { /* entity never activated, or already inactive */
++		BUG_ON(entity == entity->sched_data->in_service_entity);
++		return false;
++	}
++
++	BUG_ON(is_in_service && entity->tree && entity->tree != &st->active);
++
++	if (is_in_service)
++		bfq_calc_finish(entity, entity->service);
++
++	if (entity->tree == &st->active)
++		bfq_active_extract(st, entity);
++	else if (!is_in_service && entity->tree == &st->idle)
++		bfq_idle_extract(st, entity);
++	else if (entity->tree)
++		BUG();
++
++	if (!ins_into_idle_tree || !bfq_gt(entity->finish, st->vtime))
++		bfq_forget_entity(st, entity, is_in_service);
++	else
++		bfq_idle_insert(st, entity);
++
++	return true;
++}
++
++/**
++ * bfq_deactivate_entity - deactivate an entity representing a bfq_queue.
++ * @entity: the entity to deactivate.
++ * @ins_into_idle_tree: true if the entity can be put on the idle tree
++ */
++static void bfq_deactivate_entity(struct bfq_entity *entity,
++				  bool ins_into_idle_tree,
++				  bool expiration)
++{
++	struct bfq_sched_data *sd;
++	struct bfq_entity *parent = NULL;
++
++	for_each_entity_safe(entity, parent) {
++		sd = entity->sched_data;
++
++		BUG_ON(sd == NULL); /*
++				     * It would mean that this is the
++				     * root group.
++				     */
++
++		BUG_ON(expiration && entity != sd->in_service_entity);
++
++		BUG_ON(entity != sd->in_service_entity &&
++		       entity->tree ==
++		       &bfq_entity_service_tree(entity)->active &&
++		       !sd->next_in_service);
++
++		if (!__bfq_deactivate_entity(entity, ins_into_idle_tree)) {
++			/*
++			 * entity is not in any tree any more, so
++			 * this deactivation is a no-op, and there is
++			 * nothing to change for upper-level entities
++			 * (in case of expiration, this can never
++			 * happen).
++			 */
++			BUG_ON(expiration); /*
++					     * entity cannot be already out of
++					     * any tree
++					     */
++			return;
++		}
++
++		if (sd->next_in_service == entity)
++			/*
++			 * entity was the next_in_service entity,
++			 * then, since entity has just been
++			 * deactivated, a new one must be found.
++			 */
++			bfq_update_next_in_service(sd, NULL);
++
++		if (sd->next_in_service) {
++			/*
++			 * The parent entity is still backlogged,
++			 * because next_in_service is not NULL. So, no
++			 * further upwards deactivation must be
++			 * performed.  Yet, next_in_service has
++			 * changed.  Then the schedule does need to be
++			 * updated upwards.
++			 */
++			BUG_ON(sd->next_in_service == entity);
++			break;
++		}
++
++		/*
++		 * If we get here, then the parent is no more
++		 * backlogged and we need to propagate the
++		 * deactivation upwards. Thus let the loop go on.
++		 */
++
++		/*
++		 * Also let parent be queued into the idle tree on
++		 * deactivation, to preserve service guarantees, and
++		 * assuming that who invoked this function does not
++		 * need parent entities too to be removed completely.
++		 */
++		ins_into_idle_tree = true;
++	}
++
++	/*
++	 * If the deactivation loop is fully executed, then there are
++	 * no more entities to touch and next loop is not executed at
++	 * all. Otherwise, requeue remaining entities if they are
++	 * about to stop receiving service, or reposition them if this
++	 * is not the case.
++	 */
++	entity = parent;
++	for_each_entity(entity) {
++		struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++		/*
++		 * Invoke __bfq_requeue_entity on entity, even if
++		 * already active, to requeue/reposition it in the
++		 * active tree (because sd->next_in_service has
++		 * changed)
++		 */
++		__bfq_requeue_entity(entity);
++
++		sd = entity->sched_data;
++		BUG_ON(expiration && sd->in_service_entity != entity);
++
++		if (bfqq)
++			bfq_log_bfqq(bfqq->bfqd, bfqq,
++				     "invoking udpdate_next for this queue");
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		else {
++			struct bfq_group *bfqg =
++				container_of(entity,
++					     struct bfq_group, entity);
++
++			bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++				     "invoking udpdate_next for this entity");
++		}
++#endif
++		if (!bfq_update_next_in_service(sd, entity) &&
++		    !expiration)
++			/*
++			 * next_in_service unchanged or not causing
++			 * any change in entity->parent->sd, and no
++			 * requeueing needed for expiration: stop
++			 * here.
++			 */
++			break;
++	}
++}
++
++/**
++ * bfq_calc_vtime_jump - compute the value to which the vtime should jump,
++ *                       if needed, to have at least one entity eligible.
++ * @st: the service tree to act upon.
++ *
++ * Assumes that st is not empty.
++ */
++static u64 bfq_calc_vtime_jump(struct bfq_service_tree *st)
++{
++	struct bfq_entity *root_entity = bfq_root_active_entity(&st->active);
++
++	if (bfq_gt(root_entity->min_start, st->vtime)) {
++		struct bfq_queue *bfqq = bfq_entity_to_bfqq(root_entity);
++
++		if (bfqq)
++			bfq_log_bfqq(bfqq->bfqd, bfqq,
++				     "calc_vtime_jump: new value %llu",
++				     root_entity->min_start);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		else {
++			struct bfq_group *bfqg =
++				container_of(root_entity, struct bfq_group,
++					     entity);
++
++			bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++				     "calc_vtime_jump: new value %llu",
++				     root_entity->min_start);
++		}
++#endif
++		return root_entity->min_start;
++	}
++	return st->vtime;
++}
++
++static void bfq_update_vtime(struct bfq_service_tree *st, u64 new_value)
++{
++	if (new_value > st->vtime) {
++		st->vtime = new_value;
++		bfq_forget_idle(st);
++	}
++}
++
++/**
++ * bfq_first_active_entity - find the eligible entity with
++ *                           the smallest finish time
++ * @st: the service tree to select from.
++ * @vtime: the system virtual to use as a reference for eligibility
++ *
++ * This function searches the first schedulable entity, starting from the
++ * root of the tree and going on the left every time on this side there is
++ * a subtree with at least one eligible (start >= vtime) entity. The path on
++ * the right is followed only if a) the left subtree contains no eligible
++ * entities and b) no eligible entity has been found yet.
++ */
++static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st,
++						  u64 vtime)
++{
++	struct bfq_entity *entry, *first = NULL;
++	struct rb_node *node = st->active.rb_node;
++
++	while (node) {
++		entry = rb_entry(node, struct bfq_entity, rb_node);
++left:
++		if (!bfq_gt(entry->start, vtime))
++			first = entry;
++
++		BUG_ON(bfq_gt(entry->min_start, vtime));
++
++		if (node->rb_left) {
++			entry = rb_entry(node->rb_left,
++					 struct bfq_entity, rb_node);
++			if (!bfq_gt(entry->min_start, vtime)) {
++				node = node->rb_left;
++				goto left;
++			}
++		}
++		if (first)
++			break;
++		node = node->rb_right;
++	}
++
++	BUG_ON(!first && !RB_EMPTY_ROOT(&st->active));
++	return first;
++}
++
++/**
++ * __bfq_lookup_next_entity - return the first eligible entity in @st.
++ * @st: the service tree.
++ *
++ * If there is no in-service entity for the sched_data st belongs to,
++ * then return the entity that will be set in service if:
++ * 1) the parent entity this st belongs to is set in service;
++ * 2) no entity belonging to such parent entity undergoes a state change
++ * that would influence the timestamps of the entity (e.g., becomes idle,
++ * becomes backlogged, changes its budget, ...).
++ *
++ * In this first case, update the virtual time in @st too (see the
++ * comments on this update inside the function).
++ *
++ * In constrast, if there is an in-service entity, then return the
++ * entity that would be set in service if not only the above
++ * conditions, but also the next one held true: the currently
++ * in-service entity, on expiration,
++ * 1) gets a finish time equal to the current one, or
++ * 2) is not eligible any more, or
++ * 3) is idle.
++ */
++static struct bfq_entity *
++__bfq_lookup_next_entity(struct bfq_service_tree *st, bool in_service
++#if 0
++			 , bool force
++#endif
++	)
++{
++	struct bfq_entity *entity
++#if 0
++		, *new_next_in_service = NULL
++#endif
++		;
++	u64 new_vtime;
++	struct bfq_queue *bfqq;
++
++	if (RB_EMPTY_ROOT(&st->active))
++		return NULL;
++
++	/*
++	 * Get the value of the system virtual time for which at
++	 * least one entity is eligible.
++	 */
++	new_vtime = bfq_calc_vtime_jump(st);
++
++	/*
++	 * If there is no in-service entity for the sched_data this
++	 * active tree belongs to, then push the system virtual time
++	 * up to the value that guarantees that at least one entity is
++	 * eligible. If, instead, there is an in-service entity, then
++	 * do not make any such update, because there is already an
++	 * eligible entity, namely the in-service one (even if the
++	 * entity is not on st, because it was extracted when set in
++	 * service).
++	 */
++	if (!in_service)
++		bfq_update_vtime(st, new_vtime);
++
++	entity = bfq_first_active_entity(st, new_vtime);
++	BUG_ON(bfq_gt(entity->start, new_vtime));
++
++	/* Log some information */
++	bfqq = bfq_entity_to_bfqq(entity);
++	if (bfqq)
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			     "__lookup_next: start %llu vtime %llu st %p",
++			     ((entity->start>>10)*1000)>>12,
++			     ((new_vtime>>10)*1000)>>12, st);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			     "__lookup_next: start %llu vtime %llu st %p",
++			     ((entity->start>>10)*1000)>>12,
++			     ((new_vtime>>10)*1000)>>12, st);
++	}
++#endif
++
++	BUG_ON(!entity);
++
++	return entity;
++}
++
++/**
++ * bfq_lookup_next_entity - return the first eligible entity in @sd.
++ * @sd: the sched_data.
++ *
++ * This function is invoked when there has been a change in the trees
++ * for sd, and we need know what is the new next entity after this
++ * change.
++ */
++static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd)
++{
++	struct bfq_service_tree *st = sd->service_tree;
++	struct bfq_service_tree *idle_class_st = st + (BFQ_IOPRIO_CLASSES - 1);
++	struct bfq_entity *entity = NULL;
++	struct bfq_queue *bfqq;
++	int class_idx = 0;
++
++	BUG_ON(!sd);
++	BUG_ON(!st);
++	/*
++	 * Choose from idle class, if needed to guarantee a minimum
++	 * bandwidth to this class (and if there is some active entity
++	 * in idle class). This should also mitigate
++	 * priority-inversion problems in case a low priority task is
++	 * holding file system resources.
++	 */
++	if (time_is_before_jiffies(sd->bfq_class_idle_last_service +
++				   BFQ_CL_IDLE_TIMEOUT)) {
++		if (!RB_EMPTY_ROOT(&idle_class_st->active))
++			class_idx = BFQ_IOPRIO_CLASSES - 1;
++		/* About to be served if backlogged, or not yet backlogged */
++		sd->bfq_class_idle_last_service = jiffies;
++	}
++
++	/*
++	 * Find the next entity to serve for the highest-priority
++	 * class, unless the idle class needs to be served.
++	 */
++	for (; class_idx < BFQ_IOPRIO_CLASSES; class_idx++) {
++		entity = __bfq_lookup_next_entity(st + class_idx,
++						  sd->in_service_entity);
++
++		if (entity)
++			break;
++	}
++
++	BUG_ON(!entity &&
++	       (!RB_EMPTY_ROOT(&st->active) || !RB_EMPTY_ROOT(&(st+1)->active) ||
++		!RB_EMPTY_ROOT(&(st+2)->active)));
++
++	if (!entity)
++		return NULL;
++
++	/* Log some information */
++	bfqq = bfq_entity_to_bfqq(entity);
++	if (bfqq)
++		bfq_log_bfqq(bfqq->bfqd, bfqq, "chosen from st %p %d",
++			     st + class_idx, class_idx);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			     "chosen from st %p %d",
++			     st + class_idx, class_idx);
++	}
++#endif
++
++	return entity;
++}
++
++static bool next_queue_may_preempt(struct bfq_data *bfqd)
++{
++	struct bfq_sched_data *sd = &bfqd->root_group->sched_data;
++
++	return sd->next_in_service != sd->in_service_entity;
++}
++
++/*
++ * Get next queue for service.
++ */
++static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd)
++{
++	struct bfq_entity *entity = NULL;
++	struct bfq_sched_data *sd;
++	struct bfq_queue *bfqq;
++
++	BUG_ON(bfqd->in_service_queue);
++
++	if (bfqd->busy_queues == 0)
++		return NULL;
++
++	/*
++	 * Traverse the path from the root to the leaf entity to
++	 * serve. Set in service all the entities visited along the
++	 * way.
++	 */
++	sd = &bfqd->root_group->sched_data;
++	for (; sd ; sd = entity->my_sched_data) {
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		if (entity) {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group, entity);
++
++			bfq_log_bfqg(bfqd, bfqg,
++				     "get_next_queue: lookup in this group");
++			if (!sd->next_in_service)
++				pr_crit("get_next_queue: lookup in this group");
++		} else {
++			bfq_log_bfqg(bfqd, bfqd->root_group,
++				     "get_next_queue: lookup in root group");
++			if (!sd->next_in_service)
++				pr_crit("get_next_queue: lookup in root group");
++		}
++#endif
++
++		BUG_ON(!sd->next_in_service);
++
++		/*
++		 * WARNING. We are about to set the in-service entity
++		 * to sd->next_in_service, i.e., to the (cached) value
++		 * returned by bfq_lookup_next_entity(sd) the last
++		 * time it was invoked, i.e., the last time when the
++		 * service order in sd changed as a consequence of the
++		 * activation or deactivation of an entity. In this
++		 * respect, if we execute bfq_lookup_next_entity(sd)
++		 * in this very moment, it may, although with low
++		 * probability, yield a different entity than that
++		 * pointed to by sd->next_in_service. This rare event
++		 * happens in case there was no CLASS_IDLE entity to
++		 * serve for sd when bfq_lookup_next_entity(sd) was
++		 * invoked for the last time, while there is now one
++		 * such entity.
++		 *
++		 * If the above event happens, then the scheduling of
++		 * such entity in CLASS_IDLE is postponed until the
++		 * service of the sd->next_in_service entity
++		 * finishes. In fact, when the latter is expired,
++		 * bfq_lookup_next_entity(sd) gets called again,
++		 * exactly to update sd->next_in_service.
++		 */
++
++		/* Make next_in_service entity become in_service_entity */
++		entity = sd->next_in_service;
++		sd->in_service_entity = entity;
++
++		/*
++		 * Reset the accumulator of the amount of service that
++		 * the entity is about to receive.
++		 */
++		entity->service = 0;
++
++		/*
++		 * If entity is no longer a candidate for next
++		 * service, then we extract it from its active tree,
++		 * for the following reason. To further boost the
++		 * throughput in some special case, BFQ needs to know
++		 * which is the next candidate entity to serve, while
++		 * there is already an entity in service. In this
++		 * respect, to make it easy to compute/update the next
++		 * candidate entity to serve after the current
++		 * candidate has been set in service, there is a case
++		 * where it is necessary to extract the current
++		 * candidate from its service tree. Such a case is
++		 * when the entity just set in service cannot be also
++		 * a candidate for next service. Details about when
++		 * this conditions holds are reported in the comments
++		 * on the function bfq_no_longer_next_in_service()
++		 * invoked below.
++		 */
++		if (bfq_no_longer_next_in_service(entity))
++			bfq_active_extract(bfq_entity_service_tree(entity),
++					   entity);
++
++		/*
++		 * For the same reason why we may have just extracted
++		 * entity from its active tree, we may need to update
++		 * next_in_service for the sched_data of entity too,
++		 * regardless of whether entity has been extracted.
++		 * In fact, even if entity has not been extracted, a
++		 * descendant entity may get extracted. Such an event
++		 * would cause a change in next_in_service for the
++		 * level of the descendant entity, and thus possibly
++		 * back to upper levels.
++		 *
++		 * We cannot perform the resulting needed update
++		 * before the end of this loop, because, to know which
++		 * is the correct next-to-serve candidate entity for
++		 * each level, we need first to find the leaf entity
++		 * to set in service. In fact, only after we know
++		 * which is the next-to-serve leaf entity, we can
++		 * discover whether the parent entity of the leaf
++		 * entity becomes the next-to-serve, and so on.
++		 */
++
++		/* Log some information */
++		bfqq = bfq_entity_to_bfqq(entity);
++		if (bfqq)
++			bfq_log_bfqq(bfqd, bfqq,
++			     "get_next_queue: this queue, finish %llu",
++				(((entity->finish>>10)*1000)>>10)>>2);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		else {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group, entity);
++
++			bfq_log_bfqg(bfqd, bfqg,
++			     "get_next_queue: this entity, finish %llu",
++				(((entity->finish>>10)*1000)>>10)>>2);
++		}
++#endif
++
++	}
++
++	BUG_ON(!entity);
++	bfqq = bfq_entity_to_bfqq(entity);
++	BUG_ON(!bfqq);
++
++	/*
++	 * We can finally update all next-to-serve entities along the
++	 * path from the leaf entity just set in service to the root.
++	 */
++	for_each_entity(entity) {
++		struct bfq_sched_data *sd = entity->sched_data;
++
++		if(!bfq_update_next_in_service(sd, NULL))
++			break;
++	}
++
++	return bfqq;
++}
++
++static void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd)
++{
++	struct bfq_queue *in_serv_bfqq = bfqd->in_service_queue;
++	struct bfq_entity *in_serv_entity = &in_serv_bfqq->entity;
++	struct bfq_entity *entity = in_serv_entity;
++
++	if (bfqd->in_service_bic) {
++		put_io_context(bfqd->in_service_bic->icq.ioc);
++		bfqd->in_service_bic = NULL;
++	}
++
++	bfq_clear_bfqq_wait_request(in_serv_bfqq);
++	hrtimer_try_to_cancel(&bfqd->idle_slice_timer);
++	bfqd->in_service_queue = NULL;
++
++	/*
++	 * When this function is called, all in-service entities have
++	 * been properly deactivated or requeued, so we can safely
++	 * execute the final step: reset in_service_entity along the
++	 * path from entity to the root.
++	 */
++	for_each_entity(entity)
++		entity->sched_data->in_service_entity = NULL;
++
++	/*
++	 * in_serv_entity is no longer in service, so, if it is in no
++	 * service tree either, then release the service reference to
++	 * the queue it represents (taken with bfq_get_entity).
++	 */
++	if (!in_serv_entity->on_st)
++		bfq_put_queue(in_serv_bfqq);
++}
++
++static void set_next_in_service_bfqq(struct bfq_data *bfqd)
++{
++	struct bfq_entity *entity = NULL;
++	struct bfq_queue *bfqq;
++	struct bfq_sched_data *sd = &bfqd->root_group->sched_data;
++
++	BUG_ON(!sd);
++
++	/* Traverse the path from the root to the in-service leaf entity */
++	for (; sd ; sd = entity->my_sched_data) {
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		if (entity) {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group, entity);
++
++			bfq_log_bfqg(bfqd, bfqg,
++			"set_next_in_service_bfqq: lookup in this group");
++		} else
++			bfq_log_bfqg(bfqd, bfqd->root_group,
++			"set_next_in_service_bfqq: lookup in root group");
++#endif
++
++		entity = sd->next_in_service;
++
++		if (!entity) {
++			bfqd->next_in_service_queue = NULL;
++			return;
++		}
++
++		/* Log some information */
++		bfqq = bfq_entity_to_bfqq(entity);
++		if (bfqq)
++			bfq_log_bfqq(bfqd, bfqq,
++			"set_next_in_service_bfqq: this queue, finish %llu",
++				(((entity->finish>>10)*1000)>>10)>>2);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++		else {
++			struct bfq_group *bfqg =
++				container_of(entity, struct bfq_group, entity);
++
++			bfq_log_bfqg(bfqd, bfqg,
++			"set_next_in_service_bfqq: this entity, finish %llu",
++				(((entity->finish>>10)*1000)>>10)>>2);
++		}
++#endif
++
++	}
++	BUG_ON(!bfq_entity_to_bfqq(entity));
++
++	bfqd->next_in_service_queue = bfq_entity_to_bfqq(entity);
++}
++
++static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++				bool ins_into_idle_tree, bool expiration)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	bfq_deactivate_entity(entity, ins_into_idle_tree, expiration);
++	set_next_in_service_bfqq(bfqd);
++}
++
++static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++	struct bfq_service_tree *st = bfq_entity_service_tree(entity);
++
++	BUG_ON(bfqq == bfqd->in_service_queue);
++	BUG_ON(entity->tree != &st->active && entity->tree != &st->idle &&
++	       entity->on_st);
++
++	bfq_activate_requeue_entity(entity, bfq_bfqq_non_blocking_wait_rq(bfqq),
++				    false);
++	bfq_clear_bfqq_non_blocking_wait_rq(bfqq);
++	set_next_in_service_bfqq(bfqd);
++}
++
++static void bfq_requeue_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	struct bfq_entity *entity = &bfqq->entity;
++
++	bfq_activate_requeue_entity(entity, false,
++				    bfqq == bfqd->in_service_queue);
++	set_next_in_service_bfqq(bfqd);
++}
++
++static void bfqg_stats_update_dequeue(struct bfq_group *bfqg);
++
++/*
++ * Called when the bfqq no longer has requests pending, remove it from
++ * the service tree. As a special case, it can be invoked during an
++ * expiration.
++ */
++static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq,
++			      bool expiration)
++{
++	BUG_ON(!bfq_bfqq_busy(bfqq));
++	BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list));
++
++	bfq_log_bfqq(bfqd, bfqq, "del from busy");
++
++	bfq_clear_bfqq_busy(bfqq);
++
++	BUG_ON(bfqd->busy_queues == 0);
++	bfqd->busy_queues--;
++
++	if (!bfqq->dispatched)
++		bfq_weights_tree_remove(bfqd, &bfqq->entity,
++					&bfqd->queue_weights_tree);
++
++	if (bfqq->wr_coeff > 1)
++		bfqd->wr_busy_queues--;
++
++	bfqg_stats_update_dequeue(bfqq_group(bfqq));
++
++	BUG_ON(bfqq->entity.budget < 0);
++
++	bfq_deactivate_bfqq(bfqd, bfqq, true, expiration);
++}
++
++/*
++ * Called when an inactive queue receives a new request.
++ */
++static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq)
++{
++	BUG_ON(bfq_bfqq_busy(bfqq));
++	BUG_ON(bfqq == bfqd->in_service_queue);
++
++	bfq_log_bfqq(bfqd, bfqq, "add to busy");
++
++	bfq_activate_bfqq(bfqd, bfqq);
++
++	bfq_mark_bfqq_busy(bfqq);
++	bfqd->busy_queues++;
++
++	if (!bfqq->dispatched)
++		if (bfqq->wr_coeff == 1)
++			bfq_weights_tree_add(bfqd, &bfqq->entity,
++					     &bfqd->queue_weights_tree);
++
++	if (bfqq->wr_coeff > 1)
++		bfqd->wr_busy_queues++;
++}
+diff --git a/block/bfq.h b/block/bfq.h
+new file mode 100644
+index 000000000000..67d56670e678
+--- /dev/null
++++ b/block/bfq.h
+@@ -0,0 +1,933 @@
++/*
++ * BFQ v8r10-rc1 for 4.11.0: data structures and common functions prototypes.
++ *
++ * Based on ideas and code from CFQ:
++ * Copyright (C) 2003 Jens Axboe <axboe@kernel.dk>
++ *
++ * Copyright (C) 2008 Fabio Checconi <fabio@gandalf.sssup.it>
++ *		      Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2015 Paolo Valente <paolo.valente@unimore.it>
++ *
++ * Copyright (C) 2017 Paolo Valente <paolo.valente@linaro.org>
++ */
++
++#ifndef _BFQ_H
++#define _BFQ_H
++
++#include <linux/blktrace_api.h>
++#include <linux/hrtimer.h>
++#include <linux/blk-cgroup.h>
++
++#define BFQ_IOPRIO_CLASSES	3
++#define BFQ_CL_IDLE_TIMEOUT	(HZ/5)
++
++#define BFQ_MIN_WEIGHT			1
++#define BFQ_MAX_WEIGHT			1000
++#define BFQ_WEIGHT_CONVERSION_COEFF	10
++
++#define BFQ_DEFAULT_QUEUE_IOPRIO	4
++
++#define BFQ_WEIGHT_LEGACY_DFL	100
++#define BFQ_DEFAULT_GRP_IOPRIO	0
++#define BFQ_DEFAULT_GRP_CLASS	IOPRIO_CLASS_BE
++
++/*
++ * Soft real-time applications are extremely more latency sensitive
++ * than interactive ones. Over-raise the weight of the former to
++ * privilege them against the latter.
++ */
++#define BFQ_SOFTRT_WEIGHT_FACTOR	100
++
++struct bfq_entity;
++
++/**
++ * struct bfq_service_tree - per ioprio_class service tree.
++ *
++ * Each service tree represents a B-WF2Q+ scheduler on its own.  Each
++ * ioprio_class has its own independent scheduler, and so its own
++ * bfq_service_tree.  All the fields are protected by the queue lock
++ * of the containing bfqd.
++ */
++struct bfq_service_tree {
++	/* tree for active entities (i.e., those backlogged) */
++	struct rb_root active;
++	/* tree for idle entities (i.e., not backlogged, with V <= F_i)*/
++	struct rb_root idle;
++
++	struct bfq_entity *first_idle;	/* idle entity with minimum F_i */
++	struct bfq_entity *last_idle;	/* idle entity with maximum F_i */
++
++	u64 vtime; /* scheduler virtual time */
++	/* scheduler weight sum; active and idle entities contribute to it */
++	unsigned long wsum;
++};
++
++/**
++ * struct bfq_sched_data - multi-class scheduler.
++ *
++ * bfq_sched_data is the basic scheduler queue.  It supports three
++ * ioprio_classes, and can be used either as a toplevel queue or as an
++ * intermediate queue on a hierarchical setup.  @next_in_service
++ * points to the active entity of the sched_data service trees that
++ * will be scheduled next. It is used to reduce the number of steps
++ * needed for each hierarchical-schedule update.
++ *
++ * The supported ioprio_classes are the same as in CFQ, in descending
++ * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE.
++ * Requests from higher priority queues are served before all the
++ * requests from lower priority queues; among requests of the same
++ * queue requests are served according to B-WF2Q+.
++ * All the fields are protected by the queue lock of the containing bfqd.
++ */
++struct bfq_sched_data {
++	struct bfq_entity *in_service_entity;  /* entity in service */
++	/* head-of-the-line entity in the scheduler (see comments above) */
++	struct bfq_entity *next_in_service;
++	/* array of service trees, one per ioprio_class */
++	struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES];
++	/* last time CLASS_IDLE was served */
++	unsigned long bfq_class_idle_last_service;
++
++};
++
++/**
++ * struct bfq_weight_counter - counter of the number of all active entities
++ *                             with a given weight.
++ */
++struct bfq_weight_counter {
++	unsigned int weight; /* weight of the entities this counter refers to */
++	unsigned int num_active; /* nr of active entities with this weight */
++	/*
++	 * Weights tree member (see bfq_data's @queue_weights_tree and
++	 * @group_weights_tree)
++	 */
++	struct rb_node weights_node;
++};
++
++/**
++ * struct bfq_entity - schedulable entity.
++ *
++ * A bfq_entity is used to represent either a bfq_queue (leaf node in the
++ * cgroup hierarchy) or a bfq_group into the upper level scheduler.  Each
++ * entity belongs to the sched_data of the parent group in the cgroup
++ * hierarchy.  Non-leaf entities have also their own sched_data, stored
++ * in @my_sched_data.
++ *
++ * Each entity stores independently its priority values; this would
++ * allow different weights on different devices, but this
++ * functionality is not exported to userspace by now.  Priorities and
++ * weights are updated lazily, first storing the new values into the
++ * new_* fields, then setting the @prio_changed flag.  As soon as
++ * there is a transition in the entity state that allows the priority
++ * update to take place the effective and the requested priority
++ * values are synchronized.
++ *
++ * Unless cgroups are used, the weight value is calculated from the
++ * ioprio to export the same interface as CFQ.  When dealing with
++ * ``well-behaved'' queues (i.e., queues that do not spend too much
++ * time to consume their budget and have true sequential behavior, and
++ * when there are no external factors breaking anticipation) the
++ * relative weights at each level of the cgroups hierarchy should be
++ * guaranteed.  All the fields are protected by the queue lock of the
++ * containing bfqd.
++ */
++struct bfq_entity {
++	struct rb_node rb_node; /* service_tree member */
++	/* pointer to the weight counter associated with this entity */
++	struct bfq_weight_counter *weight_counter;
++
++	/*
++	 * Flag, true if the entity is on a tree (either the active or
++	 * the idle one of its service_tree) or is in service.
++	 */
++	bool on_st;
++
++	u64 finish; /* B-WF2Q+ finish timestamp (aka F_i) */
++	u64 start;  /* B-WF2Q+ start timestamp (aka S_i) */
++
++	/* tree the entity is enqueued into; %NULL if not on a tree */
++	struct rb_root *tree;
++
++	/*
++	 * minimum start time of the (active) subtree rooted at this
++	 * entity; used for O(log N) lookups into active trees
++	 */
++	u64 min_start;
++
++	/* amount of service received during the last service slot */
++	int service;
++
++	/* budget, used also to calculate F_i: F_i = S_i + @budget / @weight */
++	int budget;
++
++	unsigned int weight;	 /* weight of the queue */
++	unsigned int new_weight; /* next weight if a change is in progress */
++
++	/* original weight, used to implement weight boosting */
++	unsigned int orig_weight;
++
++	/* parent entity, for hierarchical scheduling */
++	struct bfq_entity *parent;
++
++	/*
++	 * For non-leaf nodes in the hierarchy, the associated
++	 * scheduler queue, %NULL on leaf nodes.
++	 */
++	struct bfq_sched_data *my_sched_data;
++	/* the scheduler queue this entity belongs to */
++	struct bfq_sched_data *sched_data;
++
++	/* flag, set to request a weight, ioprio or ioprio_class change  */
++	int prio_changed;
++};
++
++struct bfq_group;
++
++/**
++ * struct bfq_queue - leaf schedulable entity.
++ *
++ * A bfq_queue is a leaf request queue; it can be associated with an
++ * io_context or more, if it  is  async or shared  between  cooperating
++ * processes. @cgroup holds a reference to the cgroup, to be sure that it
++ * does not disappear while a bfqq still references it (mostly to avoid
++ * races between request issuing and task migration followed by cgroup
++ * destruction).
++ * All the fields are protected by the queue lock of the containing bfqd.
++ */
++struct bfq_queue {
++	/* reference counter */
++	int ref;
++	/* parent bfq_data */
++	struct bfq_data *bfqd;
++
++	/* current ioprio and ioprio class */
++	unsigned short ioprio, ioprio_class;
++	/* next ioprio and ioprio class if a change is in progress */
++	unsigned short new_ioprio, new_ioprio_class;
++
++	/*
++	 * Shared bfq_queue if queue is cooperating with one or more
++	 * other queues.
++	 */
++	struct bfq_queue *new_bfqq;
++	/* request-position tree member (see bfq_group's @rq_pos_tree) */
++	struct rb_node pos_node;
++	/* request-position tree root (see bfq_group's @rq_pos_tree) */
++	struct rb_root *pos_root;
++
++	/* sorted list of pending requests */
++	struct rb_root sort_list;
++	/* if fifo isn't expired, next request to serve */
++	struct request *next_rq;
++	/* number of sync and async requests queued */
++	int queued[2];
++	/* number of sync and async requests currently allocated */
++	int allocated[2];
++	/* number of pending metadata requests */
++	int meta_pending;
++	/* fifo list of requests in sort_list */
++	struct list_head fifo;
++
++	/* entity representing this queue in the scheduler */
++	struct bfq_entity entity;
++
++	/* maximum budget allowed from the feedback mechanism */
++	int max_budget;
++	/* budget expiration (in jiffies) */
++	unsigned long budget_timeout;
++
++	/* number of requests on the dispatch list or inside driver */
++	int dispatched;
++
++	unsigned int flags; /* status flags.*/
++
++	/* node for active/idle bfqq list inside parent bfqd */
++	struct list_head bfqq_list;
++
++	/* bit vector: a 1 for each seeky requests in history */
++	u32 seek_history;
++
++	/* node for the device's burst list */
++	struct hlist_node burst_list_node;
++
++	/* position of the last request enqueued */
++	sector_t last_request_pos;
++
++	/* Number of consecutive pairs of request completion and
++	 * arrival, such that the queue becomes idle after the
++	 * completion, but the next request arrives within an idle
++	 * time slice; used only if the queue's IO_bound flag has been
++	 * cleared.
++	 */
++	unsigned int requests_within_timer;
++
++	/* pid of the process owning the queue, used for logging purposes */
++	pid_t pid;
++
++	/*
++	 * Pointer to the bfq_io_cq owning the bfq_queue, set to %NULL
++	 * if the queue is shared.
++	 */
++	struct bfq_io_cq *bic;
++
++	/* current maximum weight-raising time for this queue */
++	unsigned long wr_cur_max_time;
++	/*
++	 * Minimum time instant such that, only if a new request is
++	 * enqueued after this time instant in an idle @bfq_queue with
++	 * no outstanding requests, then the task associated with the
++	 * queue it is deemed as soft real-time (see the comments on
++	 * the function bfq_bfqq_softrt_next_start())
++	 */
++	unsigned long soft_rt_next_start;
++	/*
++	 * Start time of the current weight-raising period if
++	 * the @bfq-queue is being weight-raised, otherwise
++	 * finish time of the last weight-raising period.
++	 */
++	unsigned long last_wr_start_finish;
++	/* factor by which the weight of this queue is multiplied */
++	unsigned int wr_coeff;
++	/*
++	 * Time of the last transition of the @bfq_queue from idle to
++	 * backlogged.
++	 */
++	unsigned long last_idle_bklogged;
++	/*
++	 * Cumulative service received from the @bfq_queue since the
++	 * last transition from idle to backlogged.
++	 */
++	unsigned long service_from_backlogged;
++	/*
++	 * Value of wr start time when switching to soft rt
++	 */
++	unsigned long wr_start_at_switch_to_srt;
++
++	unsigned long split_time; /* time of last split */
++};
++
++/**
++ * struct bfq_ttime - per process thinktime stats.
++ */
++struct bfq_ttime {
++	u64 last_end_request; /* completion time of last request */
++
++	u64 ttime_total; /* total process thinktime */
++	unsigned long ttime_samples; /* number of thinktime samples */
++	u64 ttime_mean; /* average process thinktime */
++
++};
++
++/**
++ * struct bfq_io_cq - per (request_queue, io_context) structure.
++ */
++struct bfq_io_cq {
++	/* associated io_cq structure */
++	struct io_cq icq; /* must be the first member */
++	/* array of two process queues, the sync and the async */
++	struct bfq_queue *bfqq[2];
++	/* associated @bfq_ttime struct */
++	struct bfq_ttime ttime;
++	/* per (request_queue, blkcg) ioprio */
++	int ioprio;
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	uint64_t blkcg_serial_nr; /* the current blkcg serial */
++#endif
++
++	/*
++	 * Snapshot of the idle window before merging; taken to
++	 * remember this value while the queue is merged, so as to be
++	 * able to restore it in case of split.
++	 */
++	bool saved_idle_window;
++	/*
++	 * Same purpose as the previous two fields for the I/O bound
++	 * classification of a queue.
++	 */
++	bool saved_IO_bound;
++
++	/*
++	 * Same purpose as the previous fields for the value of the
++	 * field keeping the queue's belonging to a large burst
++	 */
++	bool saved_in_large_burst;
++	/*
++	 * True if the queue belonged to a burst list before its merge
++	 * with another cooperating queue.
++	 */
++	bool was_in_burst_list;
++
++	/*
++	 * Similar to previous fields: save wr information.
++	 */
++	unsigned long saved_wr_coeff;
++	unsigned long saved_last_wr_start_finish;
++	unsigned long saved_wr_start_at_switch_to_srt;
++	unsigned int saved_wr_cur_max_time;
++};
++
++enum bfq_device_speed {
++	BFQ_BFQD_FAST,
++	BFQ_BFQD_SLOW,
++};
++
++/**
++ * struct bfq_data - per-device data structure.
++ *
++ * All the fields are protected by the @queue lock.
++ */
++struct bfq_data {
++	/* request queue for the device */
++	struct request_queue *queue;
++
++	/* root bfq_group for the device */
++	struct bfq_group *root_group;
++
++	/*
++	 * rbtree of weight counters of @bfq_queues, sorted by
++	 * weight. Used to keep track of whether all @bfq_queues have
++	 * the same weight. The tree contains one counter for each
++	 * distinct weight associated to some active and not
++	 * weight-raised @bfq_queue (see the comments to the functions
++	 * bfq_weights_tree_[add|remove] for further details).
++	 */
++	struct rb_root queue_weights_tree;
++	/*
++	 * rbtree of non-queue @bfq_entity weight counters, sorted by
++	 * weight. Used to keep track of whether all @bfq_groups have
++	 * the same weight. The tree contains one counter for each
++	 * distinct weight associated to some active @bfq_group (see
++	 * the comments to the functions bfq_weights_tree_[add|remove]
++	 * for further details).
++	 */
++	struct rb_root group_weights_tree;
++
++	/*
++	 * Number of bfq_queues containing requests (including the
++	 * queue in service, even if it is idling).
++	 */
++	int busy_queues;
++	/* number of weight-raised busy @bfq_queues */
++	int wr_busy_queues;
++	/* number of queued requests */
++	int queued;
++	/* number of requests dispatched and waiting for completion */
++	int rq_in_driver;
++
++	/*
++	 * Maximum number of requests in driver in the last
++	 * @hw_tag_samples completed requests.
++	 */
++	int max_rq_in_driver;
++	/* number of samples used to calculate hw_tag */
++	int hw_tag_samples;
++	/* flag set to one if the driver is showing a queueing behavior */
++	int hw_tag;
++
++	/* number of budgets assigned */
++	int budgets_assigned;
++
++	/*
++	 * Timer set when idling (waiting) for the next request from
++	 * the queue in service.
++	 */
++	struct hrtimer idle_slice_timer;
++	/* delayed work to restart dispatching on the request queue */
++	struct work_struct unplug_work;
++
++	/* bfq_queue in service */
++	struct bfq_queue *in_service_queue;
++	/* candidate bfq_queue to become the next in-service queue */
++	struct bfq_queue *next_in_service_queue;
++	/* bfq_io_cq (bic) associated with the @in_service_queue */
++	struct bfq_io_cq *in_service_bic;
++
++	/* on-disk position of the last served request */
++	sector_t last_position;
++
++	/* time of last request completion (ns) */
++	u64 last_completion;
++
++	/* time of first rq dispatch in current observation interval (ns) */
++	u64 first_dispatch;
++	/* time of last rq dispatch in current observation interval (ns) */
++	u64 last_dispatch;
++
++	/* beginning of the last budget */
++	ktime_t last_budget_start;
++	/* beginning of the last idle slice */
++	ktime_t last_idling_start;
++
++	/* number of samples in current observation interval */
++	int peak_rate_samples;
++	/* num of samples of seq dispatches in current observation interval */
++	u32 sequential_samples;
++	/* total num of sectors transferred in current observation interval */
++	u64 tot_sectors_dispatched;
++	/* max rq size seen during current observation interval (sectors) */
++	u32 last_rq_max_size;
++	/* time elapsed from first dispatch in current observ. interval (us) */
++	u64 delta_from_first;
++	/* current estimate of device peak rate */
++	u32 peak_rate;
++
++	/* maximum budget allotted to a bfq_queue before rescheduling */
++	int bfq_max_budget;
++
++	/* list of all the bfq_queues active on the device */
++	struct list_head active_list;
++	/* list of all the bfq_queues idle on the device */
++	struct list_head idle_list;
++
++	/*
++	 * Timeout for async/sync requests; when it fires, requests
++	 * are served in fifo order.
++	 */
++	u64 bfq_fifo_expire[2];
++	/* weight of backward seeks wrt forward ones */
++	unsigned int bfq_back_penalty;
++	/* maximum allowed backward seek */
++	unsigned int bfq_back_max;
++	/* maximum idling time */
++	u32 bfq_slice_idle;
++
++	/* user-configured max budget value (0 for auto-tuning) */
++	int bfq_user_max_budget;
++	/*
++	 * Timeout for bfq_queues to consume their budget; used to
++	 * prevent seeky queues from imposing long latencies to
++	 * sequential or quasi-sequential ones (this also implies that
++	 * seeky queues cannot receive guarantees in the service
++	 * domain; after a timeout they are charged for the time they
++	 * have been in service, to preserve fairness among them, but
++	 * without service-domain guarantees).
++	 */
++	unsigned int bfq_timeout;
++
++	/*
++	 * Number of consecutive requests that must be issued within
++	 * the idle time slice to set again idling to a queue which
++	 * was marked as non-I/O-bound (see the definition of the
++	 * IO_bound flag for further details).
++	 */
++	unsigned int bfq_requests_within_timer;
++
++	/*
++	 * Force device idling whenever needed to provide accurate
++	 * service guarantees, without caring about throughput
++	 * issues. CAVEAT: this may even increase latencies, in case
++	 * of useless idling for processes that did stop doing I/O.
++	 */
++	bool strict_guarantees;
++
++	/*
++	 * Last time at which a queue entered the current burst of
++	 * queues being activated shortly after each other; for more
++	 * details about this and the following parameters related to
++	 * a burst of activations, see the comments on the function
++	 * bfq_handle_burst.
++	 */
++	unsigned long last_ins_in_burst;
++	/*
++	 * Reference time interval used to decide whether a queue has
++	 * been activated shortly after @last_ins_in_burst.
++	 */
++	unsigned long bfq_burst_interval;
++	/* number of queues in the current burst of queue activations */
++	int burst_size;
++
++	/* common parent entity for the queues in the burst */
++	struct bfq_entity *burst_parent_entity;
++	/* Maximum burst size above which the current queue-activation
++	 * burst is deemed as 'large'.
++	 */
++	unsigned long bfq_large_burst_thresh;
++	/* true if a large queue-activation burst is in progress */
++	bool large_burst;
++	/*
++	 * Head of the burst list (as for the above fields, more
++	 * details in the comments on the function bfq_handle_burst).
++	 */
++	struct hlist_head burst_list;
++
++	/* if set to true, low-latency heuristics are enabled */
++	bool low_latency;
++	/*
++	 * Maximum factor by which the weight of a weight-raised queue
++	 * is multiplied.
++	 */
++	unsigned int bfq_wr_coeff;
++	/* maximum duration of a weight-raising period (jiffies) */
++	unsigned int bfq_wr_max_time;
++
++	/* Maximum weight-raising duration for soft real-time processes */
++	unsigned int bfq_wr_rt_max_time;
++	/*
++	 * Minimum idle period after which weight-raising may be
++	 * reactivated for a queue (in jiffies).
++	 */
++	unsigned int bfq_wr_min_idle_time;
++	/*
++	 * Minimum period between request arrivals after which
++	 * weight-raising may be reactivated for an already busy async
++	 * queue (in jiffies).
++	 */
++	unsigned long bfq_wr_min_inter_arr_async;
++
++	/* Max service-rate for a soft real-time queue, in sectors/sec */
++	unsigned int bfq_wr_max_softrt_rate;
++	/*
++	 * Cached value of the product R*T, used for computing the
++	 * maximum duration of weight raising automatically.
++	 */
++	u64 RT_prod;
++	/* device-speed class for the low-latency heuristic */
++	enum bfq_device_speed device_speed;
++
++	/* fallback dummy bfqq for extreme OOM conditions */
++	struct bfq_queue oom_bfqq;
++};
++
++enum bfqq_state_flags {
++	BFQ_BFQQ_FLAG_just_created = 0,	/* queue just allocated */
++	BFQ_BFQQ_FLAG_busy,		/* has requests or is in service */
++	BFQ_BFQQ_FLAG_wait_request,	/* waiting for a request */
++	BFQ_BFQQ_FLAG_non_blocking_wait_rq, /*
++					     * waiting for a request
++					     * without idling the device
++					     */
++	BFQ_BFQQ_FLAG_must_alloc,	/* must be allowed rq alloc */
++	BFQ_BFQQ_FLAG_fifo_expire,	/* FIFO checked in this slice */
++	BFQ_BFQQ_FLAG_idle_window,	/* slice idling enabled */
++	BFQ_BFQQ_FLAG_sync,		/* synchronous queue */
++	BFQ_BFQQ_FLAG_IO_bound,		/*
++					 * bfqq has timed-out at least once
++					 * having consumed at most 2/10 of
++					 * its budget
++					 */
++	BFQ_BFQQ_FLAG_in_large_burst,	/*
++					 * bfqq activated in a large burst,
++					 * see comments to bfq_handle_burst.
++					 */
++	BFQ_BFQQ_FLAG_softrt_update,	/*
++					 * may need softrt-next-start
++					 * update
++					 */
++	BFQ_BFQQ_FLAG_coop,		/* bfqq is shared */
++	BFQ_BFQQ_FLAG_split_coop	/* shared bfqq will be split */
++};
++
++#define BFQ_BFQQ_FNS(name)						\
++static void bfq_mark_bfqq_##name(struct bfq_queue *bfqq)		\
++{									\
++	(bfqq)->flags |= (1 << BFQ_BFQQ_FLAG_##name);			\
++}									\
++static void bfq_clear_bfqq_##name(struct bfq_queue *bfqq)		\
++{									\
++	(bfqq)->flags &= ~(1 << BFQ_BFQQ_FLAG_##name);			\
++}									\
++static int bfq_bfqq_##name(const struct bfq_queue *bfqq)		\
++{									\
++	return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0;	\
++}
++
++BFQ_BFQQ_FNS(just_created);
++BFQ_BFQQ_FNS(busy);
++BFQ_BFQQ_FNS(wait_request);
++BFQ_BFQQ_FNS(non_blocking_wait_rq);
++BFQ_BFQQ_FNS(must_alloc);
++BFQ_BFQQ_FNS(fifo_expire);
++BFQ_BFQQ_FNS(idle_window);
++BFQ_BFQQ_FNS(sync);
++BFQ_BFQQ_FNS(IO_bound);
++BFQ_BFQQ_FNS(in_large_burst);
++BFQ_BFQQ_FNS(coop);
++BFQ_BFQQ_FNS(split_coop);
++BFQ_BFQQ_FNS(softrt_update);
++#undef BFQ_BFQQ_FNS
++
++/* Logging facilities. */
++#ifdef CONFIG_BFQ_REDIRECT_TO_CONSOLE
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
++static struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg);
++
++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...)	do {			\
++	char __pbuf[128];						\
++									\
++	assert_spin_locked((bfqd)->queue->queue_lock);			\
++	blkg_path(bfqg_to_blkg(bfqq_group(bfqq)), __pbuf, sizeof(__pbuf)); \
++	pr_crit("bfq%d%c %s " fmt "\n", 			\
++		(bfqq)->pid,						\
++		bfq_bfqq_sync((bfqq)) ? 'S' : 'A',			\
++		__pbuf, ##args);					\
++} while (0)
++
++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...)	do {			\
++	char __pbuf[128];						\
++									\
++	blkg_path(bfqg_to_blkg(bfqg), __pbuf, sizeof(__pbuf));		\
++	pr_crit("%s " fmt "\n", __pbuf, ##args);	\
++} while (0)
++
++#else /* CONFIG_BFQ_GROUP_IOSCHED */
++
++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...)		\
++	pr_crit("bfq%d%c " fmt "\n", (bfqq)->pid,		\
++		bfq_bfqq_sync((bfqq)) ? 'S' : 'A',	\
++		##args)
++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...)		do {} while (0)
++
++#endif /* CONFIG_BFQ_GROUP_IOSCHED */
++
++#define bfq_log(bfqd, fmt, args...) \
++	pr_crit("bfq " fmt "\n", ##args)
++
++#else /* CONFIG_BFQ_REDIRECT_TO_CONSOLE */
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
++static struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg);
++
++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...)	do {			\
++	char __pbuf[128];						\
++									\
++	assert_spin_locked((bfqd)->queue->queue_lock);			\
++	blkg_path(bfqg_to_blkg(bfqq_group(bfqq)), __pbuf, sizeof(__pbuf)); \
++	blk_add_trace_msg((bfqd)->queue, "bfq%d%c %s " fmt, \
++			  (bfqq)->pid,			  \
++			  bfq_bfqq_sync((bfqq)) ? 'S' : 'A',	\
++			  __pbuf, ##args);				\
++} while (0)
++
++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...)	do {			\
++	char __pbuf[128];						\
++									\
++	blkg_path(bfqg_to_blkg(bfqg), __pbuf, sizeof(__pbuf));		\
++	blk_add_trace_msg((bfqd)->queue, "%s " fmt, __pbuf, ##args);	\
++} while (0)
++
++#else /* CONFIG_BFQ_GROUP_IOSCHED */
++
++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...)	\
++	blk_add_trace_msg((bfqd)->queue, "bfq%d%c " fmt, (bfqq)->pid,	\
++			bfq_bfqq_sync((bfqq)) ? 'S' : 'A',		\
++				##args)
++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...)		do {} while (0)
++
++#endif /* CONFIG_BFQ_GROUP_IOSCHED */
++
++#define bfq_log(bfqd, fmt, args...) \
++	blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args)
++#endif /* CONFIG_BFQ_REDIRECT_TO_CONSOLE */
++
++/* Expiration reasons. */
++enum bfqq_expiration {
++	BFQ_BFQQ_TOO_IDLE = 0,		/*
++					 * queue has been idling for
++					 * too long
++					 */
++	BFQ_BFQQ_BUDGET_TIMEOUT,	/* budget took too long to be used */
++	BFQ_BFQQ_BUDGET_EXHAUSTED,	/* budget consumed */
++	BFQ_BFQQ_NO_MORE_REQUESTS,	/* the queue has no more requests */
++	BFQ_BFQQ_PREEMPTED		/* preemption in progress */
++};
++
++
++struct bfqg_stats {
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	/* number of ios merged */
++	struct blkg_rwstat		merged;
++	/* total time spent on device in ns, may not be accurate w/ queueing */
++	struct blkg_rwstat		service_time;
++	/* total time spent waiting in scheduler queue in ns */
++	struct blkg_rwstat		wait_time;
++	/* number of IOs queued up */
++	struct blkg_rwstat		queued;
++	/* total disk time and nr sectors dispatched by this group */
++	struct blkg_stat		time;
++	/* sum of number of ios queued across all samples */
++	struct blkg_stat		avg_queue_size_sum;
++	/* count of samples taken for average */
++	struct blkg_stat		avg_queue_size_samples;
++	/* how many times this group has been removed from service tree */
++	struct blkg_stat		dequeue;
++	/* total time spent waiting for it to be assigned a timeslice. */
++	struct blkg_stat		group_wait_time;
++	/* time spent idling for this blkcg_gq */
++	struct blkg_stat		idle_time;
++	/* total time with empty current active q with other requests queued */
++	struct blkg_stat		empty_time;
++	/* fields after this shouldn't be cleared on stat reset */
++	uint64_t			start_group_wait_time;
++	uint64_t			start_idle_time;
++	uint64_t			start_empty_time;
++	uint16_t			flags;
++#endif
++};
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++/*
++ * struct bfq_group_data - per-blkcg storage for the blkio subsystem.
++ *
++ * @ps: @blkcg_policy_storage that this structure inherits
++ * @weight: weight of the bfq_group
++ */
++struct bfq_group_data {
++	/* must be the first member */
++	struct blkcg_policy_data pd;
++
++	unsigned int weight;
++};
++
++/**
++ * struct bfq_group - per (device, cgroup) data structure.
++ * @entity: schedulable entity to insert into the parent group sched_data.
++ * @sched_data: own sched_data, to contain child entities (they may be
++ *              both bfq_queues and bfq_groups).
++ * @bfqd: the bfq_data for the device this group acts upon.
++ * @async_bfqq: array of async queues for all the tasks belonging to
++ *              the group, one queue per ioprio value per ioprio_class,
++ *              except for the idle class that has only one queue.
++ * @async_idle_bfqq: async queue for the idle class (ioprio is ignored).
++ * @my_entity: pointer to @entity, %NULL for the toplevel group; used
++ *             to avoid too many special cases during group creation/
++ *             migration.
++ * @active_entities: number of active entities belonging to the group;
++ *                   unused for the root group. Used to know whether there
++ *                   are groups with more than one active @bfq_entity
++ *                   (see the comments to the function
++ *                   bfq_bfqq_may_idle()).
++ * @rq_pos_tree: rbtree sorted by next_request position, used when
++ *               determining if two or more queues have interleaving
++ *               requests (see bfq_find_close_cooperator()).
++ *
++ * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup
++ * there is a set of bfq_groups, each one collecting the lower-level
++ * entities belonging to the group that are acting on the same device.
++ *
++ * Locking works as follows:
++ *    o @bfqd is protected by the queue lock, RCU is used to access it
++ *      from the readers.
++ *    o All the other fields are protected by the @bfqd queue lock.
++ */
++struct bfq_group {
++	/* must be the first member */
++	struct blkg_policy_data pd;
++
++	struct bfq_entity entity;
++	struct bfq_sched_data sched_data;
++
++	void *bfqd;
++
++	struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR];
++	struct bfq_queue *async_idle_bfqq;
++
++	struct bfq_entity *my_entity;
++
++	int active_entities;
++
++	struct rb_root rq_pos_tree;
++
++	struct bfqg_stats stats;
++};
++
++#else
++struct bfq_group {
++	struct bfq_sched_data sched_data;
++
++	struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR];
++	struct bfq_queue *async_idle_bfqq;
++
++	struct rb_root rq_pos_tree;
++};
++#endif
++
++static struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity);
++
++static unsigned int bfq_class_idx(struct bfq_entity *entity)
++{
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++
++	return bfqq ? bfqq->ioprio_class - 1 :
++		BFQ_DEFAULT_GRP_CLASS - 1;
++}
++
++static struct bfq_service_tree *
++bfq_entity_service_tree(struct bfq_entity *entity)
++{
++	struct bfq_sched_data *sched_data = entity->sched_data;
++	struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity);
++	unsigned int idx = bfq_class_idx(entity);
++
++	BUG_ON(idx >= BFQ_IOPRIO_CLASSES);
++	BUG_ON(sched_data == NULL);
++
++	if (bfqq)
++		bfq_log_bfqq(bfqq->bfqd, bfqq,
++			     "entity_service_tree %p %d",
++			     sched_data->service_tree + idx, idx);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++	else {
++		struct bfq_group *bfqg =
++			container_of(entity, struct bfq_group, entity);
++
++		bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg,
++			     "entity_service_tree %p %d",
++			     sched_data->service_tree + idx, idx);
++	}
++#endif
++	return sched_data->service_tree + idx;
++}
++
++static struct bfq_queue *bic_to_bfqq(struct bfq_io_cq *bic, bool is_sync)
++{
++	return bic->bfqq[is_sync];
++}
++
++static void bic_set_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq,
++			 bool is_sync)
++{
++	bic->bfqq[is_sync] = bfqq;
++}
++
++static struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic)
++{
++	return bic->icq.q->elevator->elevator_data;
++}
++
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++
++static struct bfq_group *bfq_bfqq_to_bfqg(struct bfq_queue *bfqq)
++{
++	struct bfq_entity *group_entity = bfqq->entity.parent;
++
++	if (!group_entity)
++		group_entity = &bfqq->bfqd->root_group->entity;
++
++	return container_of(group_entity, struct bfq_group, entity);
++}
++
++#else
++
++static struct bfq_group *bfq_bfqq_to_bfqg(struct bfq_queue *bfqq)
++{
++	return bfqq->bfqd->root_group;
++}
++
++#endif
++
++static void bfq_check_ioprio_change(struct bfq_io_cq *bic, struct bio *bio);
++static void bfq_put_queue(struct bfq_queue *bfqq);
++static void bfq_dispatch_insert(struct request_queue *q, struct request *rq);
++static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd,
++				       struct bio *bio, bool is_sync,
++				       struct bfq_io_cq *bic);
++static void bfq_end_wr_async_queues(struct bfq_data *bfqd,
++				    struct bfq_group *bfqg);
++#ifdef CONFIG_BFQ_GROUP_IOSCHED
++static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg);
++#endif
++static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq);
++
++#endif /* _BFQ_H */
+diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
+index 01a696b0a4d3..29d537ddc261 100644
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -48,7 +48,7 @@ struct rq_wb;
+  * Maximum number of blkcg policies allowed to be registered concurrently.
+  * Defined here to simplify include dependency.
+  */
+-#define BLKCG_MAX_POLS		2
++#define BLKCG_MAX_POLS		3
+ 
+ typedef void (rq_end_io_fn)(struct request *, int);
+ 
diff --git a/modules/user/aszlig/system/kernel.nix b/modules/user/aszlig/system/kernel.nix
new file mode 100644
index 00000000..afbbb759
--- /dev/null
+++ b/modules/user/aszlig/system/kernel.nix
@@ -0,0 +1,31 @@
+{ config, pkgs, lib, ... }:
+
+{
+  options.vuizvui.user.aszlig.system.kernel = {
+    enable = lib.mkEnableOption "aszlig's custom kernel";
+  };
+
+  config = lib.mkIf config.vuizvui.user.aszlig.system.kernel.enable {
+    boot = {
+      kernelPatches = lib.singleton {
+        name = "bfq-v8r7";
+        patch = ./bfq.patch;
+        extraConfig = ''
+          IOSCHED_BFQ y
+          DEFAULT_BFQ y
+          DEFAULT_CFQ n
+          DEFAULT_IOSCHED bfq
+        '';
+      };
+
+      kernelPackages = let
+        inherit (lib) take splitString replaceStrings;
+        inherit (pkgs) linux_latest linux_testing;
+        dotizeVer = replaceStrings ["-"] ["."];
+        trimVer = ver: take 2 (splitString "." (dotizeVer ver));
+        tooOld = trimVer linux_latest.version == trimVer linux_testing.version;
+        kernel = if tooOld then linux_latest else linux_testing;
+      in pkgs.linuxPackagesFor kernel;
+    };
+  };
+}
diff --git a/modules/user/openlab/base.nix b/modules/user/openlab/base.nix
new file mode 100644
index 00000000..b37d34c6
--- /dev/null
+++ b/modules/user/openlab/base.nix
@@ -0,0 +1,79 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.openlab.base;
+
+in
+{
+
+  options.vuizvui.user.openlab.base.enable =
+    mkEnableOption "the base OpenLab configuration";
+
+
+  config = mkIf cfg.enable {
+    boot.loader.grub.device = mkDefault "/dev/sda";
+    boot.loader.timeout = 2;
+
+    # tmp (and nix builds) should be fast by default
+    # might make sense to disable that on machines with little RAM
+    boot.tmpOnTmpfs = mkDefault true;
+
+    fileSystems."/" = mkDefault {
+      device = "/dev/disk/by-label/labtop";
+      fsType = "ext4";
+    };
+
+    i18n = {
+      consoleFont = "lat9w-16";
+      consoleKeyMap = "us";
+      defaultLocale = "de_DE.UTF-8";
+    };
+    time.timeZone = "Europe/Berlin";
+
+    hardware.enableRedistributableFirmware = true;
+
+    # TODO: filesystems 
+
+    environment.systemPackages = with pkgs; let
+      base = [
+        ack ag
+        fish
+        git
+        manpages
+        netcat-openbsd
+        python3
+        tmux
+        screen
+        vim
+        wget
+      ];
+      in base;
+
+    # manual generation takes too long on slow machines
+    programs.man.enable = mkDefault false;
+
+    services.openssh.enable = true;
+
+    networking.firewall.enable = false;
+
+    users.mutableUsers = false;
+    users.users.openlab = {
+      uid = 1000;
+      isNormalUser = true;
+      password = "openlab";
+      extraGroups = [ "wheel" ];
+      openssh.authorizedKeys.keys = lib.singleton (lib.concatStrings [
+
+        "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJhthfk38lzDvoI7lPqRneI0yBpZEhLD"
+        "GRBpcXzpPSu+V0YlgrDix5fHhBl+EKfw4aeQNvQNuAky3pDtX+BDK1b7idbz9ZMCExy2a1"
+        "kBKDVJz/onLSQxiiZMuHlAljVj9iU4uoTOxX3vB85Ok9aZtMP1rByRIWR9e81/km4HdfZT"
+        "CjFVRLWfvo0s29H7l0fnbG9bb2E6kydlvjnXJnZFXX+KUM16X11lK53ilPdPJdm87VtxeS"
+        "KZ7GOiBz6q7FHzEd2Zc3CnzgupQiXGSblXrlN22IY3IWfm5S/8RTeQbMLVoH0TncgCeenX"
+        "H7FU/sXD79ypqQV/WaVVDYMOirsnh/ philip@nyx"
+      ]);
+    };
+  };
+
+}
diff --git a/modules/user/openlab/labtops.nix b/modules/user/openlab/labtops.nix
new file mode 100644
index 00000000..ea745c20
--- /dev/null
+++ b/modules/user/openlab/labtops.nix
@@ -0,0 +1,102 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.openlab.labtops;
+
+in
+{
+
+  options.vuizvui.user.openlab.labtops = {
+    enable = mkEnableOption "basic shared functionality of labtops";
+  };
+
+
+  config = mkIf cfg.enable {
+
+    vuizvui.user.openlab.base.enable = true;
+
+    hardware.pulseaudio = {
+      enable = true;
+      zeroconf.discovery.enable = true;
+    };
+
+    networking.wireless = {
+      enable = true;
+      networks."Labor 2.0".psk = "nerdhoehle2";
+    };
+
+    # TODO: a way to modularly specify usage patterns (e.g. 3d-printing, arduino &c.)
+    environment.systemPackages = with pkgs; let
+      baseGUI = [
+        filezilla
+        chromium
+        gnome3.gedit
+        gmpc
+        libreoffice
+        vlc
+      ];
+      image = [
+        gimp
+        inkscape
+      ];
+      media = [
+        mpv
+        vlc
+        pavucontrol
+      ];
+      three-d = [
+        # TODO doesn’t build on i686
+        # TODO add a “packageset” mechanism
+        blender
+        # TODO build fail
+        # antimony
+      ];
+      three-d-printing = [
+        freecad
+        openscad
+        printrun
+        slic3r
+      ];
+      arduinoPkgs = [
+        ino
+        arduino
+      ];
+      tools = [
+        unzip
+      ];
+      in baseGUI ++ image ++ media
+      ++ three-d ++ three-d-printing ++ arduinoPkgs
+      ++ tools;
+
+    services.xserver = {
+      enable = true;
+      layout = "us";
+      xkbOptions = "eurosign:e";
+
+      displayManager = {
+        auto.enable = true;
+        auto.user = "openlab";
+        sessionCommands = with pkgs; ''
+          ${xorg.xset}/bin/xset r rate 250 35
+        '';
+      };
+      desktopManager.xfce.enable = true;
+      synaptics = {
+        enable = true;
+        minSpeed = "0.5";
+        accelFactor = "0.01";
+      };
+    };
+
+    users.users.openlab.extraGroups = [ "dialout" ];
+
+    # fix for emacs
+    programs.bash.promptInit = "PS=\"# \"";
+
+    programs.man.enable = true;
+
+  };
+
+}
diff --git a/modules/user/openlab/stackenblocken.nix b/modules/user/openlab/stackenblocken.nix
new file mode 100644
index 00000000..4a5630fe
--- /dev/null
+++ b/modules/user/openlab/stackenblocken.nix
@@ -0,0 +1,38 @@
+{ pkgs, config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.vuizvui.user.openlab.stackenblocken;
+  package = lib.getBin pkgs.vuizvui.openlab.stackenblocken.override {
+    volumePercent = cfg.volume;
+  };
+
+in
+{
+  options.vuizvui.user.openlab.stackenblocken = {
+    enable = mkEnableOption "STACKENBLOCKEN EVERY DAY";
+
+    volume = mkOption {
+      description = "Volume in percent";
+      default = 50;
+      type = types.addCheck types.int (x: x >= 0 && x <= 100);
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.user = {
+      services.stackenblocken = {
+        description = "stackenblocken timer";
+#        wantedBy = [ "default.target" ];
+        serviceConfig = {
+          ExecStart = "${package}/bin/stackenblocken";
+        };
+        # everyday at 21:45, except Wednesday (Yoga silence)
+        startAt = [ "Mon,Tue,Thu,Fri,Sat,Sun 21:45" "Wed 22:00" ];
+      };
+    };
+
+  };
+}
diff --git a/modules/user/profpatsch/programs/scanning.nix b/modules/user/profpatsch/programs/scanning.nix
new file mode 100644
index 00000000..831c440c
--- /dev/null
+++ b/modules/user/profpatsch/programs/scanning.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+{
+  options.vuizvui.user.profpatsch.programs.scanning = {
+    enable = mkEnableOption "scanning &amp; simple-scan";
+  };
+
+  config = mkIf config.vuizvui.user.profpatsch.programs.scanning.enable {
+    environment.systemPackages = [ pkgs.simple-scan ];
+    hardware.sane.enable = true;
+  };
+}
diff --git a/nixpkgs-path.nix b/nixpkgs-path.nix
new file mode 100644
index 00000000..acd3b37e
--- /dev/null
+++ b/nixpkgs-path.nix
@@ -0,0 +1,2 @@
+# This will be replaced by the channel expression generators in release.nix!
+toString <nixpkgs>
diff --git a/pkgs/aszlig/aacolorize/aacolorize.py b/pkgs/aszlig/aacolorize/aacolorize.py
new file mode 100755
index 00000000..ff19b687
--- /dev/null
+++ b/pkgs/aszlig/aacolorize/aacolorize.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+import os
+import sys
+
+from optparse import Option, OptionParser
+
+COLORS = {
+    "k": (30, "Black"),
+    "r": (31, "Red"),
+    "g": (32, "Green"),
+    "y": (33, "Yellow"),
+    "b": (34, "Blue"),
+    "p": (35, "Pink"),
+    "c": (36, "Cyan"),
+    "w": (37, "White"),
+}
+
+ESC = chr(27)
+
+class ColorizeError(Exception):
+    pass
+
+class Color(object):
+    def __init__(self, ident=None):
+        """
+        Initialize a color object, if no `ident` is given or it's invalid,
+        the Color object represents "no color".
+        """
+        if ident is not None:
+            spec = COLORS.get(ident.lower(), None)
+        else:
+            spec = None
+
+        if spec is None:
+            self.ident = None
+            self.bold = False
+            self.code = None
+            self.name = "None"
+        else:
+            self.ident = ident
+            self.code, self.name = spec
+
+            if ident.isupper():
+                self.bold = True
+            else:
+                self.bold = False
+
+    @property
+    def attrs(self):
+        """
+        A tuple consisting of the SGR attributes.
+        """
+        if self.ident is None:
+            return ()
+
+        if self.bold:
+            return (1, self.code)
+        else:
+            return (self.code,)
+
+    def sgr_attrs(self, *attrs):
+        """
+        Return the attributes specified by `attrs` formatted according
+        to the CSI specification.
+        """
+        return ';'.join(map(lambda c: str(c), attrs))
+
+    def sgr(self, *attrs):
+        """
+        Start Set Graphics Rendition
+        Return the CSI escape code for `attrs`.
+        """
+        return "%s[%sm" % (ESC, self.sgr_attrs(*attrs))
+
+    def sgr_start(self):
+        """
+        Start Set Graphics Rendition
+        Return the CSI start escape code for the current color.
+        """
+        return self.sgr(*self.attrs)
+
+    def sgr_stop(self):
+        """
+        Clear Set Graphics Rendition
+        """
+        return self.sgr()
+
+    def apply(self, value):
+        """
+        Apply the current color to the string in `value`.
+        """
+        return "%s%s%s" % (self.sgr_start(), value, self.sgr_stop())
+
+    def describe(self):
+        """
+        Return the description of the current color IN color :-)
+        """
+        fmt = "%c: <ESC>[%sm -> [%s]"
+        return fmt % (
+            self.ident,
+            self.sgr_attrs(*self.attrs),
+            self.apply(self.name)
+        )
+
+    def transform_to(self, new_color):
+        """
+        Return the CSI sequences needed to transform into `new_color`.
+        """
+        if self.ident is None and new_color.ident is not None:
+            return new_color.sgr_start()
+        elif self.ident is not None and new_color.ident is None:
+            return self.sgr_stop()
+        elif self.ident is None and new_color.ident is None:
+            return ''
+        elif self.code == new_color.code:
+            if not self.bold and new_color.bold:
+                return self.sgr(1)
+            elif self.bold and not new_color.bold:
+                return self.sgr(22)
+            elif self.bold == new_color.bold:
+                return ''
+        else:
+            if self.bold and new_color.bold:
+                return new_color.sgr(new_color.code)
+
+        return self.sgr_stop()+new_color.sgr_start()
+
+    def __repr__(self):
+        if self.bold:
+            return "<Bold color %s>" % self.name.lower()
+        else:
+            return "<Color %s>" % self.name.lower()
+
+def print_colortable():
+    for ident in COLORS.iterkeys():
+        normal = Color(ident).describe()
+        bold = Color(ident.upper()).describe()
+        sys.stdout.write("%-35s%s\n" % (normal, bold))
+
+def colorize_art(art, colmap):
+    if len(art) != len(colmap):
+        raise ColorizeError("Art and colormap differ in size!")
+
+    no_color = Color()
+
+    out = ""
+    last_color = no_color
+    for i, char in enumerate(colmap):
+        color = Color(char)
+        out += last_color.transform_to(color) + art[i]
+        last_color = color
+
+    last_color.transform_to(no_color)
+
+    return out
+
+def colorize_file(artfile, mapfile=None):
+    if mapfile is None:
+        mapfile = os.path.splitext(artfile)[0]+'.colmap'
+
+    asciiart = open(artfile, 'r').read()
+    colormap = open(mapfile, 'r').read()
+
+    return colorize_art(asciiart, colormap)
+
+if __name__ == "__main__":
+    parser = OptionParser(usage="%prog [options] artfile [mapfile]")
+    parser.add_option("-t", "--table", action="store_true", dest="table",
+                      help="Show color table and exit.")
+
+    (options, args) = parser.parse_args()
+
+    if options.table:
+        print_colortable()
+        parser.exit()
+
+    if not len(args) in (1, 2):
+        parser.print_help()
+        parser.exit()
+    else:
+        colorized = colorize_file(*args)
+        sys.stdout.write(colorized)
diff --git a/pkgs/aszlig/aacolorize/default.nix b/pkgs/aszlig/aacolorize/default.nix
new file mode 100644
index 00000000..ef36f4e0
--- /dev/null
+++ b/pkgs/aszlig/aacolorize/default.nix
@@ -0,0 +1,13 @@
+{ pythonPackages, runCommand }:
+
+pythonPackages.buildPythonPackage {
+  name = "aacolorize";
+  src = runCommand "aacolorize-src" {} ''
+    mkdir -p "$out"
+    cp "${./aacolorize.py}" "$out/aacolorize"
+    cat > "$out/setup.py" <<SETUP
+    from distutils.core import setup
+    setup(name='aacolorize', scripts=['aacolorize'])
+    SETUP
+  '';
+}
diff --git a/pkgs/aszlig/axbo/default.nix b/pkgs/aszlig/axbo/default.nix
new file mode 100644
index 00000000..fe503863
--- /dev/null
+++ b/pkgs/aszlig/axbo/default.nix
@@ -0,0 +1,53 @@
+{ stdenv, fetchurl, fetchFromGitHub, jdk, jre, ant, makeWrapper
+, commonsLogging, librxtx_java
+}:
+
+stdenv.mkDerivation rec {
+  name = "axbo-research-${version}";
+  version = "3.0.12";
+
+  src = fetchFromGitHub {
+    owner = "jansolo";
+    repo = "aXbo-research";
+    #rev = "aXbo-research_${version}";
+    # Includes MIT license:
+    rev = "6e6888917b5f200a44509650d6f46ec42c133cdc";
+    sha256 = "0nbyxajl75q80cnyl9c0sjlyk3rmhm7k8w8mksg4lfyh78ynayyc";
+  };
+
+  sourceRoot = "${src.name}/aXbo-research";
+
+  buildInputs = [ jdk ant makeWrapper ];
+
+  buildPhase = ''
+    ant -Dplatforms.JDK_1.7.home="$JAVA_HOME" jar
+  '';
+
+  extraJars = [
+    "commons-beanutils-1.8.3"
+    "commons-digester3-3.2"
+    "commons-io-2.4"
+    "jcommon-1.0.20"
+    "jfreechart-1.0.16"
+    "streamflyer-core-1.0.1"
+    "swingx-all-1.6.4"
+  ];
+
+  installPhase = with stdenv.lib; let
+    classpath = makeSearchPath "share/java/\\*" [
+      "$out"
+      commonsLogging
+      librxtx_java
+    ];
+  in ''
+    for dep in $extraJars; do
+      install -vD -m 644 "lib/$dep.jar" "$out/share/java/$dep.jar"
+    done
+    install -vD -m 644 dist/axbo.jar "$out/share/java/axbo.jar"
+
+    mkdir -p "$out/bin"
+    makeWrapper "${jre}/bin/java" "$out/bin/axbo-research" \
+      --add-flags "-Djava.library.path='${librxtx_java}/lib'" \
+      --add-flags "-cp ${classpath} com.dreikraft.axbo.Axbo"
+  '';
+}
diff --git a/pkgs/aszlig/default.nix b/pkgs/aszlig/default.nix
new file mode 100644
index 00000000..7c042127
--- /dev/null
+++ b/pkgs/aszlig/default.nix
@@ -0,0 +1,14 @@
+{ callPackage, callPackage_i686, boost155, gajim }:
+
+{
+  aacolorize = callPackage ./aacolorize { };
+  axbo = callPackage ./axbo { };
+  gajim = callPackage ./gajim { inherit gajim; };
+  git-detach = callPackage ./git-detach { };
+  grandpa = callPackage ./grandpa { };
+  nixops = callPackage ./nixops { };
+  librxtx_java = callPackage ./librxtx-java { };
+  lockdev = callPackage ./lockdev { };
+  pvolctrl = callPackage ./pvolctrl { };
+  santander = callPackage_i686 ./santander { };
+}
diff --git a/pkgs/aszlig/gajim/default.nix b/pkgs/aszlig/gajim/default.nix
new file mode 100644
index 00000000..840aa9a1
--- /dev/null
+++ b/pkgs/aszlig/gajim/default.nix
@@ -0,0 +1,33 @@
+{ stdenv, fetchurl, fetchpatch, gmp, pythonPackages
+, gajim
+}:
+
+(gajim.override {
+  pythonPackages = pythonPackages // {
+    pycrypto = pythonPackages.buildPythonPackage rec {
+      name = "pycrypto-${version}";
+      version = "2.6.1";
+
+      src = fetchurl {
+        url = "mirror://pypi/p/pycrypto/${name}.tar.gz";
+        sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
+      };
+
+      patches = stdenv.lib.singleton (fetchpatch {
+        name = "CVE-2013-7459.patch";
+        url = "https://anonscm.debian.org/cgit/collab-maint/python-crypto.git"
+            + "/plain/debian/patches/CVE-2013-7459.patch?h=debian/2.6.1-7";
+        sha256 = "01r7aghnchc1bpxgdv58qyi2085gh34bxini973xhy3ks7fq3ir9";
+      });
+
+      buildInputs = [ gmp ];
+
+      preConfigure = ''
+        sed -i 's,/usr/include,/no-such-dir,' configure
+        sed -i "s!,'/usr/include/'!!" setup.py
+      '';
+    };
+  };
+}) // {
+  meta = gajim.meta // { platforms = [ "x86_64-linux" ]; };
+}
diff --git a/pkgs/aszlig/git-detach/default.nix b/pkgs/aszlig/git-detach/default.nix
new file mode 100644
index 00000000..fb20843e
--- /dev/null
+++ b/pkgs/aszlig/git-detach/default.nix
@@ -0,0 +1,33 @@
+{ writeScriptBin, stdenv, git, coreutils, patch }:
+
+writeScriptBin "git-detach" ''
+  #!${stdenv.shell}
+
+  if [ $# -le 0 -o "$1" = "--help" -o "$1" = "-h" ]; then
+      echo "Usage: $0 COMMAND [ARGS...]" >&2
+      echo >&2
+      echo "Run COMMAND in a clean Git working directory" >&2
+      echo "without untracked files and .git directory." >&2
+      exit 1
+  fi
+
+  diffToHead="$("${git}/bin/git" diff HEAD)"
+
+  if tmpdir="$("${coreutils}/bin/mktemp" -d git-detach.XXXXXXXXXX)"; then
+    trap "rm -rf '${"\${tmpdir//\\'/\\'\\\\\\'\\'}"}'" EXIT
+    "${git}/bin/git" archive --format=tar HEAD | (
+      set -e
+      basedir="$tmpdir/$("${coreutils}/bin/basename" "$(pwd)")"
+      mkdir "$basedir"
+      cd "$basedir"
+      tar x
+      if [ -n "$diffToHead" ]; then
+        echo "$diffToHead" | "${patch}/bin/patch" -s -p1
+      fi
+      exec "$@"
+    )
+    exit $?
+  else
+    echo "Unable to create temporary directory!" >&2
+  fi
+''
diff --git a/pkgs/aszlig/grandpa/default.nix b/pkgs/aszlig/grandpa/default.nix
new file mode 100644
index 00000000..f37f6b2f
--- /dev/null
+++ b/pkgs/aszlig/grandpa/default.nix
@@ -0,0 +1,18 @@
+{ fetchFromGitHub, pythonPackages, gpm }:
+
+pythonPackages.buildPythonPackage {
+  name = "grandpa-0.5";
+  namePrefix = "";
+
+  src = fetchFromGitHub {
+    owner = "aszlig";
+    repo = "GrandPA";
+    rev = "d8d2571f732a68ed18be7533244db2cfb822b4c1";
+    sha256 = "19zf3pnr1adngncvinvn8yyvc0sj66lp7lwiql6379rf78xxlmhn";
+  };
+
+  doCheck = false;
+
+  buildInputs = [ pythonPackages.cython gpm ];
+  propagatedBuildInputs = [ pythonPackages.pyserial ];
+}
diff --git a/pkgs/aszlig/librxtx-java/default.nix b/pkgs/aszlig/librxtx-java/default.nix
new file mode 100644
index 00000000..1553a146
--- /dev/null
+++ b/pkgs/aszlig/librxtx-java/default.nix
@@ -0,0 +1,47 @@
+{ stdenv, fetchurl, fetchpatch, unzip, jdk, lockdev }:
+
+stdenv.mkDerivation rec {
+  name = "rxtx-${version}";
+  version = "2.2pre2";
+
+  src = fetchurl {
+    urls = [
+      "http://rxtx.qbang.org/pub/rxtx/${name}.zip"
+      "ftp://ftp.freebsd.org/pub/FreeBSD/ports/distfiles/${name}.zip"
+    ];
+    sha256 = "00sv9604hkq81mshih0fhqfzn4mf01d6rish6vplsi0gfqz3fc1w";
+  };
+
+  patches = let
+    baseurl = "https://sources.debian.net/data/main/"
+            + "r/rxtx/2.2pre2-13/debian/patches";
+  in [
+    (fetchpatch {
+      url = "${baseurl}/fhs_lock_buffer_overflow_fix.patch";
+      sha256 = "1v31q6ciy5v6bm5z8a1wssqn4nwvbcg4nnplgsvv1h8mzdq2832i";
+    })
+    (fetchpatch {
+      url = "${baseurl}/fix_snprintf.patch";
+      sha256 = "09r9jca0hb13bx85l348jkxnh1p0g5i0d6dnpm142vlwsj0d7afy";
+    })
+    (fetchpatch {
+      url = "${baseurl}/format_security.patch";
+      sha256 = "0adg7y9ak4xvgyswdhx6fsxq8jlb8y55xl3s6l0p8w0mfrhw7ysk";
+    })
+  ];
+
+  buildInputs = [ unzip jdk lockdev ];
+
+  NIX_CFLAGS_COMPILE = "-DUTS_RELEASE=\"3.8.0\"";
+
+  configureFlags = [ "--enable-liblock" ];
+
+  makeFlags = [
+    "JHOME=$(out)/share/java"
+    "RXTX_PATH=$(out)/lib"
+  ];
+
+  preInstall = ''
+    mkdir -p "$out/lib" "$out/share/java"
+  '';
+}
diff --git a/pkgs/aszlig/lockdev/default.nix b/pkgs/aszlig/lockdev/default.nix
new file mode 100644
index 00000000..52e78eb5
--- /dev/null
+++ b/pkgs/aszlig/lockdev/default.nix
@@ -0,0 +1,23 @@
+{ stdenv, fetchurl, perl }:
+
+let
+  baseurl = "ftp://ftp.debian.org/debian/pool/main/l/lockdev/";
+in stdenv.mkDerivation rec {
+  name = "lockdev-${version}";
+  version = "1.0.3";
+
+  buildInputs = [ perl ];
+
+  patches = stdenv.lib.singleton (fetchurl {
+    url = baseurl + "lockdev_1.0.3-1.5.diff.gz";
+    sha256 = "1l3pq1nfb5qx3i91cjaiz3c53368gw6m28a5mv9391n5gmsdmi3r";
+  });
+
+  NIX_CFLAGS_COMPILE = "-fPIC -D_PATH_LOCK=\"/tmp\"";
+  installFlags = [ "basedir=$(out)" ];
+
+  src = fetchurl {
+    url = baseurl + "lockdev_${version}.orig.tar.gz";
+    sha256 = "10lzhq6r2dn8y3ki7wlqsa8s3ndkf842bszcjw4dbzf3g9fn7bnc";
+  };
+}
diff --git a/pkgs/aszlig/nixops/default.nix b/pkgs/aszlig/nixops/default.nix
new file mode 100644
index 00000000..f97e3716
--- /dev/null
+++ b/pkgs/aszlig/nixops/default.nix
@@ -0,0 +1,37 @@
+{ stdenv, fetchFromGitHub, fetchpatch, git }:
+
+let
+  rev = "bbf9a792d06c9a60c74dabe2937a9dfda9bff8f7";
+  sha256 = "0a1mx0ngp0zg65r1rx99rina4wbfjyzrziw2z9788v629j58p4jd";
+
+  master = stdenv.mkDerivation rec {
+    name = "nixops-upstream-patched";
+
+    src = fetchFromGitHub {
+      owner = "NixOS";
+      repo = "nixops";
+      inherit rev sha256;
+    };
+
+    phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+
+    postPatch = ''
+      sed -i -re 's!<nixpkgs([^>]*)>!${import ../../../nixpkgs-path.nix}\1!g' \
+        release.nix doc/manual/default.nix doc/manual/resource.nix
+    '';
+
+    installPhase = ''
+      cp -a . "$out"
+    '';
+  };
+
+  release = import "${master}/release.nix" {
+    nixopsSrc = {
+      outPath = master;
+      inherit rev;
+      revCount = 0;
+      shortRev = builtins.substring 0 7 rev;
+    };
+    officialRelease = false;
+  };
+in stdenv.lib.getAttr stdenv.system release.build
diff --git a/pkgs/aszlig/pvolctrl/default.nix b/pkgs/aszlig/pvolctrl/default.nix
new file mode 100644
index 00000000..5701c19e
--- /dev/null
+++ b/pkgs/aszlig/pvolctrl/default.nix
@@ -0,0 +1,35 @@
+{ stdenv, fetchurl, pkgconfig, libpulseaudio }:
+
+stdenv.mkDerivation rec {
+  name = "pvolctrl-0.23";
+
+  unpackPhase = let
+    baseurl = "https://sites.google.com/site/guenterbartsch/blog/"
+            + "volumecontrolutilityforpulseaudio/";
+    makefile = fetchurl {
+      url = baseurl + "Makefile";
+      sha256 = "0l2ffvb617csk6h29y64v6ywhpcp7la6vvcip1w4nq0yry6jhrqz";
+    };
+    source = fetchurl {
+      url = baseurl + "pvolctrl.c";
+      sha256 = "0vcd5dlw9l47jpabwmmzdvlkn67fz55dr3sryyh56sl263mibjda";
+    };
+  in ''
+    mkdir -p "${name}"
+    sed -e 's|/usr/bin/||' "${makefile}" > "${name}/Makefile"
+    sed -e 's/PA_VOLUME_MAX/PA_VOLUME_NORM/
+    /avg_vol += (avg_vol \* vol_mod) \/ 100;/ {
+      s/(avg_vol/((int)PA_VOLUME_NORM/
+    }
+    /if (vol_mod)/i \
+      if (info->name == NULL || strncmp(info->name, "combined", 8) != 0) \
+        return;' "${source}" > "${name}/pvolctrl.c"
+    sourceRoot="${name}"
+  '';
+
+  installPhase = ''
+    install -D -T pvolctrl "$out/bin/pvolctrl"
+  '';
+
+  buildInputs = [ pkgconfig libpulseaudio ];
+}
diff --git a/pkgs/aszlig/santander/default.nix b/pkgs/aszlig/santander/default.nix
new file mode 100644
index 00000000..4d0d7883
--- /dev/null
+++ b/pkgs/aszlig/santander/default.nix
@@ -0,0 +1,89 @@
+{ stdenv, fetchurl, fetchgit, runCommand, p7zip, jq, wineMinimal, pcsclite }:
+
+let
+  patchedWine = let
+    libpcsclite = "${pcsclite}/lib/libpcsclite.so";
+  in (wineMinimal.override {
+    wineBuild = "wine32";
+    wineRelease = "staging";
+  }).overrideDerivation (drv: {
+    scard4wine = fetchgit {
+      url = "git://git.code.sf.net/p/scard4wine/code";
+      rev = "c14c02c80bf1f2bb4cedd1f53a3a2ab9c48bed76";
+      sha256 = "0ffmbl9mdnaih4h3ggpnzqbih3kgbwl3wv6j1ag5s4czn8gcpdq3";
+    };
+
+    prePatch = (drv.prePatch or "") + ''
+      cp -t dlls/winscard "$scard4wine/src/"*
+      sed -i -re 's,"libpcsclite\.so(\.[0-9]+)*","${libpcsclite}",' \
+        dlls/winscard/winscard.c
+    '';
+
+    patches = (drv.patches or []) ++ [ ./winscard.patch ];
+
+    postPatch = (drv.postPatch or "") + ''
+      sed -i -e '/not owned by you/d' libs/wine/config.c
+      # Modified patch from https://bugs.winehq.org/show_bug.cgi?id=22450
+      patch -p1 < "${./wine-no-unixfs.patch}"
+    '';
+  });
+
+in stdenv.mkDerivation rec {
+  name = "TRAVIC-Sign-${version}";
+  version = "3.1.3.0";
+
+  src = fetchurl {
+    url = "https://service.santanderbank.de/special/banking/files/"
+        + "${name}-Installer.exe";
+    sha256 = "19a14av3bg6i4iy5q5pa737cwxznqji0lcrapxw0q6qb8rs1rhs7";
+  };
+
+  extensionId = "ilpoejcegjjlgpobjkpjmddkbdkdndaj";
+
+  buildInputs = [ p7zip jq ];
+
+  unpackCmd = "7z x -y -otavic-sign $curSrc";
+
+  phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+
+  postPatch = ''
+    jq '.allowed_origins = [
+      "chrome-extension://'"$extensionId"'/"
+    ] | .path = "'"$out/share/libexec/travic-sign"'"
+      | del(.allowed_extensions)' manifest-firefox.json > host.json
+
+    7z x -y -oextension FirefoxExtension.xpi
+    jq '.content_scripts[].matches = ["https://karte.santanderbank.de/*"] | {
+      # All the object attributes that we want to have (nothing more):
+      background, web_accessible_resources, content_scripts, page_action,
+      permissions, author, version, description, name, manifest_version
+    }' extension/manifest.json > new_manifest.json
+    mv new_manifest.json extension/manifest.json
+    (cd extension && 7z a -tzip ../travic-sign.crx *)
+  '';
+
+  winePrefix = runCommand "empty-wineprefix" {
+    buildInputs = [ patchedWine ];
+  } ''
+    export WINEPREFIX="$out"
+    mkdir -p "$out"
+    wine wineboot.exe
+  '';
+
+  installPhase = ''
+    libexec="$out/share/libexec/travic-sign"
+
+    install -vD -m 0644 TRAVIC-Sign-Service.exe "$libexec/service.exe"
+    install -vD -m 0644 host.json \
+      "$out/etc/chromium/native-messaging-hosts/travic-sign.json"
+    install -vD -m 0644 travic-sign.crx \
+      "$out/share/chromium/extensions/$extensionId.crx"
+
+    cat > "$libexec/travic-sign" <<EOF
+    #!${stdenv.shell}
+    export WINEPREFIX="$winePrefix"
+    exec ${patchedWine}/bin/wine "$libexec/TRAVIC-Sign-Service.exe"
+    EOF
+    chmod +x "$libexec/travic-sign"
+  '';
+}
diff --git a/pkgs/aszlig/santander/pipelight.patch b/pkgs/aszlig/santander/pipelight.patch
new file mode 100644
index 00000000..3a07da72
--- /dev/null
+++ b/pkgs/aszlig/santander/pipelight.patch
@@ -0,0 +1,13 @@
+diff --git a/src/windows/pluginloader/pluginloader.c b/src/windows/pluginloader/pluginloader.c
+index 9e8556f..c50be2a 100644
+--- a/src/windows/pluginloader/pluginloader.c
++++ b/src/windows/pluginloader/pluginloader.c
+@@ -1510,7 +1510,7 @@ void dispatcher(int functionid, Stack &stack){
+ 				NPObject *objectValue;
+ 				NPError result;
+ 
+-				if (variable == NPPVpluginScriptableNPObject)
++				if (variable == NPPVpluginScriptableNPObject && pluginFuncs.getvalue)
+ 					result = pluginFuncs.getvalue(instance, variable, &objectValue);
+ 				else{
+ 					DBG_WARN("FUNCTION_NPP_GETVALUE_OBJECT - variable %d not allowed", variable);
diff --git a/pkgs/aszlig/santander/wine-no-unixfs.patch b/pkgs/aszlig/santander/wine-no-unixfs.patch
new file mode 100644
index 00000000..b0e6a9e9
--- /dev/null
+++ b/pkgs/aszlig/santander/wine-no-unixfs.patch
@@ -0,0 +1,292 @@
+diff -urN wine-1.9.7-orig/dlls/krnl386.exe16/int21.c wine-1.9.7/dlls/krnl386.exe16/int21.c
+--- wine-1.9.7-orig/dlls/krnl386.exe16/int21.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/krnl386.exe16/int21.c	2016-04-10 02:33:20 +0900
+@@ -35,6 +35,7 @@
+ # include <unistd.h>
+ #endif
+ 
++#include "ntstatus.h"
+ #include "windef.h"
+ #include "winbase.h"
+ #include "winreg.h"
+@@ -842,11 +843,13 @@
+  */
+ static HANDLE INT21_CreateMagicDeviceHandle( LPCWSTR name )
+ {
++    HANDLE ret;
++    NTSTATUS status;
++
++#if 0
+     static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'};
+     const char *dir = wine_get_server_dir();
+     int len;
+-    HANDLE ret;
+-    NTSTATUS status;
+     OBJECT_ATTRIBUTES attr;
+     UNICODE_STRING nameW;
+     IO_STATUS_BLOCK io;
+@@ -875,12 +878,16 @@
+     status = NtCreateFile( &ret, GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, &attr, &io, NULL, 0,
+                            FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF,
+                            FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 );
++    RtlFreeUnicodeString( &nameW );
++#else
++    status = STATUS_NOT_IMPLEMENTED;
++#endif
++
+     if (status)
+     {
+         ret = 0;
+         SetLastError( RtlNtStatusToDosError(status) );
+     }
+-    RtlFreeUnicodeString( &nameW );
+     return ret;
+ }
+ 
+diff -urN wine-1.9.7-orig/dlls/krnl386.exe16/vxd.c wine-1.9.7/dlls/krnl386.exe16/vxd.c
+--- wine-1.9.7-orig/dlls/krnl386.exe16/vxd.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/krnl386.exe16/vxd.c	2016-04-10 02:33:20 +0900
+@@ -113,11 +113,13 @@
+ /* create a file handle to represent a VxD, by opening a dummy file in the wineserver directory */
+ static HANDLE open_vxd_handle( LPCWSTR name )
+ {
++    HANDLE ret;
++    NTSTATUS status;
++
++#if 0
+     static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'};
+     const char *dir = wine_get_server_dir();
+     int len;
+-    HANDLE ret;
+-    NTSTATUS status;
+     OBJECT_ATTRIBUTES attr;
+     UNICODE_STRING nameW;
+     IO_STATUS_BLOCK io;
+@@ -146,12 +148,16 @@
+     status = NtCreateFile( &ret, SYNCHRONIZE, &attr, &io, NULL, 0,
+                            FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF,
+                            FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 );
++    RtlFreeUnicodeString( &nameW );
++#else
++    status = STATUS_NOT_IMPLEMENTED;
++#endif
++
+     if (status)
+     {
+         ret = 0;
+         SetLastError( RtlNtStatusToDosError(status) );
+     }
+-    RtlFreeUnicodeString( &nameW );
+     return ret;
+ }
+ 
+diff -urN wine-1.9.7-orig/dlls/ntdll/directory.c wine-1.9.7/dlls/ntdll/directory.c
+--- wine-1.9.7-orig/dlls/ntdll/directory.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/ntdll/directory.c	2016-04-10 02:33:20 +0900
+@@ -3098,8 +3098,10 @@
+ NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
+                                           UINT disposition, BOOLEAN check_case )
+ {
++#if 0
+     static const WCHAR unixW[] = {'u','n','i','x'};
+     static const WCHAR pipeW[] = {'p','i','p','e'};
++#endif
+     static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
+ 
+     NTSTATUS status = STATUS_SUCCESS;
+@@ -3143,11 +3145,13 @@
+     name_len -= prefix_len;
+ 
+     /* check for invalid characters (all chars except 0 are valid for unix and pipes) */
++#if 0
+     if (prefix_len == 4)
+     {
+         is_unix = !memcmp( prefix, unixW, sizeof(unixW) );
+         is_pipe = !memcmp( prefix, pipeW, sizeof(pipeW) );
+     }
++#endif
+     if (is_unix || is_pipe)
+     {
+         for (p = name; p < name + name_len; p++)
+diff -urN wine-1.9.7-orig/dlls/ntdll/path.c wine-1.9.7/dlls/ntdll/path.c
+--- wine-1.9.7-orig/dlls/ntdll/path.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/ntdll/path.c	2016-04-10 02:33:20 +0900
+@@ -1025,7 +1025,9 @@
+ NTSTATUS CDECL wine_unix_to_nt_file_name( const ANSI_STRING *name, UNICODE_STRING *nt )
+ {
+     static const WCHAR prefixW[] = {'\\','?','?','\\','A',':','\\'};
++#if 0
+     static const WCHAR unix_prefixW[] = {'\\','?','?','\\','u','n','i','x'};
++#endif
+     unsigned int lenW, lenA = name->Length;
+     const char *path = name->Buffer;
+     char *cwd;
+@@ -1065,6 +1067,7 @@
+ 
+     if (status != STATUS_SUCCESS)
+     {
++#if 0
+         if (status == STATUS_OBJECT_PATH_NOT_FOUND)
+         {
+             lenW = ntdll_umbstowcs( 0, path, lenA, NULL, 0 );
+@@ -1084,6 +1087,7 @@
+             for (p = nt->Buffer + sizeof(unix_prefixW)/sizeof(WCHAR); *p; p++) if (*p == '/') *p = '\\';
+             status = STATUS_SUCCESS;
+         }
++#endif
+         goto done;
+     }
+     while (lenA && path[0] == '/') { lenA--; path++; }
+diff -urN wine-1.9.7-orig/dlls/shell32/folders.c wine-1.9.7/dlls/shell32/folders.c
+--- wine-1.9.7-orig/dlls/shell32/folders.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/folders.c	2016-04-10 02:33:20 +0900
+@@ -236,9 +236,11 @@
+                 *piIndex = -IDI_SHELL_MY_DOCUMENTS;
+             else if(IsEqualGUID(riid, &CLSID_NetworkPlaces))
+                 *piIndex = -IDI_SHELL_MY_NETWORK_PLACES;
++#if 0
+             else if(IsEqualGUID(riid, &CLSID_UnixFolder) ||
+                     IsEqualGUID(riid, &CLSID_UnixDosFolder))
+                 *piIndex = -IDI_SHELL_DRIVE;
++#endif
+             else
+                 *piIndex = -IDI_SHELL_FOLDER;
+ 	  }
+diff -urN wine-1.9.7-orig/dlls/shell32/shellole.c wine-1.9.7/dlls/shell32/shellole.c
+--- wine-1.9.7-orig/dlls/shell32/shellole.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/shellole.c	2016-04-10 02:33:20 +0900
+@@ -78,8 +78,10 @@
+ 	{&CLSID_ShellFSFolder,	IFSFolder_Constructor},
+ 	{&CLSID_ShellItem,	IShellItem_Constructor},
+ 	{&CLSID_ShellLink,	IShellLink_Constructor},
++#if 0
+ 	{&CLSID_UnixDosFolder,  UnixDosFolder_Constructor},
+ 	{&CLSID_UnixFolder,     UnixFolder_Constructor},
++#endif
+ 	{&CLSID_ExplorerBrowser,ExplorerBrowser_Constructor},
+ 	{&CLSID_KnownFolderManager, KnownFolderManager_Constructor},
+ 	{&CLSID_Shell,          IShellDispatch_Constructor},
+diff -urN wine-1.9.7-orig/dlls/shell32/shellpath.c wine-1.9.7/dlls/shell32/shellpath.c
+--- wine-1.9.7-orig/dlls/shell32/shellpath.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/shellpath.c	2016-04-10 02:33:20 +0900
+@@ -4481,9 +4481,11 @@
+         DWORD call_for_attr;
+     } folders[] =
+     {
++#if 0
+         { &CLSID_UnixFolder, TRUE, FALSE, FALSE },
+         { &CLSID_UnixDosFolder, TRUE, FALSE, FALSE,
+           SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
++#endif
+         { &CLSID_FolderShortcut, FALSE, FALSE, FALSE,
+           SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_LINK,
+           SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR },
+@@ -4613,9 +4615,11 @@
+             *ppidl = _ILCreateDesktop();
+             break;
+ 
++#if 0
+         case CSIDL_PERSONAL:
+             *ppidl = _ILCreateMyDocuments();
+             break;
++#endif
+ 
+         case CSIDL_INTERNET:
+             *ppidl = _ILCreateIExplore();
+diff -urN wine-1.9.7-orig/dlls/shell32/shfldr.h wine-1.9.7/dlls/shell32/shfldr.h
+--- wine-1.9.7-orig/dlls/shell32/shfldr.h	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/shfldr.h	2016-04-10 02:33:20 +0900
+@@ -75,5 +75,7 @@
+ void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags) DECLSPEC_HIDDEN;
+ BOOL SHELL_FS_HideExtension(LPCWSTR pwszPath) DECLSPEC_HIDDEN;
+ 
++#if 0
+ DEFINE_GUID( CLSID_UnixFolder, 0xcc702eb2, 0x7dc5, 0x11d9, 0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd );
+ DEFINE_GUID( CLSID_UnixDosFolder, 0x9d20aae8, 0x0625, 0x44b0, 0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9 );
++#endif
+diff -urN wine-1.9.7-orig/dlls/shell32/shfldr_desktop.c wine-1.9.7/dlls/shell32/shfldr_desktop.c
+--- wine-1.9.7-orig/dlls/shell32/shfldr_desktop.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/shfldr_desktop.c	2016-04-10 02:33:20 +0900
+@@ -186,26 +186,30 @@
+     }
+     else if (PathGetDriveNumberW (lpszDisplayName) >= 0)
+     {
++#if 0
+         /*
+          * UNIXFS can't handle drives without a mount point yet. We fall back
+          * to use the MyComputer interface if we can't get the file attributes
+          * on the device.
+          */
+         char drivePath[] = "A:\\";
+         drivePath[0] = 'A' + PathGetDriveNumberW(lpszDisplayName);
+ 
+         /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */
+         if (UNIXFS_is_rooted_at_desktop() &&
+             GetFileAttributesA(drivePath) != INVALID_FILE_ATTRIBUTES)
+         {
+             pidlTemp = _ILCreateGuid(PT_GUID, &CLSID_UnixDosFolder);
+             TRACE("Using unixfs for %s\n", debugstr_w(lpszDisplayName));
+         }
+         else
+         {
++#endif
+             pidlTemp = _ILCreateMyComputer ();
+             TRACE("Using MyComputer for %s\n", debugstr_w(lpszDisplayName));
++#if 0
+         }
++#endif
+         szNext = lpszDisplayName;
+     }
+     else if (PathIsUNCW(lpszDisplayName))
+diff -urN wine-1.9.7-orig/dlls/shell32/shfldr_unixfs.c wine-1.9.7/dlls/shell32/shfldr_unixfs.c
+--- wine-1.9.7-orig/dlls/shell32/shfldr_unixfs.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/dlls/shell32/shfldr_unixfs.c	2016-04-10 02:33:20 +0900
+@@ -167,7 +167,7 @@
+ 
+ WINE_DEFAULT_DEBUG_CHANNEL(shell);
+ 
+-#if !defined(__MINGW32__) && !defined(_MSC_VER)
++#if 0
+ 
+ #define LEN_SHITEMID_FIXED_PART ((USHORT) \
+     ( sizeof(USHORT)      /* SHITEMID's cb field. */ \
+@@ -2569,6 +2569,7 @@
+  *  FALSE, if not.
+  */
+ BOOL UNIXFS_is_rooted_at_desktop(void) {
++#if 0
+     HKEY hKey;
+     WCHAR wszRootedAtDesktop[69 + CHARS_IN_GUID] = {
+         'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+@@ -2582,5 +2583,6 @@
+         RegCloseKey(hKey);
+         return TRUE;
+     }
++#endif
+     return FALSE;
+ }
+diff -urN wine-1.9.7-orig/include/config.h.in wine-1.9.7/include/config.h.in
+--- wine-1.9.7-orig/include/config.h.in	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/include/config.h.in	2016-04-10 02:33:20 +0900
+@@ -7,6 +7,9 @@
+ /* Define to a function attribute for Microsoft hotpatch assembly prefix. */
+ #undef DECLSPEC_HOTPATCH
+ 
++/* Define to enable unixfs */
++#undef ENABLE_UNIXFS
++
+ /* Define to the file extension for executables. */
+ #undef EXEEXT
+ 
+diff -urN wine-1.9.7-orig/programs/wineboot/wineboot.c wine-1.9.7/programs/wineboot/wineboot.c
+--- wine-1.9.7-orig/programs/wineboot/wineboot.c	2016-04-02 00:02:39 +0900
++++ wine-1.9.7/programs/wineboot/wineboot.c	2016-04-10 02:33:20 +0900
+@@ -946,7 +946,7 @@
+                                      'I','n','s','t','a','l','l','H','i','n','f','S','e','c','t','i','o','n',0};
+     static const WCHAR definstall[] = {' ','D','e','f','a','u','l','t','I','n','s','t','a','l','l',0};
+     static const WCHAR wowinstall[] = {' ','W','o','w','6','4','I','n','s','t','a','l','l',0};
+-    static const WCHAR inf[] = {' ','1','2','8',' ','\\','\\','?','\\','u','n','i','x',0 };
++    static const WCHAR inf[] = {' ','1','2','8',' ','z',':',0 };
+ 
+     WCHAR app[MAX_PATH + sizeof(rundll)/sizeof(WCHAR)];
+     STARTUPINFOW si;
diff --git a/pkgs/aszlig/santander/winscard.patch b/pkgs/aszlig/santander/winscard.patch
new file mode 100644
index 00000000..7dfa04ac
--- /dev/null
+++ b/pkgs/aszlig/santander/winscard.patch
@@ -0,0 +1,11 @@
+--- a/dlls/winscard/winscard.c	1970-01-01 01:00:01.000000000 +0100
++++ b/dlls/winscard/winscard.c	2016-06-06 01:52:53.631444433 +0200
+@@ -1527,7 +1527,7 @@
+ {

+     LONG lRet;

+     TRACE(" 0x%08X %p %p %p %p %p %p\n",(unsigned int) hCard,mszReaderNames,pcchReaderLen,pdwState,pdwProtocol,pbAtr,pcbAtrLen);

+-    if(!pcchReaderLen || !pdwState || !pdwProtocol || !pcbAtrLen)

++    if(!pcchReaderLen || !pcbAtrLen)

+         lRet = SCARD_E_INVALID_PARAMETER;

+     else if(!liteSCardStatus)

+         lRet = SCARD_F_INTERNAL_ERROR;

diff --git a/pkgs/build-support/channel.nix b/pkgs/build-support/channel.nix
new file mode 100644
index 00000000..a837177f
--- /dev/null
+++ b/pkgs/build-support/channel.nix
@@ -0,0 +1,32 @@
+{ stdenv }:
+
+{ name, src, constituents ? [], meta ? {}, ... }@args:
+
+stdenv.mkDerivation ({
+  inherit name src constituents;
+  preferLocalBuild = true;
+  _hydraAggregate = true;
+
+  phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+  installPhase = ''
+    mkdir -p "$out/tarballs" "$out/nix-support"
+
+    tar cJf "$out/tarballs/nixexprs.tar.xz" \
+      --owner=0 --group=0 --mtime="1970-01-01 00:00:00 UTC" \
+      --transform='s!^\.!${name}!' .
+
+    echo "channel - $out/tarballs/nixexprs.tar.xz" \
+      > "$out/nix-support/hydra-build-products"
+
+    echo $constituents > "$out/nix-support/hydra-aggregate-constituents"
+    for i in $constituents; do
+      if [ -e "$i/nix-support/failed" ]; then
+        touch "$out/nix-support/failed"
+      fi
+    done
+  '';
+
+  meta = meta // {
+    isHydraChannel = true;
+  };
+} // removeAttrs args [ "name" "channelName" "src" "constituents" "meta" ])
diff --git a/pkgs/default.nix b/pkgs/default.nix
new file mode 100644
index 00000000..ce39a5bc
--- /dev/null
+++ b/pkgs/default.nix
@@ -0,0 +1,28 @@
+{ pkgs ? import (import ../nixpkgs-path.nix) {} }:
+
+let
+  inherit (pkgs.lib) callPackageWith;
+  callPackage = callPackageWith (pkgs // self.vuizvui);
+  callPackage_i686 = callPackageWith (pkgs.pkgsi686Linux // self.vuizvui);
+
+  callPackageScope = import ./lib/call-package-scope.nix {
+    pkgs = pkgs // self.vuizvui;
+    pkgsi686Linux = pkgs.pkgsi686Linux // self.vuizvui;
+  };
+
+  self.vuizvui = pkgs.recurseIntoAttrs {
+    mkChannel = callPackage ./build-support/channel.nix { };
+
+    list-gamecontrollers = callPackage ./list-gamecontrollers { };
+
+    games = import ./games {
+      inherit pkgs;
+      config = pkgs.config.vuizvui.games or null;
+    };
+
+    aszlig = callPackageScope ./aszlig;
+    openlab = callPackageScope ./openlab;
+    profpatsch = callPackageScope ./profpatsch;
+    sternenseemann = callPackageScope ./sternenseemann;
+  };
+in pkgs // self
diff --git a/pkgs/games/base-module.nix b/pkgs/games/base-module.nix
new file mode 100644
index 00000000..934893c1
--- /dev/null
+++ b/pkgs/games/base-module.nix
@@ -0,0 +1,13 @@
+{ lib, ... }:
+
+with lib;
+
+{
+  options = {
+    packages = mkOption {
+      type = types.attrsOf types.unspecified;
+      default = {};
+      description = "Available NixGames packages.";
+    };
+  };
+}
diff --git a/pkgs/games/default.nix b/pkgs/games/default.nix
new file mode 100644
index 00000000..27682563
--- /dev/null
+++ b/pkgs/games/default.nix
@@ -0,0 +1,26 @@
+{ config ? null, pkgs ? import <nixpkgs> {} }:
+
+let
+  configFilePath = let
+    xdgConfig = builtins.getEnv "XDG_CONFIG_HOME";
+    fallback = "${builtins.getEnv "HOME"}/.config";
+    basedir = if xdgConfig == "" then fallback else xdgConfig;
+  in "${basedir}/nixgames.nix";
+
+  configFile = if !builtins.pathExists configFilePath then throw ''
+    The config file "${configFilePath}" doesn't exist! Be sure to create it and
+    put your HumbleBundle email address and password in it, like this:
+
+    {
+      humblebundle.email = "fancyuser@example.com";
+      humblebundle.password = "my_super_secret_password";
+    }
+  '' else configFilePath;
+
+in (pkgs.lib.evalModules {
+  modules = [
+    (if config == null then configFilePath else config)
+    ./base-module.nix ./humblebundle ./steam
+    { config._module.args.pkgs = pkgs; }
+  ];
+}).config.packages
diff --git a/pkgs/games/humblebundle/antichamber.nix b/pkgs/games/humblebundle/antichamber.nix
new file mode 100644
index 00000000..b72a0226
--- /dev/null
+++ b/pkgs/games/humblebundle/antichamber.nix
@@ -0,0 +1,58 @@
+{ stdenv, stdenv_32bit, lib, pkgsi686Linux, fetchHumbleBundle, unzip}:
+
+stdenv_32bit.mkDerivation rec {
+  name = "antichamber-1.1";
+
+  src = fetchHumbleBundle {
+    name = "antichamber_1.01_linux_1392664980.sh";
+    machineName = "antichamber_linux";
+    md5 = "37bca01c411d813c8729259b7db2dba0";
+  };
+
+  dontStrip = true;
+  phases = ["installPhase"];
+
+  installPhase = let
+    rpath = with pkgsi686Linux; lib.makeLibraryPath [
+      "$dest/Binaries/Linux"
+      xorg.libX11
+      xorg.libXi
+      stdenv.cc.cc.lib
+      libpulseaudio
+      libvorbis
+      libogg
+      mesa
+    ];
+   in ''
+    set -x
+    dest="$out/share/antichamber"
+
+    # Unpack binaries and data into $dest
+    mkdir -p "$dest"
+    ${unzip}/bin/unzip $src "data/*" -d $dest && [ $? -le 2 ]
+    mv $dest/data/*/* $dest
+    rm -r $dest/data
+
+    # Patch heavily :-)
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" "$dest/Binaries/Linux/UDKGame-Linux"
+    for exe in $dest/Binaries/Linux/lib/*.so{,.*} ; do
+      patchelf --set-rpath "${rpath}" "$exe"
+    done
+
+    # Fixup permissions, just to be sure.
+    find "$dest" -type f -exec chmod 644 "{}" +
+    find "$dest/Binaries" -type f -exec chmod 755 "{}" +
+
+    mkdir -p "$out/bin"
+    cat > $out/bin/antichamber <<EOF
+    #!${stdenv_32bit.shell}
+    cd $dest/Binaries/Linux/
+    exec ./UDKGame-Linux "$@"
+    EOF
+    chmod 755 $out/bin/antichamber
+  '';
+
+  # meta.platforms = lib.platforms.linux32;
+}
diff --git a/pkgs/games/humblebundle/bastion.nix b/pkgs/games/humblebundle/bastion.nix
new file mode 100644
index 00000000..b4acda6b
--- /dev/null
+++ b/pkgs/games/humblebundle/bastion.nix
@@ -0,0 +1,62 @@
+{ stdenv, fetchHumbleBundle, lzma, xorg, libpulseaudio}:
+
+let
+  arch = {
+    "i686-linux" = "x86";
+    "x86_64-linux" = "x86_64";
+  }.${stdenv.system};
+in stdenv.mkDerivation rec {
+  name = "bastion-1.4";
+
+  src = fetchHumbleBundle {
+    name = "Bastion-HIB-2012-06-20.sh";
+    machineName = "bastion_linux";
+    downloadName = ".sh";
+    md5 = "aa6ccaead3b4b8a5fbd156f4019e8c8b";
+  };
+
+  dontStrip = true;
+  phases = ["installPhase"];
+
+  installPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      "$dest"
+      xorg.libX11
+      xorg.libXi
+      stdenv.cc.cc
+      libpulseaudio
+    ];
+   in ''
+    dest="$out/opt/games/bastion"
+    libdir="$dest/lib" #${stdenv.lib.optionalString (arch=="x86_64") "64"}"
+
+    # Unpack binaries and data into $dest
+    mkdir -p "$dest"
+    sh "$src" --tar xf ./instarchive_all -O           | ${lzma}/bin/lzcat | tar x -C "$dest"
+    sh "$src" --tar xf ./instarchive_linux_${arch} -O | ${lzma}/bin/lzcat | tar x -C "$dest"
+
+    # Ensure that $dest is a valid library path.
+    mv $dest/lib64 $libdir || true
+
+    # Patch heavily :-)
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" "$dest/Bastion.bin.${arch}"
+    patchelf --set-rpath "${rpath}" "$libdir/libmono-2.0.so.1"
+    patchelf --set-rpath "${rpath}" "$libdir/libfmodex.so"
+    patchelf --set-rpath "${rpath}" "$libdir/libSDL-1.2.so.0"
+
+    # Fixup permissions, just to be sure.
+    find "$dest" -type f -exec chmod 644 "{}" +
+    chmod 755 "$dest/Bastion.bin.${arch}"
+
+    # Taken from ArchLinux; might be useful to actually implement
+    #install -Dm644 "''${pkgname}".desktop "''${pkgdir}"/usr/share/applications/"''${pkgname}".desktop
+    #install -Dm755 "mesa''${pkgname}" "''${pkgdir}"/usr/bin/"''${pkgname}"mesa
+    #install -Dm644 "''${pkgdir}"/opt/games/Bastion/Bastion.png "''${pkgdir}"/usr/share/icons/"''${pkgname}".png
+
+    # XXX: Make wrapper instead of symlink ? See ArchLinux's bastionmesa above.
+    mkdir -p "$out/bin"
+    ln -s "$dest/Bastion.bin.${arch}" "$out/bin/bastion"
+  '';
+}
diff --git a/pkgs/games/humblebundle/brigador.nix b/pkgs/games/humblebundle/brigador.nix
new file mode 100644
index 00000000..1e3be1cb
--- /dev/null
+++ b/pkgs/games/humblebundle/brigador.nix
@@ -0,0 +1,103 @@
+{ stdenv, fetchurl, makeWrapper, fetchHumbleBundle, writeText
+, SDL2, mesa, glew, freeimage
+}:
+
+let
+  oldGLEW = glew.overrideDerivation (stdenv.lib.const rec {
+    name = "glew-1.12.0";
+    src = fetchurl {
+      url = "mirror://sourceforge/glew/${name}.tgz";
+      sha256 = "1gz4917k9iyv3s8k0fxylzrwdnlf7dcszlsfzbkl7d1490zi0n5g";
+    };
+  });
+
+in stdenv.mkDerivation {
+  name = "brigador-1.0";
+
+  src = fetchHumbleBundle {
+    machineName = "brigador_linux";
+    suffix = "tar";
+    md5 = "61af4a5f037b85bf6acc5ca76d295d09";
+  };
+
+  preloader = writeText "brigador-preloader.c" ''
+    #define _GNU_SOURCE
+    #include <dlfcn.h>
+    #include <fcntl.h>
+    #include <stdarg.h>
+    #include <stdio.h>
+    #include <string.h>
+    #include <sys/stat.h>
+
+    #define MANGLE_PATH(call, ...) \
+      if (strncmp(path, "assets.pack", 12) == 0 || \
+          strncmp(path, "assets/",     7)  == 0 || \
+          strncmp(path, "fonts/",      6)  == 0 || \
+          strncmp(path, "shaders/",    8)  == 0 || \
+          strncmp(path, "sounds/",     7)  == 0) { \
+        char buf[1024]; \
+        snprintf(buf, sizeof(buf), "%s/%s", LIBEXEC_PATH, path); \
+        return call(buf, __VA_ARGS__); \
+      }
+
+    ${stdenv.lib.concatMapStrings (fun: ''
+      FILE *${fun}(const char *path, const char *mode) {
+        static FILE *(*_${fun}) (const char *, const char *) = NULL;
+        if (_${fun} == NULL) _${fun} = dlsym(RTLD_NEXT, "${fun}");
+        MANGLE_PATH(_${fun}, mode);
+        return _${fun}(path, mode);
+      }
+    '') [ "fopen" "fopen64" ]}
+
+    int open(const char *path, int flags, ...) {
+      va_list ap;
+      mode_t mode;
+      static int (*_open) (const char *, int, mode_t) = NULL;
+      if (_open == NULL) _open = dlsym(RTLD_NEXT, "open");
+      va_start(ap, flags);
+      mode = va_arg(ap, mode_t);
+      va_end(ap);
+      MANGLE_PATH(_open, (flags & ~O_RDWR) | O_RDONLY, mode);
+      return _open(path, flags, mode);
+    }
+  '';
+
+  patchPhase = let
+    fmodRpath = stdenv.lib.makeLibraryPath [ "$out" stdenv.cc.cc ];
+    rpath = stdenv.lib.makeLibraryPath [ "$out" SDL2 mesa oldGLEW freeimage ];
+  in ''
+    for fmod in lib/libfmod*.so*; do
+      patchelf --set-rpath "${fmodRpath}" "$fmod"
+    done
+
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" brigador
+  '';
+
+  buildPhase = ''
+    cc -Werror -shared "$preloader" -o preloader.so -ldl -fPIC \
+      -DLIBEXEC_PATH=\"$out/libexec/brigador\"
+  '';
+
+  buildInputs = [ makeWrapper ];
+
+  installPhase = ''
+    for fmod in lib/libfmod*.so*; do
+      install -vD "$fmod" "$out/lib/$(basename "$fmod")"
+    done
+
+    install -vD brigador "$out/libexec/brigador/brigador"
+    install -vD preloader.so "$out/libexec/brigador/preloader.so"
+    install -vD -m 0644 assets.pack "$out/libexec/brigador/assets.pack"
+    cp -rt "$out/libexec/brigador" assets fonts shaders sounds
+
+    makeWrapper "$out/libexec/brigador/brigador" "$out/bin/brigador" \
+      --set LD_PRELOAD "$out/libexec/brigador/preloader.so" \
+      --run 'XDG_DATA_HOME="''${XDG_DATA_HOME:-$HOME/.local/share}"' \
+      --run 'mkdir -p "$XDG_DATA_HOME/brigador"' \
+      --run 'cd "$XDG_DATA_HOME/brigador"'
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/cavestoryplus.nix b/pkgs/games/humblebundle/cavestoryplus.nix
new file mode 100644
index 00000000..d4c744d4
--- /dev/null
+++ b/pkgs/games/humblebundle/cavestoryplus.nix
@@ -0,0 +1,39 @@
+{ stdenv, fetchHumbleBundle, makeWrapper, SDL, mesa }:
+
+stdenv.mkDerivation rec {
+  name = "cave-story-plus-${version}";
+  version = "r100";
+
+  src = fetchHumbleBundle {
+    machineName = "cavestoryplus_linux";
+    downloadName = ".tar.bz2";
+    suffix = "tar.bz2";
+    md5 = "b7ecd65644b8607bc177d7ce670f2185";
+  };
+
+  buildInputs = [ makeWrapper ];
+
+  patchPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      SDL "$out" stdenv.cc.cc mesa
+    ];
+  in ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" CaveStory+_64
+  '';
+
+  installPhase = ''
+    install -vD CaveStory+_64 "$out/libexec/cave-story-plus/cave-story-plus"
+    mkdir -p "$out/bin"
+    makeWrapper \
+      "$out/libexec/cave-story-plus/cave-story-plus" \
+      "$out/bin/cave-story-plus" \
+      --run "cd '$out/share/cave-story-plus'"
+
+    mkdir -p "$out/share/cave-story-plus"
+    cp -vrt "$out/share/cave-story-plus" data
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/default.nix b/pkgs/games/humblebundle/default.nix
new file mode 100644
index 00000000..5850bce6
--- /dev/null
+++ b/pkgs/games/humblebundle/default.nix
@@ -0,0 +1,54 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.humblebundle;
+
+  self = rec {
+    callPackage = pkgs.lib.callPackageWith (pkgs // self);
+    callPackage_i686 = pkgs.lib.callPackageWith (pkgs.pkgsi686Linux // self);
+
+    fetchHumbleBundle = callPackage ./fetch-humble-bundle {
+      inherit (config.humblebundle) email password;
+    };
+
+    antichamber = callPackage_i686 ./antichamber.nix { };
+    bastion = callPackage ./bastion.nix {};
+    brigador = callPackage ./brigador.nix {};
+    cavestoryplus = callPackage ./cavestoryplus.nix {};
+    fez = callPackage ./fez.nix {};
+    ftl = callPackage ./ftl.nix {};
+    guacamelee = callPackage_i686 ./guacamelee.nix {};
+    hammerwatch = callPackage ./hammerwatch.nix {};
+    jamestown = callPackage ./jamestown.nix {};
+    liads = callPackage ./liads.nix {};
+    megabytepunch = callPackage ./megabytepunch.nix {};
+    pico-8 = callPackage ./pico-8.nix {};
+    rocketbirds = callPackage ./rocketbirds.nix {};
+    spaz = callPackage ./spaz.nix {};
+    starbound = callPackage ./starbound.nix {};
+    swordsandsoldiers = callPackage ./swordsandsoldiers.nix {};
+    unepic = callPackage ./unepic.nix {};
+  };
+in with lib; {
+  options.humblebundle = {
+    email = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Email address for your HumbleBundle account.
+      '';
+    };
+
+    password = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Password for your HumbleBundle account.
+      '';
+    };
+  };
+
+  config.packages = {
+    humblebundle = mkIf (cfg.email != null && cfg.password != null) self;
+  };
+}
diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix
new file mode 100644
index 00000000..e8f66260
--- /dev/null
+++ b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix
@@ -0,0 +1,182 @@
+{ stdenv, curl, cacert, writeText, fetchFromGitHub, fetchpatch
+, python, pythonPackages
+
+# Dependencies for the captcha solver
+, pkgconfig, qt5, runCommandCC
+
+, email, password
+}:
+
+{ name ? null, machineName, downloadName ? "Download", suffix ? "humblebundle", md5 }: let
+  cafile = "${cacert}/etc/ssl/certs/ca-bundle.crt";
+
+  getCaptcha = let
+    injectedJS = ''
+      function waitForResponse() {
+        var response = captcha.get_response();
+        if (response != "")
+          document.title = response;
+        else
+          setTimeout(waitForResponse, 50);
+      }
+
+      waitForResponse();
+    '';
+
+    escapeCString = stdenv.lib.replaceStrings ["\"" "\n"] ["\\\"" "\\n"];
+
+    application = writeText "captcha.cc" ''
+      #include <QApplication>
+      #include <QWebEngineView>
+      #include <QTcpServer>
+
+      int main(int argc, char **argv) {
+        QApplication *app = new QApplication(argc, argv);
+        QTcpServer *server = new QTcpServer();
+        QWebEngineView *browser = new QWebEngineView();
+
+        if (!server->listen(QHostAddress::LocalHost, 18123)) {
+          qCritical() << "Unable to listen on port 18123!";
+          return 1;
+        }
+
+        qInfo() << "Waiting for connection from the HB downloader...";
+        if (!server->waitForNewConnection(-1)) {
+          qCritical() << "Unable to accept the connection!";
+          return 1;
+        }
+        qInfo() << "Connection established, spawning window to solve captcha.";
+
+        QTcpSocket *sock = server->nextPendingConnection();
+
+        browser->load(QUrl("https://www.humblebundle.com/user/captcha"));
+        browser->show();
+
+        browser->connect(browser, &QWebEngineView::loadFinished, [=]() {
+          browser->page()->runJavaScript("${escapeCString injectedJS}");
+          browser->connect(
+            browser, &QWebEngineView::titleChanged, [=](const QString &title) {
+              sock->write(title.toUtf8());
+              sock->flush();
+              sock->waitForBytesWritten();
+              sock->close();
+              server->close();
+              app->quit();
+            }
+          );
+        });
+
+        return app->exec();
+      }
+    '';
+
+  in runCommandCC "get-captcha" {
+    nativeBuildInputs = [ pkgconfig ];
+    buildInputs = [ qt5.qtbase qt5.qtwebengine ];
+  } ''
+    g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets) \
+      -Wall -std=c++11 -o "$out" ${application}
+  '';
+
+  humbleAPI = pythonPackages.buildPythonPackage rec {
+    name = "humblebundle-${version}";
+    version = "0.1.1";
+
+    src = fetchFromGitHub {
+      owner = "saik0";
+      repo = "humblebundle-python";
+      rev = version;
+      sha256 = "1kcg42nh7sbjabim1pbqx14468pypznjy7fx2bv7dicy0sqd9b8j";
+    };
+
+    propagatedBuildInputs = [ pythonPackages.requests ];
+  };
+
+  pyStr = str: "'${stdenv.lib.escape ["'" "\\"] str}'";
+
+  getDownloadURL = writeText "gethburl.py" ''
+    import socket, sys, time, humblebundle
+
+    def get_products(client):
+      gamekeys = client.get_gamekeys()
+      for gamekey in gamekeys:
+        order = hb.get_order(gamekey)
+        if order.subproducts is None:
+          continue
+        for subproduct in order.subproducts:
+          prodname = subproduct.human_name.encode('ascii', 'replace')
+          downloads = [(download.machine_name, download.download_struct)
+                       for download in subproduct.downloads]
+          yield (prodname, downloads)
+
+    def find_download(downloads):
+      for machine_name, dstruct in sum(downloads.values(), []):
+        if machine_name == ${pyStr machineName}:
+          for ds in dstruct:
+            if ds.name == ${pyStr downloadName}:
+              return ds
+          print >>sys.stderr, \
+            ${pyStr "Unable to find ${downloadName} for ${machineName}!"}
+          print >>sys.stderr, 'Available download types:'
+          for ds in dstruct:
+            print >>sys.stderr, "  " + ds.name
+          raise SystemExit(1)
+
+    def login_with_captcha(hb):
+      print >>sys.stderr, "Solving a captcha is required to log in."
+      print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} " now."
+      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      print >>sys.stderr, "Waiting for connection",
+      i = 0
+      while sock.connect_ex(("127.0.0.1", 18123)) != 0:
+        time.sleep(0.1)
+        if i % 10 == 0:
+          sys.stderr.write('.')
+          sys.stderr.flush()
+        i += 1
+      print >>sys.stderr, " connected."
+      print >>sys.stderr, "Waiting for captcha to be solved..."
+      response = sock.recv(4096)
+      sock.close()
+      print >>sys.stderr, "Captcha solved correctly, logging in."
+      hb.login(${pyStr email}, ${pyStr password}, recaptcha_response=response)
+
+    hb = humblebundle.HumbleApi()
+    try:
+      hb.login(${pyStr email}, ${pyStr password})
+    except humblebundle.exceptions.HumbleCaptchaException:
+      login_with_captcha(hb)
+
+    products = dict(get_products(hb))
+    dstruct = find_download(products)
+
+    if dstruct is None:
+      print >>sys.stderr, ${pyStr "Cannot find download for ${machineName}!"}
+      print >>sys.stderr, 'Available machine names:'
+      for name, dstructs in sorted(products.items(), key=lambda x: x[0]):
+        print >>sys.stderr, "  * " + name
+        print >>sys.stderr, "    " + ', '.join(map(lambda x: x[0], dstructs))
+      raise SystemExit(1)
+    elif dstruct.md5 != ${pyStr md5}:
+      print >>sys.stderr, \
+        ${pyStr "MD5 for ${machineName} is not ${md5} but "} \
+        + dstruct.md5 + '.'
+      raise SystemExit(1)
+    else:
+      print dstruct.url.web
+  '';
+in stdenv.mkDerivation {
+  name = if name != null then name else "${machineName}.${suffix}";
+  outputHashAlgo = "md5";
+  outputHash = md5;
+
+  buildInputs = [ python humbleAPI ];
+
+  buildCommand = ''
+    url="$(python "${getDownloadURL}")"
+    header "downloading $name from $url"
+    "${curl.bin or curl}/bin/curl" --cacert "${cafile}" --fail \
+      --output "$out" "$url"
+    stopNest
+  '';
+}
diff --git a/pkgs/games/humblebundle/fez.nix b/pkgs/games/humblebundle/fez.nix
new file mode 100644
index 00000000..5f23b97c
--- /dev/null
+++ b/pkgs/games/humblebundle/fez.nix
@@ -0,0 +1,35 @@
+{ stdenv, fetchHumbleBundle, unzip, mono, openal, SDL2 }:
+
+let
+  version = "1.0.2";
+  usVersion = stdenv.lib.replaceChars ["."] ["_"] version;
+in stdenv.mkDerivation rec {
+  name = "fez-${version}";
+  version = "09152013";
+
+  src = fetchHumbleBundle {
+    name = "${name}-bin";
+    md5 = "4ac954101835311f3528f5369e1fecb7";
+  };
+
+  unpackPhase = ''
+    ${unzip}/bin/unzip -qq "$src" 'data/*' || true
+    sourceRoot=data
+  '';
+
+  dontStrip = true;
+
+  buildPhase = ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${stdenv.lib.makeLibraryPath [ mono openal SDL2 ]}" \
+      FEZ.bin.x86_64
+  '';
+
+  installPhase = ''
+    ensureDir "$out/bin" "$out/libexec/fez/mono/2.0"
+    install -vD FEZ.bin.x86_64 "$out/libexec/fez/fez"
+    install -vt "$out/libexec/fez/mono/2.0" *.dll
+    ln -s "$out/libexec/fez/fez" "$out/bin/fez"
+  '';
+}
diff --git a/pkgs/games/humblebundle/ftl.nix b/pkgs/games/humblebundle/ftl.nix
new file mode 100644
index 00000000..7423951a
--- /dev/null
+++ b/pkgs/games/humblebundle/ftl.nix
@@ -0,0 +1,38 @@
+{ stdenv, fetchHumbleBundle, makeWrapper, SDL, mesa, libdevil, freetype }:
+
+stdenv.mkDerivation rec {
+  name = "ftl-${version}";
+  version = "1.5.13";
+
+  src = fetchHumbleBundle {
+    machineName = "ftlfasterthanlight_soundtrack_linux";
+    downloadName = ".tar.gz";
+    suffix = "tar.gz";
+    md5 = "791e0bc8de73fcdcd5f461a4548ea2d8";
+  };
+
+  buildInputs = [ makeWrapper ];
+
+  patchPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      SDL "$out" stdenv.cc.cc mesa libdevil freetype
+    ];
+  in ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" data/amd64/bin/FTL
+  '';
+
+  installPhase = ''
+    install -vD "data/amd64/bin/FTL" "$out/libexec/ftl/FTL"
+    install -vD "data/amd64/lib/libbass.so" "$out/lib/libbass.so"
+    install -vD "data/amd64/lib/libbassmix.so" "$out/lib/libbassmix.so"
+
+    mkdir -p "$out/bin" "$out/share/ftl"
+    cp -vrt "$out/share/ftl" data/resources
+    makeWrapper "$out/libexec/ftl/FTL" "$out/bin/ftl" \
+      --run "cd '$out/share/ftl'"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/guacamelee.nix b/pkgs/games/humblebundle/guacamelee.nix
new file mode 100644
index 00000000..537ec945
--- /dev/null
+++ b/pkgs/games/humblebundle/guacamelee.nix
@@ -0,0 +1,61 @@
+{ stdenv, fetchHumbleBundle, unzip, SDL2, mesa, writeText, makeWrapper }:
+
+stdenv.mkDerivation rec {
+  name = "guacamelee-${version}";
+  version = "1393037377";
+
+  src = fetchHumbleBundle {
+    machineName = "guacamelee_goldedition_linux";
+    suffix = "sh";
+    md5 = "b06af932c1aaefb8f157c977061388ef";
+  };
+
+  unpackCmd = ''
+    ${unzip}/bin/unzip "$src" 'data/*' || :
+  '';
+
+  preloader = writeText "guacamelee-preloader.c" ''
+    #define _GNU_SOURCE
+    #include <dlfcn.h>
+
+    int chdir(const char *path) {
+      int (*_chdir) (const char *) = dlsym(RTLD_NEXT, "chdir");
+      return _chdir(DATA);
+    }
+  '';
+
+  buildInputs = [ makeWrapper ];
+
+  buildPhase = let
+    rpath = stdenv.lib.makeLibraryPath [ SDL2 stdenv.cc.cc mesa ];
+    fmodRpath = stdenv.lib.makeLibraryPath [ stdenv.cc.cc ];
+  in ''
+    gcc -Werror -shared "$preloader" -o preloader.so -ldl \
+      -DDATA=\"$out/share/guacamelee\"
+
+    for i in libfmodevent-4.44.27.so libfmodex-4.44.27.so; do
+      patchelf --set-rpath "${fmodRpath}:$out/libexec/guacamelee" \
+        "x86/lib32/$i"
+    done
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}:$out/libexec/guacamelee" x86/game-bin
+  '';
+
+  installPhase = ''
+    install -vD x86/game-bin "$out/libexec/guacamelee/guacamelee"
+    install -vD preloader.so "$out/libexec/guacamelee/preloader.so"
+
+    makeWrapper "$out/libexec/guacamelee/guacamelee" "$out/bin/guacamelee" \
+      --set LD_PRELOAD "$out/libexec/guacamelee/preloader.so"
+
+    for i in libfmodevent-4.44.27.so libfmodex-4.44.27.so; do
+      install -vD "x86/lib32/$i" "$out/libexec/guacamelee/$i"
+    done
+
+    mkdir -p "$out/share"
+    cp -vRd noarch "$out/share/guacamelee"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/hammerwatch.nix b/pkgs/games/humblebundle/hammerwatch.nix
new file mode 100644
index 00000000..8cf65211
--- /dev/null
+++ b/pkgs/games/humblebundle/hammerwatch.nix
@@ -0,0 +1,40 @@
+{ stdenv, fetchHumbleBundle, makeWrapper, unzip, mono, SDL2, mesa, openal
+, pulseaudio
+}:
+
+# FIXME: Dosn't support the XDG Base Directory Specification,
+#        so enforce it using LD_PRELOAD maybe?
+
+stdenv.mkDerivation rec {
+  name = "hammerwatch-${version}";
+  version = "1.3";
+
+  src = fetchHumbleBundle {
+    machineName = "hammerwatch_linux";
+    suffix = "zip";
+    md5 = "7cd77e4395f394c3062322c96e418732";
+  };
+
+  buildInputs = [ unzip makeWrapper ];
+
+  installPhase = let
+    rpath = stdenv.lib.makeLibraryPath [ SDL2 mesa openal pulseaudio ];
+    monoNoLLVM = mono.override { withLLVM = false; };
+  in ''
+    mkdir -p "$out/lib"
+    cp -rt "$out/lib" SDL2-CS.dll SDL2-CS.dll.config \
+      TiltedEngine.dll Lidgren.Network.dll FarseerPhysicsOTK.dll \
+      ICSharpCode.SharpZipLib.dll SteamworksManaged.dll NVorbis.dll
+
+    libexec="$out/libexec/hammerwatch"
+    install -vD Hammerwatch.exe "$libexec/hammerwatch.exe"
+    cp -rt "$libexec" assets.bin editor levels
+
+    makeWrapper "${monoNoLLVM}/bin/mono" "$out/bin/hammerwatch" \
+      --add-flags "$libexec/hammerwatch.exe" \
+      --set MONO_PATH "$out/lib" \
+      --set LD_LIBRARY_PATH "${rpath}"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/jamestown.nix b/pkgs/games/humblebundle/jamestown.nix
new file mode 100644
index 00000000..15900bba
--- /dev/null
+++ b/pkgs/games/humblebundle/jamestown.nix
@@ -0,0 +1,55 @@
+{ stdenv, fetchHumbleBundle, unzip, pkgsi686Linux, expect, makeWrapper
+, SDL, openal
+}:
+
+let
+  version = "1.0.2";
+  usVersion = stdenv.lib.replaceChars ["."] ["_"] version;
+in stdenv.mkDerivation rec {
+  name = "jamestown-${version}";
+
+  src = fetchHumbleBundle {
+    machineName = "jamestown_linux";
+    downloadName = ".zip";
+    suffix = "zip";
+    md5 = "dcfb4348aba89f0f26bf5b4c7e05d936";
+  };
+
+  buildInputs = [ makeWrapper ];
+
+  unpackPhase = ''
+    ${unzip}/bin/unzip -q "$src"
+    patchelf --set-interpreter "${pkgsi686Linux.glibc}"/lib/ld-linux.so.* \
+      "JamestownInstaller_${usVersion}-bin"
+    ${expect}/bin/expect <<INSTALL
+    spawn "./JamestownInstaller_${usVersion}-bin"
+    expect "see more?"
+    send "n\r"
+    expect "Accept this license?"
+    send "y\r"
+    expect "Press enter to continue."
+    send "\r"
+    expect "Enter path"
+    send "$(pwd)/${name}\r"
+    expect eof
+    INSTALL
+    sourceRoot="$(pwd)/${name}"
+  '';
+
+  installPhase = let
+    rpath = stdenv.lib.makeLibraryPath [ SDL openal ];
+  in ''
+    libexec="$out/libexec/jamestown"
+    install -vD Jamestown-amd64 "$libexec/jamestown"
+
+    mkdir -p "$out/share"
+    mv Archives "$out/share/jamestown"
+
+    makeWrapper "$(cat "$NIX_CC/nix-support/dynamic-linker")" \
+      "$out/bin/jamestown" \
+      --add-flags "$libexec/jamestown" \
+      --set LD_LIBRARY_PATH "${rpath}"
+
+    false # Both amd64 and i686 binaries are fucking BROKEN, wait for 1.0.3...
+  '';
+}
diff --git a/pkgs/games/humblebundle/liads.nix b/pkgs/games/humblebundle/liads.nix
new file mode 100644
index 00000000..281b137d
--- /dev/null
+++ b/pkgs/games/humblebundle/liads.nix
@@ -0,0 +1,43 @@
+{ stdenv, fetchHumbleBundle, unzip, mesa, xorg, libpulseaudio }:
+
+stdenv.mkDerivation rec {
+  name = "liads-${version}";
+  version = "20160121";
+
+  src = fetchHumbleBundle {
+    machineName = "loversinadangerousspacetime_linux";
+    suffix = "zip";
+    md5 = "e838cad67e8814e955dab42efd4995e2";
+  };
+
+  unpackCmd = ''
+    ${unzip}/bin/unzip -qq -d liads "$src" || :
+  '';
+
+  arch = if stdenv.system == "x86_64-linux" then "x86_64" else "x86";
+  executable = "LoversInADangerousSpacetime.${arch}";
+
+  buildPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      stdenv.cc.cc mesa xorg.libX11 xorg.libXcursor xorg.libXrandr libpulseaudio
+    ];
+  in ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" "$executable"
+  '';
+
+  installPhase = ''
+    install -vD "$executable" "$out/libexec/liads/liads"
+    ln -s "$out/share/liads" "$out/libexec/liads/Data"
+
+    mkdir -p "$out/bin"
+    ln -s "$out/libexec/liads/liads" "$out/bin/liads"
+
+    mkdir -p "$out/share"
+    cp -vRd LoversInADangerousSpacetime_Data "$out/share/liads"
+  '';
+
+  dontStrip = true;
+  dontPatchELF = true;
+}
diff --git a/pkgs/games/humblebundle/megabytepunch.nix b/pkgs/games/humblebundle/megabytepunch.nix
new file mode 100644
index 00000000..643e5835
--- /dev/null
+++ b/pkgs/games/humblebundle/megabytepunch.nix
@@ -0,0 +1,18 @@
+{ stdenv, fetchHumbleBundle }:
+
+stdenv.mkDerivation rec {
+  name = "megabytepunch-${version}";
+  version = "1.12";
+
+  src = fetchHumbleBundle {
+    machineName = "megabytepunch_linux";
+    suffix = "tar.gz";
+    md5 = "13487ae35c99817ce5f19b45fa51158b";
+  };
+
+  patchPhase = ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_GCC/nix-support/dynamic-linker)" \
+
+  '';
+}
diff --git a/pkgs/games/humblebundle/pico-8.nix b/pkgs/games/humblebundle/pico-8.nix
new file mode 100644
index 00000000..ead0c511
--- /dev/null
+++ b/pkgs/games/humblebundle/pico-8.nix
@@ -0,0 +1,51 @@
+{ stdenv, fetchHumbleBundle, SDL2, unzip, xorg, libudev, alsaLib, dbus
+, libpulseaudio, libdrm, libvorbis, json_c }:
+
+stdenv.mkDerivation rec {
+  name = "pico-8-${version}";
+  version = "0.1.9";
+
+  src = fetchHumbleBundle {
+    name = "pico8_linux";
+    machineName = "pico8_linux";
+    downloadName = {
+      "x86_64-linux" = "64-bit";
+      "i686-linux"   = "32-bit";
+    }.${stdenv.system};
+    md5 = {
+      "x86_64-linux" = "d6bb4bcae76aba431d31a344da798553";
+      "i686-linux"   = "377eb626672e3184ea7bb459bb7183a0";
+    }.${stdenv.system};
+  };
+
+  unpackCmd = ''
+    ${unzip}/bin/unzip -qq -d . "$src" || :
+  '';
+
+  phases = [ "unpackPhase" "buildPhase" "installPhase" ];
+
+  buildPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      stdenv.cc.cc SDL2 xorg.libXxf86vm xorg.libXcursor xorg.libXi
+      xorg.libXrandr libudev alsaLib dbus
+      libpulseaudio libdrm libvorbis json_c
+    ];
+  in ''
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" pico8
+  '';
+
+  installPhase = ''
+    install -vD pico8 "$out/share/pico8"
+    install -vD pico8.dat "$out/share/pico8.dat"
+    install -vD pico-8.txt "$out/share/pico-8.txt"
+    install -vD license.txt "$out/share/license.txt"
+    install -vD lexaloffle-pico8.png "$out/share/lexaloffle-pico8.png"
+
+    mkdir -p "$out/bin"
+    ln -s "$out/share/pico8" "$out/bin/pico8"
+  '';
+
+  dontPatchELF = true;
+}
diff --git a/pkgs/games/humblebundle/rocketbirds.nix b/pkgs/games/humblebundle/rocketbirds.nix
new file mode 100644
index 00000000..a57d1562
--- /dev/null
+++ b/pkgs/games/humblebundle/rocketbirds.nix
@@ -0,0 +1,11 @@
+{ stdenv, fetchHumbleBundle }:
+
+stdenv.mkDerivation rec {
+  name = "rocketbirds-${version}";
+  version = "20130917";
+
+  src = fetchHumbleBundle {
+    name = "Rocketbirds${version}.sh";
+    md5 = "7c5e6da4cd7fc7f2f51861f8b96a386f";
+  };
+}
diff --git a/pkgs/games/humblebundle/spaz.nix b/pkgs/games/humblebundle/spaz.nix
new file mode 100644
index 00000000..5e40ea49
--- /dev/null
+++ b/pkgs/games/humblebundle/spaz.nix
@@ -0,0 +1,39 @@
+{ stdenv, fetchHumbleBundle, unzip, pkgsi686Linux }:
+
+stdenv.mkDerivation rec {
+  name = "spaz-${version}";
+  version = "09182012";
+
+  src = fetchHumbleBundle {
+    name = "spaz-linux-humblebundle-${version}-bin";
+    md5 = "9b2f28009949f2dff9f3a737e46fabfd";
+  };
+
+  buildInputs = [ pkgsi686Linux.makeWrapper ];
+
+  unpackCmd = ''
+    ${unzip}/bin/unzip -qq "$src" 'data/*' || true
+  '';
+
+  dontStrip = true;
+
+  buildPhase = let
+    libs = pkgsi686Linux.stdenv.lib.makeLibraryPath [
+      pkgsi686Linux.stdenv.cc.cc pkgsi686Linux.SDL
+    ];
+  in ''
+    patchelf --set-interpreter "${pkgsi686Linux.glibc}"/lib/ld-linux.so.* \
+             --set-rpath "${libs}" SPAZ
+  '';
+
+  installPhase = let
+    libs = pkgsi686Linux.stdenv.lib.makeLibraryPath [
+      pkgsi686Linux.mesa pkgsi686Linux.openal pkgsi686Linux.alsaPlugins
+    ];
+  in ''
+    install -vD SPAZ "$out/libexec/spaz/spaz"
+    cp -rt "$out/libexec/spaz" audio.so common game mods
+    makeWrapper "$out/libexec/spaz/spaz" "$out/bin/spaz" \
+      --set LD_LIBRARY_PATH "${libs}"
+  '';
+}
diff --git a/pkgs/games/humblebundle/starbound.nix b/pkgs/games/humblebundle/starbound.nix
new file mode 100644
index 00000000..6cd41e8e
--- /dev/null
+++ b/pkgs/games/humblebundle/starbound.nix
@@ -0,0 +1,329 @@
+{ stdenv, fetchHumbleBundle, unzip, fetchurl, writeText, SDL2, mesa, xorg
+, makeDesktopItem
+}:
+
+let
+  binaryDeps = {
+    starbound.deps = [
+      SDL2 mesa xorg.libX11 xorg.libICE xorg.libSM xorg.libXext
+    ];
+    starbound.needsBootconfig = true;
+
+    starbound_server.name = "starbound-server";
+    starbound_server.needsBootconfig = true;
+
+    asset_packer.name = "starbound-asset-packer";
+    asset_unpacker.name = "starbound-asset-unpacker";
+
+    dump_versioned_json.name = "starbound-dump-versioned-json";
+    make_versioned_json.name = "starbound-make-versioned-json";
+
+    planet_mapgen.name = "starbound-planet-mapgen";
+  };
+
+  desktopItem = makeDesktopItem {
+    name = "starbound";
+    exec = "starbound";
+    icon = fetchurl {
+      url = "http://i1305.photobucket.com/albums/s544/ClockworkBarber/"
+          + "logo_zps64c4860d.png";
+      sha256 = "11fiiy0vcxzix1j81732cjh16wi48k4vag040vmbhad50ps3mg0q";
+    };
+    comment = "An extraterrestrial sandbox adventure game";
+    desktopName = "Starbound";
+    genericName = "starbound";
+    categories = "Game;";
+  };
+
+  patchBinary = bin: attrs: ''
+    mkdir -p "patched/$(dirname "${bin}")"
+    cp -t "patched/$(dirname "${bin}")" "linux/${bin}"
+    chmod +x "patched/$(basename "${bin}")"
+    ${stdenv.lib.optionalString (attrs.needsBootconfig or false) ''
+      for offset in $(
+        grep -abz '^sbinit\.config''$' "patched/$(basename "${bin}")" \
+          | cut -d: -f1 -z | xargs -0
+      ); do
+        for i in $(seq 13); do printf '\x07'; done \
+          | dd of="patched/$(basename "${bin}")" \
+               obs=1 seek="$offset" \
+               count=13 conv=notrunc
+      done
+      patchelf \
+        --add-needed "$lib/lib/libstarbound-preload.so" \
+        "patched/$(basename "${bin}")"
+    ''}
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${stdenv.lib.makeLibraryPath (attrs.deps or [])}" \
+      "patched/$(basename "${bin}")"
+    if ldd "patched/$(basename "${bin}")" | grep -F 'not found' \
+       | grep -v 'libstarbound-preload\.so\|libsteam_api\.so'; then
+      exit 1;
+    fi
+  '';
+
+  preloaderSource = writeText "starbound-preloader.c" ''
+    #define _GNU_SOURCE
+    #include <dlfcn.h>
+    #include <fcntl.h>
+    #include <malloc.h>
+    #include <stdarg.h>
+    #include <stdio.h>
+    #include <stdlib.h>
+    #include <string.h>
+    #include <sys/stat.h>
+    #include <sys/wait.h>
+    #include <unistd.h>
+
+    #define MAGIC "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07"
+
+    static char *getXdgDataHome(void) {
+      int envlen;
+      char *result;
+      const char *env;
+
+      if ((env = getenv("XDG_DATA_HOME")) != NULL)
+        return strdup(env);
+      if ((env = getenv("HOME")) == NULL)
+        return NULL;
+
+      envlen = strlen(env);
+      if ((result = malloc(envlen + 14)) == NULL)
+        return NULL;
+      strncpy(result, env, envlen);
+      strncpy(result + envlen, "/.local/share", 14);
+      return result;
+    }
+
+    static char *mkJsonString(const char *str) {
+      char *result, *out;
+
+      if ((result = malloc(strlen(str) * 6 + 3)) == NULL)
+        return NULL;
+
+      out = result;
+      *out++ = '"';
+
+      for (; *str != '\0'; str++) {
+        switch (*str) {
+          case '"':  *out++ = '\\'; *out++ = '"'; break;
+          case '\\': *out++ = '\\'; *out++ = '\\'; break;
+          case '\b': *out++ = '\\'; *out++ = 'b'; break;
+          case '\f': *out++ = '\\'; *out++ = 'f'; break;
+          case '\n': *out++ = '\\'; *out++ = 'n'; break;
+          case '\r': *out++ = '\\'; *out++ = 'r'; break;
+          case '\t': *out++ = '\\'; *out++ = 't'; break;
+          default:
+            if (*str >= 0 && *str <= 31) {
+              *out++ = '\\';
+              *out++ = 'u';
+              snprintf(out, 4, "%04x", *str);
+              out += 4;
+            } else {
+              *out++ = *str;
+            }
+            break;
+        }
+      }
+
+      *out++ = '"';
+      *out = 0;
+
+      return result;
+    }
+
+    static char *mkDataDir(const char *dataHome, const char *append) {
+      char *buf, *out;
+      int homeLen = strlen(dataHome);
+      int appendLen = strlen(append);
+
+      if ((buf = malloc(homeLen + appendLen + 2)) == NULL)
+        return NULL;
+
+      snprintf(buf, homeLen + appendLen + 2, "%s/%s", dataHome, append);
+
+      out = mkJsonString(buf);
+      free(buf);
+
+      return out;
+    }
+
+    static int makedirs(const char *path) {
+      char *buf, *p;
+
+      if (strlen(path) == 0)
+        return 1;
+
+      if ((buf = strdup(path)) == NULL)
+        return 1;
+
+      for (p = buf + 1; *p != '\0'; p++) {
+        if (*p != '/') continue;
+        *p = '\0';
+        mkdir(buf, 0777);
+        *p = '/';
+      }
+
+      free(buf);
+      return 0;
+    }
+
+    static int writeSBInit(FILE *sbinit) {
+      char *buf, *dataHome;
+      int homeLen, ret;
+
+      if ((dataHome = getXdgDataHome()) == NULL)
+        return 1;
+
+      homeLen = strlen(dataHome);
+      if ((buf = malloc(homeLen + 12)) == NULL)
+        goto errout;
+      strncpy(buf, dataHome, homeLen);
+      strncpy(buf + homeLen, "/starbound/", 12);
+      ret = makedirs(buf);
+      free(buf);
+      if (ret != 0) goto errout;
+
+      fputs("{\"assetDirectories\":[", sbinit);
+
+      if ((buf = mkJsonString(STARBOUND_ASSET_DIR)) == NULL)
+        goto errout;
+      fputs(buf, sbinit);
+      free(buf);
+
+      fputc(',', sbinit);
+
+      if ((buf = mkDataDir(dataHome, "starbound/mods/")) == NULL)
+        goto errout;
+      fputs(buf, sbinit);
+      free(buf);
+
+      fputs("],\"storageDirectory\":", sbinit);
+
+      if ((buf = mkDataDir(dataHome, "starbound/")) == NULL)
+        goto errout;
+      fputs(buf, sbinit);
+      free(buf);
+
+      fputs("}", sbinit);
+      free(dataHome);
+      return 0;
+    errout:
+      free(dataHome);
+      return 1;
+    }
+
+    static FILE *fakeSBInitHandle = NULL;
+
+    static int fakeSBInit(void) {
+      fakeSBInitHandle = tmpfile();
+      if (writeSBInit(fakeSBInitHandle) != 0) {
+        fclose(fakeSBInitHandle);
+        fakeSBInitHandle = NULL;
+        return -1;
+      }
+      rewind(fakeSBInitHandle);
+      return fileno(fakeSBInitHandle);
+    }
+
+    int open(const char *path, int flags, ...) {
+      va_list ap;
+      mode_t mode;
+      static int (*_open) (const char *, int, mode_t) = NULL;
+
+      if (_open == NULL)
+        _open = dlsym(RTLD_NEXT, "open");
+
+      va_start(ap, flags);
+      mode = va_arg(ap, mode_t);
+      va_end(ap);
+
+      if (strncmp(path, MAGIC, 14) != 0)
+        return _open(path, flags, mode);
+
+      return fakeSBInit();
+    }
+
+    int close(int fd) {
+      int ret;
+      static int (*_close) (int) = NULL;
+
+      if (_close == NULL)
+        _close = dlsym(RTLD_NEXT, "close");
+
+      if (fakeSBInitHandle != NULL) {
+        ret = fclose(fakeSBInitHandle);
+        fakeSBInitHandle = NULL;
+      } else {
+        ret = _close(fd);
+      }
+
+      return ret;
+    }
+
+    int SteamAPI_Init(void) {
+      return 0;
+    }
+  '';
+
+in stdenv.mkDerivation rec {
+  name = "starbound-${version}";
+  version = "1.3.1";
+
+  src = fetchHumbleBundle {
+    name = "starbound-linux-${version}.zip";
+    machineName = "starbound_linux";
+    md5 = "a6dfbb17feac9d54fbcb917d8ba179b4";
+  };
+
+  outputs = [ "out" "lib" "assets" ];
+
+  nativeBuildInputs = [ unzip ];
+  unpackCmd = "unzip -qq \"$curSrc\" starbound_client_linux/\\*";
+
+  buildPhase = with stdenv.lib; ''
+    cc -Werror -shared "${preloaderSource}" -o preload.so -ldl -fPIC \
+      -DSTARBOUND_ASSET_DIR="\"$assets\""
+    ${concatStrings (mapAttrsToList patchBinary binaryDeps)}
+    patchelf --remove-needed libsteam_api.so patched/starbound
+  '';
+
+  doCheck = true;
+
+  checkPhase = ''
+    checkFailed=
+    for i in linux/*; do
+      [ -f "$i" ] || continue
+
+      case "$(basename "$i")" in
+        sbinit.config) continue;;
+        *.s[ho]) continue;;
+      esac
+
+      [ ! -e "patched/$(basename "$i")" ] || continue
+
+      echo "Found missing binary $i from the upstream tree."
+      checkFailed=1
+    done
+
+    [ -z "$checkFailed" ]
+  '';
+
+  installPhase = ''
+    install -vsD preload.so "$lib/lib/libstarbound-preload.so"
+
+    ${stdenv.lib.concatStrings (stdenv.lib.mapAttrsToList (bin: attrs: let
+      basename = builtins.baseNameOf bin;
+    in ''
+      install -vD "patched/${basename}" "$out/bin/${attrs.name or basename}"
+    '') binaryDeps)}
+
+    install -m 0644 -vD "${desktopItem}/share/applications/starbound.desktop" \
+      "$out/share/applications/starbound.desktop"
+
+    cp -vr assets "$assets"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/swordsandsoldiers.nix b/pkgs/games/humblebundle/swordsandsoldiers.nix
new file mode 100644
index 00000000..2fd4fc4b
--- /dev/null
+++ b/pkgs/games/humblebundle/swordsandsoldiers.nix
@@ -0,0 +1,43 @@
+{ stdenv, fetchHumbleBundle, makeWrapper
+, SDL, mesa, zlib, openal, libvorbis, xorg, fontconfig, freetype, libogg
+}:
+
+stdenv.mkDerivation rec {
+  name = "swordsandsoldiers-${version}";
+  version = "20120325";
+
+  src = fetchHumbleBundle {
+    machineName = "swordsandsoldiers_android_and_pc_linux";
+    downloadName = "x86_64.tar.gz";
+    suffix = "tar.gz";
+    md5 = "5f0c9789fa053cbf6bac021a338245bb";
+  };
+
+  buildInputs = [ makeWrapper ];
+
+  patchPhase = let
+    rpath = stdenv.lib.makeLibraryPath [
+      SDL mesa zlib openal libvorbis fontconfig freetype stdenv.cc.cc libogg
+      xorg.libX11 xorg.libXft xorg.libXinerama xorg.libXext xorg.libXpm
+    ];
+  in ''
+    for i in SwordsAndSoldiers.bin SwordsAndSoldiersSetup.bin; do
+      patchelf \
+        --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+        --set-rpath "${rpath}" "$i"
+    done
+  '';
+
+  installPhase = ''
+    libexec="$out/libexec/swordsandsoldiers"
+    install -vD SwordsAndSoldiers.bin "$libexec/swordsandsoldiers"
+    install -vD SwordsAndSoldiersSetup.bin "$libexec/setup"
+    mv Data "$libexec/"
+
+    mkdir -p "$out/bin"
+    ln -s "$libexec/swordsandsoldiers" "$out/bin/swordsandsoldiers"
+    ln -s "$libexec/setup" "$out/bin/swordsandsoldiers-setup"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/games/humblebundle/unepic.nix b/pkgs/games/humblebundle/unepic.nix
new file mode 100644
index 00000000..cc4099f5
--- /dev/null
+++ b/pkgs/games/humblebundle/unepic.nix
@@ -0,0 +1,44 @@
+{ stdenv, fetchHumbleBundle, unzip, makeWrapper, SDL2, SDL2_mixer, zlib }:
+
+let
+  version = "1.50.5";
+  versionName = "15005";
+  arch = { 
+    "i686-linux" = "32";
+    "x86_64-linux" = "64";
+  }.${stdenv.system};
+in stdenv.mkDerivation rec {
+  name = "unepic-${version}";
+
+  src = fetchHumbleBundle {
+    name = "unepic-15005.run";
+    machineName = "unepic_linux";
+    downloadName = ".run";
+    md5 = "940824c4de6e48522845f63423e87783";
+  };
+
+  phases = [ "installPhase" ];
+
+  buildInputs = [ unzip makeWrapper ];
+
+  installPhase = let
+    rpath = stdenv.lib.makeLibraryPath [ SDL2 SDL2_mixer zlib stdenv.cc.cc ];
+  in ''
+    dest="$out/opt/games/unepic"
+    exe="$dest/unepic${arch}"
+
+    mkdir -p "$out/opt/games"
+    unzip "$src" "data/*" -d "$out/opt/games" || [ "$?" -eq 1 ]
+    mv "$out/opt/games/data" "$dest"
+    rm -r "$dest"/lib*
+
+    # Patch $exe acccording to arch.
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${rpath}" "$exe"
+
+    mkdir -p "$out/bin"
+
+    makeWrapper "$exe" "$out/bin/unepic" --run "cd '$dest'"
+  '';
+}
diff --git a/pkgs/games/steam/default.nix b/pkgs/games/steam/default.nix
new file mode 100644
index 00000000..03e6b180
--- /dev/null
+++ b/pkgs/games/steam/default.nix
@@ -0,0 +1,38 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.steam;
+
+  self = rec {
+    callPackage = pkgs.lib.callPackageWith (pkgs // self);
+
+    fetchSteam = callPackage ./fetchsteam {
+      inherit (config.steam) username password;
+    };
+
+    starbound = callPackage ./starbound.nix { flavor = "stable"; };
+    starbound-unstable = callPackage ./starbound.nix { flavor = "unstable"; };
+  };
+in with lib; {
+  options.steam = {
+    username = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        User name for your Steam account.
+      '';
+    };
+
+    password = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Password for your Steam account.
+      '';
+    };
+  };
+
+  config.packages = {
+    steam = mkIf (cfg.username != null && cfg.password != null) self;
+  };
+}
diff --git a/pkgs/games/steam/fetchsteam/default.nix b/pkgs/games/steam/fetchsteam/default.nix
new file mode 100644
index 00000000..5c1faf55
--- /dev/null
+++ b/pkgs/games/steam/fetchsteam/default.nix
@@ -0,0 +1,92 @@
+{ stdenv, runCommand, writeText, fetchFromGitHub, buildDotnetPackage
+, username, password
+}:
+
+{ name, appId, depotId, manifestId, branch ? null, sha256, fileList ? [] }:
+
+let
+  protobuf-net = buildDotnetPackage rec {
+    baseName = "protobuf-net";
+    version = "2.0.0.668";
+
+    src = fetchFromGitHub {
+      owner = "mgravell";
+      repo = "protobuf-net";
+      rev = "r668";
+      sha256 = "1060pihqkbr9pd2z6m01d6fsbc9nj56m6y5a0pch9mqdmviv4896";
+    };
+
+    sourceRoot = "${src.name}/${baseName}";
+  };
+
+  SteamKit2 = buildDotnetPackage rec {
+    baseName = "SteamKit2";
+    version = "1.6.4";
+
+    src = fetchFromGitHub {
+      owner = "SteamRE";
+      repo = "SteamKit";
+      rev = "SteamKit_${version}";
+      sha256 = "17d7wi2f396qhp4w9sf37lazvsaqws8x071hfis9gv5llv6s7q46";
+    };
+
+    buildInputs = [ protobuf-net ];
+
+    xBuildFiles = [ "SteamKit2/SteamKit2.sln" ];
+    outputFiles = [ "SteamKit2/SteamKit2/bin/Release/*" ];
+  };
+
+  DepotDownloader = buildDotnetPackage rec {
+    baseName = "DepotDownloader";
+    version = "2.1.1git20160207";
+
+    src = fetchFromGitHub {
+      owner = "SteamRE";
+      repo = baseName;
+      rev = "5fa6621d9f9448fcd20c974b427a8bd2cb044cb4";
+      sha256 = "0vb566d7x1scd96c8ybq6gdbc2cv5jjq453ld458qcvfy587amfn";
+    };
+
+    patches = [ ./downloader.patch ];
+
+    postPatch = ''
+      sed -i \
+        -e 's/\(<[Rr]eference *[Ii]nclude="[^", ]\+\)[^"]*/\1/g' \
+        -e 's,<[Ss]pecific[Vv]ersion>[Tt]rue</[Ss]pecific[Vv]ersion>,,g' \
+        DepotDownloader/DepotDownloader.csproj
+      sed -i -e 's/ version="[^"]*"//g' DepotDownloader/packages.config
+    '';
+
+    buildInputs = [ SteamKit2 protobuf-net ];
+
+    outputFiles = [ "${baseName}/bin/Release/*" ];
+
+    # UUUGLY, but I don't want to spend a week trying to get this working
+    # without that nasty wrapper.
+    makeWrapperArgs = let
+      mkMono = name: path: "${path}/lib/dotnet/${name}";
+      paths = stdenv.lib.mapAttrsToList mkMono {
+        inherit SteamKit2 protobuf-net;
+      };
+      monoPath = stdenv.lib.concatStringsSep ":" paths;
+    in [ "--prefix MONO_PATH : \"${monoPath}\"" ];
+  };
+
+  fileListFile = let
+    content = stdenv.lib.concatStringsSep "\n" fileList;
+  in writeText "steam-file-list-${name}.txt" content;
+
+in with stdenv.lib; runCommand "${name}-src" {
+  buildInputs = [ DepotDownloader ];
+  inherit username password appId depotId manifestId;
+  outputHashAlgo = "sha256";
+  outputHash = sha256;
+  outputHashMode = "recursive";
+} ''
+  depotdownloader -app "$appId" -depot "$depotId" -manifest "$manifestId" \
+    ${optionalString (fileList != []) "-filelist \"${fileListFile}\""} \
+    ${optionalString (branch != null) "-branch \"${branch}\""} \
+    -username "$username" -password "$password" -dir "$out"
+  rm -r "$out/.DepotDownloader"
+  rm "$out/_steam_depot_manifest_$depotId.csv"
+''
diff --git a/pkgs/games/steam/fetchsteam/downloader.patch b/pkgs/games/steam/fetchsteam/downloader.patch
new file mode 100644
index 00000000..72e5c473
--- /dev/null
+++ b/pkgs/games/steam/fetchsteam/downloader.patch
@@ -0,0 +1,34 @@
+diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs
+index 21c317e..81f2a93 100644
+--- a/DepotDownloader/ContentDownloader.cs
++++ b/DepotDownloader/ContentDownloader.cs
+@@ -34,7 +34,7 @@ namespace DepotDownloader
+             public string installDir { get; private set; }
+             public string contentName { get; private set; }
+ 
+-            public ulong manifestId { get; private set; }
++            public ulong manifestId { get; set; }
+             public byte[] depotKey;
+ 
+             public DepotDownloadInfo(uint depotid, ulong manifestId, string installDir, string contentName)
+@@ -198,9 +198,6 @@ namespace DepotDownloader
+ 
+         static ulong GetSteam3DepotManifest(uint depotId, uint appId, string branch)
+         {
+-            if (Config.ManifestId != INVALID_MANIFEST_ID)
+-                return Config.ManifestId;
+-
+             KeyValue depots = GetSteam3AppSection(appId, EAppInfoSection.Depots);
+             KeyValue depotChild = depots[depotId.ToString()];
+ 
+@@ -583,6 +580,10 @@ namespace DepotDownloader
+                 ConfigStore.TheConfig.LastManifests[depot.id] = INVALID_MANIFEST_ID;
+                 ConfigStore.Save();
+ 
++                Console.WriteLine("Latest manifest ID is {0}.", depot.manifestId);
++                if (Config.ManifestId != INVALID_MANIFEST_ID)
++                    depot.manifestId = Config.ManifestId;
++
+                 if (lastManifestId != INVALID_MANIFEST_ID)
+                 {
+                     var oldManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", lastManifestId));
diff --git a/pkgs/games/steam/starbound.nix b/pkgs/games/steam/starbound.nix
new file mode 100644
index 00000000..25272169
--- /dev/null
+++ b/pkgs/games/steam/starbound.nix
@@ -0,0 +1,207 @@
+{ stdenv, fetchSteam, fetchurl, writeText, SDL, mesa, jq, makeDesktopItem
+, flavor ? "stable"
+}:
+
+let
+  renameAttrs = f: let
+    rename = name: value: {
+      name = f name;
+      inherit value;
+    };
+  in stdenv.lib.mapAttrs' rename;
+
+  darwinize = renameAttrs (bin: "Starbound.app/Contents/MacOS/${bin}");
+  winize = renameAttrs (bin: "${bin}.exe");
+
+  mkOsBinaryDeps = with stdenv.lib;
+    if stdenv.system == "x86_64-darwin" then darwinize
+    else if elem stdenv.system [ "i686-cygwin" "x86_64-cygwin" ] then winize
+    else id;
+
+  binaryDeps = mkOsBinaryDeps {
+    starbound.deps = [ SDL mesa ];
+    starbound.hasBootconfigArg = true;
+    starbound_server.name = "starbound-server";
+    starbound_server.hasBootconfigArg = true;
+    asset_packer.name = "starbound-asset-packer";
+    asset_unpacker.name = "starbound-asset-unpacker";
+    dump_versioned_json.name = "starbound-dump-versioned-json";
+    make_versioned_json.name = "starbound-make-versioned-json";
+    planet_mapgen.name = "starbound-planet-mapgen";
+  };
+
+  binpath = if stdenv.system == "x86_64-linux" then "linux64"
+            else if stdenv.system == "i686-linux" then "linux32"
+            else if stdenv.system == "x86_64-darwin" then "osx"
+            else if stdenv.system == "i686-cygwin" then "win32"
+            else if stdenv.system == "x86_64-cygwin" then "win64"
+            else throw "Unsupported system ${stdenv.system} for Starbound";
+
+  throwUnsupported = throw "Unsupported flavor `${flavor}', use either "
+                         + "`stable' or `unstable'.";
+
+  upstreamInfo = if flavor == "stable" then {
+    name = "starbound";
+    version = "20151216";
+    appId = 211820;
+    depotId = 211821;
+    manifestId = 1842730272313189605;
+    sha256 = "0qppfn56c778wsg38hi6sxgi3rl9nv72h9rmmxybi1vzpf3p49py";
+  } else if flavor == "unstable" then {
+    name = "starbound-unstable";
+    version = "20160223";
+    appId = 367540;
+    depotId = 367541;
+    manifestId = 6970641909803280413;
+    sha256 = "0qppfn56c778wsg38hi6sxgi3rl9nv72h9rmmxybi1vzpf3p49py";
+  } else throwUnsupported;
+
+  upstream = fetchSteam {
+    inherit (upstreamInfo) name appId depotId manifestId sha256;
+    fileList = [
+      "^(?:assets|tiled)/"
+      ( "^${binpath}(?:/Starbound\\.app/Contents/MacOS)?"
+      + "/(?:[a-zA-Z0-9_-]+(?:\\.exe)?|sbboot\\.config)$")
+    ];
+  };
+
+  staticBootOverrides = writeText "bootconfig.overrides" (builtins.toJSON {
+    assetSources = [
+      "${upstream}/assets/packed.pak"
+      "${upstream}/assets/user"
+    ];
+  });
+
+  bootOverrides = {
+    storageDirectory = "$XDG_DATA_HOME/${settingsDir}/";
+    modSource = "$XDG_DATA_HOME/${settingsDir}/mods/";
+  };
+
+  settingsDir =
+    if flavor == "stable" then "starbound"
+    else if flavor == "unstable" then "starbound-unstable"
+    else throwUnsupported;
+
+  mkProg = bin: attrs: let
+    basename = builtins.baseNameOf bin;
+
+    hasBootconfigArg = attrs.hasBootconfigArg or false;
+
+    bootconfigArgs = with stdenv.lib; let
+      mkArg = opt: val: "-${opt} \"${val}\"";
+    in " " + (concatStringsSep " " (mapAttrsToList mkArg {
+      bootconfig = "$XDG_DATA_HOME/${settingsDir}/sbboot.config";
+    }));
+
+    wrapper = writeText "starbound-wrapper.sh" ''
+      #!${stdenv.shell} -e
+      [ -n "$XDG_DATA_HOME" ] || XDG_DATA_HOME="$HOME/.local/share"
+
+      mkdir -p "${bootOverrides.storageDirectory}" \
+               "${bootOverrides.modSource}"
+
+      "${jq}/bin/jq" -s '.[0] * .[1]' "@out@/etc/sbboot.config" - \
+        > "$XDG_DATA_HOME/${settingsDir}/sbboot.config" \
+      <<BOOTCONFIG_OVERRIDES
+      ${builtins.toJSON bootOverrides}
+      BOOTCONFIG_OVERRIDES
+
+      ${if hasBootconfigArg then ''
+        # This is needed because Starbound aborts if
+        # a command line argument is specified twice.
+        hasBootconfigArg() {
+          while [ $# -gt 0 ]; do
+            if [ "x''${1#-}" != "x$1" ]; then
+              case "''${1#-}" in
+                bootconfig) return 0;;
+                # Arguments that expect a parameter
+                loglevel|logfile|configfile|setconfig) shift;;
+              esac
+            fi
+            shift
+          done
+          return 1
+        }
+
+        if hasBootconfigArg "$@"; then
+          exec "@out@/libexec/starbound/${basename}" "$@"
+        else
+          exec "@out@/libexec/starbound/${basename}"${bootconfigArgs} "$@"
+        fi
+      '' else ''
+        exec "@out@/libexec/starbound/${basename}" "$@"
+      ''}
+    '';
+
+  in ''
+    install -vD "patched/${basename}" "$out/libexec/starbound/${basename}"
+    substituteAll "${wrapper}" "$out/bin/${attrs.name or basename}"
+    chmod +x "$out/bin/${attrs.name or basename}"
+  '';
+
+  desktopItem = makeDesktopItem {
+    name = "starbound";
+    exec = "starbound";
+    icon = fetchurl {
+      url = "http://i1305.photobucket.com/albums/s544/ClockworkBarber/"
+          + "logo_zps64c4860d.png";
+      sha256 = "11fiiy0vcxzix1j81732cjh16wi48k4vag040vmbhad50ps3mg0q";
+    };
+    comment = "An extraterrestrial sandbox adventure game";
+    desktopName = "Starbound";
+    genericName = "starbound";
+    categories = "Game;";
+  };
+
+  patchBinary = bin: attrs: ''
+    mkdir -p "patched/$(dirname "${bin}")"
+    cp -t "patched/$(dirname "${bin}")" "$upstream/$binpath/${bin}"
+    chmod +x "patched/$(basename "${bin}")"
+    patchelf \
+      --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+      --set-rpath "${stdenv.lib.makeLibraryPath (attrs.deps or [])}" \
+      "patched/$(basename "${bin}")"
+    if ldd "patched/$(basename "${bin}")" | grep -F 'not found'; then
+      exit 1;
+    fi
+  '';
+
+in stdenv.mkDerivation {
+  name = "${upstreamInfo.name}-${upstreamInfo.version}";
+  inherit (upstreamInfo) version;
+
+  unpackPhase = ":";
+
+  inherit binpath upstream;
+
+  buildPhase = with stdenv.lib;
+    concatStrings (mapAttrsToList patchBinary binaryDeps);
+
+  doCheck = true;
+
+  checkPhase = ''
+    checkFailed=
+    for i in "$upstream/$binpath"/*; do
+      [ -f "$i" ] || continue
+      [ "$(basename "$i")" != sbboot.config ] || continue
+      [ "$(basename "$i")" != launcher ] || continue
+      [ ! -e "patched/$(basename "$i")" ] || continue
+
+      echo "Found missing binary $i from the upstream tree."
+      checkFailed=1
+    done
+    [ -z "$checkFailed" ]
+  '';
+
+  installPhase = ''
+    mkdir -p "$out/bin" "$out/etc"
+    sed -e 's,//.*,,' "${upstream}/${binpath}/sbboot.config" \
+      | "${jq}/bin/jq" -s '.[0] * .[1]' - "${staticBootOverrides}" \
+      > "$out/etc/sbboot.config"
+    ${stdenv.lib.concatStrings (stdenv.lib.mapAttrsToList mkProg binaryDeps)}
+    install -m 0644 -vD "${desktopItem}/share/applications/starbound.desktop" \
+      "$out/share/applications/starbound.desktop"
+  '';
+
+  dontStrip = true;
+}
diff --git a/pkgs/lib/call-package-scope.nix b/pkgs/lib/call-package-scope.nix
new file mode 100644
index 00000000..75c19faf
--- /dev/null
+++ b/pkgs/lib/call-package-scope.nix
@@ -0,0 +1,25 @@
+{ pkgs, pkgsi686Linux }:
+
+fn: let
+  inherit (builtins) isFunction intersectAttrs functionArgs;
+
+  f = if isFunction fn then fn else import fn;
+
+  autoArgs = pkgs // {
+    callPackage = pkgs.lib.callPackageWith (pkgs // super);
+    callPackage_i686 = pkgs.lib.callPackageWith (pkgsi686Linux // super);
+  };
+  args = intersectAttrs (functionArgs f) autoArgs;
+
+  mkOverridable = overrideFun: origArgs: let
+    superSet = overrideFun origArgs;
+    overrideWith = newArgs: let
+      overridden = if isFunction newArgs then newArgs origArgs else newArgs;
+    in origArgs // overridden;
+  in superSet // {
+    override = newArgs: mkOverridable overrideFun (overrideWith newArgs);
+  };
+
+  super = mkOverridable f args;
+
+in pkgs.recurseIntoAttrs super
diff --git a/pkgs/list-gamecontrollers/default.nix b/pkgs/list-gamecontrollers/default.nix
new file mode 100644
index 00000000..c207d2eb
--- /dev/null
+++ b/pkgs/list-gamecontrollers/default.nix
@@ -0,0 +1,10 @@
+{ runCommand, pkgconfig, SDL2 }:
+
+runCommand "list-gamecontrollers" {
+  buildInputs = [ pkgconfig SDL2 ];
+} ''
+  mkdir -p "$out/bin"
+  gcc -Werror "${./list-gc.c}" \
+    $(pkg-config --libs --cflags sdl2) \
+    -o "$out/bin/list-gamecontrollers"
+''
diff --git a/pkgs/list-gamecontrollers/list-gc.c b/pkgs/list-gamecontrollers/list-gc.c
new file mode 100644
index 00000000..f40b7da6
--- /dev/null
+++ b/pkgs/list-gamecontrollers/list-gc.c
@@ -0,0 +1,31 @@
+#include <SDL.h>
+
+void dump_guid(SDL_Joystick *js) {
+    SDL_JoystickGUID guid;
+    const char *name;
+    char guidstr[33];
+
+    guid = SDL_JoystickGetGUID(js);
+    name = SDL_JoystickName(js);
+    SDL_JoystickGetGUIDString(guid, guidstr, sizeof(guidstr));
+
+    printf("%s: %s\n", name, guidstr);
+}
+
+int main()
+{
+    int i;
+    SDL_Joystick *js;
+
+    SDL_Init(SDL_INIT_JOYSTICK);
+    atexit(SDL_Quit);
+
+    for (i = 0; i < SDL_NumJoysticks(); ++i) {
+        if ((js = SDL_JoystickOpen(i)) != NULL) {
+            dump_guid(js);
+            SDL_JoystickClose(js);
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/pkgs/openlab/default.nix b/pkgs/openlab/default.nix
new file mode 100644
index 00000000..328e88ed
--- /dev/null
+++ b/pkgs/openlab/default.nix
@@ -0,0 +1,6 @@
+{ callPackage, haskell }:
+
+{
+  gitit = callPackage ./gitit { hlib = haskell.lib; };
+  stackenblocken = callPackage ./stackenblocken {};
+}
diff --git a/pkgs/openlab/gitit/default.nix b/pkgs/openlab/gitit/default.nix
new file mode 100644
index 00000000..dca2822a
--- /dev/null
+++ b/pkgs/openlab/gitit/default.nix
@@ -0,0 +1,18 @@
+{ hlib, haskellPackages, fetchFromGitHub }:
+
+let hp = haskellPackages.override {
+  overrides = (self: super: {
+    gitit = (hlib.overrideCabal super.gitit (drv: rec {
+      src = fetchFromGitHub {
+        owner = "openlab-aux";
+        repo = "gitit";
+        rev = "3da7c841f9382d0c62242a1b718511acec97e9f7";
+        sha256 = "0qhkbvm4ixav4nln3m9845w9m3gzfq5xh4nxp2c9qj4w9p79if7z";
+      };
+      broken = true;
+      platforms = [ "x86_64-linux" ];
+      hydraPlatforms = platforms;
+    }));
+  });
+};
+in hp.gitit
diff --git a/pkgs/openlab/stackenblocken/default.nix b/pkgs/openlab/stackenblocken/default.nix
new file mode 100644
index 00000000..8049c655
--- /dev/null
+++ b/pkgs/openlab/stackenblocken/default.nix
@@ -0,0 +1,86 @@
+{ lib, fetchFromGitHub, writeScriptBin, curl, bash, gawk
+, haskellPackages, mpg321
+, volumePercent ? 50 }:
+
+let
+  repo = fetchFromGitHub {
+    owner = "openlab-aux";
+    repo = "stackenblocken";
+    rev = "labpingbot";
+    sha256 = "1x319sbkk8hl3lad2zapkdv6ihqqsl8f5l0a2n9fvppcm5c7pz0d";
+ };
+
+ bot = haskellPackages.callPackage "${repo}/stackenblocken.nix" {};
+ jingle = "${repo}/stackenblocken_jingle.mp3";
+
+ script = ''
+    #!${lib.getBin bash}/bin/bash
+    percent=10
+    no_stackenblocken="no STACKENBLOCKEN today"
+    tmpd=$(mktemp -d)
+
+    # kill everything on SIGINT
+    trap exit SIGINT
+    # also running background processes
+    trap "kill 0" EXIT
+
+    function icsfile {
+      ${lib.getBin gawk}/bin/awk -v date=''${1:-nodate} '
+        /BEGIN:VEVENT/ { cache = 1; }
+        /DTSTART:/ {
+          if( index( $0, date ) )
+            printf( "%s", cached_lines );
+          else
+            drop = 1;
+          cached_lines = "";
+          cache = 0;
+        }
+        cache  {
+          cached_lines = cached_lines $0 "\n";
+          next;
+        };
+        !drop { print; }
+        /END:VEVENT/ { drop = 0; }
+      '
+    }
+
+    function check_events {
+      ${lib.getBin curl}/bin/curl -s https://openlab-augsburg.de/veranstaltungen/events.ics \
+        | icsfile `date --utc +%Y%m%d` \
+        > "$tmpd/events-today"
+
+      # filter out events that have the no-stackenblocken tag
+      # and skip it on those days
+      if <"$tmpd/events-today" grep -q "CATEGORIES.*no-stackenblocken"; then
+        events=$(<$tmpd/events-today sed -ne 's/SUMMARY:\(.*\)$/\1/p')
+        echo "$no_stackenblocken because of event(s):"
+        echo "$events"
+        exit 0
+      fi
+    }
+
+    function check_random {
+      rnumber=$RANDOM
+      ((rnumber %= 100))
+      # lt for percent (numbers begin from 0)
+      if [ $rnumber -lt $percent ]; then
+        echo "$no_stackenblocken because lucks says so! ($percent% chance)"
+        exit 0
+      fi
+    }
+
+    check_events
+    check_random
+
+    for i in $(seq 2); do
+      echo "starting .labping bot"
+      ${lib.getBin bot}/bin/stackenblocken &
+      echo "DOING STACKENBLOCKEN"
+      ${lib.getBin mpg321}/bin/mpg321 --gain ${toString volumePercent} -q ${jingle}
+    done
+  '';
+
+
+in
+  writeScriptBin "stackenblocken" script
+
diff --git a/pkgs/profpatsch/backlight/backlight.py b/pkgs/profpatsch/backlight/backlight.py
new file mode 100755
index 00000000..d580260a
--- /dev/null
+++ b/pkgs/profpatsch/backlight/backlight.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# xbacklight uses a linear percentage,
+# but the backlight intensity is actually logarithmic *facepalm*.
+# So we read the current "percentage" given by xbacklight
+# and calculate the next step, base 2.
+
+import subprocess as sub
+import math
+import sys
+
+xbacklight = "xbacklight"
+
+def usage():
+    print("usage: backlight [inc|dec]", file=sys.stderr)
+    sys.exit(1)
+
+# read current value
+current_backlight = float(sub.run(xbacklight, stdout=sub.PIPE).stdout.strip())
+# find the actual value, base 2
+current_val = round(math.sqrt(current_backlight))
+
+if len(sys.argv) == 1: usage()
+else:
+    mode = sys.argv[1]
+
+# modify actual value
+if mode == "inc":
+    new_val = current_val + 1
+elif mode == "dec":
+    new_val = current_val - 1
+else:
+    usage()
+
+# clamp value
+new_backlight = min(10, max(0, new_val))
+
+# pow it again and set
+sub.run([xbacklight, "-set", str(math.pow(new_backlight, 2))])
diff --git a/pkgs/profpatsch/backlight/default.nix b/pkgs/profpatsch/backlight/default.nix
new file mode 100644
index 00000000..d2f40be0
--- /dev/null
+++ b/pkgs/profpatsch/backlight/default.nix
@@ -0,0 +1,17 @@
+{ stdenv, python3, xbacklight}:
+
+stdenv.mkDerivation rec {
+  name = "backlight";
+
+  src = ./backlight.py;
+  phases = [ "installPhase" "fixupPhase" ];
+
+  buildInputs = [ python3 ];
+
+  installPhase = ''
+    install -D ${src} $out/bin/backlight
+    substituteInPlace $out/bin/backlight \
+      --replace '"xbacklight"' '"${xbacklight}/bin/xbacklight"'
+  '';
+
+}
diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix
new file mode 100644
index 00000000..62f96e27
--- /dev/null
+++ b/pkgs/profpatsch/default.nix
@@ -0,0 +1,38 @@
+{ pkgs, callPackage, haskellPackages, jmtpfs, libmtp, droopy, fetchFromGitHub }:
+
+{
+  display-infos = callPackage ./display-infos {};
+  nman = callPackage ./nman {};
+  warpspeed = callPackage ./warpspeed {
+    inherit (haskellPackages) ghcWithPackages;
+  };
+  show-qr-code = callPackage ./show-qr-code { };
+
+  jmtpfs = jmtpfs.override {
+    libmtp = libmtp.overrideDerivation (old: {
+      patches = old.patches or [] ++ [
+        ./patches/mtp-jolla.patch
+      ];
+    });
+  };
+
+  backlight = callPackage ./backlight { inherit (pkgs.xorg) xbacklight; };
+
+  # patched version of droopy, with javascript user-enhancement
+  droopy = droopy.overrideDerivation (old: {
+    src = fetchFromGitHub {
+      owner = "Profpatsch";
+      repo = "Droopy";
+      rev = "dc63d0ac9cecd74cdff84ab9ea2a5849d6953e8a";
+      sha256 = "09sms524wrnpdkhnpv9f2qbq30s8h02ljiv934g0dvmxy8571ph7";
+    };
+    installPhase = old.installPhase or "" + ''
+      mkdir -p $out/share/droopy
+      cp -r $src/static $out/share/droopy
+    '';
+    makeWrapperArgs = old.makeWrapperArgs or [] ++ [
+      "--set DROOPY_STATIC \"$out/share/droopy/static\""
+    ];
+
+  });
+}
diff --git a/pkgs/profpatsch/display-infos/default.nix b/pkgs/profpatsch/display-infos/default.nix
new file mode 100644
index 00000000..d213241c
--- /dev/null
+++ b/pkgs/profpatsch/display-infos/default.nix
@@ -0,0 +1,38 @@
+{ lib, runCommand, python3, libnotify }:
+
+let
+  name = "display-infos-0.1.0";
+  script = builtins.toFile (name + "-script") ''
+    #!@python3@
+
+    import sys
+    import glob
+    import subprocess as sub
+    import os.path as path
+    import statistics as st
+
+    full = 0
+    now  = 0
+    for bat in glob.iglob("/sys/class/power_supply/BAT*"):
+        def readint(fn):
+            with open(fn, 'r') as f:
+                return int(f.read())
+
+        full += readint(path.join(bat, "energy_full"))
+        now  += readint(path.join(bat, "energy_now" ))
+
+    bat = round( now/full, 2 )
+    date = sub.run(["date", "+%d.%m. %a %T"], stdout=sub.PIPE).stdout.strip().decode()
+    notify = "BAT: {}% | {}".format(int(bat*100), date)
+    sub.run(["@notify-send@", notify])
+  '';
+
+in
+  with lib; runCommand "display-infos" {
+    meta.description = "Script to display time & battery";
+  } ''
+    substitute ${script} script \
+      --replace "@python3@" "${getBin python3}/bin/python3" \
+      --replace "@notify-send@" "${getBin libnotify}/bin/notify-send"
+    install -D script $out/bin/display-infos
+  ''
diff --git a/pkgs/profpatsch/nman/default.nix b/pkgs/profpatsch/nman/default.nix
new file mode 100644
index 00000000..10e5417f
--- /dev/null
+++ b/pkgs/profpatsch/nman/default.nix
@@ -0,0 +1,12 @@
+{ lib, runCommandCC }:
+
+runCommandCC "nman" {
+  meta = with lib; {
+    description = "Invoke manpage in temporary nix-shell";
+    license = licenses.gpl3;
+  };
+} ''
+    cc -o nman ${./nman.c}
+    install -D nman $out/bin/nman
+''
+
diff --git a/pkgs/profpatsch/nman/nman.c b/pkgs/profpatsch/nman/nman.c
new file mode 100644
index 00000000..0e02ff3e
--- /dev/null
+++ b/pkgs/profpatsch/nman/nman.c
@@ -0,0 +1,80 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+const char* USAGE =
+	"nman drvAttr [section|page] [page]\n"
+	"\n"
+	"Open man pages in a temporary nix-shell.\n"
+	"1 If one argument is given, the drvAttr & page have the same name.\n"
+	"2 If two arguments are given and the second arg is\n"
+	"    a <number>) like 1, but in man section <number>\n"
+	"    a <page>  ) like 1, but open the page <page>\n"
+	"3 If three arguments are given, the order is <drvAttr> <sect> <page>\n";
+
+void execNixShell(char const* drvAttr, int manSection, const char* manPage) {
+	assert(manSection >= -1);
+	char* manCmd;
+	// holy debug printf
+	//printf("attr: %s, sect: %d, page: %s\n", drvAttr, manSection, manPage);
+	int ret;
+	if (-1 == manSection) {
+		ret = asprintf(&manCmd, "man %s", manPage);
+	} else {
+	 	ret = asprintf(&manCmd, "man %d %s", manSection, manPage);
+	}
+	assert(ret != -1);
+
+	if (-1 == execlp("nix-shell", "nix-shell", "-p", drvAttr, "--run", manCmd, NULL)) {
+		fprintf(stderr, "%s\n", strerror(errno));
+		exit(-1);
+	}
+	free(manCmd);
+}
+
+int main(int argc, char** argv) {
+	int manSection = -1;
+	char* drvAttr;
+	char* manPage;
+	if (argc >= 3) {
+		// man section or -1 if no man section
+		int i = strtol(argv[2], NULL, 10);
+		if (i > 1) {
+			manSection = i;
+		}
+	}
+	// the first argument is always a derivation attribute
+	drvAttr = argv[1];
+
+	// nman does a convenient selection based on the number
+	// of attributes given, in order to do what the user means.
+	switch (argc) {
+	case 1:
+		fprintf(stderr, "%s", USAGE);
+		exit(-1);
+		break;
+	case 2:
+		// arg is package and drv attr
+		manPage = argv[1];
+		break;
+	case 3:
+		if (manSection == -1) {
+			// like 2:, but arg 2 is package
+			manPage = argv[2];
+		} else {
+			// man section given, page is arg 1
+			manPage = argv[1];
+		}
+		break;
+	case 4:
+		// arg 2 is manSection, arg 3 is package
+		manPage = argv[3];
+		break;
+	}
+
+	execNixShell(drvAttr, manSection, manPage);
+}
diff --git a/pkgs/profpatsch/patches/mtp-jolla.patch b/pkgs/profpatsch/patches/mtp-jolla.patch
new file mode 100644
index 00000000..e893d3b7
--- /dev/null
+++ b/pkgs/profpatsch/patches/mtp-jolla.patch
@@ -0,0 +1,34 @@
+diff --git a/src/music-players.h b/src/music-players.h
+index a44460a..d05f52f 100644
+--- a/src/music-players.h
++++ b/src/music-players.h
+@@ -3102,6 +3102,16 @@
+   { "Jolla", 0x2931, "Sailfish (ID1)", 0x0a01,
+       DEVICE_FLAGS_ANDROID_BUGS },
+ 
++  /* In update 4 the order of devices was changed for
++     better OS X / Windows suport and another device-id
++     got assigned for the MTP */
++  { "Jolla", 0x2931, "Sailfish (ID2)", 0x0a05,
++      DEVICE_FLAGS_ANDROID_BUGS },
++
++  /* In a later version, the ID changed again. */
++  { "Jolla", 0x2931, "Sailfish (ID3)", 0x0a07,
++      DEVICE_FLAGS_ANDROID_BUGS },
++
+   /*
+    * TCL? Alcatel?
+    */
+@@ -3129,12 +3139,6 @@
+   { "DigiLand", 0x1f3a, "DL701Q", 0x0c02,
+       DEVICE_FLAGS_ANDROID_BUGS },
+ 
+-  /* In update 4 the order of devices was changed for
+-     better OS X / Windows suport and another device-id
+-     got assigned for the MTP */
+-  { "Jolla", 0x2931, "Sailfish (ID2)", 0x0a05,
+-      DEVICE_FLAGS_ANDROID_BUGS },
+-
+   /*
+    * bq
+    * https://sourceforge.net/p/libmtp/feature-requests/128/
diff --git a/pkgs/profpatsch/show-qr-code/default.nix b/pkgs/profpatsch/show-qr-code/default.nix
new file mode 100644
index 00000000..17d9847a
--- /dev/null
+++ b/pkgs/profpatsch/show-qr-code/default.nix
@@ -0,0 +1,28 @@
+{ stdenv, writeScriptBin, gtkdialog, qrencode }:
+
+let script = writeScriptBin "show-qr-code" ''
+  #!/bin/sh
+  TMP=$(mktemp)
+  ${qrencode}/bin/qrencode -s 8 -o "$TMP" -t PNG "$1"
+
+  export DIALOG='
+  <vbox>
+      <pixmap>
+          <input file>'$TMP'</input>
+      </pixmap>
+  </vbox>
+  '
+
+  ${gtkdialog}/bin/gtkdialog --program=DIALOG > /dev/null &
+
+  sleep 0.2
+
+  rm "$TMP"
+
+  '';
+
+in script // {
+  meta = {
+    description = "Show the given string as qr code in a gtk window";
+  };
+}
diff --git a/pkgs/profpatsch/warpspeed/default.nix b/pkgs/profpatsch/warpspeed/default.nix
new file mode 100644
index 00000000..a73169b5
--- /dev/null
+++ b/pkgs/profpatsch/warpspeed/default.nix
@@ -0,0 +1,34 @@
+{ lib, runCommand, ghcWithPackages }:
+
+let
+  name = "warpspeed-1.0";
+
+  script = builtins.toFile "${name}.hs" ''
+    {-# LANGUAGE OverloadedStrings #-}
+    module Main where
+
+    import Safe
+    import System.Environment (getArgs)
+    import System.Exit (die)
+    import Network.Wai
+    import Network.Wai.Middleware.Static
+    import Network.Wai.Handler.Warp
+    import Network.HTTP.Types.Status
+
+    main :: IO ()
+    main = do
+      args <- getArgs
+      port <- case headMay args >>= readMay of
+        Just p -> pure $ p
+        Nothing -> die "please specify a port"
+      runEnv port $ static $ \_ resp -> resp $ responseLBS notFound404 [] ""
+   '';
+
+   deps = hp: with hp; [ wai-middleware-static warp safe ];
+
+in runCommand name {
+  meta.description = "Trivial and very fast static HTTP file server";
+} ''
+  mkdir -p $out/bin
+  ${ghcWithPackages deps}/bin/ghc -O2 -Wall -o "$out/bin/warpspeed" ${script}
+''
diff --git a/pkgs/sternenseemann/default.nix b/pkgs/sternenseemann/default.nix
new file mode 100644
index 00000000..a7ea47d1
--- /dev/null
+++ b/pkgs/sternenseemann/default.nix
@@ -0,0 +1,6 @@
+{ haskellPackages, ocamlPackages_4_02 }:
+
+{
+  spacecookie = haskellPackages.callPackage ./spacecookie {};
+  logbook = ocamlPackages_4_02.callPackage ./logbook {};
+}
diff --git a/pkgs/sternenseemann/logbook/default.nix b/pkgs/sternenseemann/logbook/default.nix
new file mode 100644
index 00000000..dcfb700e
--- /dev/null
+++ b/pkgs/sternenseemann/logbook/default.nix
@@ -0,0 +1,25 @@
+{ stdenv, ocaml, topkg, ocamlbuild, findlib, ocaml_lwt
+, jingoo, ptime, angstrom, astring, opam, cow
+, fetchgit }:
+
+stdenv.mkDerivation rec {
+  version = "2017-02-18";
+  name = "ocaml${ocaml.version}-logbook-${version}";
+
+  src = fetchgit {
+    url    = "https://github.com/sternenseemann/logbook";
+    rev    = "518771882f3216f99570a5e4711a4870bb064366";
+    sha256 = "1h40xfzx61nyj1r2x7r35mj66fhjgfq1pvvbbr4hmi1mmyi38xsz";
+  };
+
+  buildInputs = [ ocaml findlib ocamlbuild topkg opam cow
+                  ocaml_lwt jingoo ptime angstrom astring
+                ];
+
+  inherit (topkg) buildPhase installPhase;
+  meta = with stdenv.lib; {
+    description = "A tool for personal log files";
+    platforms = ocaml.meta.platforms;
+    license = licenses.bsd3;
+  };
+}
diff --git a/pkgs/sternenseemann/spacecookie/default.nix b/pkgs/sternenseemann/spacecookie/default.nix
new file mode 100644
index 00000000..42ee21fa
--- /dev/null
+++ b/pkgs/sternenseemann/spacecookie/default.nix
@@ -0,0 +1,25 @@
+{ mkDerivation, aeson, attoparsec, base, bytestring, containers
+, directory, fetchgit, filepath, hxt-unicode, mtl, socket, stdenv
+, transformers, unix, fast-logger
+}:
+mkDerivation {
+  pname = "spacecookie";
+  version = "0.2.0.0";
+  src = fetchgit {
+    url = "https://github.com/sternenseemann/spacecookie";
+    sha256 = "1hsanzhxg29alc49rlfny778afn6xznjamnqd8m7a4ynj3iswg42";
+    rev = "39001d0f70891caab774376a48f61b91a66d9f30";
+  };
+  isLibrary = true;
+  isExecutable = true;
+  libraryHaskellDepends = [
+    attoparsec base bytestring containers directory filepath
+    hxt-unicode mtl socket transformers unix fast-logger
+  ];
+  executableHaskellDepends = [
+    aeson attoparsec base bytestring containers directory filepath mtl
+    transformers unix
+  ];
+  description = "gopher server daemon";
+  license = stdenv.lib.licenses.gpl3;
+}
diff --git a/release.nix b/release.nix
index 10c2a790..b71ad0c3 100644
--- a/release.nix
+++ b/release.nix
@@ -1,13 +1,198 @@
-### THIS FILE IS FOR HYDRA BUILD ONLY! YOU CAN IGNORE THIS FILE IF YOU ARE NOT USING A HYDRA!
+{ vuizvuiSrc ? null
+, nixpkgsSrc ? <nixpkgs>
+, supportedSystems ? [ "i686-linux" "x86_64-linux" ]
+}:
 
 let
-  supportedSystems = [ "x86_64-linux" ];
+  nixpkgsRevCount = nixpkgsSrc.revCount or 12345;
+  nixpkgsShortRev = nixpkgsSrc.shortRev or "abcdefg";
+  nixpkgsVersion = "pre${toString nixpkgsRevCount}.${nixpkgsShortRev}-vuizvui";
+
+  nixpkgs = nixpkgsSrc;
+
+  vuizvuiRevCount = vuizvuiSrc.revCount or 12345;
+  vuizvuiShortRev = vuizvuiSrc.shortRev or "abcdefg";
+  vuizvuiVersion = "pre${toString vuizvuiRevCount}.${vuizvuiShortRev}";
+
+  vuizvui = let
+    patchedVuizvui = (import nixpkgs {}).stdenv.mkDerivation {
+      name = "vuizvui-${vuizvuiVersion}";
+      inherit nixpkgsVersion;
+      src = vuizvuiSrc;
+      phases = [ "unpackPhase" "installPhase" ];
+      installPhase = ''
+        cp -r --no-preserve=ownership "${nixpkgs}/" nixpkgs
+        chmod -R u+w nixpkgs
+        echo -n "$nixpkgsVersion" > nixpkgs/.version-suffix
+        echo "echo '$nixpkgsVersion'" \
+          > nixpkgs/nixos/modules/installer/tools/get-version-suffix
+        echo -n ${nixpkgs.rev or nixpkgsShortRev} > nixpkgs/.git-revision
+        echo './nixpkgs' > nixpkgs-path.nix
+        cp -r . "$out"
+      '';
+    };
+  in if vuizvuiSrc == null then ./. else patchedVuizvui;
+
   system = "x86_64-linux";
+  pkgsUpstream = import nixpkgs { inherit system; };
+  root = import vuizvui { inherit system; };
+
+  mpath = if vuizvuiSrc == null then ./machines else "${vuizvui}/machines";
+  allMachines = import mpath;
+
+  allTests = with import ./lib; getVuizvuiTests ({
+    inherit system nixpkgs;
+    excludeVuizvuiGames = true;
+  } // pkgsUpstream.lib.optionalAttrs (vuizvuiSrc != null) {
+    vuizvuiTests = "${vuizvui}/tests";
+  });
+
+  pkgs = with pkgsUpstream.lib; let
+    noGames = flip removeAttrs [ "games" ];
+    releaseLib = import "${nixpkgs}/pkgs/top-level/release-lib.nix" {
+      inherit supportedSystems;
+      packageSet = attrs: noGames (import vuizvui attrs).pkgs.vuizvui;
+    };
+
+    packagePlatforms = mapAttrs (name: value: let
+      platforms = value.meta.hydraPlatforms or (value.meta.platforms or []);
+      isRecursive = value.recurseForDerivations or false
+                 || value.recurseForRelease or false;
+      result = if isDerivation value then platforms
+               else if isRecursive then packagePlatforms value
+               else [];
+      tried = builtins.tryEval result;
+    in if tried.success then tried.value else []);
+
+  in with releaseLib; mapTestOn (packagePlatforms releaseLib.pkgs);
+
+in with pkgsUpstream.lib; with builtins; {
+
+  machines = let
+    # We need to expose all the real builds within vuizvui.lazyPackages to make
+    # sure they don't get garbage collected on the Hydra instance.
+    wrapLazy = machine: pkgsUpstream.runCommand machine.build.name {
+      fakeRuntimeDeps = machine.eval.config.vuizvui.lazyPackages;
+      product = machine.build;
+    } ''
+      mkdir -p "$out/nix-support"
+      echo "$product" > "$out/nix-support/fake-runtime-dependencies"
+      for i in $fakeRuntimeDeps; do
+        echo "$i" >> "$out/nix-support/fake-runtime-dependencies"
+      done
+    '';
+  in mapAttrsRecursiveCond (m: !(m ? eval)) (const wrapLazy) allMachines;
+
+  isoImages = let
+    buildIso = attrs: let
+      name = attrs.iso.config.networking.hostName;
+      cond = attrs.iso.config.vuizvui.createISO;
+    in if !cond then {} else pkgsUpstream.runCommand "vuizvui-iso-${name}" {
+      meta.description = "Live CD/USB stick of ${name}";
+      iso = attrs.iso.config.system.build.isoImage;
+      passthru.config = attrs.iso.config;
+    } ''
+      mkdir -p "$out/nix-support"
+      echo "file iso" $iso/iso/*.iso* \
+        >> "$out/nix-support/hydra-build-products"
+    '';
+  in mapAttrsRecursiveCond (m: !(m ? iso)) (const buildIso) allMachines;
+
+  tests = let
+    machineList = collect (m: m ? eval) allMachines;
+    activatedTests = unique (concatMap (machine:
+      machine.eval.config.vuizvui.requiresTests
+    ) machineList);
+    mkTest = path: setAttrByPath path (getAttrFromPath path allTests);
+  in fold recursiveUpdate {} (map mkTest activatedTests) // {
+    inherit (allTests) vuizvui;
+  };
+
+  inherit pkgs;
+
+  channels = let
+    mkChannel = attrs: root.pkgs.vuizvui.mkChannel (rec {
+      name = "vuizvui-channel-${attrs.name or "generic"}-${vuizvuiVersion}";
+      src = vuizvui;
+      patchPhase = ''
+        touch .update-on-nixos-rebuild
+      '';
+    } // removeAttrs attrs [ "name" ]);
+
+    gatherTests = active: map (path: getAttrFromPath path allTests) active;
+
+  in {
+    generic = mkChannel {
+      constituents = concatMap (collect isDerivation) [
+        allTests.vuizvui pkgs
+      ];
+    };
+
+    machines = mapAttrsRecursiveCond (m: !(m ? eval)) (path: attrs: mkChannel {
+      name = "machine-${last path}";
+      constituents = singleton attrs.eval.config.system.build.toplevel
+                  ++ gatherTests attrs.eval.config.vuizvui.requiresTests;
+    }) allMachines;
+  };
+
+  manual = let
+    modules = import "${nixpkgs}/nixos/lib/eval-config.nix" {
+      modules = import "${vuizvui}/modules/module-list.nix";
+      check = false;
+      inherit system;
+    };
+
+    patchedDocbookXSL = overrideDerivation pkgsUpstream.docbook5_xsl (drv: {
+      # Don't chunk off <preface/>
+      postPatch = (drv.postPatch or "") + ''
+        sed -i -e '
+          /<xsl:when.*preface/d
+          /<xsl:for-each/s!|//d:preface \+!!g
+          /<xsl:variable/s!|[a-z]\+::d:preface\[1\] \+!!g
+        ' xhtml/chunk-common.xsl
+
+        sed -i -e '
+          /<xsl:when.*preface/,/<\/xsl:when>/d
+          /<xsl:template/s!|d:preface!!g
+        ' xhtml/chunk-code.xsl
+      '';
+    });
+
+    isVuizvui = opt: head (splitString "." opt.name) == "vuizvui";
+    filterDoc = filter (opt: isVuizvui opt && opt.visible && !opt.internal);
+    optionsXML = toXML (filterDoc (optionAttrSetToDocList modules.options));
+    optionsFile = toFile "options.xml" (unsafeDiscardStringContext optionsXML);
+  in pkgsUpstream.stdenv.mkDerivation {
+    name = "vuizvui-options";
+
+    buildInputs = singleton pkgsUpstream.libxslt;
+
+    xsltFlags = ''
+      --param section.autolabel 1
+      --param section.label.includes.component.label 1
+      --param html.stylesheet 'style.css'
+      --param xref.with.number.and.title 1
+      --param admon.style '''
+    '';
+
+    buildCommand = ''
+      cp -r "${./doc}" doc
+      chmod -R +w doc
+      xsltproc -o doc/options-db.xml \
+        "${nixpkgs}/nixos/doc/manual/options-to-docbook.xsl" \
+        ${optionsFile}
+
+      dest="$out/share/doc/vuizvui"
+      mkdir -p "$dest"
+
+      xsltproc -o "$dest/" $xsltFlags -nonet -xinclude \
+        ${patchedDocbookXSL}/xml/xsl/docbook/xhtml/chunk.xsl \
+        doc/index.xml
 
-in
+      cp "${nixpkgs}/nixos/doc/manual/style.css" "$dest/style.css"
 
-{
-  skunkworks = (import <nixpkgs/nixos> { configuration = ./entry-skunkworks.nix; }).system;
-  titan = (import <nixpkgs/nixos> { configuration = ./entry-titan.nix; }).system;
-  eris = (import <nixpkgs/nixos> { configuration = ./entry-eris.nix; }).system;
+      mkdir -p "$out/nix-support"
+      echo "doc manual $dest" > "$out/nix-support/hydra-build-products"
+    '';
+  };
 }
diff --git a/tests/default.nix b/tests/default.nix
new file mode 100644
index 00000000..5f7ae897
--- /dev/null
+++ b/tests/default.nix
@@ -0,0 +1,19 @@
+{ system ? builtins.currentSystem, ... }:
+
+let
+  callTest = path: import ./make-test.nix (import path) {
+    inherit system;
+  };
+
+in {
+  games = {
+    starbound = callTest ./games/starbound.nix;
+  };
+  programs = {
+    gnupg = callTest ./programs/gnupg;
+  };
+  richi235 = {
+    # Currently broken
+    #multipath-vpn = callTest ./richi235/multipath-vpn.nix;
+  };
+}
diff --git a/tests/games/starbound.nix b/tests/games/starbound.nix
new file mode 100644
index 00000000..5f545fe0
--- /dev/null
+++ b/tests/games/starbound.nix
@@ -0,0 +1,103 @@
+{ pkgs, ... }:
+
+let
+  xdo = { name, description, xdoScript }: let
+    xdoFile = pkgs.writeText "${name}.xdo" ''
+      search --onlyvisible --class starbound
+      windowfocus --sync
+      windowactivate --sync
+      ${xdoScript}
+    '';
+    escapeScreenshot = pkgs.lib.replaceStrings ["-"] ["_"];
+  in ''
+    $client->nest("${description}", sub {
+      $client->screenshot("before_${escapeScreenshot name}");
+      $client->succeed("${pkgs.xdotool}/bin/xdotool '${xdoFile}'");
+    });
+  '';
+
+  clickAt = name: x: y: xdo {
+    name = "click-${name}";
+    description = "clicking on ${name} (coords ${toString x} ${toString y})";
+    xdoScript = ''
+      mousemove --window %1 --sync ${toString x} ${toString y}
+      click --repeat 10 1
+    '';
+  };
+
+  typeText = name: text: xdo {
+    name = "type-${name}";
+    description = "typing `${text}' into Starbound";
+    xdoScript = ''
+      type --delay 200 '${text}'
+    '';
+  };
+
+in {
+  name = "starbound";
+
+  enableOCR = true;
+
+  nodes = {
+    server = {
+      vuizvui.services.starbound = {
+        enable = true;
+        # Use a different dataDir than the default to make
+        # sure everything is still working.
+        dataDir = "/var/lib/starbound-test";
+        users.alice.password = "secret";
+      };
+      virtualisation.memorySize = 2047;
+      networking.interfaces.eth1.ipAddress = "192.168.0.1";
+      networking.interfaces.eth1.prefixLength = 24;
+      networking.firewall.enable = false;
+    };
+
+    client = { pkgs, ... }: {
+      imports = [
+        "${import ../../nixpkgs-path.nix}/nixos/tests/common/x11.nix"
+      ];
+      virtualisation.memorySize = 2047;
+      environment.systemPackages = [
+        pkgs.vuizvui.games.humblebundle.starbound
+      ];
+      networking.interfaces.eth1.ipAddress = "192.168.0.2";
+      networking.interfaces.eth1.prefixLength = 24;
+      networking.firewall.enable = false;
+    };
+  };
+
+  testScript = ''
+    $server->waitForUnit("starbound.service");
+
+    $client->nest("waiting for client to start up", sub {
+      $client->waitForX;
+      $client->succeed("starbound >&2 &");
+      $client->waitForText(qr/options/i);
+    });
+
+    ${clickAt "join-game" 100 560}
+    $client->waitForText(qr/select/i);
+    ${clickAt "new-character" 460 220}
+    $client->waitForText(qr/randomise/i);
+    ${clickAt "create-character" 600 625}
+    $client->waitForText(qr/select/i);
+    ${clickAt "use-character" 460 220}
+    $client->waitForText(qr/ser[vu]er/i);
+
+    ${clickAt "server-address" 460 322}
+    ${typeText "server-address" "192.168.0.1"}
+
+    ${clickAt "server-account" 490 354}
+    ${typeText "server-account" "alice"}
+
+    ${clickAt "server-password" 490 386}
+    ${typeText "server-password" "secret"}
+
+    ${clickAt "join-server" 495 420}
+
+    $client->waitForText(qr/graduation/i);
+    $client->sleep(30);
+    $client->screenshot("client");
+  '';
+}
diff --git a/tests/make-test.nix b/tests/make-test.nix
new file mode 100644
index 00000000..d98ff87f
--- /dev/null
+++ b/tests/make-test.nix
@@ -0,0 +1,30 @@
+f: { system ? builtins.currentSystem, ... } @ args: let
+  nixpkgsPath = import ../nixpkgs-path.nix;
+
+  lib = import "${nixpkgsPath}/lib";
+
+  testLib = import "${nixpkgsPath}/nixos/lib/testing.nix" {
+    inherit system;
+  };
+
+  pkgs = import nixpkgsPath { inherit system; };
+
+  testArgs = if builtins.isFunction f then f (args // {
+    pkgs = pkgs // {
+      vuizvui = import ../pkgs { inherit pkgs; };
+    };
+  }) else f;
+
+  nodes = testArgs.nodes or (if testArgs ? machine then {
+    inherit (testArgs) machine;
+  } else {});
+
+  injectCommon = name: conf: {
+    imports = [ conf ] ++ import ../modules/module-list.nix;
+  };
+
+  testArgsWithCommon = removeAttrs testArgs [ "machine" ] // {
+    nodes = lib.mapAttrs injectCommon nodes;
+  };
+
+in testLib.makeTest testArgsWithCommon
diff --git a/tests/programs/gnupg/default.nix b/tests/programs/gnupg/default.nix
new file mode 100644
index 00000000..7d0059b6
--- /dev/null
+++ b/tests/programs/gnupg/default.nix
@@ -0,0 +1,136 @@
+{ pkgs, ... }:
+
+let
+  mkExpect = expectScript: script: pkgs.writeScript "test-gnupg-cli" ''
+    #!${pkgs.expect}/bin/expect -f
+    set timeout 20
+    spawn ${pkgs.writeScript "cli-testscript.sh" ''
+      #!${pkgs.stdenv.shell} -e
+      ${script}
+    ''}
+    ${expectScript}
+    set ret [wait]
+    exit [lindex $ret 3]
+  '';
+
+  cliTestWithPassphrase = mkExpect ''
+    expect -regexp ---+.*Please.enter
+    send supersecret\r
+  '';
+
+  cliTest = mkExpect "";
+
+in {
+  name = "gnupg";
+
+  enableOCR = true;
+
+  machine = { lib, ... }: {
+    imports = map (what:
+      "${import ../../../nixpkgs-path.nix}/nixos/tests/common/${what}.nix"
+    ) [ "user-account" "x11" ];
+
+    services.openssh.enable = true;
+    services.xserver.displayManager.auto.user = "alice";
+
+    vuizvui.programs.gnupg.enable = true;
+    vuizvui.programs.gnupg.agent.enable = true;
+    vuizvui.programs.gnupg.agent.sshSupport = true;
+    vuizvui.programs.gnupg.agent.scdaemon.enable = true;
+
+    programs.ssh.startAgent = false;
+  };
+
+  testScript = ''
+    $machine->waitForUnit("sshd.service");
+    $machine->succeed("ssh-keygen -t ed25519 -f /root/id_ed25519 -N '''");
+    my $cmd = 'mkdir -p ~/.ssh && cat > ~/.ssh/authorized_keys';
+    $machine->succeed("su -c 'umask 0077; $cmd' alice < /root/id_ed25519.pub");
+
+    $machine->waitForX;
+
+    sub ssh ($) {
+      my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
+      return "ssh -q -i /root/id_ed25519".
+             " -o StrictHostKeyChecking=no".
+             " alice\@127.0.0.1 -- '$esc'";
+    }
+
+    sub xsu ($) {
+      my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
+      return "DISPLAY=:0 su alice -c '$esc'";
+    }
+
+    $machine->nest("import snakeoil key", sub {
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        gpg2 --import ${./snakeoil.asc}
+      ''}");
+      $machine->succeed(ssh "${mkExpect ''
+        expect gpg>
+        send trust\r
+        expect decision?
+        send 5\r
+        expect "Do you really want"
+        send y\r
+        expect gpg>
+        send save\r
+      '' "gpg2 --edit-key ECC15FE1"}");
+    });
+
+    subtest "test SSH agent support", sub {
+      $machine->succeed(ssh 'ssh-keygen -t ed25519 -f ~/testkey -N ""');
+      $machine->succeed(ssh '${mkExpect ''
+        expect -regexp ---+.*Please.enter
+        send supersecret\r
+        expect -regexp ---+.*Please.re-en
+        send supersecret\r
+      '' "ssh-add ~/testkey"}');
+
+      $machine->succeed("umask 0077; $cmd < ~alice/testkey.pub");
+      $machine->succeed(ssh 'rm ~/testkey*');
+
+      $machine->succeed(ssh 'ssh -o StrictHostKeyChecking=no root@127.0.0.1'.
+                            ' touch /i_have_thu_powarr');
+      $machine->succeed("test -e /i_have_thu_powarr");
+
+      $machine->succeed(ssh "systemctl --user reload gpg-agent");
+
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        ssh -o StrictHostKeyChecking=no root@127.0.0.1 \
+          touch /i_still_have_thu_powarr
+      ''}");
+      $machine->succeed("test -e /i_still_have_thu_powarr");
+    };
+
+    subtest "socket persists after restart", sub {
+      $machine->succeed(ssh 'test -e "$SSH_AUTH_SOCK"');
+      $machine->succeed(ssh 'systemctl --user stop gpg-agent.service');
+      $machine->succeed(ssh 'test -e "$SSH_AUTH_SOCK"');
+    };
+
+    subtest "test from SSH", sub {
+      $machine->execute(ssh "systemctl --user reload gpg-agent");
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        echo encrypt me > to_encrypt
+        gpg2 -sea -r ECC15FE1 to_encrypt
+        rm to_encrypt
+      ''}");
+      $machine->succeed(ssh "${cliTest ''
+        [ "$(gpg2 -d to_encrypt.asc)" = "encrypt me" ]
+      ''}");
+    };
+
+    subtest "test from X", sub {
+      $machine->execute(ssh "systemctl --user reload gpg-agent");
+      my $pid = $machine->succeed(xsu
+        'echo encrypt me | gpg2 -sea -r ECC15FE1 > encrypted_x.asc & echo $!'
+      );
+      chomp $pid;
+      $machine->waitForText(qr/Passphrase/);
+      $machine->screenshot("passphrase_dialog");
+      $machine->sendChars("supersecret\n");
+      $machine->waitUntilFails("kill -0 $pid");
+      $machine->succeed(xsu '[ "$(gpg2 -d encrypted_x.asc)" = "encrypt me" ]');
+    };
+  '';
+}
diff --git a/tests/programs/gnupg/snakeoil.asc b/tests/programs/gnupg/snakeoil.asc
new file mode 100644
index 00000000..59c07011
--- /dev/null
+++ b/tests/programs/gnupg/snakeoil.asc
@@ -0,0 +1,59 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2
+
+lQO+BFb12VYBCADBxfyzvHKtc5L2b9tqw5oOgAxnAWnsj5Weapm/zlK+gd32/PIy
+LN++ZBoxJDr5geSU8vdoI6aKAP8zhOlWU9B+vE83cDuCtvLaR7DiWxXpvACr+2pL
+Hd9ZUDVGC8HGJOljpqF04rkyHFvWIksQz2ihGR616kR3Ir2YOnGkiefsREnS/CF3
+1GXYfg4w9YO77GdCMAdXJ1I3PH+axkHjveWDKFD5f31dcolAqChl2zMoFXkPLnrf
+tA91his15YJFTIjt9KIA++J+2VEtOPvUqC6yI+DlS+j3Ie2BPi1yo10PG9TR//WI
+r2jQ36AvON87ZVNsA0YOQiZUbbS8NeUx+Y6NABEBAAH+AwMC8LL9GSjcywXbhmNt
+SMvlVHJwECg1pu/+VD0F+PTg6zXIYTeIoM2QZxxFsN2ugC8d7jfn15qX843c0npu
+hP8OeCv62pyAdSIaE8tLczPHjy613w67S4DSazaGjMA6ED/YyHOimi6Iz7+GYksZ
+DwNRe2jULr15+yVgLDXpL6Z+ROZDK6i8ovR0VZ6ueINISza3TYgsm9j/rCMbtjCh
+Ut6I4e6Ja8nJgTwwN8WezTcpo1QGBS8x0C4SYC3rDLYjlYidOXQX4OfzAYO66ABd
+/g3+NeKEFRT7EBoZgiwYX8jXhJiU14H0ZmJl5donKjZURD+kZEYj0oS6Q8VhHGfP
+eqVj5O09RRYLa9aAln/6C2J/FHDz0FhPkojISKximPN2ATxypBMweyTPuMBYKVcj
+52Dzj2crrZeTfVDmJojuM/enz7jJ2VyUsCF+V6x6Zgj3PYJEsw55C2elLNhQg9No
+GN4QXpiC3bArrEINQpcZy0Nhr56HHIBuIvLY39h0uNJFmtwog9lyyW+iG9snE2rp
+kmwd8aglH2VZhtE5SV4D/Hf9raDrrP4sLNWTeDF9vmJZ/gdnGwVYNaAfDlxyyReR
+ptqnJ8Q3mm4pQ65zKFG89UOw8ZmUVkofgdMOAAGNjVRMPkQAIu2O1oJVfAGJT+gv
+G2tplAFgMbRqpjOlL3Rvh8K3gNeA1iwa4Na9qZfo/GcmvJ150zi7TBWcT2EmOyJc
+xUMMQgXTm5JJkY/fDw75Fv7FogN5VkG2Uf8+kkfW++zkT4kF8yyD4Lw2JUuUn5l5
+JWsXDmN6fK0vhXInubkmyV2DXmsS2YVTPmtZvIYa2nVvdamh6QDwUULmnI1VPqdk
+/i1v8dMkoV5eV91pir9N6JcWng5OKz1DAY1X8fWH9bbCD0Rf/xpCbJoRSchEQqqM
+W7QZQWxpY2UgPGFsaWNlQGV4YW1wbGUub3JnPokBOQQTAQgAIwUCVvXZVgIbAwcL
+CQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJENWKNpXswV/hbnMH/35mgiPd9prX
+YqRrylVyxSiHdewVeR8nghjo/g2tR9D/A9feGoz3WL24J3NINAVmZZzKnvpT9Ut+
+Nzy5vL111TSSkMdYIrcjMu4/iUoc8w2JFExMeg1JI1EOS7ctd3qOWMYeWHtlzEJS
+DORsR/IGqq8KHNKtJPywpcjSCpXtiqzjjJrE8F2SbYMFY21SBza6QQY+Vlerr+Bo
+fwa8f3z+cyr0ISHHtEI1h8KoCCWTp/YU1FIEYc22CGz80ExMgCbBxWYukn709Wxd
+6QTFqmNtNUHi4xq1zOA/m0JMASdZPzbRcsbUQGWlwW85Dq4jYV08kC4mPLW537Lx
+A3+rzT5aiB+dA74EVvXZVgEIALXQn98p0mzZYki0aFkS5APQ1gpuXcsMRqlGQTd+
+6gZF32yEWMRrQO8gs59T4zZjGa1EhrMStMHdApxYw82oxhUU8krjYkhqOxZyW363
+H+MTYohiwr3Q6YEdVm6E8lcZwHE2d3WD5bdS0JsDjlZXMXjcJ1bivmwGGAWaucxi
+jAOayYTRpSKUFZDiFTln5dmiFFejyhU/jkTYm7VtXOQbNTwsUCkQtxT8Z468x7M6
+GzDdEvRDgN4VMVbJ2IWQdgS1WaAP9GvZgjS5B2yKUA6ONlOQOdF6gZChr2ej6Jue
+P/feNiuF9ZEqzwB1t1RrljGoyL0jjMH7RCJo2iy/OcL/nocAEQEAAf4DAwLwsv0Z
+KNzLBdvWJwkmTkAjVJRk779nD95vjddWFZgT0zy43U7AyiCYITHms0+/TM3qI5Yt
+teLBARbRddHz3+Wp6ed9zFHlCZW89Qa1yfmSsPFdp+UyN+SVHsaQIGZmFDPQ5uEd
+JRMwgnI5k09APCIq5YCE6bDcvcVLEBFT9IsuY6oWB8FLjh4fe+WAZxDlePHCxf7H
+jAfe4RDiN+bKEZQruGIfhwyuehQW/SOzY6L9PnNfouVWq5nUAl4oxGwsJfhyMpte
+MhqXox5uEeLn8S4gWZtD57Ux8CQAtAZccvjWG5jZXa2bNaEpIRBZGL6r0TS0aKTG
+v2n3CThLsYEudMiWzB7+l74ANFggZnMBXsc2nSElg57GjaCygFkpHnGeghiOjL/9
+cj/yHRz1SKH18lI+Uet/i/QFoHCGeZFbtQ8RUSp93meCHzsFKQ2ZG+djK8HqV5T0
+Tfov1RuHD9RyU00Ohc3RJWSTyeMjxAgjhJKnnfEb1w2JMcXbBCakudBAAMa2Sbdw
+a7h1I+IVTLr9SWRLYg1bWR1hCKjrjBGTA09VZF8BAH1yrszKxOPovV/fLNjohDd5
+xUXu96amSVDhq0M1DVFu8gEADN80+FhUYXIZs1HSoXuw8gusd2Bjq12oyaKNEVd0
+gazgrZ83uAT3PTkEtD4UKjCURPXJ/b4IeQlwkehcwGT7cWhgt8waNPSU5+majRXa
+RJZ/nqdk41E+NN2RvkIuyxl3ggosc3g8jtr8h2115JnoRmGzoZThrhceqVa9aLUd
+Cf6EIoXxL5RPRwaAkimuOEflHEx0NetRNVCIqhq7GLyc4LVMGhTi5U+XAg95X6gJ
+LzvVtrx3P7XG/gd74nAAW5MnW9sVXiuZZzfD56Fl7h79wAg7k3refnbERNSP1WEL
+hmUPS9SW/cKUiQEfBBgBCAAJBQJW9dlWAhsMAAoJENWKNpXswV/h5UAH/itFIGwr
+p7taEh9+x23vPdw0IuKl2lRmx4QIIC55AlzU1Tlij3jppz8PgfLArJDBY9cLe2ir
+cxXIEf+/L59832Q1Z09OXTElqpLw82wWjxTN4b4ZQjgkHGwO4RgxQKdvwDpWVt6g
+JaI1d4LAyW/RxF1vvtC4OzoUtjNXxPLHzga0PP9TOhpuPSB0fc4FDU9QaLUemkJZ
+VUICqAOcTQpENMHdDJcizYsahca2bg5gYaV1Tv/sNINNxKqcSGb1iUdJz4hAaRmO
++y4+aKxJkyt+WqmUOa5aZ9D3s9P87IuSNMc51lgiBFKWBrqSQCTfLBxMbSsPZk9h
+75FOlpj5VS82Sl0=
+=3HD3
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/tests/richi235/multipath-vpn.nix b/tests/richi235/multipath-vpn.nix
new file mode 100644
index 00000000..3595b074
--- /dev/null
+++ b/tests/richi235/multipath-vpn.nix
@@ -0,0 +1,189 @@
+{
+  name = "multipath-test";
+
+  nodes = let
+    common = { lib, ... }: {
+      networking.firewall.enable = false;
+      networking.useNetworkd = true;
+      systemd.network.netdevs."40-eth0".netdevConfig = {
+        Name = "eth0";
+        Kind = "dummy";
+      };
+    };
+
+  in {
+    client = { lib, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 10 ];
+      networking.defaultGateway = "42.0.0.1";
+      networking.interfaces.eth1.ip4 = lib.singleton {
+        address = "42.0.0.2";
+        prefixLength = 8;
+      };
+    };
+    mtc = { lib, nodes, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 1 2 10 ];
+      networking.interfaces.eth1.ip4 = lib.mkForce (lib.singleton {
+        address = "1.0.0.2";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth2.ip4 = lib.mkForce (lib.singleton {
+        address = "2.0.0.2";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth3.ip4 = lib.mkForce (lib.singleton {
+        address = "42.0.0.1";
+        prefixLength = 8;
+      });
+      systemd.network.networks."40-eth1".routes = lib.singleton {
+        routeConfig.Gateway = "1.0.0.1";
+        routeConfig.Destination = "10.0.0.0/8";
+      };
+      systemd.network.networks."40-eth2".routes = lib.singleton {
+        routeConfig.Gateway = "2.0.0.1";
+        routeConfig.Destination = "11.0.0.0/8";
+      };
+      vuizvui.services.multipath-vpn.client.enable = true;
+      vuizvui.services.multipath-vpn.client.links.vlan1 = {
+        interface = "eth1";
+        destAddress = "10.0.0.1";
+      };
+      vuizvui.services.multipath-vpn.client.links.vlan2 = {
+        interface = "eth2";
+        destAddress = "11.0.0.1";
+      };
+      vuizvui.services.multipath-vpn.client.tun = {
+        ip = "192.168.66.2";
+        mask = 24;
+      };
+      vuizvui.services.multipath-vpn.client.route = {
+        network = "0.0.0.0";
+        mask = 0;
+        gateway = "192.168.66.1";
+      };
+    };
+    relay1 = { lib, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 1 3 ];
+      networking.useDHCP = false;
+      networking.interfaces.eth1.ip4 = lib.mkForce (lib.singleton {
+        address = "1.0.0.1";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth2.ip4 = lib.mkForce (lib.singleton {
+        address = "10.0.0.2";
+        prefixLength = 8;
+      });
+      systemd.network.networks."40-eth2".routes = lib.singleton {
+        routeConfig.Gateway = "10.0.0.1";
+        routeConfig.Destination = "10.0.0.0/8";
+      };
+      networking.nat.enable = true;
+      networking.nat.internalInterfaces = [ "eth1" ];
+      networking.nat.externalInterface = "eth2";
+    };
+    relay2 = { lib, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 2 4 ];
+      networking.useDHCP = false;
+      networking.interfaces.eth1.ip4 = lib.mkForce (lib.singleton {
+        address = "2.0.0.1";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth2.ip4 = lib.mkForce (lib.singleton {
+        address = "11.0.0.2";
+        prefixLength = 8;
+      });
+      systemd.network.networks."40-eth2".routes = lib.singleton {
+        routeConfig.Gateway = "11.0.0.1";
+        routeConfig.Destination = "11.0.0.0/8";
+      };
+      networking.nat.enable = true;
+      networking.nat.internalInterfaces = [ "eth1" ];
+      networking.nat.externalInterface = "eth2";
+    };
+    mts = { lib, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 3 4 5 ];
+      networking.interfaces.eth1.ip4 = lib.mkForce (lib.singleton {
+        address = "10.0.0.1";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth2.ip4 = lib.mkForce (lib.singleton {
+        address = "11.0.0.1";
+        prefixLength = 8;
+      });
+      networking.interfaces.eth3.ip4 = lib.mkForce (lib.singleton {
+        address = "6.6.6.42";
+        prefixLength = 8;
+      });
+      networking.nat.enable = true;
+      #networking.nat.internalInterfaces = [ "tun0" ];
+      networking.nat.externalInterface = "eth3";
+      vuizvui.services.multipath-vpn.server.enable = true;
+      vuizvui.services.multipath-vpn.server.links.vlan3 = {
+        interface = "eth1";
+        destAddress = "0"; # XXX
+        destPort = 0; # XXX
+      };
+      vuizvui.services.multipath-vpn.server.links.vlan4 = {
+        interface = "eth2";
+        destAddress = "0"; # XXX
+        destPort = 0; # XXX
+      };
+      vuizvui.services.multipath-vpn.server.tun = {
+        ip = "192.168.66.1";
+        mask = 24;
+      };
+      vuizvui.services.multipath-vpn.server.route = {
+        #network = "192.168.0.0";
+        #network = "6.0.0.0";
+        #mask = 8;
+        network = "0.0.0.0";
+        mask = 0;
+        gateway = "192.168.66.2";
+      };
+    };
+    zs = { lib, ... }: {
+      imports = [ common ];
+      virtualisation.vlans = [ 5 ];
+      networking.interfaces.eth1.ip4 = lib.mkForce (lib.singleton {
+        address = "6.6.6.23";
+        prefixLength = 8;
+      });
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    startAll;
+    $mtc->waitForUnit("multipath-vpn-client.service");
+    $mts->waitForUnit("multipath-vpn-server.service");
+
+    $mtc->sleep(30);
+
+    subtest "test network topology", sub {
+      $mtc->succeed("ping -c1 10.0.0.1 >&2");
+      $mtc->succeed("ping -c1 11.0.0.1 >&2");
+      $mts->succeed("ping -c1 6.6.6.23 >&2");
+    };
+
+    subtest "test tunnel connectivity", sub {
+      $client->execute("ifconfig >&2");
+      $client->execute("ip route >&2");
+      $zs->execute("ifconfig >&2");
+      $zs->execute("ip route >&2");
+
+      $mtc->execute("ifconfig >&2");
+      $mtc->execute("ip route >&2");
+      $mts->execute("ifconfig >&2");
+      $mts->execute("ip route >&2");
+
+      $mtc->succeed("ping -c1 192.168.66.1 >&2");
+      $mtc->succeed("ping -c1 6.6.6.23 >&2");
+
+      #$client->succeed("ping -c1 192.168.66.1 >&2");
+      $client->succeed("ping -c1 6.6.6.23 >&2");
+    };
+  '';
+}