Merge tag 'driver-core-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
Linus Torvalds [Tue, 20 Mar 2012 18:16:20 +0000 (11:16 -0700)]
Pull driver core patches for 3.4-rc1 from Greg KH:
 "Here's the big driver core merge for 3.4-rc1.

  Lots of various things here, sysfs fixes/tweaks (with the nlink
  breakage reverted), dynamic debugging updates, w1 drivers, hyperv
  driver updates, and a variety of other bits and pieces, full
  information in the shortlog."

* tag 'driver-core-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (78 commits)
  Tools: hv: Support enumeration from all the pools
  Tools: hv: Fully support the new KVP verbs in the user level daemon
  Drivers: hv: Support the newly introduced KVP messages in the driver
  Drivers: hv: Add new message types to enhance KVP
  regulator: Support driver probe deferral
  Revert "sysfs: Kill nlink counting."
  uevent: send events in correct order according to seqnum (v3)
  driver core: minor comment formatting cleanups
  driver core: move the deferred probe pointer into the private area
  drivercore: Add driver probe deferral mechanism
  DS2781 Maxim Stand-Alone Fuel Gauge battery and w1 slave drivers
  w1_bq27000: Only one thread can access the bq27000 at a time.
  w1_bq27000 - remove w1_bq27000_write
  w1_bq27000: remove unnecessary NULL test.
  sysfs: Fix memory leak in sysfs_sd_setsecdata().
  intel_idle: Revert change of auto_demotion_disable_flags for Nehalem
  w1: Fix w1_bq27000
  driver-core: documentation: fix up Greg's email address
  powernow-k6: Really enable auto-loading
  powernow-k7: Fix CPU family number
  ...

109 files changed:
Documentation/ABI/removed/devfs
Documentation/ABI/stable/sysfs-driver-usb-usbtmc
Documentation/ABI/testing/sysfs-class
Documentation/ABI/testing/sysfs-devices
Documentation/ABI/testing/sysfs-devices-soc [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-samsung-laptop
Documentation/dynamic-debug-howto.txt
Documentation/filesystems/debugfs.txt
Documentation/ioctl/ioctl-number.txt
Documentation/ko_KR/HOWTO
Documentation/kobject.txt
Documentation/zh_CN/HOWTO
arch/x86/Kconfig
arch/x86/crypto/aesni-intel_glue.c
arch/x86/crypto/crc32c-intel.c
arch/x86/crypto/ghash-clmulni-intel_glue.c
arch/x86/include/asm/cpu_device_id.h [new file with mode: 0644]
arch/x86/include/asm/cpufeature.h
arch/x86/kernel/cpu/Makefile
arch/x86/kernel/cpu/match.c [new file with mode: 0644]
arch/x86/kernel/cpu/scattered.c
arch/x86/kernel/microcode_core.c
drivers/acpi/processor_driver.c
drivers/acpi/processor_perflib.c
drivers/base/Kconfig
drivers/base/Makefile
drivers/base/base.h
drivers/base/bus.c
drivers/base/core.c
drivers/base/cpu.c
drivers/base/dd.c
drivers/base/driver.c
drivers/base/soc.c [new file with mode: 0644]
drivers/cpufreq/cpufreq-nforce2.c
drivers/cpufreq/e_powersaver.c
drivers/cpufreq/elanfreq.c
drivers/cpufreq/gx-suspmod.c
drivers/cpufreq/longhaul.c
drivers/cpufreq/longrun.c
drivers/cpufreq/p4-clockmod.c
drivers/cpufreq/powernow-k6.c
drivers/cpufreq/powernow-k7.c
drivers/cpufreq/powernow-k8.c
drivers/cpufreq/sc520_freq.c
drivers/cpufreq/speedstep-centrino.c
drivers/cpufreq/speedstep-ich.c
drivers/cpufreq/speedstep-lib.c
drivers/cpufreq/speedstep-smi.c
drivers/crypto/padlock-aes.c
drivers/crypto/padlock-sha.c
drivers/hid/hid-core.c
drivers/hv/channel_mgmt.c
drivers/hv/hv.c
drivers/hv/hv_kvp.c
drivers/hv/hv_kvp.h [deleted file]
drivers/hv/hv_util.c
drivers/hv/hyperv_vmbus.h
drivers/hwmon/coretemp.c
drivers/hwmon/via-cputemp.c
drivers/idle/intel_idle.c
drivers/input/gameport/gameport.c
drivers/input/serio/serio.c
drivers/media/video/cx18/cx18-alsa-main.c
drivers/media/video/ivtv/ivtvfb.c
drivers/media/video/s5p-fimc/fimc-mdevice.c
drivers/media/video/s5p-tv/mixer_video.c
drivers/net/phy/phy_device.c
drivers/pci/pci-driver.c
drivers/pci/xen-pcifront.c
drivers/pcmcia/ds.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/ds2781_battery.c [new file with mode: 0644]
drivers/regulator/core.c
drivers/s390/cio/ccwgroup.c
drivers/s390/cio/device.c
drivers/s390/net/smsgiucv_app.c
drivers/ssb/main.c
drivers/usb/core/driver.c
drivers/usb/dwc3/dwc3-pci.c
drivers/w1/masters/w1-gpio.c
drivers/w1/slaves/Kconfig
drivers/w1/slaves/Makefile
drivers/w1/slaves/w1_bq27000.c
drivers/w1/slaves/w1_ds2781.c [new file with mode: 0644]
drivers/w1/slaves/w1_ds2781.h [new file with mode: 0644]
drivers/w1/w1_family.h
fs/debugfs/inode.c
fs/sysfs/dir.c
fs/sysfs/inode.c
fs/sysfs/mount.c
fs/sysfs/sysfs.h
include/acpi/processor.h
include/linux/connector.h
include/linux/cpu.h
include/linux/device.h
include/linux/dynamic_debug.h
include/linux/errno.h
include/linux/hyperv.h
include/linux/mod_devicetable.h
include/linux/netdevice.h
include/linux/pci.h
include/linux/printk.h
include/linux/sys_soc.h [new file with mode: 0644]
lib/dma-debug.c
lib/dynamic_debug.c
lib/kobject_uevent.c
scripts/mod/file2alias.c
tools/hv/hv_kvp_daemon.c

index 8ffd28b..0020c49 100644 (file)
@@ -1,6 +1,6 @@
 What:          devfs
 Date:          July 2005 (scheduled), finally removed in kernel v2.6.18
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
        devfs has been unmaintained for a number of years, has unfixable
        races, contains a naming policy within the kernel that is
index 9a75fb2..23a43b8 100644 (file)
@@ -1,7 +1,7 @@
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities
 Date:          August 2008
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                These files show the various USB TMC capabilities as described
                by the device itself.  The full description of the bitfields
@@ -15,7 +15,7 @@ Description:
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities
 Date:          August 2008
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                These files show the various USB TMC capabilities as described
                by the device itself.  The full description of the bitfields
@@ -29,7 +29,7 @@ Description:
 
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/TermChar
 Date:          August 2008
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                This file is the TermChar value to be sent to the USB TMC
                device as described by the document, "Universal Serial Bus Test
@@ -42,7 +42,7 @@ Description:
 
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled
 Date:          August 2008
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                This file determines if the TermChar is to be sent to the
                device on every transaction or not.  For more details about
@@ -53,7 +53,7 @@ Description:
 
 What:          /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort
 Date:          August 2008
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                This file determines if the the transaction of the USB TMC
                device is to be automatically aborted if there is any error.
index 4b0cb89..676530f 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/class/
 Date:          Febuary 2006
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                The /sys/class directory will consist of a group of
                subdirectories describing individual classes of devices
index 6a25671..5fcc943 100644 (file)
@@ -1,6 +1,6 @@
 What:          /sys/devices
 Date:          February 2006
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
                The /sys/devices tree contains a snapshot of the
                internal state of the kernel device tree.  Devices will
diff --git a/Documentation/ABI/testing/sysfs-devices-soc b/Documentation/ABI/testing/sysfs-devices-soc
new file mode 100644 (file)
index 0000000..6d9cc25
--- /dev/null
@@ -0,0 +1,58 @@
+What:          /sys/devices/socX
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               The /sys/devices/ directory contains a sub-directory for each
+               System-on-Chip (SoC) device on a running platform. Information
+               regarding each SoC can be obtained by reading sysfs files. This
+               functionality is only available if implemented by the platform.
+
+               The directory created for each SoC will also house information
+               about devices which are commonly contained in /sys/devices/platform.
+               It has been agreed that if an SoC device exists, its supported
+               devices would be better suited to appear as children of that SoC.
+
+What:          /sys/devices/socX/machine
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               Read-only attribute common to all SoCs. Contains the SoC machine
+               name (e.g. Ux500).
+
+What:          /sys/devices/socX/family
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               Read-only attribute common to all SoCs. Contains SoC family name
+               (e.g. DB8500).
+
+What:          /sys/devices/socX/soc_id
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               Read-only attribute supported by most SoCs. In the case of
+               ST-Ericsson's chips this contains the SoC serial number.
+
+What:          /sys/devices/socX/revision
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               Read-only attribute supported by most SoCs. Contains the SoC's
+               manufacturing revision number.
+
+What:          /sys/devices/socX/process
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               Read-only attribute supported ST-Ericsson's silicon. Contains the
+               the process by which the silicon chip was manufactured.
+
+What:          /sys/bus/soc
+Date:          January 2012
+contact:       Lee Jones <lee.jones@linaro.org>
+Description:
+               The /sys/bus/soc/ directory contains the usual sub-folders
+               expected under most buses. /sys/bus/soc/devices is of particular
+               interest, as it contains a symlink for each SoC device found on
+               the system. Each symlink points back into the aforementioned
+               /sys/devices/socX devices.
index 0a81023..e82e7c2 100644 (file)
@@ -1,7 +1,7 @@
 What:          /sys/devices/platform/samsung/performance_level
 Date:          January 1, 2010
 KernelVersion: 2.6.33
-Contact:       Greg Kroah-Hartman <gregkh@suse.de>
+Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:   Some Samsung laptops have different "performance levels"
                that are can be modified by a function key, and by this
                sysfs file.  These values don't always make a whole lot
index f959909..74e6c77 100644 (file)
@@ -12,7 +12,7 @@ dynamically enabled per-callsite.
 Dynamic debug has even more useful features:
 
  * Simple query language allows turning on and off debugging statements by
-   matching any combination of:
+   matching any combination of 0 or 1 of:
 
    - source filename
    - function name
@@ -79,31 +79,24 @@ Command Language Reference
 ==========================
 
 At the lexical level, a command comprises a sequence of words separated
-by whitespace characters.  Note that newlines are treated as word
-separators and do *not* end a command or allow multiple commands to
-be done together.  So these are all equivalent:
+by spaces or tabs.  So these are all equivalent:
 
 nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' >
                                <debugfs>/dynamic_debug/control
 nullarbor:~ # echo -c '  file   svcsock.c     line  1603 +p  ' >
                                <debugfs>/dynamic_debug/control
-nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' >
-                               <debugfs>/dynamic_debug/control
 nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' >
                                <debugfs>/dynamic_debug/control
 
-Commands are bounded by a write() system call.  If you want to do
-multiple commands you need to do a separate "echo" for each, like:
+Command submissions are bounded by a write() system call.
+Multiple commands can be written together, separated by ';' or '\n'.
 
-nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\
-> echo 'file svcsock.c line 1563 +p' > /proc/dprintk
+  ~# echo "func pnpacpi_get_resources +p; func pnp_assign_mem +p" \
+     > <debugfs>/dynamic_debug/control
 
-or even like:
+If your query set is big, you can batch them too:
 
-nullarbor:~ # (
-> echo 'file svcsock.c line 1603 +p' ;\
-> echo 'file svcsock.c line 1563 +p' ;\
-> ) > /proc/dprintk
+  ~# cat query-batch-file > <debugfs>/dynamic_debug/control
 
 At the syntactical level, a command comprises a sequence of match
 specifications, followed by a flags change specification.
@@ -144,11 +137,12 @@ func
     func svc_tcp_accept
 
 file
-    The given string is compared against either the full
-    pathname or the basename of the source file of each
-    callsite.  Examples:
+    The given string is compared against either the full pathname, the
+    src-root relative pathname, or the basename of the source file of
+    each callsite.  Examples:
 
     file svcsock.c
+    file kernel/freezer.c
     file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c
 
 module
index 6872c91..4e25758 100644 (file)
@@ -14,7 +14,10 @@ Debugfs is typically mounted with a command like:
 
     mount -t debugfs none /sys/kernel/debug
 
-(Or an equivalent /etc/fstab line). 
+(Or an equivalent /etc/fstab line).
+The debugfs root directory is accessible by anyone by default. To
+restrict access to the tree the "uid", "gid" and "mode" mount
+options can be used.
 
 Note that the debugfs API is exported GPL-only to modules.
 
index 4840334..5dbd906 100644 (file)
@@ -189,7 +189,7 @@ Code  Seq#(hex)     Include File            Comments
 'Y'    all     linux/cyclades.h
 'Z'    14-15   drivers/message/fusion/mptctl.h
 '['    00-07   linux/usb/tmc.h         USB Test and Measurement Devices
-                                       <mailto:gregkh@suse.de>
+                                       <mailto:gregkh@linuxfoundation.org>
 'a'    all     linux/atm*.h, linux/sonet.h     ATM on linux
                                        <http://lrcwww.epfl.ch/>
 'b'    00-FF                           conflict! bit3 vme host bridge
index ab5189a..2f48f20 100644 (file)
@@ -354,7 +354,7 @@ Andrew Morton에 의해 배포된 실험적인 커널 패치들이다. Andrew는
     git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
 
   quilt trees:
-    - USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@suse.de>
+    - USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@linuxfoundation.org>
     kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
     - x86-64, partly i386, Andi Kleen < ak@suse.de>
         ftp.firstfloor.org:/pub/ak/x86_64/quilt/
index 3ab2472..49578cf 100644 (file)
@@ -1,6 +1,6 @@
 Everything you never wanted to know about kobjects, ksets, and ktypes
 
-Greg Kroah-Hartman <gregkh@suse.de>
+Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 
 Based on an original article by Jon Corbet for lwn.net written October 1,
 2003 and located at http://lwn.net/Articles/51437/
index faf976c..7fba5aa 100644 (file)
@@ -316,7 +316,7 @@ linux-kernel邮件列表中提供反馈,告诉大家你遇到了问题还是
        git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
 
   使用quilt管理的补丁集:
-    - USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman <gregkh@suse.de>
+    - USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman <gregkh@linuxfoundation.org>
        kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
     - x86-64, 部分i386, Andi Kleen <ak@suse.de>
        ftp.firstfloor.org:/pub/ak/x86_64/quilt/
index 09675d3..d523a6c 100644 (file)
@@ -180,6 +180,9 @@ config ARCH_HAS_DEFAULT_IDLE
 config ARCH_HAS_CACHE_LINE_SIZE
        def_bool y
 
+config ARCH_HAS_CPU_AUTOPROBE
+       def_bool y
+
 config HAVE_SETUP_PER_CPU_AREA
        def_bool y
 
index 545d0ce..b3350bd 100644 (file)
@@ -28,6 +28,7 @@
 #include <crypto/aes.h>
 #include <crypto/cryptd.h>
 #include <crypto/ctr.h>
+#include <asm/cpu_device_id.h>
 #include <asm/i387.h>
 #include <asm/aes.h>
 #include <crypto/scatterwalk.h>
@@ -1253,14 +1254,19 @@ static struct crypto_alg __rfc4106_alg = {
 };
 #endif
 
+
+static const struct x86_cpu_id aesni_cpu_id[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_AES),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, aesni_cpu_id);
+
 static int __init aesni_init(void)
 {
        int err;
 
-       if (!cpu_has_aes) {
-               printk(KERN_INFO "Intel AES-NI instructions are not detected.\n");
+       if (!x86_match_cpu(aesni_cpu_id))
                return -ENODEV;
-       }
 
        if ((err = crypto_fpu_init()))
                goto fpu_err;
index b9d0026..493f959 100644 (file)
@@ -31,6 +31,7 @@
 #include <crypto/internal/hash.h>
 
 #include <asm/cpufeature.h>
+#include <asm/cpu_device_id.h>
 
 #define CHKSUM_BLOCK_SIZE      1
 #define CHKSUM_DIGEST_SIZE     4
@@ -173,13 +174,17 @@ static struct shash_alg alg = {
        }
 };
 
+static const struct x86_cpu_id crc32c_cpu_id[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_XMM4_2),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, crc32c_cpu_id);
 
 static int __init crc32c_intel_mod_init(void)
 {
-       if (cpu_has_xmm4_2)
-               return crypto_register_shash(&alg);
-       else
+       if (!x86_match_cpu(crc32c_cpu_id))
                return -ENODEV;
+       return crypto_register_shash(&alg);
 }
 
 static void __exit crc32c_intel_mod_fini(void)
index 976aa64..b4bf0a6 100644 (file)
@@ -20,6 +20,7 @@
 #include <crypto/gf128mul.h>
 #include <crypto/internal/hash.h>
 #include <asm/i387.h>
+#include <asm/cpu_device_id.h>
 
 #define GHASH_BLOCK_SIZE       16
 #define GHASH_DIGEST_SIZE      16
@@ -294,15 +295,18 @@ static struct ahash_alg ghash_async_alg = {
        },
 };
 
+static const struct x86_cpu_id pcmul_cpu_id[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_PCLMULQDQ), /* Pickle-Mickle-Duck */
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id);
+
 static int __init ghash_pclmulqdqni_mod_init(void)
 {
        int err;
 
-       if (!cpu_has_pclmulqdq) {
-               printk(KERN_INFO "Intel PCLMULQDQ-NI instructions are not"
-                      " detected.\n");
+       if (!x86_match_cpu(pcmul_cpu_id))
                return -ENODEV;
-       }
 
        err = crypto_register_shash(&ghash_alg);
        if (err)
diff --git a/arch/x86/include/asm/cpu_device_id.h b/arch/x86/include/asm/cpu_device_id.h
new file mode 100644 (file)
index 0000000..ff501e5
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _CPU_DEVICE_ID
+#define _CPU_DEVICE_ID 1
+
+/*
+ * Declare drivers belonging to specific x86 CPUs
+ * Similar in spirit to pci_device_id and related PCI functions
+ */
+
+#include <linux/mod_devicetable.h>
+
+extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match);
+
+#endif
index 8d67d42..dcb839e 100644 (file)
 #define X86_FEATURE_PLN                (7*32+ 5) /* Intel Power Limit Notification */
 #define X86_FEATURE_PTS                (7*32+ 6) /* Intel Package Thermal Status */
 #define X86_FEATURE_DTS                (7*32+ 7) /* Digital Thermal Sensor */
+#define X86_FEATURE_HW_PSTATE  (7*32+ 8) /* AMD HW-PState */
 
 /* Virtualization flags: Linux defined, word 8 */
 #define X86_FEATURE_TPR_SHADOW  (8*32+ 0) /* Intel TPR Shadow */
index 25f24dc..6ab6aa2 100644 (file)
@@ -16,6 +16,7 @@ obj-y                 := intel_cacheinfo.o scattered.o topology.o
 obj-y                  += proc.o capflags.o powerflags.o common.o
 obj-y                  += vmware.o hypervisor.o sched.o mshyperv.o
 obj-y                  += rdrand.o
+obj-y                  += match.o
 
 obj-$(CONFIG_X86_32)   += bugs.o
 obj-$(CONFIG_X86_64)   += bugs_64.o
diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c
new file mode 100644 (file)
index 0000000..5502b28
--- /dev/null
@@ -0,0 +1,91 @@
+#include <asm/cpu_device_id.h>
+#include <asm/processor.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/**
+ * x86_match_cpu - match current CPU again an array of x86_cpu_ids
+ * @match: Pointer to array of x86_cpu_ids. Last entry terminated with
+ *         {}.
+ *
+ * Return the entry if the current CPU matches the entries in the
+ * passed x86_cpu_id match table. Otherwise NULL.  The match table
+ * contains vendor (X86_VENDOR_*), family, model and feature bits or
+ * respective wildcard entries.
+ *
+ * A typical table entry would be to match a specific CPU
+ * { X86_VENDOR_INTEL, 6, 0x12 }
+ * or to match a specific CPU feature
+ * { X86_FEATURE_MATCH(X86_FEATURE_FOOBAR) }
+ *
+ * Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY,
+ * %X86_MODEL_ANY, %X86_FEATURE_ANY or 0 (except for vendor)
+ *
+ * Arrays used to match for this should also be declared using
+ * MODULE_DEVICE_TABLE(x86_cpu, ...)
+ *
+ * This always matches against the boot cpu, assuming models and features are
+ * consistent over all CPUs.
+ */
+const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
+{
+       const struct x86_cpu_id *m;
+       struct cpuinfo_x86 *c = &boot_cpu_data;
+
+       for (m = match; m->vendor | m->family | m->model | m->feature; m++) {
+               if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
+                       continue;
+               if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
+                       continue;
+               if (m->model != X86_MODEL_ANY && c->x86_model != m->model)
+                       continue;
+               if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature))
+                       continue;
+               return m;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(x86_match_cpu);
+
+ssize_t arch_print_cpu_modalias(struct device *dev,
+                               struct device_attribute *attr,
+                               char *bufptr)
+{
+       int size = PAGE_SIZE;
+       int i, n;
+       char *buf = bufptr;
+
+       n = snprintf(buf, size, "x86cpu:vendor:%04X:family:%04X:"
+                    "model:%04X:feature:",
+               boot_cpu_data.x86_vendor,
+               boot_cpu_data.x86,
+               boot_cpu_data.x86_model);
+       size -= n;
+       buf += n;
+       size -= 1;
+       for (i = 0; i < NCAPINTS*32; i++) {
+               if (boot_cpu_has(i)) {
+                       n = snprintf(buf, size, ",%04X", i);
+                       if (n >= size) {
+                               WARN(1, "x86 features overflow page\n");
+                               break;
+                       }
+                       size -= n;
+                       buf += n;
+               }
+       }
+       *buf++ = '\n';
+       return buf - bufptr;
+}
+
+int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (buf) {
+               arch_print_cpu_modalias(NULL, NULL, buf);
+               add_uevent_var(env, "MODALIAS=%s", buf);
+               kfree(buf);
+       }
+       return 0;
+}
index c7f64e6..addf9e8 100644 (file)
@@ -40,6 +40,7 @@ void __cpuinit init_scattered_cpuid_features(struct cpuinfo_x86 *c)
                { X86_FEATURE_EPB,              CR_ECX, 3, 0x00000006, 0 },
                { X86_FEATURE_XSAVEOPT,         CR_EAX, 0, 0x0000000d, 1 },
                { X86_FEATURE_CPB,              CR_EDX, 9, 0x80000007, 0 },
+               { X86_FEATURE_HW_PSTATE,        CR_EDX, 7, 0x80000007, 0 },
                { X86_FEATURE_NPT,              CR_EDX, 0, 0x8000000a, 0 },
                { X86_FEATURE_LBRV,             CR_EDX, 1, 0x8000000a, 0 },
                { X86_FEATURE_SVML,             CR_EDX, 2, 0x8000000a, 0 },
index fda91c3..87a0f86 100644 (file)
@@ -86,6 +86,7 @@
 
 #include <asm/microcode.h>
 #include <asm/processor.h>
+#include <asm/cpu_device_id.h>
 
 MODULE_DESCRIPTION("Microcode Update Driver");
 MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
@@ -504,6 +505,20 @@ static struct notifier_block __refdata mc_cpu_notifier = {
        .notifier_call  = mc_cpu_callback,
 };
 
+#ifdef MODULE
+/* Autoload on Intel and AMD systems */
+static const struct x86_cpu_id microcode_id[] = {
+#ifdef CONFIG_MICROCODE_INTEL
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, },
+#endif
+#ifdef CONFIG_MICROCODE_AMD
+       { X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, },
+#endif
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, microcode_id);
+#endif
+
 static int __init microcode_init(void)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
index 8ae05ce..2801b41 100644 (file)
@@ -474,6 +474,7 @@ static __ref int acpi_processor_start(struct acpi_processor *pr)
 
 #ifdef CONFIG_CPU_FREQ
        acpi_processor_ppc_has_changed(pr, 0);
+       acpi_processor_load_module(pr);
 #endif
        acpi_processor_get_throttling_info(pr);
        acpi_processor_get_limit_info(pr);
index 85b3237..0af48a8 100644 (file)
@@ -240,6 +240,28 @@ void acpi_processor_ppc_exit(void)
        acpi_processor_ppc_status &= ~PPC_REGISTERED;
 }
 
+/*
+ * Do a quick check if the systems looks like it should use ACPI
+ * cpufreq. We look at a _PCT method being available, but don't
+ * do a whole lot of sanity checks.
+ */
+void acpi_processor_load_module(struct acpi_processor *pr)
+{
+       static int requested;
+       acpi_status status = 0;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       if (!arch_has_acpi_pdc() || requested)
+               return;
+       status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
+       if (!ACPI_FAILURE(status)) {
+               printk(KERN_INFO PREFIX "Requesting acpi_cpufreq\n");
+               request_module_nowait("acpi_cpufreq");
+               requested = 1;
+       }
+       kfree(buffer.pointer);
+}
+
 static int acpi_processor_get_performance_control(struct acpi_processor *pr)
 {
        int result = 0;
index 7be9f79..9aa618a 100644 (file)
@@ -176,6 +176,9 @@ config GENERIC_CPU_DEVICES
        bool
        default n
 
+config SOC_BUS
+       bool
+
 source "drivers/base/regmap/Kconfig"
 
 config DMA_SHARED_BUFFER
index 610f999..b6d1b9c 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
 obj-$(CONFIG_REGMAP)   += regmap/
+obj-$(CONFIG_SOC_BUS) += soc.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
 
index b858dfd..6ee17bb 100644 (file)
@@ -59,6 +59,10 @@ struct driver_private {
  * @knode_parent - node in sibling list
  * @knode_driver - node in driver list
  * @knode_bus - node in bus list
+ * @deferred_probe - entry in deferred_probe_list which is used to retry the
+ *     binding of drivers which were unable to get all the resources needed by
+ *     the device; typically because it depends on another driver getting
+ *     probed first.
  * @driver_data - private pointer for driver specific info.  Will turn into a
  * list soon.
  * @device - pointer back to the struct class that this structure is
@@ -71,6 +75,7 @@ struct device_private {
        struct klist_node knode_parent;
        struct klist_node knode_driver;
        struct klist_node knode_bus;
+       struct list_head deferred_probe;
        void *driver_data;
        struct device *device;
 };
@@ -105,6 +110,7 @@ extern void bus_remove_driver(struct device_driver *drv);
 
 extern void driver_detach(struct device_driver *drv);
 extern int driver_probe_device(struct device_driver *drv, struct device *dev);
+extern void driver_deferred_probe_del(struct device *dev);
 static inline int driver_match_device(struct device_driver *drv,
                                      struct device *dev)
 {
index 40fb122..26a06b8 100644 (file)
@@ -1194,13 +1194,15 @@ EXPORT_SYMBOL_GPL(subsys_interface_register);
 
 void subsys_interface_unregister(struct subsys_interface *sif)
 {
-       struct bus_type *subsys = sif->subsys;
+       struct bus_type *subsys;
        struct subsys_dev_iter iter;
        struct device *dev;
 
-       if (!sif)
+       if (!sif || !sif->subsys)
                return;
 
+       subsys = sif->subsys;
+
        mutex_lock(&subsys->p->mutex);
        list_del_init(&sif->node);
        if (sif->remove_dev) {
index 74dda4f..7050a75 100644 (file)
@@ -921,6 +921,7 @@ int device_private_init(struct device *dev)
        dev->p->device = dev;
        klist_init(&dev->p->klist_children, klist_children_get,
                   klist_children_put);
+       INIT_LIST_HEAD(&dev->p->deferred_probe);
        return 0;
 }
 
@@ -1188,6 +1189,7 @@ void device_del(struct device *dev)
        device_remove_file(dev, &uevent_attr);
        device_remove_attrs(dev);
        bus_remove_device(dev);
+       driver_deferred_probe_del(dev);
 
        /*
         * Some platform devices are driven without driver attached
index 4dabf50..adf937b 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/node.h>
 #include <linux/gfp.h>
+#include <linux/slab.h>
 #include <linux/percpu.h>
 
 #include "base.h"
@@ -244,6 +245,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num)
        cpu->dev.id = num;
        cpu->dev.bus = &cpu_subsys;
        cpu->dev.release = cpu_device_release;
+#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
+       cpu->dev.bus->uevent = arch_cpu_uevent;
+#endif
        error = device_register(&cpu->dev);
        if (!error && cpu->hotpluggable)
                register_cpu_control(cpu);
@@ -268,6 +272,10 @@ struct device *get_cpu_device(unsigned cpu)
 }
 EXPORT_SYMBOL_GPL(get_cpu_device);
 
+#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
+static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL);
+#endif
+
 static struct attribute *cpu_root_attrs[] = {
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
        &dev_attr_probe.attr,
@@ -278,6 +286,9 @@ static struct attribute *cpu_root_attrs[] = {
        &cpu_attrs[2].attr.attr,
        &dev_attr_kernel_max.attr,
        &dev_attr_offline.attr,
+#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
+       &dev_attr_modalias.attr,
+#endif
        NULL
 };
 
index 142e3d6..1b1cbb5 100644 (file)
 #include "base.h"
 #include "power/power.h"
 
+/*
+ * Deferred Probe infrastructure.
+ *
+ * Sometimes driver probe order matters, but the kernel doesn't always have
+ * dependency information which means some drivers will get probed before a
+ * resource it depends on is available.  For example, an SDHCI driver may
+ * first need a GPIO line from an i2c GPIO controller before it can be
+ * initialized.  If a required resource is not available yet, a driver can
+ * request probing to be deferred by returning -EPROBE_DEFER from its probe hook
+ *
+ * Deferred probe maintains two lists of devices, a pending list and an active
+ * list.  A driver returning -EPROBE_DEFER causes the device to be added to the
+ * pending list.  A successful driver probe will trigger moving all devices
+ * from the pending to the active list so that the workqueue will eventually
+ * retry them.
+ *
+ * The deferred_probe_mutex must be held any time the deferred_probe_*_list
+ * of the (struct device*)->p->deferred_probe pointers are manipulated
+ */
+static DEFINE_MUTEX(deferred_probe_mutex);
+static LIST_HEAD(deferred_probe_pending_list);
+static LIST_HEAD(deferred_probe_active_list);
+static struct workqueue_struct *deferred_wq;
+
+/**
+ * deferred_probe_work_func() - Retry probing devices in the active list.
+ */
+static void deferred_probe_work_func(struct work_struct *work)
+{
+       struct device *dev;
+       struct device_private *private;
+       /*
+        * This block processes every device in the deferred 'active' list.
+        * Each device is removed from the active list and passed to
+        * bus_probe_device() to re-attempt the probe.  The loop continues
+        * until every device in the active list is removed and retried.
+        *
+        * Note: Once the device is removed from the list and the mutex is
+        * released, it is possible for the device get freed by another thread
+        * and cause a illegal pointer dereference.  This code uses
+        * get/put_device() to ensure the device structure cannot disappear
+        * from under our feet.
+        */
+       mutex_lock(&deferred_probe_mutex);
+       while (!list_empty(&deferred_probe_active_list)) {
+               private = list_first_entry(&deferred_probe_active_list,
+                                       typeof(*dev->p), deferred_probe);
+               dev = private->device;
+               list_del_init(&private->deferred_probe);
+
+               get_device(dev);
+
+               /*
+                * Drop the mutex while probing each device; the probe path may
+                * manipulate the deferred list
+                */
+               mutex_unlock(&deferred_probe_mutex);
+               dev_dbg(dev, "Retrying from deferred list\n");
+               bus_probe_device(dev);
+               mutex_lock(&deferred_probe_mutex);
+
+               put_device(dev);
+       }
+       mutex_unlock(&deferred_probe_mutex);
+}
+static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func);
+
+static void driver_deferred_probe_add(struct device *dev)
+{
+       mutex_lock(&deferred_probe_mutex);
+       if (list_empty(&dev->p->deferred_probe)) {
+               dev_dbg(dev, "Added to deferred list\n");
+               list_add(&dev->p->deferred_probe, &deferred_probe_pending_list);
+       }
+       mutex_unlock(&deferred_probe_mutex);
+}
+
+void driver_deferred_probe_del(struct device *dev)
+{
+       mutex_lock(&deferred_probe_mutex);
+       if (!list_empty(&dev->p->deferred_probe)) {
+               dev_dbg(dev, "Removed from deferred list\n");
+               list_del_init(&dev->p->deferred_probe);
+       }
+       mutex_unlock(&deferred_probe_mutex);
+}
+
+static bool driver_deferred_probe_enable = false;
+/**
+ * driver_deferred_probe_trigger() - Kick off re-probing deferred devices
+ *
+ * This functions moves all devices from the pending list to the active
+ * list and schedules the deferred probe workqueue to process them.  It
+ * should be called anytime a driver is successfully bound to a device.
+ */
+static void driver_deferred_probe_trigger(void)
+{
+       if (!driver_deferred_probe_enable)
+               return;
+
+       /*
+        * A successful probe means that all the devices in the pending list
+        * should be triggered to be reprobed.  Move all the deferred devices
+        * into the active list so they can be retried by the workqueue
+        */
+       mutex_lock(&deferred_probe_mutex);
+       list_splice_tail_init(&deferred_probe_pending_list,
+                             &deferred_probe_active_list);
+       mutex_unlock(&deferred_probe_mutex);
+
+       /*
+        * Kick the re-probe thread.  It may already be scheduled, but it is
+        * safe to kick it again.
+        */
+       queue_work(deferred_wq, &deferred_probe_work);
+}
+
+/**
+ * deferred_probe_initcall() - Enable probing of deferred devices
+ *
+ * We don't want to get in the way when the bulk of drivers are getting probed.
+ * Instead, this initcall makes sure that deferred probing is delayed until
+ * late_initcall time.
+ */
+static int deferred_probe_initcall(void)
+{
+       deferred_wq = create_singlethread_workqueue("deferwq");
+       if (WARN_ON(!deferred_wq))
+               return -ENOMEM;
+
+       driver_deferred_probe_enable = true;
+       driver_deferred_probe_trigger();
+       return 0;
+}
+late_initcall(deferred_probe_initcall);
 
 static void driver_bound(struct device *dev)
 {
@@ -42,6 +177,13 @@ static void driver_bound(struct device *dev)
 
        klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
 
+       /*
+        * Make sure the device is no longer in one of the deferred lists and
+        * kick off retrying all pending devices
+        */
+       driver_deferred_probe_del(dev);
+       driver_deferred_probe_trigger();
+
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_BOUND_DRIVER, dev);
@@ -142,7 +284,11 @@ probe_failed:
        driver_sysfs_remove(dev);
        dev->driver = NULL;
 
-       if (ret != -ENODEV && ret != -ENXIO) {
+       if (ret == -EPROBE_DEFER) {
+               /* Driver requested deferred probing */
+               dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
+               driver_deferred_probe_add(dev);
+       } else if (ret != -ENODEV && ret != -ENXIO) {
                /* driver matched but the probe failed */
                printk(KERN_WARNING
                       "%s: probe of %s failed with error %d\n",
index b631f7c..60e4f77 100644 (file)
@@ -153,34 +153,6 @@ int driver_add_kobj(struct device_driver *drv, struct kobject *kobj,
 }
 EXPORT_SYMBOL_GPL(driver_add_kobj);
 
-/**
- * get_driver - increment driver reference count.
- * @drv: driver.
- */
-struct device_driver *get_driver(struct device_driver *drv)
-{
-       if (drv) {
-               struct driver_private *priv;
-               struct kobject *kobj;
-
-               kobj = kobject_get(&drv->p->kobj);
-               priv = to_driver(kobj);
-               return priv->driver;
-       }
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(get_driver);
-
-/**
- * put_driver - decrement driver's refcount.
- * @drv: driver.
- */
-void put_driver(struct device_driver *drv)
-{
-       kobject_put(&drv->p->kobj);
-}
-EXPORT_SYMBOL_GPL(put_driver);
-
 static int driver_add_groups(struct device_driver *drv,
                             const struct attribute_group **groups)
 {
@@ -234,7 +206,6 @@ int driver_register(struct device_driver *drv)
 
        other = driver_find(drv->name, drv->bus);
        if (other) {
-               put_driver(other);
                printk(KERN_ERR "Error: Driver '%s' is already registered, "
                        "aborting...\n", drv->name);
                return -EBUSY;
@@ -275,7 +246,9 @@ EXPORT_SYMBOL_GPL(driver_unregister);
  * Call kset_find_obj() to iterate over list of drivers on
  * a bus to find driver by name. Return driver if found.
  *
- * Note that kset_find_obj increments driver's reference count.
+ * This routine provides no locking to prevent the driver it returns
+ * from being unregistered or unloaded while the caller is using it.
+ * The caller is responsible for preventing this.
  */
 struct device_driver *driver_find(const char *name, struct bus_type *bus)
 {
@@ -283,6 +256,8 @@ struct device_driver *driver_find(const char *name, struct bus_type *bus)
        struct driver_private *priv;
 
        if (k) {
+               /* Drop reference added by kset_find_obj() */
+               kobject_put(k);
                priv = to_driver(k);
                return priv->driver;
        }
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
new file mode 100644 (file)
index 0000000..05f1503
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/spinlock.h>
+#include <linux/sys_soc.h>
+#include <linux/err.h>
+
+static DEFINE_IDR(soc_ida);
+static DEFINE_SPINLOCK(soc_lock);
+
+static ssize_t soc_info_get(struct device *dev,
+                           struct device_attribute *attr,
+                           char *buf);
+
+struct soc_device {
+       struct device dev;
+       struct soc_device_attribute *attr;
+       int soc_dev_num;
+};
+
+static struct bus_type soc_bus_type = {
+       .name  = "soc",
+};
+
+static DEVICE_ATTR(machine,  S_IRUGO, soc_info_get,  NULL);
+static DEVICE_ATTR(family,   S_IRUGO, soc_info_get,  NULL);
+static DEVICE_ATTR(soc_id,   S_IRUGO, soc_info_get,  NULL);
+static DEVICE_ATTR(revision, S_IRUGO, soc_info_get,  NULL);
+
+struct device *soc_device_to_device(struct soc_device *soc_dev)
+{
+       return &soc_dev->dev;
+}
+
+static mode_t soc_attribute_mode(struct kobject *kobj,
+                                 struct attribute *attr,
+                                 int index)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+
+       if ((attr == &dev_attr_machine.attr)
+           && (soc_dev->attr->machine != NULL))
+               return attr->mode;
+       if ((attr == &dev_attr_family.attr)
+           && (soc_dev->attr->family != NULL))
+               return attr->mode;
+       if ((attr == &dev_attr_revision.attr)
+           && (soc_dev->attr->revision != NULL))
+               return attr->mode;
+       if ((attr == &dev_attr_soc_id.attr)
+           && (soc_dev->attr->soc_id != NULL))
+               return attr->mode;
+
+       /* Unknown or unfilled attribute. */
+       return 0;
+}
+
+static ssize_t soc_info_get(struct device *dev,
+                           struct device_attribute *attr,
+                           char *buf)
+{
+       struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+
+       if (attr == &dev_attr_machine)
+               return sprintf(buf, "%s\n", soc_dev->attr->machine);
+       if (attr == &dev_attr_family)
+               return sprintf(buf, "%s\n", soc_dev->attr->family);
+       if (attr == &dev_attr_revision)
+               return sprintf(buf, "%s\n", soc_dev->attr->revision);
+       if (attr == &dev_attr_soc_id)
+               return sprintf(buf, "%s\n", soc_dev->attr->soc_id);
+
+       return -EINVAL;
+
+}
+
+static struct attribute *soc_attr[] = {
+       &dev_attr_machine.attr,
+       &dev_attr_family.attr,
+       &dev_attr_soc_id.attr,
+       &dev_attr_revision.attr,
+       NULL,
+};
+
+static const struct attribute_group soc_attr_group = {
+       .attrs = soc_attr,
+       .is_visible = soc_attribute_mode,
+};
+
+static const struct attribute_group *soc_attr_groups[] = {
+       &soc_attr_group,
+       NULL,
+};
+
+static void soc_release(struct device *dev)
+{
+       struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+
+       kfree(soc_dev);
+}
+
+struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
+{
+       struct soc_device *soc_dev;
+       int ret;
+
+       soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
+       if (!soc_dev) {
+               ret = -ENOMEM;
+               goto out1;
+       }
+
+       /* Fetch a unique (reclaimable) SOC ID. */
+       do {
+               if (!ida_pre_get(&soc_ida, GFP_KERNEL)) {
+                       ret = -ENOMEM;
+                       goto out2;
+               }
+
+               spin_lock(&soc_lock);
+               ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num);
+               spin_unlock(&soc_lock);
+
+       } while (ret == -EAGAIN);
+
+       if (ret)
+                goto out2;
+
+       soc_dev->attr = soc_dev_attr;
+       soc_dev->dev.bus = &soc_bus_type;
+       soc_dev->dev.groups = soc_attr_groups;
+       soc_dev->dev.release = soc_release;
+
+       dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
+
+       ret = device_register(&soc_dev->dev);
+       if (ret)
+               goto out3;
+
+       return soc_dev;
+
+out3:
+       ida_remove(&soc_ida, soc_dev->soc_dev_num);
+out2:
+       kfree(soc_dev);
+out1:
+       return ERR_PTR(ret);
+}
+
+/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
+void soc_device_unregister(struct soc_device *soc_dev)
+{
+       ida_remove(&soc_ida, soc_dev->soc_dev_num);
+
+       device_unregister(&soc_dev->dev);
+}
+
+static int __init soc_bus_register(void)
+{
+       spin_lock_init(&soc_lock);
+
+       return bus_register(&soc_bus_type);
+}
+core_initcall(soc_bus_register);
+
+static void __exit soc_bus_unregister(void)
+{
+       ida_destroy(&soc_ida);
+
+       bus_unregister(&soc_bus_type);
+}
+module_exit(soc_bus_unregister);
index 7bac808..13d311e 100644 (file)
@@ -385,6 +385,14 @@ static struct cpufreq_driver nforce2_driver = {
        .owner = THIS_MODULE,
 };
 
+#ifdef MODULE
+static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
+       { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
+       {}
+};
+MODULE_DEVICE_TABLE(pci, nforce2_ids);
+#endif
+
 /**
  * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
  *
index 4bd6815..3fffbe6 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 
+#include <asm/cpu_device_id.h>
 #include <asm/msr.h>
 #include <asm/tsc.h>
 
@@ -437,18 +438,19 @@ static struct cpufreq_driver eps_driver = {
        .attr           = eps_attr,
 };
 
+
+/* This driver will work only on Centaur C7 processors with
+ * Enhanced SpeedStep/PowerSaver registers */
+static const struct x86_cpu_id eps_cpu_id[] = {
+       { X86_VENDOR_CENTAUR, 6, X86_MODEL_ANY, X86_FEATURE_EST },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
+
 static int __init eps_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       /* This driver will work only on Centaur C7 processors with
-        * Enhanced SpeedStep/PowerSaver registers */
-       if (c->x86_vendor != X86_VENDOR_CENTAUR
-           || c->x86 != 6 || c->x86_model < 10)
-               return -ENODEV;
-       if (!cpu_has(c, X86_FEATURE_EST))
+       if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
                return -ENODEV;
-
        if (cpufreq_register_driver(&eps_driver))
                return -EINVAL;
        return 0;
index c587db4..960671f 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/delay.h>
 #include <linux/cpufreq.h>
 
+#include <asm/cpu_device_id.h>
 #include <asm/msr.h>
 #include <linux/timex.h>
 #include <linux/io.h>
@@ -277,17 +278,16 @@ static struct cpufreq_driver elanfreq_driver = {
        .attr           = elanfreq_attr,
 };
 
+static const struct x86_cpu_id elan_id[] = {
+       { X86_VENDOR_AMD, 4, 10, },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, elan_id);
 
 static int __init elanfreq_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       /* Test if we have the right hardware */
-       if ((c->x86_vendor != X86_VENDOR_AMD) ||
-               (c->x86 != 4) || (c->x86_model != 10)) {
-               printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
+       if (!x86_match_cpu(elan_id))
                return -ENODEV;
-       }
        return cpufreq_register_driver(&elanfreq_driver);
 }
 
index ffe1f2c..456bee0 100644 (file)
@@ -82,6 +82,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 
+#include <asm/cpu_device_id.h>
 #include <asm/processor-cyrix.h>
 
 /* PCI config registers, all at F0 */
@@ -171,6 +172,7 @@ static struct pci_device_id gx_chipset_tbl[] __initdata = {
        { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), },
        { 0, },
 };
+MODULE_DEVICE_TABLE(pci, gx_chipset_tbl);
 
 static void gx_write_byte(int reg, int value)
 {
@@ -185,13 +187,6 @@ static __init struct pci_dev *gx_detect_chipset(void)
 {
        struct pci_dev *gx_pci = NULL;
 
-       /* check if CPU is a MediaGX or a Geode. */
-       if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
-           (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
-               pr_debug("error: no MediaGX/Geode processor found!\n");
-               return NULL;
-       }
-
        /* detect which companion chip is used */
        for_each_pci_dev(gx_pci) {
                if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
index f47d26e..53ddbc7 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/acpi.h>
 
 #include <asm/msr.h>
+#include <asm/cpu_device_id.h>
 #include <acpi/processor.h>
 
 #include "longhaul.h"
@@ -951,12 +952,17 @@ static struct cpufreq_driver longhaul_driver = {
        .attr   = longhaul_attr,
 };
 
+static const struct x86_cpu_id longhaul_id[] = {
+       { X86_VENDOR_CENTAUR, 6 },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, longhaul_id);
 
 static int __init longhaul_init(void)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
 
-       if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
+       if (!x86_match_cpu(longhaul_id))
                return -ENODEV;
 
 #ifdef CONFIG_SMP
index 34ea359..8bc9f5f 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <asm/msr.h>
 #include <asm/processor.h>
+#include <asm/cpu_device_id.h>
 
 static struct cpufreq_driver   longrun_driver;
 
@@ -288,6 +289,12 @@ static struct cpufreq_driver longrun_driver = {
        .owner          = THIS_MODULE,
 };
 
+static const struct x86_cpu_id longrun_ids[] = {
+       { X86_VENDOR_TRANSMETA, X86_FAMILY_ANY, X86_MODEL_ANY,
+         X86_FEATURE_LONGRUN },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, longrun_ids);
 
 /**
  * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
@@ -296,12 +303,8 @@ static struct cpufreq_driver longrun_driver = {
  */
 static int __init longrun_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       if (c->x86_vendor != X86_VENDOR_TRANSMETA ||
-           !cpu_has(c, X86_FEATURE_LONGRUN))
+       if (!x86_match_cpu(longrun_ids))
                return -ENODEV;
-
        return cpufreq_register_driver(&longrun_driver);
 }
 
index 6be3e07..827629c 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/processor.h>
 #include <asm/msr.h>
 #include <asm/timer.h>
+#include <asm/cpu_device_id.h>
 
 #include "speedstep-lib.h"
 
@@ -289,21 +290,25 @@ static struct cpufreq_driver p4clockmod_driver = {
        .attr           = p4clockmod_attr,
 };
 
+static const struct x86_cpu_id cpufreq_p4_id[] = {
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
+       {}
+};
+
+/*
+ * Intentionally no MODULE_DEVICE_TABLE here: this driver should not
+ * be auto loaded.  Please don't add one.
+ */
 
 static int __init cpufreq_p4_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
        int ret;
 
        /*
         * THERM_CONTROL is architectural for IA32 now, so
         * we can rely on the capability checks
         */
-       if (c->x86_vendor != X86_VENDOR_INTEL)
-               return -ENODEV;
-
-       if (!test_cpu_cap(c, X86_FEATURE_ACPI) ||
-                               !test_cpu_cap(c, X86_FEATURE_ACC))
+       if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
                return -ENODEV;
 
        ret = cpufreq_register_driver(&p4clockmod_driver);
index b3379d6..af23e0b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/timex.h>
 #include <linux/io.h>
 
+#include <asm/cpu_device_id.h>
 #include <asm/msr.h>
 
 #define POWERNOW_IOPORT 0xfff0          /* it doesn't matter where, as long
@@ -210,6 +211,12 @@ static struct cpufreq_driver powernow_k6_driver = {
        .attr           = powernow_k6_attr,
 };
 
+static const struct x86_cpu_id powernow_k6_ids[] = {
+       { X86_VENDOR_AMD, 5, 12 },
+       { X86_VENDOR_AMD, 5, 13 },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, powernow_k6_ids);
 
 /**
  * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
@@ -220,10 +227,7 @@ static struct cpufreq_driver powernow_k6_driver = {
  */
 static int __init powernow_k6_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
-               ((c->x86_model != 12) && (c->x86_model != 13)))
+       if (!x86_match_cpu(powernow_k6_ids))
                return -ENODEV;
 
        if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
index d71d9f3..cf7e1ee 100644 (file)
@@ -28,6 +28,7 @@
 #include <asm/timer.h>         /* Needed for recalibrate_cpu_khz() */
 #include <asm/msr.h>
 #include <asm/system.h>
+#include <asm/cpu_device_id.h>
 
 #ifdef CONFIG_X86_POWERNOW_K7_ACPI
 #include <linux/acpi.h>
@@ -110,18 +111,19 @@ static int check_fsb(unsigned int fsbspeed)
        return delta < 5;
 }
 
+static const struct x86_cpu_id powernow_k7_cpuids[] = {
+       { X86_VENDOR_AMD, 6, },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
+
 static int check_powernow(void)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
        unsigned int maxei, eax, ebx, ecx, edx;
 
-       if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
-#ifdef MODULE
-               printk(KERN_INFO PFX "This module only works with "
-                               "AMD K7 CPUs\n");
-#endif
+       if (!x86_match_cpu(powernow_k7_cpuids))
                return 0;
-       }
 
        /* Get maximum capabilities */
        maxei = cpuid_eax(0x80000000);
index 8f9b2ce..c0e8164 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/delay.h>
 
 #include <asm/msr.h>
+#include <asm/cpu_device_id.h>
 
 #include <linux/acpi.h>
 #include <linux/mutex.h>
@@ -520,6 +521,15 @@ static int core_voltage_post_transition(struct powernow_k8_data *data,
        return 0;
 }
 
+static const struct x86_cpu_id powernow_k8_ids[] = {
+       /* IO based frequency switching */
+       { X86_VENDOR_AMD, 0xf },
+       /* MSR based frequency switching supported */
+       X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, powernow_k8_ids);
+
 static void check_supported_cpu(void *_rc)
 {
        u32 eax, ebx, ecx, edx;
@@ -527,13 +537,7 @@ static void check_supported_cpu(void *_rc)
 
        *rc = -ENODEV;
 
-       if (__this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_AMD)
-               return;
-
        eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
-       if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
-           ((eax & CPUID_XFAM) < CPUID_XFAM_10H))
-               return;
 
        if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
                if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
@@ -1553,6 +1557,9 @@ static int __cpuinit powernowk8_init(void)
        unsigned int i, supported_cpus = 0, cpu;
        int rv;
 
+       if (!x86_match_cpu(powernow_k8_ids))
+               return -ENODEV;
+
        for_each_online_cpu(i) {
                int rc;
                smp_call_function_single(i, check_supported_cpu, &rc, 1);
index 1e205e6..e42e073 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/timex.h>
 #include <linux/io.h>
 
+#include <asm/cpu_device_id.h>
 #include <asm/msr.h>
 
 #define MMCR_BASE      0xfffef000      /* The default base address */
@@ -150,18 +151,19 @@ static struct cpufreq_driver sc520_freq_driver = {
        .attr   = sc520_freq_attr,
 };
 
+static const struct x86_cpu_id sc520_ids[] = {
+       { X86_VENDOR_AMD, 4, 9 },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, sc520_ids);
 
 static int __init sc520_freq_init(void)
 {
-       struct cpuinfo_x86 *c = &cpu_data(0);
        int err;
 
-       /* Test if we have the right hardware */
-       if (c->x86_vendor != X86_VENDOR_AMD ||
-           c->x86 != 4 || c->x86_model != 9) {
-               pr_debug("no Elan SC520 processor found!\n");
+       if (!x86_match_cpu(sc520_ids))
                return -ENODEV;
-       }
+
        cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
        if (!cpuctl) {
                printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
index 6ea3455..3a953d5 100644 (file)
@@ -25,6 +25,7 @@
 #include <asm/msr.h>
 #include <asm/processor.h>
 #include <asm/cpufeature.h>
+#include <asm/cpu_device_id.h>
 
 #define PFX            "speedstep-centrino: "
 #define MAINTAINER     "cpufreq@vger.kernel.org"
@@ -595,6 +596,24 @@ static struct cpufreq_driver centrino_driver = {
        .owner          = THIS_MODULE,
 };
 
+/*
+ * This doesn't replace the detailed checks above because
+ * the generic CPU IDs don't have a way to match for steppings
+ * or ASCII model IDs.
+ */
+static const struct x86_cpu_id centrino_ids[] = {
+       { X86_VENDOR_INTEL, 6, 9, X86_FEATURE_EST },
+       { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
+       { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
+       { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST },
+       { X86_VENDOR_INTEL, 15, 3, X86_FEATURE_EST },
+       { X86_VENDOR_INTEL, 15, 4, X86_FEATURE_EST },
+       {}
+};
+#if 0
+/* Autoload or not? Do not for now. */
+MODULE_DEVICE_TABLE(x86cpu, centrino_ids);
+#endif
 
 /**
  * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
@@ -612,11 +631,8 @@ static struct cpufreq_driver centrino_driver = {
  */
 static int __init centrino_init(void)
 {
-       struct cpuinfo_x86 *cpu = &cpu_data(0);
-
-       if (!cpu_has(cpu, X86_FEATURE_EST))
+       if (!x86_match_cpu(centrino_ids))
                return -ENODEV;
-
        return cpufreq_register_driver(&centrino_driver);
 }
 
index a748ce7..7432b3a 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/pci.h>
 #include <linux/sched.h>
 
+#include <asm/cpu_device_id.h>
+
 #include "speedstep-lib.h"
 
 
@@ -388,6 +390,16 @@ static struct cpufreq_driver speedstep_driver = {
        .attr   = speedstep_attr,
 };
 
+static const struct x86_cpu_id ss_smi_ids[] = {
+       { X86_VENDOR_INTEL, 6, 0xb, },
+       { X86_VENDOR_INTEL, 6, 0x8, },
+       { X86_VENDOR_INTEL, 15, 2 },
+       {}
+};
+#if 0
+/* Autoload or not? Do not for now. */
+MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids);
+#endif
 
 /**
  * speedstep_init - initializes the SpeedStep CPUFreq driver
@@ -398,6 +410,9 @@ static struct cpufreq_driver speedstep_driver = {
  */
 static int __init speedstep_init(void)
 {
+       if (!x86_match_cpu(ss_smi_ids))
+               return -ENODEV;
+
        /* detect processor */
        speedstep_processor = speedstep_detect_processor();
        if (!speedstep_processor) {
index 8af2d2f..7047821 100644 (file)
@@ -249,6 +249,7 @@ EXPORT_SYMBOL_GPL(speedstep_get_frequency);
  *                 DETECT SPEEDSTEP-CAPABLE PROCESSOR                *
  *********************************************************************/
 
+/* Keep in sync with the x86_cpu_id tables in the different modules */
 unsigned int speedstep_detect_processor(void)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
index c76ead3..6a457fc 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <asm/ist.h>
+#include <asm/cpu_device_id.h>
 
 #include "speedstep-lib.h"
 
@@ -379,6 +380,17 @@ static struct cpufreq_driver speedstep_driver = {
        .attr           = speedstep_attr,
 };
 
+static const struct x86_cpu_id ss_smi_ids[] = {
+       { X86_VENDOR_INTEL, 6, 0xb, },
+       { X86_VENDOR_INTEL, 6, 0x8, },
+       { X86_VENDOR_INTEL, 15, 2 },
+       {}
+};
+#if 0
+/* Not auto loaded currently */
+MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids);
+#endif
+
 /**
  * speedstep_init - initializes the SpeedStep CPUFreq driver
  *
@@ -388,6 +400,9 @@ static struct cpufreq_driver speedstep_driver = {
  */
 static int __init speedstep_init(void)
 {
+       if (!x86_match_cpu(ss_smi_ids))
+               return -ENODEV;
+
        speedstep_processor = speedstep_detect_processor();
 
        switch (speedstep_processor) {
index 29b9469..37b2e94 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/percpu.h>
 #include <linux/smp.h>
 #include <linux/slab.h>
+#include <asm/cpu_device_id.h>
 #include <asm/byteorder.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
@@ -503,12 +504,18 @@ static struct crypto_alg cbc_aes_alg = {
        }
 };
 
+static struct x86_cpu_id padlock_cpu_id[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_XCRYPT),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, padlock_cpu_id);
+
 static int __init padlock_init(void)
 {
        int ret;
        struct cpuinfo_x86 *c = &cpu_data(0);
 
-       if (!cpu_has_xcrypt)
+       if (!x86_match_cpu(padlock_cpu_id))
                return -ENODEV;
 
        if (!cpu_has_xcrypt_enabled) {
index 06bdb4b..9266c0e 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/scatterlist.h>
+#include <asm/cpu_device_id.h>
 #include <asm/i387.h>
 
 struct padlock_sha_desc {
@@ -526,6 +527,12 @@ static struct shash_alg sha256_alg_nano = {
        }
 };
 
+static struct x86_cpu_id padlock_sha_ids[] = {
+       X86_FEATURE_MATCH(X86_FEATURE_PHE),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, padlock_sha_ids);
+
 static int __init padlock_init(void)
 {
        int rc = -ENODEV;
@@ -533,15 +540,8 @@ static int __init padlock_init(void)
        struct shash_alg *sha1;
        struct shash_alg *sha256;
 
-       if (!cpu_has_phe) {
-               printk(KERN_NOTICE PFX "VIA PadLock Hash Engine not detected.\n");
-               return -ENODEV;
-       }
-
-       if (!cpu_has_phe_enabled) {
-               printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
+       if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled)
                return -ENODEV;
-       }
 
        /* Register the newly added algorithm module if on *
        * VIA Nano processor, or else just do as before */
index af08ce7..bce53fa 100644 (file)
@@ -1619,11 +1619,7 @@ static ssize_t store_new_id(struct device_driver *drv, const char *buf,
        list_add_tail(&dynid->list, &hdrv->dyn_list);
        spin_unlock(&hdrv->dyn_lock);
 
-       ret = 0;
-       if (get_driver(&hdrv->driver)) {
-               ret = driver_attach(&hdrv->driver);
-               put_driver(&hdrv->driver);
-       }
+       ret = driver_attach(&hdrv->driver);
 
        return ret ? : count;
 }
index 36484db..9ffbfc5 100644 (file)
@@ -37,81 +37,6 @@ struct vmbus_channel_message_table_entry {
        void (*message_handler)(struct vmbus_channel_message_header *msg);
 };
 
-#define MAX_MSG_TYPES                    4
-#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8
-
-static const uuid_le
-       supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
-       /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
-       /* Storage - SCSI */
-       {
-               .b  = {
-                       0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
-                       0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
-               }
-       },
-
-       /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
-       /* Network */
-       {
-               .b = {
-                       0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
-                       0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
-               }
-       },
-
-       /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
-       /* Input */
-       {
-               .b = {
-                       0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
-                       0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
-               }
-       },
-
-       /* {32412632-86cb-44a2-9b5c-50d1417354f5} */
-       /* IDE */
-       {
-               .b = {
-                       0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
-                       0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
-               }
-       },
-       /* 0E0B6031-5213-4934-818B-38D90CED39DB */
-       /* Shutdown */
-       {
-               .b = {
-                       0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
-                       0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
-               }
-       },
-       /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */
-       /* TimeSync */
-       {
-               .b = {
-                       0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49,
-                       0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf
-               }
-       },
-       /* {57164f39-9115-4e78-ab55-382f3bd5422d} */
-       /* Heartbeat */
-       {
-               .b = {
-                       0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e,
-                       0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d
-               }
-       },
-       /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */
-       /* KVP */
-       {
-               .b = {
-                       0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d,
-                       0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3,  0xe6
-       }
-       },
-
-};
-
 
 /**
  * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
@@ -321,20 +246,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
        struct vmbus_channel *newchannel;
        uuid_le *guidtype;
        uuid_le *guidinstance;
-       int i;
-       int fsupported = 0;
 
        offer = (struct vmbus_channel_offer_channel *)hdr;
-       for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
-               if (!uuid_le_cmp(offer->offer.if_type,
-                               supported_device_classes[i])) {
-                       fsupported = 1;
-                       break;
-               }
-       }
-
-       if (!fsupported)
-               return;
 
        guidtype = &offer->offer.if_type;
        guidinstance = &offer->offer.if_instance;
index 12aa97f..15956bd 100644 (file)
@@ -155,9 +155,9 @@ int hv_init(void)
        union hv_x64_msr_hypercall_contents hypercall_msr;
        void *virtaddr = NULL;
 
-       memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS);
+       memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
        memset(hv_context.synic_message_page, 0,
-              sizeof(void *) * MAX_NUM_CPUS);
+              sizeof(void *) * NR_CPUS);
 
        if (!query_hypervisor_presence())
                goto cleanup;
index 0e8343f..6186025 100644 (file)
@@ -28,8 +28,6 @@
 #include <linux/workqueue.h>
 #include <linux/hyperv.h>
 
-#include "hv_kvp.h"
-
 
 
 /*
 static struct {
        bool active; /* transaction status - active or not */
        int recv_len; /* number of bytes received. */
-       int index; /* current index */
+       struct hv_kvp_msg  *kvp_msg; /* current message */
        struct vmbus_channel *recv_channel; /* chn we got the request */
        u64 recv_req_id; /* request ID. */
+       void *kvp_context; /* for the channel callback */
 } kvp_transaction;
 
 static void kvp_send_key(struct work_struct *dummy);
@@ -73,15 +72,20 @@ kvp_register(void)
 {
 
        struct cn_msg *msg;
+       struct hv_kvp_msg *kvp_msg;
+       char *version;
 
-       msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC);
+       msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC);
 
        if (msg) {
+               kvp_msg = (struct hv_kvp_msg *)msg->data;
+               version = kvp_msg->body.kvp_register.version;
                msg->id.idx =  CN_KVP_IDX;
                msg->id.val = CN_KVP_VAL;
-               msg->seq = KVP_REGISTER;
-               strcpy(msg->data, HV_DRV_VERSION);
-               msg->len = strlen(HV_DRV_VERSION) + 1;
+
+               kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+               strcpy(version, HV_DRV_VERSION);
+               msg->len = sizeof(struct hv_kvp_msg);
                cn_netlink_send(msg, 0, GFP_ATOMIC);
                kfree(msg);
        }
@@ -103,23 +107,28 @@ kvp_work_func(struct work_struct *dummy)
 static void
 kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 {
-       struct hv_ku_msg *message;
+       struct hv_kvp_msg *message;
+       struct hv_kvp_msg_enumerate *data;
 
-       message = (struct hv_ku_msg *)msg->data;
-       if (msg->seq == KVP_REGISTER) {
+       message = (struct hv_kvp_msg *)msg->data;
+       switch (message->kvp_hdr.operation) {
+       case KVP_OP_REGISTER:
                pr_info("KVP: user-mode registering done.\n");
                kvp_register();
-       }
+               kvp_transaction.active = false;
+               hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+               break;
 
-       if (msg->seq == KVP_USER_SET) {
+       default:
+               data = &message->body.kvp_enum_data;
                /*
                 * Complete the transaction by forwarding the key value
                 * to the host. But first, cancel the timeout.
                 */
                if (cancel_delayed_work_sync(&kvp_work))
-                       kvp_respond_to_host(message->kvp_key,
-                                               message->kvp_value,
-                                               !strlen(message->kvp_key));
+                       kvp_respond_to_host(data->data.key,
+                                        data->data.value,
+                                       !strlen(data->data.key));
        }
 }
 
@@ -127,19 +136,105 @@ static void
 kvp_send_key(struct work_struct *dummy)
 {
        struct cn_msg *msg;
-       int index = kvp_transaction.index;
+       struct hv_kvp_msg *message;
+       struct hv_kvp_msg *in_msg;
+       __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
+       __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool;
+       __u32 val32;
+       __u64 val64;
 
        msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
+       if (!msg)
+               return;
 
-       if (msg) {
-               msg->id.idx =  CN_KVP_IDX;
-               msg->id.val = CN_KVP_VAL;
-               msg->seq = KVP_KERNEL_GET;
-               ((struct hv_ku_msg *)msg->data)->kvp_index = index;
-               msg->len = sizeof(struct hv_ku_msg);
-               cn_netlink_send(msg, 0, GFP_ATOMIC);
-               kfree(msg);
+       msg->id.idx =  CN_KVP_IDX;
+       msg->id.val = CN_KVP_VAL;
+
+       message = (struct hv_kvp_msg *)msg->data;
+       message->kvp_hdr.operation = operation;
+       message->kvp_hdr.pool = pool;
+       in_msg = kvp_transaction.kvp_msg;
+
+       /*
+        * The key/value strings sent from the host are encoded in
+        * in utf16; convert it to utf8 strings.
+        * The host assures us that the utf16 strings will not exceed
+        * the max lengths specified. We will however, reserve room
+        * for the string terminating character - in the utf16s_utf8s()
+        * function we limit the size of the buffer where the converted
+        * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee
+        * that the strings can be properly terminated!
+        */
+
+       switch (message->kvp_hdr.operation) {
+       case KVP_OP_SET:
+               switch (in_msg->body.kvp_set.data.value_type) {
+               case REG_SZ:
+                       /*
+                        * The value is a string - utf16 encoding.
+                        */
+                       message->body.kvp_set.data.value_size =
+                               utf16s_to_utf8s(
+                               (wchar_t *)in_msg->body.kvp_set.data.value,
+                               in_msg->body.kvp_set.data.value_size,
+                               UTF16_LITTLE_ENDIAN,
+                               message->body.kvp_set.data.value,
+                               HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1;
+                               break;
+
+               case REG_U32:
+                       /*
+                        * The value is a 32 bit scalar.
+                        * We save this as a utf8 string.
+                        */
+                       val32 = in_msg->body.kvp_set.data.value_u32;
+                       message->body.kvp_set.data.value_size =
+                               sprintf(message->body.kvp_set.data.value,
+                                       "%d", val32) + 1;
+                       break;
+
+               case REG_U64:
+                       /*
+                        * The value is a 64 bit scalar.
+                        * We save this as a utf8 string.
+                        */
+                       val64 = in_msg->body.kvp_set.data.value_u64;
+                       message->body.kvp_set.data.value_size =
+                               sprintf(message->body.kvp_set.data.value,
+                                       "%llu", val64) + 1;
+                       break;
+
+               }
+       case KVP_OP_GET:
+               message->body.kvp_set.data.key_size =
+                       utf16s_to_utf8s(
+                       (wchar_t *)in_msg->body.kvp_set.data.key,
+                       in_msg->body.kvp_set.data.key_size,
+                       UTF16_LITTLE_ENDIAN,
+                       message->body.kvp_set.data.key,
+                       HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
+                       break;
+
+       case KVP_OP_DELETE:
+               message->body.kvp_delete.key_size =
+                       utf16s_to_utf8s(
+                       (wchar_t *)in_msg->body.kvp_delete.key,
+                       in_msg->body.kvp_delete.key_size,
+                       UTF16_LITTLE_ENDIAN,
+                       message->body.kvp_delete.key,
+                       HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
+                       break;
+
+       case KVP_OP_ENUMERATE:
+               message->body.kvp_enum_data.index =
+                       in_msg->body.kvp_enum_data.index;
+                       break;
        }
+
+       msg->len = sizeof(struct hv_kvp_msg);
+       cn_netlink_send(msg, 0, GFP_ATOMIC);
+       kfree(msg);
+
        return;
 }
 
@@ -151,10 +246,11 @@ static void
 kvp_respond_to_host(char *key, char *value, int error)
 {
        struct hv_kvp_msg  *kvp_msg;
-       struct hv_kvp_msg_enumerate  *kvp_data;
+       struct hv_kvp_exchg_msg_value  *kvp_data;
        char    *key_name;
        struct icmsg_hdr *icmsghdrp;
-       int     keylen, valuelen;
+       int     keylen = 0;
+       int     valuelen = 0;
        u32     buf_len;
        struct vmbus_channel *channel;
        u64     req_id;
@@ -181,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error)
 
        kvp_transaction.active = false;
 
+       icmsghdrp = (struct icmsg_hdr *)
+                       &recv_buffer[sizeof(struct vmbuspipe_hdr)];
+
        if (channel->onchannel_callback == NULL)
                /*
                 * We have raced with util driver being unloaded;
@@ -188,41 +287,67 @@ kvp_respond_to_host(char *key, char *value, int error)
                 */
                return;
 
-       icmsghdrp = (struct icmsg_hdr *)
-                       &recv_buffer[sizeof(struct vmbuspipe_hdr)];
-       kvp_msg = (struct hv_kvp_msg *)
-                       &recv_buffer[sizeof(struct vmbuspipe_hdr) +
-                       sizeof(struct icmsg_hdr)];
-       kvp_data = &kvp_msg->kvp_data;
-       key_name = key;
 
        /*
-        * If the error parameter is set, terminate the host's enumeration.
+        * If the error parameter is set, terminate the host's enumeration
+        * on this pool.
         */
        if (error) {
                /*
-                * We don't support this index or the we have timedout;
-                * terminate the host-side iteration by returning an error.
+                * Something failed or the we have timedout;
+                * terminate the current  host-side iteration.
                 */
-               icmsghdrp->status = HV_E_FAIL;
+               icmsghdrp->status = HV_S_CONT;
                goto response_done;
        }
 
+       icmsghdrp->status = HV_S_OK;
+
+       kvp_msg = (struct hv_kvp_msg *)
+                       &recv_buffer[sizeof(struct vmbuspipe_hdr) +
+                       sizeof(struct icmsg_hdr)];
+
+       switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+       case KVP_OP_GET:
+               kvp_data = &kvp_msg->body.kvp_get.data;
+               goto copy_value;
+
+       case KVP_OP_SET:
+       case KVP_OP_DELETE:
+               goto response_done;
+
+       default:
+               break;
+       }
+
+       kvp_data = &kvp_msg->body.kvp_enum_data.data;
+       key_name = key;
+
        /*
         * The windows host expects the key/value pair to be encoded
-        * in utf16.
+        * in utf16. Ensure that the key/value size reported to the host
+        * will be less than or equal to the MAX size (including the
+        * terminating character).
         */
        keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
-                               (wchar_t *) kvp_data->data.key,
-                               HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2);
-       kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */
+                               (wchar_t *) kvp_data->key,
+                               (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2);
+       kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
+
+copy_value:
        valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
-                               (wchar_t *) kvp_data->data.value,
-                               HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2);
-       kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */
+                               (wchar_t *) kvp_data->value,
+                               (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
+       kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */
 
-       kvp_data->data.value_type = REG_SZ; /* all our values are strings */
-       icmsghdrp->status = HV_S_OK;
+       /*
+        * If the utf8s to utf16s conversion failed; notify host
+        * of the error.
+        */
+       if ((keylen < 0) || (valuelen < 0))
+               icmsghdrp->status = HV_E_FAIL;
+
+       kvp_data->value_type = REG_SZ; /* all our values are strings */
 
 response_done:
        icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
@@ -249,11 +374,18 @@ void hv_kvp_onchannelcallback(void *context)
        u64 requestid;
 
        struct hv_kvp_msg *kvp_msg;
-       struct hv_kvp_msg_enumerate *kvp_data;
 
        struct icmsg_hdr *icmsghdrp;
        struct icmsg_negotiate *negop = NULL;
 
+       if (kvp_transaction.active) {
+               /*
+                * We will defer processing this callback once
+                * the current transaction is complete.
+                */
+               kvp_transaction.kvp_context = context;
+               return;
+       }
 
        vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
 
@@ -268,29 +400,16 @@ void hv_kvp_onchannelcallback(void *context)
                                sizeof(struct vmbuspipe_hdr) +
                                sizeof(struct icmsg_hdr)];
 
-                       kvp_data = &kvp_msg->kvp_data;
-
-                       /*
-                        * We only support the "get" operation on
-                        * "KVP_POOL_AUTO" pool.
-                        */
-
-                       if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) ||
-                               (kvp_msg->kvp_hdr.operation !=
-                               KVP_OP_ENUMERATE)) {
-                               icmsghdrp->status = HV_E_FAIL;
-                               goto callback_done;
-                       }
-
                        /*
                         * Stash away this global state for completing the
                         * transaction; note transactions are serialized.
                         */
+
                        kvp_transaction.recv_len = recvlen;
                        kvp_transaction.recv_channel = channel;
                        kvp_transaction.recv_req_id = requestid;
                        kvp_transaction.active = true;
-                       kvp_transaction.index = kvp_data->index;
+                       kvp_transaction.kvp_msg = kvp_msg;
 
                        /*
                         * Get the information from the
@@ -308,8 +427,6 @@ void hv_kvp_onchannelcallback(void *context)
 
                }
 
-callback_done:
-
                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
                        | ICMSGHDRFLAG_RESPONSE;
 
@@ -330,6 +447,14 @@ hv_kvp_init(struct hv_util_service *srv)
                return err;
        recv_buffer = srv->recv_buffer;
 
+       /*
+        * When this driver loads, the user level daemon that
+        * processes the host requests may not yet be running.
+        * Defer processing channel callbacks until the daemon
+        * has registered.
+        */
+       kvp_transaction.active = true;
+
        return 0;
 }
 
diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h
deleted file mode 100644 (file)
index 9b765d7..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * An implementation of HyperV key value pair (KVP) functionality for Linux.
- *
- *
- * Copyright (C) 2010, Novell, Inc.
- * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
- *
- * 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.
- *
- * 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, GOOD TITLE or
- * NON INFRINGEMENT.  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, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-#ifndef        _KVP_H
-#define        _KVP_H_
-
-/*
- * Maximum value size - used for both key names and value data, and includes
- * any applicable NULL terminators.
- *
- * Note:  This limit is somewhat arbitrary, but falls easily within what is
- * supported for all native guests (back to Win 2000) and what is reasonable
- * for the IC KVP exchange functionality.  Note that Windows Me/98/95 are
- * limited to 255 character key names.
- *
- * MSDN recommends not storing data values larger than 2048 bytes in the
- * registry.
- *
- * Note:  This value is used in defining the KVP exchange message - this value
- * cannot be modified without affecting the message size and compatibility.
- */
-
-/*
- * bytes, including any null terminators
- */
-#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE          (2048)
-
-
-/*
- * Maximum key size - the registry limit for the length of an entry name
- * is 256 characters, including the null terminator
- */
-
-#define HV_KVP_EXCHANGE_MAX_KEY_SIZE            (512)
-
-/*
- * In Linux, we implement the KVP functionality in two components:
- * 1) The kernel component which is packaged as part of the hv_utils driver
- * is responsible for communicating with the host and responsible for
- * implementing the host/guest protocol. 2) A user level daemon that is
- * responsible for data gathering.
- *
- * Host/Guest Protocol: The host iterates over an index and expects the guest
- * to assign a key name to the index and also return the value corresponding to
- * the key. The host will have atmost one KVP transaction outstanding at any
- * given point in time. The host side iteration stops when the guest returns
- * an error. Microsoft has specified the following mapping of key names to
- * host specified index:
- *
- *     Index           Key Name
- *     0               FullyQualifiedDomainName
- *     1               IntegrationServicesVersion
- *     2               NetworkAddressIPv4
- *     3               NetworkAddressIPv6
- *     4               OSBuildNumber
- *     5               OSName
- *     6               OSMajorVersion
- *     7               OSMinorVersion
- *     8               OSVersion
- *     9               ProcessorArchitecture
- *
- * The Windows host expects the Key Name and Key Value to be encoded in utf16.
- *
- * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
- * data gathering functionality in a user mode daemon. The user level daemon
- * is also responsible for binding the key name to the index as well. The
- * kernel and user-level daemon communicate using a connector channel.
- *
- * The user mode component first registers with the
- * the kernel component. Subsequently, the kernel component requests, data
- * for the specified keys. In response to this message the user mode component
- * fills in the value corresponding to the specified key. We overload the
- * sequence field in the cn_msg header to define our KVP message types.
- *
- *
- * The kernel component simply acts as a conduit for communication between the
- * Windows host and the user-level daemon. The kernel component passes up the
- * index received from the Host to the user-level daemon. If the index is
- * valid (supported), the corresponding key as well as its
- * value (both are strings) is returned. If the index is invalid
- * (not supported), a NULL key string is returned.
- */
-
-/*
- *
- * The following definitions are shared with the user-mode component; do not
- * change any of this without making the corresponding changes in
- * the KVP user-mode component.
- */
-
-#define CN_KVP_VAL             0x1 /* This supports queries from the kernel */
-#define CN_KVP_USER_VAL       0x2 /* This supports queries from the user */
-
-enum hv_ku_op {
-       KVP_REGISTER = 0, /* Register the user mode component */
-       KVP_KERNEL_GET, /* Kernel is requesting the value */
-       KVP_KERNEL_SET, /* Kernel is providing the value */
-       KVP_USER_GET,  /* User is requesting the value */
-       KVP_USER_SET  /* User is providing the value */
-};
-
-struct hv_ku_msg {
-       __u32 kvp_index; /* Key index */
-       __u8  kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
-       __u8  kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key  value */
-};
-
-
-
-
-#ifdef __KERNEL__
-
-/*
- * Registry value types.
- */
-
-#define REG_SZ 1
-
-enum hv_kvp_exchg_op {
-       KVP_OP_GET = 0,
-       KVP_OP_SET,
-       KVP_OP_DELETE,
-       KVP_OP_ENUMERATE,
-       KVP_OP_COUNT /* Number of operations, must be last. */
-};
-
-enum hv_kvp_exchg_pool {
-       KVP_POOL_EXTERNAL = 0,
-       KVP_POOL_GUEST,
-       KVP_POOL_AUTO,
-       KVP_POOL_AUTO_EXTERNAL,
-       KVP_POOL_AUTO_INTERNAL,
-       KVP_POOL_COUNT /* Number of pools, must be last. */
-};
-
-struct hv_kvp_hdr {
-       u8 operation;
-       u8 pool;
-};
-
-struct hv_kvp_exchg_msg_value {
-       u32 value_type;
-       u32 key_size;
-       u32 value_size;
-       u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
-       u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
-};
-
-struct hv_kvp_msg_enumerate {
-       u32 index;
-       struct hv_kvp_exchg_msg_value data;
-};
-
-struct hv_kvp_msg {
-       struct hv_kvp_hdr       kvp_hdr;
-       struct hv_kvp_msg_enumerate     kvp_data;
-};
-
-int hv_kvp_init(struct hv_util_service *);
-void hv_kvp_deinit(void);
-void hv_kvp_onchannelcallback(void *);
-
-#endif /* __KERNEL__ */
-#endif /* _KVP_H */
-
index 55d58f2..dbb8b8e 100644 (file)
@@ -28,9 +28,6 @@
 #include <linux/reboot.h>
 #include <linux/hyperv.h>
 
-#include "hv_kvp.h"
-
-
 static void shutdown_onchannelcallback(void *context);
 static struct hv_util_service util_shutdown = {
        .util_cb = shutdown_onchannelcallback,
index 6d7d286..699f0d8 100644 (file)
@@ -457,7 +457,6 @@ static const uuid_le VMBUS_SERVICE_ID = {
        },
 };
 
-#define MAX_NUM_CPUS   32
 
 
 struct hv_input_signal_event_buffer {
@@ -483,8 +482,8 @@ struct hv_context {
        /* 8-bytes aligned of the buffer above */
        struct hv_input_signal_event *signal_event_param;
 
-       void *synic_message_page[MAX_NUM_CPUS];
-       void *synic_event_page[MAX_NUM_CPUS];
+       void *synic_message_page[NR_CPUS];
+       void *synic_event_page[NR_CPUS];
 };
 
 extern struct hv_context hv_context;
index a6c6ec3..249ac46 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/moduleparam.h>
 #include <asm/msr.h>
 #include <asm/processor.h>
+#include <asm/cpu_device_id.h>
 
 #define DRVNAME        "coretemp"
 
@@ -759,13 +760,23 @@ static struct notifier_block coretemp_cpu_notifier __refdata = {
        .notifier_call = coretemp_cpu_callback,
 };
 
+static const struct x86_cpu_id coretemp_ids[] = {
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTS },
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, coretemp_ids);
+
 static int __init coretemp_init(void)
 {
        int i, err = -ENODEV;
 
-       /* quick check if we run Intel */
-       if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
-               goto exit;
+       /*
+        * CPUID.06H.EAX[0] indicates whether the CPU has thermal
+        * sensors. We check this bit only, all the early CPUs
+        * without thermal sensors will be filtered out.
+        */
+       if (!x86_match_cpu(coretemp_ids))
+               return -ENODEV;
 
        err = platform_driver_register(&coretemp_driver);
        if (err)
index 8eac67d..8689664 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/cpu.h>
 #include <asm/msr.h>
 #include <asm/processor.h>
+#include <asm/cpu_device_id.h>
 
 #define DRVNAME        "via_cputemp"
 
@@ -308,15 +309,20 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = {
        .notifier_call = via_cputemp_cpu_callback,
 };
 
+static const struct x86_cpu_id cputemp_ids[] = {
+       { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */
+       { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */
+       { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
+
 static int __init via_cputemp_init(void)
 {
        int i, err;
 
-       if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) {
-               printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n");
-               err = -ENODEV;
-               goto exit;
-       }
+       if (!x86_match_cpu(cputemp_ids))
+               return -ENODEV;
 
        err = platform_driver_register(&via_cputemp_driver);
        if (err)
index 54ab97b..1c15e9b 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/notifier.h>
 #include <linux/cpu.h>
 #include <linux/module.h>
+#include <asm/cpu_device_id.h>
 #include <asm/mwait.h>
 #include <asm/msr.h>
 
@@ -81,6 +82,17 @@ static unsigned int mwait_substates;
 /* Reliable LAPIC Timer States, bit 1 for C1 etc.  */
 static unsigned int lapic_timer_reliable_states = (1 << 1);     /* Default to only C1 */
 
+struct idle_cpu {
+       struct cpuidle_state *state_table;
+
+       /*
+        * Hardware C-state auto-demotion may not always be optimal.
+        * Indicate which enable bits to clear here.
+        */
+       unsigned long auto_demotion_disable_flags;
+};
+
+static const struct idle_cpu *icpu;
 static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
 static int intel_idle(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv, int index);
@@ -88,12 +100,6 @@ static int intel_idle(struct cpuidle_device *dev,
 static struct cpuidle_state *cpuidle_state_table;
 
 /*
- * Hardware C-state auto-demotion may not always be optimal.
- * Indicate which enable bits to clear here.
- */
-static unsigned long long auto_demotion_disable_flags;
-
-/*
  * Set this flag for states where the HW flushes the TLB for us
  * and so we don't need cross-calls to keep it consistent.
  * If this flag is set, SW flushes the TLB, so even if the
@@ -319,27 +325,68 @@ static void auto_demotion_disable(void *dummy)
        unsigned long long msr_bits;
 
        rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
-       msr_bits &= ~auto_demotion_disable_flags;
+       msr_bits &= ~(icpu->auto_demotion_disable_flags);
        wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
 }
 
+static const struct idle_cpu idle_cpu_nehalem = {
+       .state_table = nehalem_cstates,
+       .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE,
+};
+
+static const struct idle_cpu idle_cpu_atom = {
+       .state_table = atom_cstates,
+};
+
+static const struct idle_cpu idle_cpu_lincroft = {
+       .state_table = atom_cstates,
+       .auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE,
+};
+
+static const struct idle_cpu idle_cpu_snb = {
+       .state_table = snb_cstates,
+};
+
+#define ICPU(model, cpu) \
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu }
+
+static const struct x86_cpu_id intel_idle_ids[] = {
+       ICPU(0x1a, idle_cpu_nehalem),
+       ICPU(0x1e, idle_cpu_nehalem),
+       ICPU(0x1f, idle_cpu_nehalem),
+       ICPU(0x25, idle_cpu_nehalem),
+       ICPU(0x2c, idle_cpu_nehalem),
+       ICPU(0x2e, idle_cpu_nehalem),
+       ICPU(0x1c, idle_cpu_atom),
+       ICPU(0x26, idle_cpu_lincroft),
+       ICPU(0x2f, idle_cpu_nehalem),
+       ICPU(0x2a, idle_cpu_snb),
+       ICPU(0x2d, idle_cpu_snb),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids);
+
 /*
  * intel_idle_probe()
  */
 static int intel_idle_probe(void)
 {
        unsigned int eax, ebx, ecx;
+       const struct x86_cpu_id *id;
 
        if (max_cstate == 0) {
                pr_debug(PREFIX "disabled\n");
                return -EPERM;
        }
 
-       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
-               return -ENODEV;
-
-       if (!boot_cpu_has(X86_FEATURE_MWAIT))
+       id = x86_match_cpu(intel_idle_ids);
+       if (!id) {
+               if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
+                   boot_cpu_data.x86 == 6)
+                       pr_debug(PREFIX "does not run on family %d model %d\n",
+                               boot_cpu_data.x86, boot_cpu_data.x86_model);
                return -ENODEV;
+       }
 
        if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
                return -ENODEV;
@@ -353,43 +400,8 @@ static int intel_idle_probe(void)
 
        pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates);
 
-
-       if (boot_cpu_data.x86 != 6)     /* family 6 */
-               return -ENODEV;
-
-       switch (boot_cpu_data.x86_model) {
-
-       case 0x1A:      /* Core i7, Xeon 5500 series */
-       case 0x1E:      /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
-       case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
-       case 0x2E:      /* Nehalem-EX Xeon */
-       case 0x2F:      /* Westmere-EX Xeon */
-       case 0x25:      /* Westmere */
-       case 0x2C:      /* Westmere */
-               cpuidle_state_table = nehalem_cstates;
-               auto_demotion_disable_flags =
-                       (NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE);
-               break;
-
-       case 0x1C:      /* 28 - Atom Processor */
-               cpuidle_state_table = atom_cstates;
-               break;
-
-       case 0x26:      /* 38 - Lincroft Atom Processor */
-               cpuidle_state_table = atom_cstates;
-               auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE;
-               break;
-
-       case 0x2A:      /* SNB */
-       case 0x2D:      /* SNB Xeon */
-               cpuidle_state_table = snb_cstates;
-               break;
-
-       default:
-               pr_debug(PREFIX "does not run on family %d model %d\n",
-                       boot_cpu_data.x86, boot_cpu_data.x86_model);
-               return -ENODEV;
-       }
+       icpu = (const struct idle_cpu *)id->driver_data;
+       cpuidle_state_table = icpu->state_table;
 
        if (boot_cpu_has(X86_FEATURE_ARAT))     /* Always Reliable APIC Timer */
                lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE;
@@ -470,7 +482,7 @@ static int intel_idle_cpuidle_driver_init(void)
                drv->state_count += 1;
        }
 
-       if (auto_demotion_disable_flags)
+       if (icpu->auto_demotion_disable_flags)
                on_each_cpu(auto_demotion_disable, NULL, 1);
 
        return 0;
@@ -522,7 +534,7 @@ int intel_idle_cpu_init(int cpu)
                return -EIO;
        }
 
-       if (auto_demotion_disable_flags)
+       if (icpu->auto_demotion_disable_flags)
                smp_call_function_single(cpu, auto_demotion_disable, NULL, 1);
 
        return 0;
index c351aa4..da739d9 100644 (file)
@@ -449,7 +449,6 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut
        } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
                gameport_disconnect_port(gameport);
                error = gameport_bind_driver(gameport, to_gameport_driver(drv));
-               put_driver(drv);
        } else {
                error = -EINVAL;
        }
index ba70058..d0f7533 100644 (file)
@@ -441,7 +441,6 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
        } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
                serio_disconnect_port(serio);
                error = serio_bind_driver(serio, to_serio_driver(drv));
-               put_driver(drv);
                serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT);
        } else {
                error = -EINVAL;
index a1e6c2a..e118361 100644 (file)
@@ -285,7 +285,6 @@ static void __exit cx18_alsa_exit(void)
 
        drv = driver_find("cx18", &pci_bus_type);
        ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
-       put_driver(drv);
 
        cx18_ext_init = NULL;
        printk(KERN_INFO "cx18-alsa: module unload complete\n");
index d0fbfcf..e5e7fa9 100644 (file)
@@ -1293,7 +1293,6 @@ static int __init ivtvfb_init(void)
 
        drv = driver_find("ivtv", &pci_bus_type);
        err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
-       put_driver(drv);
        if (!registered) {
                printk(KERN_ERR "ivtvfb:  no cards found\n");
                return -ENODEV;
@@ -1310,7 +1309,6 @@ static void ivtvfb_cleanup(void)
 
        drv = driver_find("ivtv", &pci_bus_type);
        err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
-       put_driver(drv);
 }
 
 module_init(ivtvfb_init);
index 8ea4ee1..63eccb5 100644 (file)
@@ -344,16 +344,13 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd)
                return -ENODEV;
        ret = driver_for_each_device(driver, NULL, fmd,
                                     fimc_register_callback);
-       put_driver(driver);
        if (ret)
                return ret;
 
        driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
-       if (driver) {
+       if (driver)
                ret = driver_for_each_device(driver, NULL, fmd,
                                             csis_register_callback);
-               put_driver(driver);
-       }
        return ret;
 }
 
index 7884bae..f7ca5cc 100644 (file)
@@ -58,7 +58,6 @@ static struct v4l2_subdev *find_and_register_subdev(
        }
 
 done:
-       put_driver(drv);
        return sd;
 }
 
index f320f46..e8c42d6 100644 (file)
@@ -915,9 +915,7 @@ static int phy_probe(struct device *dev)
 
        phydev = to_phy_device(dev);
 
-       /* Make sure the driver is held.
-        * XXX -- Is this correct? */
-       drv = get_driver(phydev->dev.driver);
+       drv = phydev->dev.driver;
        phydrv = to_phy_driver(drv);
        phydev->drv = phydrv;
 
@@ -957,8 +955,6 @@ static int phy_remove(struct device *dev)
 
        if (phydev->drv->remove)
                phydev->drv->remove(phydev);
-
-       put_driver(dev->driver);
        phydev->drv = NULL;
 
        return 0;
index 3623d65..8d9616b 100644 (file)
@@ -72,9 +72,7 @@ int pci_add_dynid(struct pci_driver *drv,
        list_add_tail(&dynid->node, &drv->dynids.list);
        spin_unlock(&drv->dynids.lock);
 
-       get_driver(&drv->driver);
        retval = driver_attach(&drv->driver);
-       put_driver(&drv->driver);
 
        return retval;
 }
@@ -190,43 +188,34 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
 static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
 
 static int
-pci_create_newid_file(struct pci_driver *drv)
+pci_create_newid_files(struct pci_driver *drv)
 {
        int error = 0;
-       if (drv->probe != NULL)
-               error = driver_create_file(&drv->driver, &driver_attr_new_id);
-       return error;
-}
-
-static void pci_remove_newid_file(struct pci_driver *drv)
-{
-       driver_remove_file(&drv->driver, &driver_attr_new_id);
-}
 
-static int
-pci_create_removeid_file(struct pci_driver *drv)
-{
-       int error = 0;
-       if (drv->probe != NULL)
-               error = driver_create_file(&drv->driver,&driver_attr_remove_id);
+       if (drv->probe != NULL) {
+               error = driver_create_file(&drv->driver, &driver_attr_new_id);
+               if (error == 0) {
+                       error = driver_create_file(&drv->driver,
+                                       &driver_attr_remove_id);
+                       if (error)
+                               driver_remove_file(&drv->driver,
+                                               &driver_attr_new_id);
+               }
+       }
        return error;
 }
 
-static void pci_remove_removeid_file(struct pci_driver *drv)
+static void pci_remove_newid_files(struct pci_driver *drv)
 {
        driver_remove_file(&drv->driver, &driver_attr_remove_id);
+       driver_remove_file(&drv->driver, &driver_attr_new_id);
 }
 #else /* !CONFIG_HOTPLUG */
-static inline int pci_create_newid_file(struct pci_driver *drv)
+static inline int pci_create_newid_files(struct pci_driver *drv)
 {
        return 0;
 }
-static inline void pci_remove_newid_file(struct pci_driver *drv) {}
-static inline int pci_create_removeid_file(struct pci_driver *drv)
-{
-       return 0;
-}
-static inline void pci_remove_removeid_file(struct pci_driver *drv) {}
+static inline void pci_remove_newid_files(struct pci_driver *drv) {}
 #endif
 
 /**
@@ -1138,18 +1127,12 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
        if (error)
                goto out;
 
-       error = pci_create_newid_file(drv);
+       error = pci_create_newid_files(drv);
        if (error)
                goto out_newid;
-
-       error = pci_create_removeid_file(drv);
-       if (error)
-               goto out_removeid;
 out:
        return error;
 
-out_removeid:
-       pci_remove_newid_file(drv);
 out_newid:
        driver_unregister(&drv->driver);
        goto out;
@@ -1168,8 +1151,7 @@ out_newid:
 void
 pci_unregister_driver(struct pci_driver *drv)
 {
-       pci_remove_removeid_file(drv);
-       pci_remove_newid_file(drv);
+       pci_remove_newid_files(drv);
        driver_unregister(&drv->driver);
        pci_free_dynids(drv);
 }
index 1620088..4010901 100644 (file)
@@ -593,7 +593,7 @@ static pci_ers_result_t pcifront_common_process(int cmd,
        }
        pdrv = pcidev->driver;
 
-       if (get_driver(&pdrv->driver)) {
+       if (pdrv) {
                if (pdrv->err_handler && pdrv->err_handler->error_detected) {
                        dev_dbg(&pcidev->dev,
                                "trying to call AER service\n");
@@ -623,7 +623,6 @@ static pci_ers_result_t pcifront_common_process(int cmd,
                                }
                        }
                }
-               put_driver(&pdrv->driver);
        }
        if (!flag)
                result = PCI_ERS_RESULT_NONE;
index 1932029..079629b 100644 (file)
@@ -127,10 +127,7 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
        list_add_tail(&dynid->node, &pdrv->dynids.list);
        mutex_unlock(&pdrv->dynids.lock);
 
-       if (get_driver(&pdrv->drv)) {
-               retval = driver_attach(&pdrv->drv);
-               put_driver(&pdrv->drv);
-       }
+       retval = driver_attach(&pdrv->drv);
 
        if (retval)
                return retval;
@@ -160,6 +157,11 @@ pcmcia_create_newid_file(struct pcmcia_driver *drv)
        return error;
 }
 
+static void
+pcmcia_remove_newid_file(struct pcmcia_driver *drv)
+{
+       driver_remove_file(&drv->drv, &driver_attr_new_id);
+}
 
 /**
  * pcmcia_register_driver - register a PCMCIA driver with the bus core
@@ -204,6 +206,7 @@ EXPORT_SYMBOL(pcmcia_register_driver);
 void pcmcia_unregister_driver(struct pcmcia_driver *driver)
 {
        pr_debug("unregistering driver %s\n", driver->name);
+       pcmcia_remove_newid_file(driver);
        driver_unregister(&driver->drv);
        pcmcia_free_dynids(driver);
 }
index 3a8daf8..459f664 100644 (file)
@@ -76,6 +76,20 @@ config BATTERY_DS2780
        help
          Say Y here to enable support for batteries with ds2780 chip.
 
+config BATTERY_DS2781
+       tristate "2781 battery driver"
+       depends on HAS_IOMEM
+       select W1
+       select W1_SLAVE_DS2781
+       help
+         If you enable this you will have the DS2781 battery driver support.
+
+         The battery monitor chip is used in many batteries/devices
+         as the one who is responsible for charging/discharging/monitoring
+         Li+ batteries.
+
+         If you are unsure, say N.
+
 config BATTERY_DS2782
        tristate "DS2782/DS2786 standalone gas-gauge"
        depends on I2C
index e429008..c590fa5 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_TEST_POWER)      += test_power.o
 
 obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
 obj-$(CONFIG_BATTERY_DS2780)   += ds2780_battery.o
+obj-$(CONFIG_BATTERY_DS2781)   += ds2781_battery.o
 obj-$(CONFIG_BATTERY_DS2782)   += ds2782_battery.o
 obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c
new file mode 100644 (file)
index 0000000..ca0d653
--- /dev/null
@@ -0,0 +1,874 @@
+/*
+ * 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on ds2780_battery drivers
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/param.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+
+#include "../w1/w1.h"
+#include "../w1/slaves/w1_ds2781.h"
+
+/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
+#define DS2781_CURRENT_UNITS   1563
+/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
+#define DS2781_CHARGE_UNITS            6250
+/* Number of bytes in user EEPROM space */
+#define DS2781_USER_EEPROM_SIZE                (DS2781_EEPROM_BLOCK0_END - \
+                                       DS2781_EEPROM_BLOCK0_START + 1)
+/* Number of bytes in parameter EEPROM space */
+#define DS2781_PARAM_EEPROM_SIZE       (DS2781_EEPROM_BLOCK1_END - \
+                                       DS2781_EEPROM_BLOCK1_START + 1)
+
+struct ds2781_device_info {
+       struct device *dev;
+       struct power_supply bat;
+       struct device *w1_dev;
+       struct task_struct *mutex_holder;
+};
+
+enum current_types {
+       CURRENT_NOW,
+       CURRENT_AVG,
+};
+
+static const char model[] = "DS2781";
+static const char manufacturer[] = "Maxim/Dallas";
+
+static inline struct ds2781_device_info *
+to_ds2781_device_info(struct power_supply *psy)
+{
+       return container_of(psy, struct ds2781_device_info, bat);
+}
+
+static inline struct power_supply *to_power_supply(struct device *dev)
+{
+       return dev_get_drvdata(dev);
+}
+
+static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
+       char *buf, int addr, size_t count, int io)
+{
+       if (dev_info->mutex_holder == current)
+               return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr,
+                               count, io);
+       else
+               return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
+}
+
+int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
+               int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, buf, addr, count, 0);
+}
+
+static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val,
+       int addr)
+{
+       return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0);
+}
+
+static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val,
+       int addr)
+{
+       int ret;
+       u8 raw[2];
+
+       ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0);
+       if (ret < 0)
+               return ret;
+
+       *val = (raw[0] << 8) | raw[1];
+
+       return 0;
+}
+
+static inline int ds2781_read_block(struct ds2781_device_info *dev_info,
+       u8 *val, int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, val, addr, count, 0);
+}
+
+static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val,
+       int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, val, addr, count, 1);
+}
+
+static inline int ds2781_store_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA);
+}
+
+static inline int ds2781_recall_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA);
+}
+
+static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg)
+{
+       int ret;
+
+       ret = ds2781_store_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_recall_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* Set sense resistor value in mhos */
+static int ds2781_set_sense_register(struct ds2781_device_info *dev_info,
+       u8 conductance)
+{
+       int ret;
+
+       ret = ds2781_write(dev_info, &conductance,
+                               DS2781_RSNSP, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_RSNSP);
+}
+
+/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info,
+       u16 *rsgain)
+{
+       return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB);
+}
+
+/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info,
+       u16 rsgain)
+{
+       int ret;
+       u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
+
+       ret = ds2781_write(dev_info, raw,
+                               DS2781_RSGAIN_MSB, sizeof(raw));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB);
+}
+
+static int ds2781_get_voltage(struct ds2781_device_info *dev_info,
+       int *voltage_uV)
+{
+       int ret;
+       char val[2];
+       int voltage_raw;
+
+       ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8));
+       if (ret < 0)
+               return ret;
+       /*
+        * The voltage value is located in 10 bits across the voltage MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the voltage value is in bit 7 of the voltage MSB register
+        * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
+        * voltage MSB register
+        * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
+        * voltage LSB register
+        */
+       voltage_raw = (val[0] << 3) |
+               (val[1] >> 5);
+
+       /* DS2781 reports voltage in units of 9.76mV, but the battery class
+        * reports in units of uV, so convert by multiplying by 9760. */
+       *voltage_uV = voltage_raw * 9760;
+
+       return 0;
+}
+
+static int ds2781_get_temperature(struct ds2781_device_info *dev_info,
+       int *temp)
+{
+       int ret;
+       char val[2];
+       int temp_raw;
+
+       ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8));
+       if (ret < 0)
+               return ret;
+       /*
+        * The temperature value is located in 10 bits across the temperature
+        * MSB and LSB registers in two's compliment form
+        * Sign bit of the temperature value is in bit 7 of the temperature
+        * MSB register
+        * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
+        * temperature MSB register
+        * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
+        * temperature LSB register
+        */
+       temp_raw = ((val[0]) << 3) |
+               (val[1] >> 5);
+       *temp = temp_raw + (temp_raw / 4);
+
+       return 0;
+}
+
+static int ds2781_get_current(struct ds2781_device_info *dev_info,
+       enum current_types type, int *current_uA)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw, reg_msb;
+
+       /*
+        * The units of measurement for current are dependent on the value of
+        * the sense resistor.
+        */
+       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -EINVAL;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       if (type == CURRENT_NOW)
+               reg_msb = DS2781_CURRENT_MSB;
+       else if (type == CURRENT_AVG)
+               reg_msb = DS2781_IAVG_MSB;
+       else
+               return -EINVAL;
+
+       /*
+        * The current value is located in 16 bits across the current MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the current value is in bit 7 of the current MSB register
+        * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
+        * MSB register
+        * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &current_raw, reg_msb);
+       if (ret < 0)
+               return ret;
+
+       *current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info,
+       int *accumulated_current)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw;
+
+       /*
+        * The units of measurement for accumulated current are dependent on
+        * the value of the sense resistor.
+        */
+       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -EINVAL;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       /*
+        * The ACR value is located in 16 bits across the ACR MSB and
+        * LSB registers
+        * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
+        * MSB register
+        * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &current_raw, DS2781_ACR_MSB);
+       if (ret < 0)
+               return ret;
+
+       *accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2781_get_capacity(struct ds2781_device_info *dev_info,
+       int *capacity)
+{
+       int ret;
+       u8 raw;
+
+       ret = ds2781_read8(dev_info, &raw, DS2781_RARC);
+       if (ret < 0)
+               return ret;
+
+       *capacity = raw;
+       return 0;
+}
+
+static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status)
+{
+       int ret, current_uA, capacity;
+
+       ret = ds2781_get_current(dev_info, CURRENT_NOW, &current_uA);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_get_capacity(dev_info, &capacity);
+       if (ret < 0)
+               return ret;
+
+       if (power_supply_am_i_supplied(&dev_info->bat)) {
+               if (capacity == 100)
+                       *status = POWER_SUPPLY_STATUS_FULL;
+               else if (current_uA > 50000)
+                       *status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               *status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+       return 0;
+}
+
+static int ds2781_get_charge_now(struct ds2781_device_info *dev_info,
+       int *charge_now)
+{
+       int ret;
+       u16 charge_raw;
+
+       /*
+        * The RAAC value is located in 16 bits across the RAAC MSB and
+        * LSB registers
+        * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
+        * MSB register
+        * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB);
+       if (ret < 0)
+               return ret;
+
+       *charge_now = charge_raw * 1600;
+       return 0;
+}
+
+static int ds2781_get_control_register(struct ds2781_device_info *dev_info,
+       u8 *control_reg)
+{
+       return ds2781_read8(dev_info, control_reg, DS2781_CONTROL);
+}
+
+static int ds2781_set_control_register(struct ds2781_device_info *dev_info,
+       u8 control_reg)
+{
+       int ret;
+
+       ret = ds2781_write(dev_info, &control_reg,
+                               DS2781_CONTROL, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_CONTROL);
+}
+
+static int ds2781_battery_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = ds2781_get_voltage(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = ds2781_get_temperature(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = model;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = manufacturer;
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = ds2781_get_status(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = ds2781_get_capacity(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = ds2781_get_accumulated_current(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = ds2781_get_charge_now(dev_info, &val->intval);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property ds2781_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static ssize_t ds2781_get_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 control_reg;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       /* Get power mode */
+       ret = ds2781_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n",
+                !!(control_reg & DS2781_CONTROL_PMOD));
+}
+
+static ssize_t ds2781_set_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 control_reg, new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       /* Set power mode */
+       ret = ds2781_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       if (new_setting)
+               control_reg |= DS2781_CONTROL_PMOD;
+       else
+               control_reg &= ~DS2781_CONTROL_PMOD;
+
+       ret = ds2781_set_control_register(dev_info, control_reg);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sense_resistor;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sense_resistor);
+       return ret;
+}
+
+static ssize_t ds2781_set_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_set_sense_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u16 rsgain;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_get_rsgain_register(dev_info, &rsgain);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", rsgain);
+}
+
+static ssize_t ds2781_set_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u16 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou16(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       /* Gain can only be from 0 to 1.999 in steps of .001 */
+       if (new_setting > 1999) {
+               dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2781_set_rsgain_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sfr;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_read8(dev_info, &sfr, DS2781_SFR);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
+       return ret;
+}
+
+static ssize_t ds2781_set_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2781_write(dev_info, &new_setting,
+                               DS2781_SFR, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       count = min_t(loff_t, count,
+               DS2781_EEPROM_BLOCK1_END -
+               DS2781_EEPROM_BLOCK1_START + 1 - off);
+
+       return ds2781_read_block(dev_info, buf,
+                               DS2781_EEPROM_BLOCK1_START + off, count);
+}
+
+static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+       int ret;
+
+       count = min_t(loff_t, count,
+               DS2781_EEPROM_BLOCK1_END -
+               DS2781_EEPROM_BLOCK1_START + 1 - off);
+
+       ret = ds2781_write(dev_info, buf,
+                               DS2781_EEPROM_BLOCK1_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2781_param_eeprom_bin_attr = {
+       .attr = {
+               .name = "param_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1,
+       .read = ds2781_read_param_eeprom_bin,
+       .write = ds2781_write_param_eeprom_bin,
+};
+
+static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       count = min_t(loff_t, count,
+               DS2781_EEPROM_BLOCK0_END -
+               DS2781_EEPROM_BLOCK0_START + 1 - off);
+
+       return ds2781_read_block(dev_info, buf,
+                               DS2781_EEPROM_BLOCK0_START + off, count);
+
+}
+
+static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+       int ret;
+
+       count = min_t(loff_t, count,
+               DS2781_EEPROM_BLOCK0_END -
+               DS2781_EEPROM_BLOCK0_START + 1 - off);
+
+       ret = ds2781_write(dev_info, buf,
+                               DS2781_EEPROM_BLOCK0_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2781_user_eeprom_bin_attr = {
+       .attr = {
+               .name = "user_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1,
+       .read = ds2781_read_user_eeprom_bin,
+       .write = ds2781_write_user_eeprom_bin,
+};
+
+static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled,
+       ds2781_set_pmod_enabled);
+static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
+       ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value);
+static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting,
+       ds2781_set_rsgain_setting);
+static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin,
+       ds2781_set_pio_pin);
+
+
+static struct attribute *ds2781_attributes[] = {
+       &dev_attr_pmod_enabled.attr,
+       &dev_attr_sense_resistor_value.attr,
+       &dev_attr_rsgain_setting.attr,
+       &dev_attr_pio_pin.attr,
+       NULL
+};
+
+static const struct attribute_group ds2781_attr_group = {
+       .attrs = ds2781_attributes,
+};
+
+static int __devinit ds2781_battery_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct ds2781_device_info *dev_info;
+
+       dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+       if (!dev_info) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       platform_set_drvdata(pdev, dev_info);
+
+       dev_info->dev                   = &pdev->dev;
+       dev_info->w1_dev                = pdev->dev.parent;
+       dev_info->bat.name              = dev_name(&pdev->dev);
+       dev_info->bat.type              = POWER_SUPPLY_TYPE_BATTERY;
+       dev_info->bat.properties        = ds2781_battery_props;
+       dev_info->bat.num_properties    = ARRAY_SIZE(ds2781_battery_props);
+       dev_info->bat.get_property      = ds2781_battery_get_property;
+       dev_info->mutex_holder          = current;
+
+       ret = power_supply_register(&pdev->dev, &dev_info->bat);
+       if (ret) {
+               dev_err(dev_info->dev, "failed to register battery\n");
+               goto fail_free_info;
+       }
+
+       ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
+       if (ret) {
+               dev_err(dev_info->dev, "failed to create sysfs group\n");
+               goto fail_unregister;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
+                                       &ds2781_param_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create param eeprom bin file");
+               goto fail_remove_group;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
+                                       &ds2781_user_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create user eeprom bin file");
+               goto fail_remove_bin_file;
+       }
+
+       dev_info->mutex_holder = NULL;
+
+       return 0;
+
+fail_remove_bin_file:
+       sysfs_remove_bin_file(&dev_info->bat.dev->kobj,
+                               &ds2781_param_eeprom_bin_attr);
+fail_remove_group:
+       sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
+fail_unregister:
+       power_supply_unregister(&dev_info->bat);
+fail_free_info:
+       kfree(dev_info);
+fail:
+       return ret;
+}
+
+static int __devexit ds2781_battery_remove(struct platform_device *pdev)
+{
+       struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
+
+       dev_info->mutex_holder = current;
+
+       /* remove attributes */
+       sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
+
+       power_supply_unregister(&dev_info->bat);
+
+       kfree(dev_info);
+       return 0;
+}
+
+static struct platform_driver ds2781_battery_driver = {
+       .driver = {
+               .name = "ds2781-battery",
+       },
+       .probe    = ds2781_battery_probe,
+       .remove   = __devexit_p(ds2781_battery_remove),
+};
+
+static int __init ds2781_battery_init(void)
+{
+       return platform_driver_register(&ds2781_battery_driver);
+}
+
+static void __exit ds2781_battery_exit(void)
+{
+       platform_driver_unregister(&ds2781_battery_driver);
+}
+
+module_init(ds2781_battery_init);
+module_exit(ds2781_battery_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
+MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver");
+MODULE_ALIAS("platform:ds2781-battery");
+
index e9a83f8..fcde037 100644 (file)
@@ -1210,7 +1210,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
 {
        struct regulator_dev *rdev;
        struct regulator_map *map;
-       struct regulator *regulator = ERR_PTR(-ENODEV);
+       struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
        const char *devname = NULL;
        int ret;
 
@@ -2834,7 +2834,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
 
                if (!r) {
                        dev_err(dev, "Failed to find supply %s\n", supply);
-                       ret = -ENODEV;
+                       ret = -EPROBE_DEFER;
                        goto scrub;
                }
 
index 4f1989d..5f1dc6f 100644 (file)
@@ -580,7 +580,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
        struct device *dev;
 
        /* We don't want ccwgroup devices to live longer than their driver. */
-       get_driver(&cdriver->driver);
        while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
                                         __ccwgroup_match_all))) {
                struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
@@ -592,7 +591,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
                mutex_unlock(&gdev->reg_mutex);
                put_device(dev);
        }
-       put_driver(&cdriver->driver);
        driver_unregister(&cdriver->driver);
 }
 EXPORT_SYMBOL(ccwgroup_driver_unregister);
index 4726985..02d0152 100644 (file)
@@ -1676,15 +1676,9 @@ struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv,
                                       const char *bus_id)
 {
        struct device *dev;
-       struct device_driver *drv;
 
-       drv = get_driver(&cdrv->driver);
-       if (!drv)
-               return NULL;
-
-       dev = driver_find_device(drv, NULL, (void *)bus_id,
+       dev = driver_find_device(&cdrv->driver, NULL, (void *)bus_id,
                                 __ccwdev_check_busid);
-       put_driver(drv);
 
        return dev ? to_ccwdev(dev) : NULL;
 }
index 4d2ea40..32515a2 100644 (file)
@@ -168,7 +168,7 @@ static int __init smsgiucv_app_init(void)
        rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
        if (rc) {
                kfree(smsg_app_dev);
-               goto fail_put_driver;
+               goto fail;
        }
        smsg_app_dev->bus = &iucv_bus;
        smsg_app_dev->parent = iucv_root;
@@ -177,7 +177,7 @@ static int __init smsgiucv_app_init(void)
        rc = device_register(smsg_app_dev);
        if (rc) {
                put_device(smsg_app_dev);
-               goto fail_put_driver;
+               goto fail;
        }
 
        /* convert sender to uppercase characters */
@@ -191,12 +191,11 @@ static int __init smsgiucv_app_init(void)
        rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
        if (rc) {
                device_unregister(smsg_app_dev);
-               goto fail_put_driver;
+               goto fail;
        }
 
        rc = 0;
-fail_put_driver:
-       put_driver(smsgiucv_drv);
+fail:
        return rc;
 }
 module_init(smsgiucv_app_init);
index bb6317f..ff109ae 100644 (file)
@@ -140,19 +140,6 @@ static void ssb_device_put(struct ssb_device *dev)
                put_device(dev->dev);
 }
 
-static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv)
-{
-       if (drv)
-               get_driver(&drv->drv);
-       return drv;
-}
-
-static inline void ssb_driver_put(struct ssb_driver *drv)
-{
-       if (drv)
-               put_driver(&drv->drv);
-}
-
 static int ssb_device_resume(struct device *dev)
 {
        struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
@@ -250,11 +237,9 @@ int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx)
                        ssb_device_put(sdev);
                        continue;
                }
-               sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver));
-               if (!sdrv || SSB_WARN_ON(!sdrv->remove)) {
-                       ssb_device_put(sdev);
+               sdrv = drv_to_ssb_drv(sdev->dev->driver);
+               if (SSB_WARN_ON(!sdrv->remove))
                        continue;
-               }
                sdrv->remove(sdev);
                ctx->device_frozen[i] = 1;
        }
@@ -293,7 +278,6 @@ int ssb_devices_thaw(struct ssb_freeze_context *ctx)
                                   dev_name(sdev->dev));
                        result = err;
                }
-               ssb_driver_put(sdrv);
                ssb_device_put(sdev);
        }
 
index d40ff95..4fee024 100644 (file)
@@ -71,10 +71,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
        list_add_tail(&dynid->node, &dynids->list);
        spin_unlock(&dynids->lock);
 
-       if (get_driver(driver)) {
-               retval = driver_attach(driver);
-               put_driver(driver);
-       }
+       retval = driver_attach(driver);
 
        if (retval)
                return retval;
@@ -132,43 +129,39 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
 }
 static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
 
-static int usb_create_newid_file(struct usb_driver *usb_drv)
+static int usb_create_newid_files(struct usb_driver *usb_drv)
 {
        int error = 0;
 
        if (usb_drv->no_dynamic_id)
                goto exit;
 
-       if (usb_drv->probe != NULL)
+       if (usb_drv->probe != NULL) {
                error = driver_create_file(&usb_drv->drvwrap.driver,
                                           &driver_attr_new_id);
+               if (error == 0) {
+                       error = driver_create_file(&usb_drv->drvwrap.driver,
+                                       &driver_attr_remove_id);
+                       if (error)
+                               driver_remove_file(&usb_drv->drvwrap.driver,
+                                               &driver_attr_new_id);
+               }
+       }
 exit:
        return error;
 }
 
-static void usb_remove_newid_file(struct usb_driver *usb_drv)
+static void usb_remove_newid_files(struct usb_driver *usb_drv)
 {
        if (usb_drv->no_dynamic_id)
                return;
 
-       if (usb_drv->probe != NULL)
+       if (usb_drv->probe != NULL) {
                driver_remove_file(&usb_drv->drvwrap.driver,
-                                  &driver_attr_new_id);
-}
-
-static int
-usb_create_removeid_file(struct usb_driver *drv)
-{
-       int error = 0;
-       if (drv->probe != NULL)
-               error = driver_create_file(&drv->drvwrap.driver,
                                &driver_attr_remove_id);
-       return error;
-}
-
-static void usb_remove_removeid_file(struct usb_driver *drv)
-{
-       driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
+               driver_remove_file(&usb_drv->drvwrap.driver,
+                                  &driver_attr_new_id);
+       }
 }
 
 static void usb_free_dynids(struct usb_driver *usb_drv)
@@ -183,22 +176,12 @@ static void usb_free_dynids(struct usb_driver *usb_drv)
        spin_unlock(&usb_drv->dynids.lock);
 }
 #else
-static inline int usb_create_newid_file(struct usb_driver *usb_drv)
+static inline int usb_create_newid_files(struct usb_driver *usb_drv)
 {
        return 0;
 }
 
-static void usb_remove_newid_file(struct usb_driver *usb_drv)
-{
-}
-
-static int
-usb_create_removeid_file(struct usb_driver *drv)
-{
-       return 0;
-}
-
-static void usb_remove_removeid_file(struct usb_driver *drv)
+static void usb_remove_newid_files(struct usb_driver *usb_drv)
 {
 }
 
@@ -875,22 +858,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
 
        usbfs_update_special();
 
-       retval = usb_create_newid_file(new_driver);
+       retval = usb_create_newid_files(new_driver);
        if (retval)
                goto out_newid;
 
-       retval = usb_create_removeid_file(new_driver);
-       if (retval)
-               goto out_removeid;
-
        pr_info("%s: registered new interface driver %s\n",
                        usbcore_name, new_driver->name);
 
 out:
        return retval;
 
-out_removeid:
-       usb_remove_newid_file(new_driver);
 out_newid:
        driver_unregister(&new_driver->drvwrap.driver);
 
@@ -917,10 +894,9 @@ void usb_deregister(struct usb_driver *driver)
        pr_info("%s: deregistering interface driver %s\n",
                        usbcore_name, driver->name);
 
-       usb_remove_removeid_file(driver);
-       usb_remove_newid_file(driver);
-       usb_free_dynids(driver);
+       usb_remove_newid_files(driver);
        driver_unregister(&driver->drvwrap.driver);
+       usb_free_dynids(driver);
 
        usbfs_update_special();
 }
index 64e1f7c..c68e427 100644 (file)
@@ -171,14 +171,4 @@ MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
 
-static int __devinit dwc3_pci_init(void)
-{
-       return pci_register_driver(&dwc3_pci_driver);
-}
-module_init(dwc3_pci_init);
-
-static void __exit dwc3_pci_exit(void)
-{
-       pci_unregister_driver(&dwc3_pci_driver);
-}
-module_exit(dwc3_pci_exit);
+module_pci_driver(dwc3_pci_driver);
index fcbe742..df600d1 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/w1-gpio.h>
+#include <linux/gpio.h>
 
 #include "../w1.h"
 #include "../w1_int.h"
 
-#include <asm/gpio.h>
-
 static void w1_gpio_write_bit_dir(void *data, u8 bit)
 {
        struct w1_gpio_platform_data *pdata = data;
index d0cb01b..eb9e376 100644 (file)
@@ -81,6 +81,19 @@ config W1_SLAVE_DS2780
 
          If you are unsure, say N.
 
+config W1_SLAVE_DS2781
+       tristate "Dallas 2781 battery monitor chip"
+       depends on W1
+       help
+         If you enable this you will have the DS2781 battery monitor
+         chip support.
+
+         The battery monitor chip is used in many batteries/devices
+         as the one who is responsible for charging/discharging/monitoring
+         Li+ batteries.
+
+         If you are unsure, say N.
+
 config W1_SLAVE_BQ27000
        tristate "BQ27000 slave support"
        depends on W1
index 1f31e9f..c4f1859 100644 (file)
@@ -10,4 +10,5 @@ obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
 obj-$(CONFIG_W1_SLAVE_DS2433)  += w1_ds2433.o
 obj-$(CONFIG_W1_SLAVE_DS2760)  += w1_ds2760.o
 obj-$(CONFIG_W1_SLAVE_DS2780)  += w1_ds2780.o
+obj-$(CONFIG_W1_SLAVE_DS2781)  += w1_ds2781.o
 obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
index 8f4c91f..52ad812 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/types.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
+#include <linux/power/bq27x00_battery.h>
 
 #include "../w1.h"
 #include "../w1_int.h"
 
 static int F_ID;
 
-void w1_bq27000_write(struct device *dev, u8 buf, u8 reg)
-{
-       struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
-       if (!dev) {
-               pr_info("Could not obtain slave dev ptr\n");
-               return;
-       }
-
-       w1_write_8(sl->master, HDQ_CMD_WRITE | reg);
-       w1_write_8(sl->master, buf);
-}
-EXPORT_SYMBOL(w1_bq27000_write);
-
-int w1_bq27000_read(struct device *dev, u8 reg)
+static int w1_bq27000_read(struct device *dev, unsigned int reg)
 {
        u8 val;
-       struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
-       if (!dev)
-               return 0;
+       struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev);
 
+       mutex_lock(&sl->master->mutex);
        w1_write_8(sl->master, HDQ_CMD_READ | reg);
        val = w1_read_8(sl->master);
+       mutex_unlock(&sl->master->mutex);
 
        return val;
 }
-EXPORT_SYMBOL(w1_bq27000_read);
+
+static struct bq27000_platform_data bq27000_battery_info = {
+       .read   = w1_bq27000_read,
+       .name   = "bq27000-battery",
+};
 
 static int w1_bq27000_add_slave(struct w1_slave *sl)
 {
        int ret;
-       int id = 1;
        struct platform_device *pdev;
 
-       pdev = platform_device_alloc("bq27000-battery", id);
+       pdev = platform_device_alloc("bq27000-battery", -1);
        if (!pdev) {
                ret = -ENOMEM;
                return ret;
        }
+       ret = platform_device_add_data(pdev,
+                                      &bq27000_battery_info,
+                                      sizeof(bq27000_battery_info));
        pdev->dev.parent = &sl->dev;
 
        ret = platform_device_add(pdev);
diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c
new file mode 100644 (file)
index 0000000..0d0c798
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 1-Wire implementation for the ds2781 chip
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on w1-ds2780 driver
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2781.h"
+
+static int w1_ds2781_do_io(struct device *dev, char *buf, int addr,
+                       size_t count, int io)
+{
+       struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+       if (addr > DS2781_DATA_SIZE || addr < 0)
+               return 0;
+
+       count = min_t(int, count, DS2781_DATA_SIZE - addr);
+
+       if (w1_reset_select_slave(sl) == 0) {
+               if (io) {
+                       w1_write_8(sl->master, W1_DS2781_WRITE_DATA);
+                       w1_write_8(sl->master, addr);
+                       w1_write_block(sl->master, buf, count);
+               } else {
+                       w1_write_8(sl->master, W1_DS2781_READ_DATA);
+                       w1_write_8(sl->master, addr);
+                       count = w1_read_block(sl->master, buf, count);
+               }
+       }
+
+       return count;
+}
+
+int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
+                       int io)
+{
+       struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+       int ret;
+
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&sl->master->mutex);
+
+       ret = w1_ds2781_do_io(dev, buf, addr, count, io);
+
+       mutex_unlock(&sl->master->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL(w1_ds2781_io);
+
+int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr, size_t count,
+                       int io)
+{
+       int ret;
+
+       if (!dev)
+               return -ENODEV;
+
+       ret = w1_ds2781_do_io(dev, buf, addr, count, io);
+
+       return ret;
+}
+EXPORT_SYMBOL(w1_ds2781_io_nolock);
+
+int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+       struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+       if (!dev)
+               return -EINVAL;
+
+       mutex_lock(&sl->master->mutex);
+
+       if (w1_reset_select_slave(sl) == 0) {
+               w1_write_8(sl->master, cmd);
+               w1_write_8(sl->master, addr);
+       }
+
+       mutex_unlock(&sl->master->mutex);
+       return 0;
+}
+EXPORT_SYMBOL(w1_ds2781_eeprom_cmd);
+
+static ssize_t w1_ds2781_read_bin(struct file *filp,
+                                 struct kobject *kobj,
+                                 struct bin_attribute *bin_attr,
+                                 char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       return w1_ds2781_io(dev, buf, off, count, 0);
+}
+
+static struct bin_attribute w1_ds2781_bin_attr = {
+       .attr = {
+               .name = "w1_slave",
+               .mode = S_IRUGO,
+       },
+       .size = DS2781_DATA_SIZE,
+       .read = w1_ds2781_read_bin,
+};
+
+static DEFINE_IDA(bat_ida);
+
+static int w1_ds2781_add_slave(struct w1_slave *sl)
+{
+       int ret;
+       int id;
+       struct platform_device *pdev;
+
+       id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               ret = id;
+               goto noid;
+       }
+
+       pdev = platform_device_alloc("ds2781-battery", id);
+       if (!pdev) {
+               ret = -ENOMEM;
+               goto pdev_alloc_failed;
+       }
+       pdev->dev.parent = &sl->dev;
+
+       ret = platform_device_add(pdev);
+       if (ret)
+               goto pdev_add_failed;
+
+       ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr);
+       if (ret)
+               goto bin_attr_failed;
+
+       dev_set_drvdata(&sl->dev, pdev);
+
+       return 0;
+
+bin_attr_failed:
+pdev_add_failed:
+       platform_device_unregister(pdev);
+pdev_alloc_failed:
+       ida_simple_remove(&bat_ida, id);
+noid:
+       return ret;
+}
+
+static void w1_ds2781_remove_slave(struct w1_slave *sl)
+{
+       struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+       int id = pdev->id;
+
+       platform_device_unregister(pdev);
+       ida_simple_remove(&bat_ida, id);
+       sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr);
+}
+
+static struct w1_family_ops w1_ds2781_fops = {
+       .add_slave    = w1_ds2781_add_slave,
+       .remove_slave = w1_ds2781_remove_slave,
+};
+
+static struct w1_family w1_ds2781_family = {
+       .fid = W1_FAMILY_DS2781,
+       .fops = &w1_ds2781_fops,
+};
+
+static int __init w1_ds2781_init(void)
+{
+       ida_init(&bat_ida);
+       return w1_register_family(&w1_ds2781_family);
+}
+
+static void __exit w1_ds2781_exit(void)
+{
+       w1_unregister_family(&w1_ds2781_family);
+       ida_destroy(&bat_ida);
+}
+
+module_init(w1_ds2781_init);
+module_exit(w1_ds2781_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC");
diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h
new file mode 100644 (file)
index 0000000..82bc664
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on w1-ds2760 driver
+ *
+ * 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.
+ *
+ */
+
+#ifndef _W1_DS2781_H
+#define _W1_DS2781_H
+
+/* Function commands */
+#define W1_DS2781_READ_DATA            0x69
+#define W1_DS2781_WRITE_DATA           0x6C
+#define W1_DS2781_COPY_DATA            0x48
+#define W1_DS2781_RECALL_DATA          0xB8
+#define W1_DS2781_LOCK                 0x6A
+
+/* Register map */
+/* Register 0x00 Reserved */
+#define DS2781_STATUS                  0x01
+#define DS2781_RAAC_MSB                        0x02
+#define DS2781_RAAC_LSB                        0x03
+#define DS2781_RSAC_MSB                        0x04
+#define DS2781_RSAC_LSB                        0x05
+#define DS2781_RARC                    0x06
+#define DS2781_RSRC                    0x07
+#define DS2781_IAVG_MSB                        0x08
+#define DS2781_IAVG_LSB                        0x09
+#define DS2781_TEMP_MSB                        0x0A
+#define DS2781_TEMP_LSB                        0x0B
+#define DS2781_VOLT_MSB                        0x0C
+#define DS2781_VOLT_LSB                        0x0D
+#define DS2781_CURRENT_MSB             0x0E
+#define DS2781_CURRENT_LSB             0x0F
+#define DS2781_ACR_MSB                 0x10
+#define DS2781_ACR_LSB                 0x11
+#define DS2781_ACRL_MSB                        0x12
+#define DS2781_ACRL_LSB                        0x13
+#define DS2781_AS                      0x14
+#define DS2781_SFR                     0x15
+#define DS2781_FULL_MSB                        0x16
+#define DS2781_FULL_LSB                        0x17
+#define DS2781_AE_MSB                  0x18
+#define DS2781_AE_LSB                  0x19
+#define DS2781_SE_MSB                  0x1A
+#define DS2781_SE_LSB                  0x1B
+/* Register 0x1C - 0x1E Reserved */
+#define DS2781_EEPROM          0x1F
+#define DS2781_EEPROM_BLOCK0_START     0x20
+/* Register 0x20 - 0x2F User EEPROM */
+#define DS2781_EEPROM_BLOCK0_END       0x2F
+/* Register 0x30 - 0x5F Reserved */
+#define DS2781_EEPROM_BLOCK1_START     0x60
+#define DS2781_CONTROL                 0x60
+#define DS2781_AB                      0x61
+#define DS2781_AC_MSB                  0x62
+#define DS2781_AC_LSB                  0x63
+#define DS2781_VCHG                    0x64
+#define DS2781_IMIN                    0x65
+#define DS2781_VAE                     0x66
+#define DS2781_IAE                     0x67
+#define DS2781_AE_40                   0x68
+#define DS2781_RSNSP                   0x69
+#define DS2781_FULL_40_MSB             0x6A
+#define DS2781_FULL_40_LSB             0x6B
+#define DS2781_FULL_4_SLOPE            0x6C
+#define DS2781_FULL_3_SLOPE            0x6D
+#define DS2781_FULL_2_SLOPE            0x6E
+#define DS2781_FULL_1_SLOPE            0x6F
+#define DS2781_AE_4_SLOPE              0x70
+#define DS2781_AE_3_SLOPE              0x71
+#define DS2781_AE_2_SLOPE              0x72
+#define DS2781_AE_1_SLOPE              0x73
+#define DS2781_SE_4_SLOPE              0x74
+#define DS2781_SE_3_SLOPE              0x75
+#define DS2781_SE_2_SLOPE              0x76
+#define DS2781_SE_1_SLOPE              0x77
+#define DS2781_RSGAIN_MSB              0x78
+#define DS2781_RSGAIN_LSB              0x79
+#define DS2781_RSTC                    0x7A
+#define DS2781_COB                     0x7B
+#define DS2781_TBP34                   0x7C
+#define DS2781_TBP23                   0x7D
+#define DS2781_TBP12                   0x7E
+#define DS2781_EEPROM_BLOCK1_END       0x7F
+/* Register 0x7D - 0xFF Reserved */
+
+#define DS2781_FSGAIN_MSB              0xB0
+#define DS2781_FSGAIN_LSB              0xB1
+
+/* Number of valid register addresses */
+#define DS2781_DATA_SIZE               0xB2
+
+/* Status register bits */
+#define DS2781_STATUS_CHGTF            (1 << 7)
+#define DS2781_STATUS_AEF              (1 << 6)
+#define DS2781_STATUS_SEF              (1 << 5)
+#define DS2781_STATUS_LEARNF           (1 << 4)
+/* Bit 3 Reserved */
+#define DS2781_STATUS_UVF              (1 << 2)
+#define DS2781_STATUS_PORF             (1 << 1)
+/* Bit 0 Reserved */
+
+/* Control register bits */
+/* Bit 7 Reserved */
+#define DS2781_CONTROL_NBEN            (1 << 7)
+#define DS2781_CONTROL_UVEN            (1 << 6)
+#define DS2781_CONTROL_PMOD            (1 << 5)
+#define DS2781_CONTROL_RNAOP           (1 << 4)
+#define DS1781_CONTROL_UVTH            (1 << 3)
+/* Bit 0 - 2 Reserved */
+
+/* Special feature register bits */
+/* Bit 1 - 7 Reserved */
+#define DS2781_SFR_PIOSC               (1 << 0)
+
+/* EEPROM register bits */
+#define DS2781_EEPROM_EEC              (1 << 7)
+#define DS2781_EEPROM_LOCK             (1 << 6)
+/* Bit 2 - 6 Reserved */
+#define DS2781_EEPROM_BL1              (1 << 1)
+#define DS2781_EEPROM_BL0              (1 << 0)
+
+extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
+                       int io);
+extern int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr,
+                       size_t count, int io);
+extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd);
+
+#endif /* !_W1_DS2781_H */
index 490cda2..874aeb0 100644 (file)
@@ -38,6 +38,7 @@
 #define W1_EEPROM_DS2431       0x2D
 #define W1_FAMILY_DS2760       0x30
 #define W1_FAMILY_DS2780       0x32
+#define W1_FAMILY_DS2781       0x3D
 #define W1_THERM_DS28EA00      0x42
 
 #define MAXNAMELEN             32
index 956d5dd..b80bc84 100644 (file)
 #include <linux/debugfs.h>
 #include <linux/fsnotify.h>
 #include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
 
+#define DEBUGFS_DEFAULT_MODE   0755
+
 static struct vfsmount *debugfs_mount;
 static int debugfs_mount_count;
 static bool debugfs_registered;
@@ -125,11 +129,154 @@ static inline int debugfs_positive(struct dentry *dentry)
        return dentry->d_inode && !d_unhashed(dentry);
 }
 
+struct debugfs_mount_opts {
+       uid_t uid;
+       gid_t gid;
+       umode_t mode;
+};
+
+enum {
+       Opt_uid,
+       Opt_gid,
+       Opt_mode,
+       Opt_err
+};
+
+static const match_table_t tokens = {
+       {Opt_uid, "uid=%u"},
+       {Opt_gid, "gid=%u"},
+       {Opt_mode, "mode=%o"},
+       {Opt_err, NULL}
+};
+
+struct debugfs_fs_info {
+       struct debugfs_mount_opts mount_opts;
+};
+
+static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
+{
+       substring_t args[MAX_OPT_ARGS];
+       int option;
+       int token;
+       char *p;
+
+       opts->mode = DEBUGFS_DEFAULT_MODE;
+
+       while ((p = strsep(&data, ",")) != NULL) {
+               if (!*p)
+                       continue;
+
+               token = match_token(p, tokens, args);
+               switch (token) {
+               case Opt_uid:
+                       if (match_int(&args[0], &option))
+                               return -EINVAL;
+                       opts->uid = option;
+                       break;
+               case Opt_gid:
+                       if (match_octal(&args[0], &option))
+                               return -EINVAL;
+                       opts->gid = option;
+                       break;
+               case Opt_mode:
+                       if (match_octal(&args[0], &option))
+                               return -EINVAL;
+                       opts->mode = option & S_IALLUGO;
+                       break;
+               /*
+                * We might like to report bad mount options here;
+                * but traditionally debugfs has ignored all mount options
+                */
+               }
+       }
+
+       return 0;
+}
+
+static int debugfs_apply_options(struct super_block *sb)
+{
+       struct debugfs_fs_info *fsi = sb->s_fs_info;
+       struct inode *inode = sb->s_root->d_inode;
+       struct debugfs_mount_opts *opts = &fsi->mount_opts;
+
+       inode->i_mode &= ~S_IALLUGO;
+       inode->i_mode |= opts->mode;
+
+       inode->i_uid = opts->uid;
+       inode->i_gid = opts->gid;
+
+       return 0;
+}
+
+static int debugfs_remount(struct super_block *sb, int *flags, char *data)
+{
+       int err;
+       struct debugfs_fs_info *fsi = sb->s_fs_info;
+
+       err = debugfs_parse_options(data, &fsi->mount_opts);
+       if (err)
+               goto fail;
+
+       debugfs_apply_options(sb);
+
+fail:
+       return err;
+}
+
+static int debugfs_show_options(struct seq_file *m, struct dentry *root)
+{
+       struct debugfs_fs_info *fsi = root->d_sb->s_fs_info;
+       struct debugfs_mount_opts *opts = &fsi->mount_opts;
+
+       if (opts->uid != 0)
+               seq_printf(m, ",uid=%u", opts->uid);
+       if (opts->gid != 0)
+               seq_printf(m, ",gid=%u", opts->gid);
+       if (opts->mode != DEBUGFS_DEFAULT_MODE)
+               seq_printf(m, ",mode=%o", opts->mode);
+
+       return 0;
+}
+
+static const struct super_operations debugfs_super_operations = {
+       .statfs         = simple_statfs,
+       .remount_fs     = debugfs_remount,
+       .show_options   = debugfs_show_options,
+};
+
 static int debug_fill_super(struct super_block *sb, void *data, int silent)
 {
        static struct tree_descr debug_files[] = {{""}};
+       struct debugfs_fs_info *fsi;
+       int err;
+
+       save_mount_options(sb, data);
+
+       fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL);
+       sb->s_fs_info = fsi;
+       if (!fsi) {
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       err = debugfs_parse_options(data, &fsi->mount_opts);
+       if (err)
+               goto fail;
+
+       err  =  simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
+       if (err)
+               goto fail;
+
+       sb->s_op = &debugfs_super_operations;
+
+       debugfs_apply_options(sb);
+
+       return 0;
 
-       return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
+fail:
+       kfree(fsi);
+       sb->s_fs_info = NULL;
+       return err;
 }
 
 static struct dentry *debug_mount(struct file_system_type *fs_type,
index 7fdf6a7..2a7a3f5 100644 (file)
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/security.h>
+#include <linux/hash.h>
 #include "sysfs.h"
 
 DEFINE_MUTEX(sysfs_mutex);
 DEFINE_SPINLOCK(sysfs_assoc_lock);
 
+#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
+
 static DEFINE_SPINLOCK(sysfs_ino_lock);
 static DEFINE_IDA(sysfs_ino_ida);
 
 /**
- *     sysfs_link_sibling - link sysfs_dirent into sibling list
+ *     sysfs_name_hash
+ *     @ns:   Namespace tag to hash
+ *     @name: Null terminated string to hash
+ *
+ *     Returns 31 bit hash of ns + name (so it fits in an off_t )
+ */
+static unsigned int sysfs_name_hash(const void *ns, const char *name)
+{
+       unsigned long hash = init_name_hash();
+       unsigned int len = strlen(name);
+       while (len--)
+               hash = partial_name_hash(*name++, hash);
+       hash = ( end_name_hash(hash) ^ hash_ptr( (void *)ns, 31 ) );
+       hash &= 0x7fffffffU;
+       /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
+       if (hash < 1)
+               hash += 2;
+       if (hash >= INT_MAX)
+               hash = INT_MAX - 1;
+       return hash;
+}
+
+static int sysfs_name_compare(unsigned int hash, const void *ns,
+       const char *name, const struct sysfs_dirent *sd)
+{
+       if (hash != sd->s_hash)
+               return hash - sd->s_hash;
+       if (ns != sd->s_ns)
+               return ns - sd->s_ns;
+       return strcmp(name, sd->s_name);
+}
+
+static int sysfs_sd_compare(const struct sysfs_dirent *left,
+                           const struct sysfs_dirent *right)
+{
+       return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name,
+                                 right);
+}
+
+/**
+ *     sysfs_link_subling - link sysfs_dirent into sibling rbtree
  *     @sd: sysfs_dirent of interest
  *
- *     Link @sd into its sibling list which starts from
+ *     Link @sd into its sibling rbtree which starts from
  *     sd->s_parent->s_dir.children.
  *
  *     Locking:
  *     mutex_lock(sysfs_mutex)
+ *
+ *     RETURNS:
+ *     0 on susccess -EEXIST on failure.
  */
-static void sysfs_link_sibling(struct sysfs_dirent *sd)
+static int sysfs_link_sibling(struct sysfs_dirent *sd)
 {
-       struct sysfs_dirent *parent_sd = sd->s_parent;
-
-       struct rb_node **p;
-       struct rb_node *parent;
+       struct rb_node **node = &sd->s_parent->s_dir.children.rb_node;
+       struct rb_node *parent = NULL;
 
        if (sysfs_type(sd) == SYSFS_DIR)
-               parent_sd->s_dir.subdirs++;
-
-       p = &parent_sd->s_dir.inode_tree.rb_node;
-       parent = NULL;
-       while (*p) {
-               parent = *p;
-#define node   rb_entry(parent, struct sysfs_dirent, inode_node)
-               if (sd->s_ino < node->s_ino) {
-                       p = &node->inode_node.rb_left;
-               } else if (sd->s_ino > node->s_ino) {
-                       p = &node->inode_node.rb_right;
-               } else {
-                       printk(KERN_CRIT "sysfs: inserting duplicate inode '%lx'\n",
-                              (unsigned long) sd->s_ino);
-                       BUG();
-               }
-#undef node
-       }
-       rb_link_node(&sd->inode_node, parent, p);
-       rb_insert_color(&sd->inode_node, &parent_sd->s_dir.inode_tree);
-
-       p = &parent_sd->s_dir.name_tree.rb_node;
-       parent = NULL;
-       while (*p) {
-               int c;
-               parent = *p;
-#define node   rb_entry(parent, struct sysfs_dirent, name_node)
-               c = strcmp(sd->s_name, node->s_name);
-               if (c < 0) {
-                       p = &node->name_node.rb_left;
-               } else {
-                       p = &node->name_node.rb_right;
-               }
-#undef node
+               sd->s_parent->s_dir.subdirs++;
+
+       while (*node) {
+               struct sysfs_dirent *pos;
+               int result;
+
+               pos = to_sysfs_dirent(*node);
+               parent = *node;
+               result = sysfs_sd_compare(sd, pos);
+               if (result < 0)
+                       node = &pos->s_rb.rb_left;
+               else if (result > 0)
+                       node = &pos->s_rb.rb_right;
+               else
+                       return -EEXIST;
        }
-       rb_link_node(&sd->name_node, parent, p);
-       rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree);
+       /* add new node and rebalance the tree */
+       rb_link_node(&sd->s_rb, parent, node);
+       rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
+       return 0;
 }
 
 /**
- *     sysfs_unlink_sibling - unlink sysfs_dirent from sibling list
+ *     sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree
  *     @sd: sysfs_dirent of interest
  *
- *     Unlink @sd from its sibling list which starts from
+ *     Unlink @sd from its sibling rbtree which starts from
  *     sd->s_parent->s_dir.children.
  *
  *     Locking:
@@ -102,8 +129,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
        if (sysfs_type(sd) == SYSFS_DIR)
                sd->s_parent->s_dir.subdirs--;
 
-       rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree);
-       rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree);
+       rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
 }
 
 /**
@@ -198,7 +224,7 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
        rwsem_release(&sd->dep_map, 1, _RET_IP_);
 }
 
-static int sysfs_alloc_ino(ino_t *pino)
+static int sysfs_alloc_ino(unsigned int *pino)
 {
        int ino, rc;
 
@@ -217,7 +243,7 @@ static int sysfs_alloc_ino(ino_t *pino)
        return rc;
 }
 
-static void sysfs_free_ino(ino_t ino)
+static void sysfs_free_ino(unsigned int ino)
 {
        spin_lock(&sysfs_ino_lock);
        ida_remove(&sysfs_ino_ida, ino);
@@ -402,6 +428,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
 int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 {
        struct sysfs_inode_attrs *ps_iattr;
+       int ret;
 
        if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
                WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -410,12 +437,12 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
                return -EINVAL;
        }
 
-       if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
-               return -EEXIST;
-
+       sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
        sd->s_parent = sysfs_get(acxt->parent_sd);
 
-       sysfs_link_sibling(sd);
+       ret = sysfs_link_sibling(sd);
+       if (ret)
+               return ret;
 
        /* Update timestamps on the parent */
        ps_iattr = acxt->parent_sd->s_iattr;
@@ -565,8 +592,8 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
                                       const void *ns,
                                       const unsigned char *name)
 {
-       struct rb_node *p = parent_sd->s_dir.name_tree.rb_node;
-       struct sysfs_dirent *found = NULL;
+       struct rb_node *node = parent_sd->s_dir.children.rb_node;
+       unsigned int hash;
 
        if (!!sysfs_ns_type(parent_sd) != !!ns) {
                WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -575,33 +602,21 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
                return NULL;
        }
 
-       while (p) {
-               int c;
-#define node   rb_entry(p, struct sysfs_dirent, name_node)
-               c = strcmp(name, node->s_name);
-               if (c < 0) {
-                       p = node->name_node.rb_left;
-               } else if (c > 0) {
-                       p = node->name_node.rb_right;
-               } else {
-                       found = node;
-                       p = node->name_node.rb_left;
-               }
-#undef node
-       }
-
-       if (found) {
-               while (found->s_ns != ns) {
-                       p = rb_next(&found->name_node);
-                       if (!p)
-                               return NULL;
-                       found = rb_entry(p, struct sysfs_dirent, name_node);
-                       if (strcmp(name, found->s_name))
-                               return NULL;
-               }
+       hash = sysfs_name_hash(ns, name);
+       while (node) {
+               struct sysfs_dirent *sd;
+               int result;
+
+               sd = to_sysfs_dirent(node);
+               result = sysfs_name_compare(hash, ns, name, sd);
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return sd;
        }
-
-       return found;
+       return NULL;
 }
 
 /**
@@ -804,9 +819,9 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
 
        pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
        sysfs_addrm_start(&acxt, dir_sd);
-       pos = rb_first(&dir_sd->s_dir.inode_tree);
+       pos = rb_first(&dir_sd->s_dir.children);
        while (pos) {
-               struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node);
+               struct sysfs_dirent *sd = to_sysfs_dirent(pos);
                pos = rb_next(pos);
                if (sysfs_type(sd) != SYSFS_DIR)
                        sysfs_remove_one(&acxt, sd);
@@ -863,6 +878,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
 
                dup_name = sd->s_name;
                sd->s_name = new_name;
+               sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
        }
 
        /* Move to the appropriate place in the appropriate directories rbtree. */
@@ -919,38 +935,36 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp)
 }
 
 static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
-       struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
+       struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos)
 {
        if (pos) {
                int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
                        pos->s_parent == parent_sd &&
-                       ino == pos->s_ino;
+                       hash == pos->s_hash;
                sysfs_put(pos);
                if (!valid)
                        pos = NULL;
        }
-       if (!pos && (ino > 1) && (ino < INT_MAX)) {
-               struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node;
-               while (p) {
-#define node   rb_entry(p, struct sysfs_dirent, inode_node)
-                       if (ino < node->s_ino) {
-                               pos = node;
-                               p = node->inode_node.rb_left;
-                       } else if (ino > node->s_ino) {
-                               p = node->inode_node.rb_right;
-                       } else {
-                               pos = node;
+       if (!pos && (hash > 1) && (hash < INT_MAX)) {
+               struct rb_node *node = parent_sd->s_dir.children.rb_node;
+               while (node) {
+                       pos = to_sysfs_dirent(node);
+
+                       if (hash < pos->s_hash)
+                               node = node->rb_left;
+                       else if (hash > pos->s_hash)
+                               node = node->rb_right;
+                       else
                                break;
-                       }
-#undef node
                }
        }
+       /* Skip over entries in the wrong namespace */
        while (pos && pos->s_ns != ns) {
-               struct rb_node *p = rb_next(&pos->inode_node);
-               if (!p)
+               struct rb_node *node = rb_next(&pos->s_rb);
+               if (!node)
                        pos = NULL;
                else
-                       pos = rb_entry(p, struct sysfs_dirent, inode_node);
+                       pos = to_sysfs_dirent(node);
        }
        return pos;
 }
@@ -960,11 +974,11 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
 {
        pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
        if (pos) do {
-               struct rb_node *p = rb_next(&pos->inode_node);
-               if (!p)
+               struct rb_node *node = rb_next(&pos->s_rb);
+               if (!node)
                        pos = NULL;
                else
-                       pos = rb_entry(p, struct sysfs_dirent, inode_node);
+                       pos = to_sysfs_dirent(node);
        } while (pos && pos->s_ns != ns);
        return pos;
 }
@@ -1006,7 +1020,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
                len = strlen(name);
                ino = pos->s_ino;
                type = dt_type(pos);
-               filp->f_pos = ino;
+               filp->f_pos = pos->s_hash;
                filp->private_data = sysfs_get(pos);
 
                mutex_unlock(&sysfs_mutex);
index 85eb816..feb2d69 100644 (file)
@@ -136,12 +136,13 @@ static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *sec
        void *old_secdata;
        size_t old_secdata_len;
 
-       iattrs = sd->s_iattr;
-       if (!iattrs)
-               iattrs = sysfs_init_inode_attrs(sd);
-       if (!iattrs)
-               return -ENOMEM;
+       if (!sd->s_iattr) {
+               sd->s_iattr = sysfs_init_inode_attrs(sd);
+               if (!sd->s_iattr)
+                       return -ENOMEM;
+       }
 
+       iattrs = sd->s_iattr;
        old_secdata = iattrs->ia_secdata;
        old_secdata_len = iattrs->ia_secdata_len;
 
index e34f0d9..140f26a 100644 (file)
@@ -36,7 +36,7 @@ struct sysfs_dirent sysfs_root = {
        .s_name         = "",
        .s_count        = ATOMIC_INIT(1),
        .s_flags        = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
-       .s_mode         = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
+       .s_mode         = S_IFDIR | S_IRUGO | S_IXUGO,
        .s_ino          = 1,
 };
 
index 7484a36..661a963 100644 (file)
@@ -20,9 +20,8 @@ struct sysfs_elem_dir {
        struct kobject          *kobj;
 
        unsigned long           subdirs;
-
-       struct rb_root          inode_tree;
-       struct rb_root          name_tree;
+       /* children rbtree starts here and goes through sd->s_rb */
+       struct rb_root          children;
 };
 
 struct sysfs_elem_symlink {
@@ -62,8 +61,7 @@ struct sysfs_dirent {
        struct sysfs_dirent     *s_parent;
        const char              *s_name;
 
-       struct rb_node          inode_node;
-       struct rb_node          name_node;
+       struct rb_node          s_rb;
 
        union {
                struct completion       *completion;
@@ -71,6 +69,7 @@ struct sysfs_dirent {
        } u;
 
        const void              *s_ns; /* namespace tag */
+       unsigned int            s_hash; /* ns + name hash */
        union {
                struct sysfs_elem_dir           s_dir;
                struct sysfs_elem_symlink       s_symlink;
@@ -78,9 +77,9 @@ struct sysfs_dirent {
                struct sysfs_elem_bin_attr      s_bin_attr;
        };
 
-       unsigned int            s_flags;
+       unsigned short          s_flags;
        umode_t                 s_mode;
-       ino_t                   s_ino;
+       unsigned int            s_ino;
        struct sysfs_inode_attrs *s_iattr;
 };
 
@@ -95,11 +94,11 @@ struct sysfs_dirent {
 #define SYSFS_ACTIVE_REF               (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
 
 /* identify any namespace tag on sysfs_dirents */
-#define SYSFS_NS_TYPE_MASK             0xff00
+#define SYSFS_NS_TYPE_MASK             0xf00
 #define SYSFS_NS_TYPE_SHIFT            8
 
 #define SYSFS_FLAG_MASK                        ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK)
-#define SYSFS_FLAG_REMOVED             0x020000
+#define SYSFS_FLAG_REMOVED             0x02000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 {
index 8cf7e98..9d65047 100644 (file)
@@ -225,6 +225,7 @@ struct acpi_processor_errata {
        } piix4;
 };
 
+extern void acpi_processor_load_module(struct acpi_processor *pr);
 extern int acpi_processor_preregister_performance(struct
                                                  acpi_processor_performance
                                                  __percpu *performance);
index 3c9c54f..7638407 100644 (file)
@@ -43,6 +43,7 @@
 #define CN_IDX_DRBD                    0x8
 #define CN_VAL_DRBD                    0x1
 #define CN_KVP_IDX                     0x9     /* HyperV KVP */
+#define CN_KVP_VAL                     0x1     /* queries from the kernel */
 
 #define CN_NETLINK_USERS               10      /* Highest index + 1 */
 
index 1f65875..6e53b48 100644 (file)
@@ -44,6 +44,13 @@ extern ssize_t arch_cpu_release(const char *, size_t);
 #endif
 struct notifier_block;
 
+#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
+extern int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env);
+extern ssize_t arch_print_cpu_modalias(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *bufptr);
+#endif
+
 /*
  * CPU notifier priorities.
  */
index b63fb39..f62e216 100644 (file)
@@ -238,8 +238,6 @@ struct device_driver {
 extern int __must_check driver_register(struct device_driver *drv);
 extern void driver_unregister(struct device_driver *drv);
 
-extern struct device_driver *get_driver(struct device_driver *drv);
-extern void put_driver(struct device_driver *drv);
 extern struct device_driver *driver_find(const char *name,
                                         struct bus_type *bus);
 extern int driver_probe_done(void);
@@ -946,14 +944,14 @@ int _dev_info(const struct device *dev, const char *fmt, ...)
 
 #define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
 
-#if defined(DEBUG)
-#define dev_dbg(dev, format, arg...)           \
-       dev_printk(KERN_DEBUG, dev, format, ##arg)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
 #define dev_dbg(dev, format, ...)                   \
 do {                                                \
        dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
 } while (0)
+#elif defined(DEBUG)
+#define dev_dbg(dev, format, arg...)           \
+       dev_printk(KERN_DEBUG, dev, format, ##arg)
 #else
 #define dev_dbg(dev, format, arg...)                           \
 ({                                                             \
index 0564e3c..7e3c53a 100644 (file)
@@ -15,20 +15,24 @@ struct _ddebug {
        const char *function;
        const char *filename;
        const char *format;
-       unsigned int lineno:24;
+       unsigned int lineno:18;
        /*
         * The flags field controls the behaviour at the callsite.
         * The bits here are changed dynamically when the user
         * writes commands to <debugfs>/dynamic_debug/control
         */
-#define _DPRINTK_FLAGS_PRINT   (1<<0)  /* printk() a message using the format */
+#define _DPRINTK_FLAGS_NONE    0
+#define _DPRINTK_FLAGS_PRINT   (1<<0) /* printk() a message using the format */
 #define _DPRINTK_FLAGS_INCL_MODNAME    (1<<1)
 #define _DPRINTK_FLAGS_INCL_FUNCNAME   (1<<2)
 #define _DPRINTK_FLAGS_INCL_LINENO     (1<<3)
 #define _DPRINTK_FLAGS_INCL_TID                (1<<4)
+#if defined DEBUG
+#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
+#else
 #define _DPRINTK_FLAGS_DEFAULT 0
+#endif
        unsigned int flags:8;
-       char enabled;
 } __attribute__((aligned(8)));
 
 
@@ -62,21 +66,20 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor,
                .format = (fmt),                                \
                .lineno = __LINE__,                             \
                .flags =  _DPRINTK_FLAGS_DEFAULT,               \
-               .enabled = false,                               \
        }
 
 #define dynamic_pr_debug(fmt, ...)                             \
 do {                                                           \
        DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);         \
-       if (unlikely(descriptor.enabled))                       \
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))  \
                __dynamic_pr_debug(&descriptor, pr_fmt(fmt),    \
                                   ##__VA_ARGS__);              \
 } while (0)
 
 #define dynamic_dev_dbg(dev, fmt, ...)                         \
 do {                                                           \
-       DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
-       if (unlikely(descriptor.enabled))                       \
+       DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);         \
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))  \
                __dynamic_dev_dbg(&descriptor, dev, fmt,        \
                                  ##__VA_ARGS__);               \
 } while (0)
@@ -84,7 +87,7 @@ do {                                                          \
 #define dynamic_netdev_dbg(dev, fmt, ...)                      \
 do {                                                           \
        DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);         \
-       if (unlikely(descriptor.enabled))                       \
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))  \
                __dynamic_netdev_dbg(&descriptor, dev, fmt,     \
                                     ##__VA_ARGS__);            \
 } while (0)
index 4668583..2d09bfa 100644 (file)
@@ -16,6 +16,7 @@
 #define ERESTARTNOHAND 514     /* restart if no handler.. */
 #define ENOIOCTLCMD    515     /* No ioctl command */
 #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
+#define EPROBE_DEFER   517     /* Driver requests probe retry */
 
 /* Defined for the NFSv3 protocol */
 #define EBADHANDLE     521     /* Illegal NFS file handle */
index 0ae065a..5852545 100644 (file)
 #ifndef _HYPERV_H
 #define _HYPERV_H
 
+#include <linux/types.h>
+
+/*
+ * An implementation of HyperV key value pair (KVP) functionality for Linux.
+ *
+ *
+ * Copyright (C) 2010, Novell, Inc.
+ * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
+ *
+ */
+
+/*
+ * Maximum value size - used for both key names and value data, and includes
+ * any applicable NULL terminators.
+ *
+ * Note:  This limit is somewhat arbitrary, but falls easily within what is
+ * supported for all native guests (back to Win 2000) and what is reasonable
+ * for the IC KVP exchange functionality.  Note that Windows Me/98/95 are
+ * limited to 255 character key names.
+ *
+ * MSDN recommends not storing data values larger than 2048 bytes in the
+ * registry.
+ *
+ * Note:  This value is used in defining the KVP exchange message - this value
+ * cannot be modified without affecting the message size and compatibility.
+ */
+
+/*
+ * bytes, including any null terminators
+ */
+#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE          (2048)
+
+
+/*
+ * Maximum key size - the registry limit for the length of an entry name
+ * is 256 characters, including the null terminator
+ */
+
+#define HV_KVP_EXCHANGE_MAX_KEY_SIZE            (512)
+
+/*
+ * In Linux, we implement the KVP functionality in two components:
+ * 1) The kernel component which is packaged as part of the hv_utils driver
+ * is responsible for communicating with the host and responsible for
+ * implementing the host/guest protocol. 2) A user level daemon that is
+ * responsible for data gathering.
+ *
+ * Host/Guest Protocol: The host iterates over an index and expects the guest
+ * to assign a key name to the index and also return the value corresponding to
+ * the key. The host will have atmost one KVP transaction outstanding at any
+ * given point in time. The host side iteration stops when the guest returns
+ * an error. Microsoft has specified the following mapping of key names to
+ * host specified index:
+ *
+ *     Index           Key Name
+ *     0               FullyQualifiedDomainName
+ *     1               IntegrationServicesVersion
+ *     2               NetworkAddressIPv4
+ *     3               NetworkAddressIPv6
+ *     4               OSBuildNumber
+ *     5               OSName
+ *     6               OSMajorVersion
+ *     7               OSMinorVersion
+ *     8               OSVersion
+ *     9               ProcessorArchitecture
+ *
+ * The Windows host expects the Key Name and Key Value to be encoded in utf16.
+ *
+ * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
+ * data gathering functionality in a user mode daemon. The user level daemon
+ * is also responsible for binding the key name to the index as well. The
+ * kernel and user-level daemon communicate using a connector channel.
+ *
+ * The user mode component first registers with the
+ * the kernel component. Subsequently, the kernel component requests, data
+ * for the specified keys. In response to this message the user mode component
+ * fills in the value corresponding to the specified key. We overload the
+ * sequence field in the cn_msg header to define our KVP message types.
+ *
+ *
+ * The kernel component simply acts as a conduit for communication between the
+ * Windows host and the user-level daemon. The kernel component passes up the
+ * index received from the Host to the user-level daemon. If the index is
+ * valid (supported), the corresponding key as well as its
+ * value (both are strings) is returned. If the index is invalid
+ * (not supported), a NULL key string is returned.
+ */
+
+
+/*
+ * Registry value types.
+ */
+
+#define REG_SZ 1
+#define REG_U32 4
+#define REG_U64 8
+
+enum hv_kvp_exchg_op {
+       KVP_OP_GET = 0,
+       KVP_OP_SET,
+       KVP_OP_DELETE,
+       KVP_OP_ENUMERATE,
+       KVP_OP_REGISTER,
+       KVP_OP_COUNT /* Number of operations, must be last. */
+};
+
+enum hv_kvp_exchg_pool {
+       KVP_POOL_EXTERNAL = 0,
+       KVP_POOL_GUEST,
+       KVP_POOL_AUTO,
+       KVP_POOL_AUTO_EXTERNAL,
+       KVP_POOL_AUTO_INTERNAL,
+       KVP_POOL_COUNT /* Number of pools, must be last. */
+};
+
+struct hv_kvp_hdr {
+       __u8 operation;
+       __u8 pool;
+       __u16 pad;
+} __attribute__((packed));
+
+struct hv_kvp_exchg_msg_value {
+       __u32 value_type;
+       __u32 key_size;
+       __u32 value_size;
+       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+       union {
+               __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+               __u32 value_u32;
+               __u64 value_u64;
+       };
+} __attribute__((packed));
+
+struct hv_kvp_msg_enumerate {
+       __u32 index;
+       struct hv_kvp_exchg_msg_value data;
+} __attribute__((packed));
+
+struct hv_kvp_msg_get {
+       struct hv_kvp_exchg_msg_value data;
+};
+
+struct hv_kvp_msg_set {
+       struct hv_kvp_exchg_msg_value data;
+};
+
+struct hv_kvp_msg_delete {
+       __u32 key_size;
+       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+};
+
+struct hv_kvp_register {
+       __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+};
+
+struct hv_kvp_msg {
+       struct hv_kvp_hdr       kvp_hdr;
+       union {
+               struct hv_kvp_msg_get           kvp_get;
+               struct hv_kvp_msg_set           kvp_set;
+               struct hv_kvp_msg_delete        kvp_delete;
+               struct hv_kvp_msg_enumerate     kvp_enum_data;
+               struct hv_kvp_register          kvp_register;
+       } body;
+} __attribute__((packed));
+
+#ifdef __KERNEL__
 #include <linux/scatterlist.h>
 #include <linux/list.h>
 #include <linux/uuid.h>
@@ -785,6 +952,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
 
 #define HV_S_OK                                0x00000000
 #define HV_E_FAIL                      0x80004005
+#define HV_S_CONT                      0x80070103
 #define HV_ERROR_NOT_SUPPORTED         0x80070032
 #define HV_ERROR_MACHINE_LOCKED                0x800704F7
 
@@ -870,4 +1038,9 @@ struct hyperv_service_callback {
 extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
                                      struct icmsg_negotiate *, u8 *);
 
+int hv_kvp_init(struct hv_util_service *);
+void hv_kvp_deinit(void);
+void hv_kvp_onchannelcallback(void *);
+
+#endif /* __KERNEL__ */
 #endif /* _HYPERV_H */
index 83ac071..fb69ad1 100644 (file)
@@ -560,4 +560,25 @@ struct amba_id {
 #endif
 };
 
+/*
+ * Match x86 CPUs for CPU specific drivers.
+ * See documentation of "x86_match_cpu" for details.
+ */
+
+struct x86_cpu_id {
+       __u16 vendor;
+       __u16 family;
+       __u16 model;
+       __u16 feature;  /* bit index */
+       kernel_ulong_t driver_data;
+};
+
+#define X86_FEATURE_MATCH(x) \
+       { X86_VENDOR_ANY, X86_FAMILY_ANY, X86_MODEL_ANY, x }
+
+#define X86_VENDOR_ANY 0xffff
+#define X86_FAMILY_ANY 0
+#define X86_MODEL_ANY  0
+#define X86_FEATURE_ANY 0      /* Same as FPU, you can't test for that */
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
index 7dfaae7..3e5cb25 100644 (file)
@@ -2687,14 +2687,14 @@ int netdev_info(const struct net_device *dev, const char *format, ...);
 #define MODULE_ALIAS_NETDEV(device) \
        MODULE_ALIAS("netdev-" device)
 
-#if defined(DEBUG)
-#define netdev_dbg(__dev, format, args...)                     \
-       netdev_printk(KERN_DEBUG, __dev, format, ##args)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
 #define netdev_dbg(__dev, format, args...)                     \
 do {                                                           \
        dynamic_netdev_dbg(__dev, format, ##args);              \
 } while (0)
+#elif defined(DEBUG)
+#define netdev_dbg(__dev, format, args...)                     \
+       netdev_printk(KERN_DEBUG, __dev, format, ##args)
 #else
 #define netdev_dbg(__dev, format, args...)                     \
 ({                                                             \
index a16b1df..d4afd70 100644 (file)
@@ -946,6 +946,19 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *,
        __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
 
 void pci_unregister_driver(struct pci_driver *dev);
+
+/**
+ * module_pci_driver() - Helper macro for registering a PCI driver
+ * @__pci_driver: pci_driver struct
+ *
+ * Helper macro for PCI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_pci_driver(__pci_driver) \
+       module_driver(__pci_driver, pci_register_driver, \
+                      pci_unregister_driver)
+
 void pci_remove_behind_bridge(struct pci_dev *dev);
 struct pci_driver *pci_dev_driver(const struct pci_dev *dev);
 int pci_add_dynid(struct pci_driver *drv,
index 1f77a41..0525927 100644 (file)
@@ -190,13 +190,13 @@ extern void dump_stack(void) __cold;
 #endif
 
 /* If you are writing a driver, please use dev_dbg instead */
-#if defined(DEBUG)
-#define pr_debug(fmt, ...) \
-       printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
 /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
 #define pr_debug(fmt, ...) \
        dynamic_pr_debug(fmt, ##__VA_ARGS__)
+#elif defined(DEBUG)
+#define pr_debug(fmt, ...) \
+       printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #else
 #define pr_debug(fmt, ...) \
        no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
new file mode 100644 (file)
index 0000000..2739ccb
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+#ifndef __SOC_BUS_H
+#define __SOC_BUS_H
+
+#include <linux/device.h>
+
+struct soc_device_attribute {
+       const char *machine;
+       const char *family;
+       const char *revision;
+       const char *soc_id;
+};
+
+/**
+ * soc_device_register - register SoC as a device
+ * @soc_plat_dev_attr: Attributes passed from platform to be attributed to a SoC
+ */
+struct soc_device *soc_device_register(
+       struct soc_device_attribute *soc_plat_dev_attr);
+
+/**
+ * soc_device_unregister - unregister SoC device
+ * @dev: SoC device to be unregistered
+ */
+void soc_device_unregister(struct soc_device *soc_dev);
+
+/**
+ * soc_device_to_device - helper function to fetch struct device
+ * @soc: Previously registered SoC device container
+ */
+struct device *soc_device_to_device(struct soc_device *soc);
+
+#endif /* __SOC_BUS_H */
index fea790a..13ef233 100644 (file)
@@ -170,7 +170,7 @@ static bool driver_filter(struct device *dev)
                return false;
 
        /* driver filter on but not yet initialized */
-       drv = get_driver(dev->driver);
+       drv = dev->driver;
        if (!drv)
                return false;
 
@@ -185,7 +185,6 @@ static bool driver_filter(struct device *dev)
        }
 
        read_unlock_irqrestore(&driver_name_lock, flags);
-       put_driver(drv);
 
        return ret;
 }
index dcdade3..310c753 100644 (file)
@@ -60,6 +60,7 @@ struct ddebug_iter {
 static DEFINE_MUTEX(ddebug_lock);
 static LIST_HEAD(ddebug_tables);
 static int verbose = 0;
+module_param(verbose, int, 0644);
 
 /* Return the last part of a pathname */
 static inline const char *basename(const char *path)
@@ -68,12 +69,24 @@ static inline const char *basename(const char *path)
        return tail ? tail+1 : path;
 }
 
+/* Return the path relative to source root */
+static inline const char *trim_prefix(const char *path)
+{
+       int skip = strlen(__FILE__) - strlen("lib/dynamic_debug.c");
+
+       if (strncmp(path, __FILE__, skip))
+               skip = 0; /* prefix mismatch, don't skip */
+
+       return path + skip;
+}
+
 static struct { unsigned flag:8; char opt_char; } opt_array[] = {
        { _DPRINTK_FLAGS_PRINT, 'p' },
        { _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
        { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
        { _DPRINTK_FLAGS_INCL_LINENO, 'l' },
        { _DPRINTK_FLAGS_INCL_TID, 't' },
+       { _DPRINTK_FLAGS_NONE, '_' },
 };
 
 /* format a string into buf[] which describes the _ddebug's flags */
@@ -83,58 +96,74 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,
        char *p = buf;
        int i;
 
-       BUG_ON(maxlen < 4);
+       BUG_ON(maxlen < 6);
        for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
                if (dp->flags & opt_array[i].flag)
                        *p++ = opt_array[i].opt_char;
        if (p == buf)
-               *p++ = '-';
+               *p++ = '_';
        *p = '\0';
 
        return buf;
 }
 
+#define vpr_info_dq(q, msg)                                            \
+do {                                                                   \
+       if (verbose)                                                    \
+               /* trim last char off format print */                   \
+               pr_info("%s: func=\"%s\" file=\"%s\" "                  \
+                       "module=\"%s\" format=\"%.*s\" "                \
+                       "lineno=%u-%u",                                 \
+                       msg,                                            \
+                       q->function ? q->function : "",                 \
+                       q->filename ? q->filename : "",                 \
+                       q->module ? q->module : "",                     \
+                       (int)(q->format ? strlen(q->format) - 1 : 0),   \
+                       q->format ? q->format : "",                     \
+                       q->first_lineno, q->last_lineno);               \
+} while (0)
+
 /*
- * Search the tables for _ddebug's which match the given
- * `query' and apply the `flags' and `mask' to them.  Tells
- * the user which ddebug's were changed, or whether none
- * were matched.
+ * Search the tables for _ddebug's which match the given `query' and
+ * apply the `flags' and `mask' to them.  Returns number of matching
+ * callsites, normally the same as number of changes.  If verbose,
+ * logs the changes.  Takes ddebug_lock.
  */
-static void ddebug_change(const struct ddebug_query *query,
-                          unsigned int flags, unsigned int mask)
+static int ddebug_change(const struct ddebug_query *query,
+                       unsigned int flags, unsigned int mask)
 {
        int i;
        struct ddebug_table *dt;
        unsigned int newflags;
        unsigned int nfound = 0;
-       char flagbuf[8];
+       char flagbuf[10];
 
        /* search for matching ddebugs */
        mutex_lock(&ddebug_lock);
        list_for_each_entry(dt, &ddebug_tables, link) {
 
                /* match against the module name */
-               if (query->module != NULL &&
-                   strcmp(query->module, dt->mod_name))
+               if (query->module && strcmp(query->module, dt->mod_name))
                        continue;
 
                for (i = 0 ; i < dt->num_ddebugs ; i++) {
                        struct _ddebug *dp = &dt->ddebugs[i];
 
                        /* match against the source filename */
-                       if (query->filename != NULL &&
+                       if (query->filename &&
                            strcmp(query->filename, dp->filename) &&
-                           strcmp(query->filename, basename(dp->filename)))
+                           strcmp(query->filename, basename(dp->filename)) &&
+                           strcmp(query->filename, trim_prefix(dp->filename)))
                                continue;
 
                        /* match against the function */
-                       if (query->function != NULL &&
+                       if (query->function &&
                            strcmp(query->function, dp->function))
                                continue;
 
                        /* match against the format */
-                       if (query->format != NULL &&
-                           strstr(dp->format, query->format) == NULL)
+                       if (query->format &&
+                           !strstr(dp->format, query->format))
                                continue;
 
                        /* match against the line number range */
@@ -151,13 +180,9 @@ static void ddebug_change(const struct ddebug_query *query,
                        if (newflags == dp->flags)
                                continue;
                        dp->flags = newflags;
-                       if (newflags)
-                               dp->enabled = 1;
-                       else
-                               dp->enabled = 0;
                        if (verbose)
-                               pr_info("changed %s:%d [%s]%s %s\n",
-                                       dp->filename, dp->lineno,
+                               pr_info("changed %s:%d [%s]%s =%s\n",
+                                       trim_prefix(dp->filename), dp->lineno,
                                        dt->mod_name, dp->function,
                                        ddebug_describe_flags(dp, flagbuf,
                                                        sizeof(flagbuf)));
@@ -167,6 +192,8 @@ static void ddebug_change(const struct ddebug_query *query,
 
        if (!nfound && verbose)
                pr_info("no matches for query\n");
+
+       return nfound;
 }
 
 /*
@@ -186,8 +213,10 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
                buf = skip_spaces(buf);
                if (!*buf)
                        break;  /* oh, it was trailing whitespace */
+               if (*buf == '#')
+                       break;  /* token starts comment, skip rest of line */
 
-               /* Run `end' over a word, either whitespace separated or quoted */
+               /* find `end' of word, whitespace separated or quoted */
                if (*buf == '"' || *buf == '\'') {
                        int quote = *buf++;
                        for (end = buf ; *end && *end != quote ; end++)
@@ -199,8 +228,8 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
                                ;
                        BUG_ON(end == buf);
                }
-               /* Here `buf' is the start of the word, `end' is one past the end */
 
+               /* `buf' is start of word, `end' is one past its end */
                if (nwords == maxwords)
                        return -EINVAL; /* ran out of words[] before bytes */
                if (*end)
@@ -279,6 +308,19 @@ static char *unescape(char *str)
        return str;
 }
 
+static int check_set(const char **dest, char *src, char *name)
+{
+       int rc = 0;
+
+       if (*dest) {
+               rc = -EINVAL;
+               pr_err("match-spec:%s val:%s overridden by %s",
+                       name, *dest, src);
+       }
+       *dest = src;
+       return rc;
+}
+
 /*
  * Parse words[] as a ddebug query specification, which is a series
  * of (keyword, value) pairs chosen from these possibilities:
@@ -290,11 +332,15 @@ static char *unescape(char *str)
  * format <escaped-string-to-find-in-format>
  * line <lineno>
  * line <first-lineno>-<last-lineno> // where either may be empty
+ *
+ * Only 1 of each type is allowed.
+ * Returns 0 on success, <0 on error.
  */
 static int ddebug_parse_query(char *words[], int nwords,
                               struct ddebug_query *query)
 {
        unsigned int i;
+       int rc;
 
        /* check we have an even number of words */
        if (nwords % 2 != 0)
@@ -303,41 +349,43 @@ static int ddebug_parse_query(char *words[], int nwords,
 
        for (i = 0 ; i < nwords ; i += 2) {
                if (!strcmp(words[i], "func"))
-                       query->function = words[i+1];
+                       rc = check_set(&query->function, words[i+1], "func");
                else if (!strcmp(words[i], "file"))
-                       query->filename = words[i+1];
+                       rc = check_set(&query->filename, words[i+1], "file");
                else if (!strcmp(words[i], "module"))
-                       query->module = words[i+1];
+                       rc = check_set(&query->module, words[i+1], "module");
                else if (!strcmp(words[i], "format"))
-                       query->format = unescape(words[i+1]);
+                       rc = check_set(&query->format, unescape(words[i+1]),
+                               "format");
                else if (!strcmp(words[i], "line")) {
                        char *first = words[i+1];
                        char *last = strchr(first, '-');
+                       if (query->first_lineno || query->last_lineno) {
+                               pr_err("match-spec:line given 2 times\n");
+                               return -EINVAL;
+                       }
                        if (last)
                                *last++ = '\0';
                        if (parse_lineno(first, &query->first_lineno) < 0)
                                return -EINVAL;
-                       if (last != NULL) {
+                       if (last) {
                                /* range <first>-<last> */
-                               if (parse_lineno(last, &query->last_lineno) < 0)
+                               if (parse_lineno(last, &query->last_lineno)
+                                   < query->first_lineno) {
+                                       pr_err("last-line < 1st-line\n");
                                        return -EINVAL;
+                               }
                        } else {
                                query->last_lineno = query->first_lineno;
                        }
                } else {
-                       if (verbose)
-                               pr_err("unknown keyword \"%s\"\n", words[i]);
+                       pr_err("unknown keyword \"%s\"\n", words[i]);
                        return -EINVAL;
                }
+               if (rc)
+                       return rc;
        }
-
-       if (verbose)
-               pr_info("q->function=\"%s\" q->filename=\"%s\" "
-                       "q->module=\"%s\" q->format=\"%s\" q->lineno=%u-%u\n",
-                       query->function, query->filename,
-                       query->module, query->format, query->first_lineno,
-                       query->last_lineno);
-
+       vpr_info_dq(query, "parsed");
        return 0;
 }
 
@@ -375,8 +423,6 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
                if (i < 0)
                        return -EINVAL;
        }
-       if (flags == 0)
-               return -EINVAL;
        if (verbose)
                pr_info("flags=0x%x\n", flags);
 
@@ -405,7 +451,7 @@ static int ddebug_exec_query(char *query_string)
        unsigned int flags = 0, mask = 0;
        struct ddebug_query query;
 #define MAXWORDS 9
-       int nwords;
+       int nwords, nfound;
        char *words[MAXWORDS];
 
        nwords = ddebug_tokenize(query_string, words, MAXWORDS);
@@ -417,8 +463,47 @@ static int ddebug_exec_query(char *query_string)
                return -EINVAL;
 
        /* actually go and implement the change */
-       ddebug_change(&query, flags, mask);
-       return 0;
+       nfound = ddebug_change(&query, flags, mask);
+       vpr_info_dq((&query), (nfound) ? "applied" : "no-match");
+
+       return nfound;
+}
+
+/* handle multiple queries in query string, continue on error, return
+   last error or number of matching callsites.  Module name is either
+   in param (for boot arg) or perhaps in query string.
+*/
+static int ddebug_exec_queries(char *query)
+{
+       char *split;
+       int i, errs = 0, exitcode = 0, rc, nfound = 0;
+
+       for (i = 0; query; query = split) {
+               split = strpbrk(query, ";\n");
+               if (split)
+                       *split++ = '\0';
+
+               query = skip_spaces(query);
+               if (!query || !*query || *query == '#')
+                       continue;
+
+               if (verbose)
+                       pr_info("query %d: \"%s\"\n", i, query);
+
+               rc = ddebug_exec_query(query);
+               if (rc < 0) {
+                       errs++;
+                       exitcode = rc;
+               } else
+                       nfound += rc;
+               i++;
+       }
+       pr_info("processed %d queries, with %d matches, %d errs\n",
+                i, nfound, errs);
+
+       if (exitcode)
+               return exitcode;
+       return nfound;
 }
 
 #define PREFIX_SIZE 64
@@ -452,7 +537,8 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
                pos += snprintf(buf + pos, remaining(pos), "%s:",
                                        desc->function);
        if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
-               pos += snprintf(buf + pos, remaining(pos), "%d:", desc->lineno);
+               pos += snprintf(buf + pos, remaining(pos), "%d:",
+                                       desc->lineno);
        if (pos - pos_after_tid)
                pos += snprintf(buf + pos, remaining(pos), " ");
        if (pos >= PREFIX_SIZE)
@@ -527,14 +613,16 @@ EXPORT_SYMBOL(__dynamic_netdev_dbg);
 
 #endif
 
-static __initdata char ddebug_setup_string[1024];
+#define DDEBUG_STRING_SIZE 1024
+static __initdata char ddebug_setup_string[DDEBUG_STRING_SIZE];
+
 static __init int ddebug_setup_query(char *str)
 {
-       if (strlen(str) >= 1024) {
+       if (strlen(str) >= DDEBUG_STRING_SIZE) {
                pr_warn("ddebug boot param string too large\n");
                return 0;
        }
-       strcpy(ddebug_setup_string, str);
+       strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE);
        return 1;
 }
 
@@ -544,25 +632,33 @@ __setup("ddebug_query=", ddebug_setup_query);
  * File_ops->write method for <debugfs>/dynamic_debug/conrol.  Gathers the
  * command text from userspace, parses and executes it.
  */
+#define USER_BUF_PAGE 4096
 static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
                                  size_t len, loff_t *offp)
 {
-       char tmpbuf[256];
+       char *tmpbuf;
        int ret;
 
        if (len == 0)
                return 0;
-       /* we don't check *offp -- multiple writes() are allowed */
-       if (len > sizeof(tmpbuf)-1)
+       if (len > USER_BUF_PAGE - 1) {
+               pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE);
                return -E2BIG;
-       if (copy_from_user(tmpbuf, ubuf, len))
+       }
+       tmpbuf = kmalloc(len + 1, GFP_KERNEL);
+       if (!tmpbuf)
+               return -ENOMEM;
+       if (copy_from_user(tmpbuf, ubuf, len)) {
+               kfree(tmpbuf);
                return -EFAULT;
+       }
        tmpbuf[len] = '\0';
        if (verbose)
                pr_info("read %d bytes from userspace\n", (int)len);
 
-       ret = ddebug_exec_query(tmpbuf);
-       if (ret)
+       ret = ddebug_exec_queries(tmpbuf);
+       kfree(tmpbuf);
+       if (ret < 0)
                return ret;
 
        *offp += len;
@@ -668,7 +764,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
 {
        struct ddebug_iter *iter = m->private;
        struct _ddebug *dp = p;
-       char flagsbuf[8];
+       char flagsbuf[10];
 
        if (verbose)
                pr_info("called m=%p p=%p\n", m, p);
@@ -679,10 +775,10 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
                return 0;
        }
 
-       seq_printf(m, "%s:%u [%s]%s %s \"",
-                  dp->filename, dp->lineno,
-                  iter->table->mod_name, dp->function,
-                  ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf)));
+       seq_printf(m, "%s:%u [%s]%s =%s \"",
+               trim_prefix(dp->filename), dp->lineno,
+               iter->table->mod_name, dp->function,
+               ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf)));
        seq_escape(m, dp->format, "\t\r\n\"");
        seq_puts(m, "\"\n");
 
@@ -708,10 +804,11 @@ static const struct seq_operations ddebug_proc_seqops = {
 };
 
 /*
- * File_ops->open method for <debugfs>/dynamic_debug/control.  Does the seq_file
- * setup dance, and also creates an iterator to walk the _ddebugs.
- * Note that we create a seq_file always, even for O_WRONLY files
- * where it's not needed, as doing so simplifies the ->release method.
+ * File_ops->open method for <debugfs>/dynamic_debug/control.  Does
+ * the seq_file setup dance, and also creates an iterator to walk the
+ * _ddebugs.  Note that we create a seq_file always, even for O_WRONLY
+ * files where it's not needed, as doing so simplifies the ->release
+ * method.
  */
 static int ddebug_proc_open(struct inode *inode, struct file *file)
 {
@@ -846,33 +943,40 @@ static int __init dynamic_debug_init(void)
        int ret = 0;
        int n = 0;
 
-       if (__start___verbose != __stop___verbose) {
-               iter = __start___verbose;
-               modname = iter->modname;
-               iter_start = iter;
-               for (; iter < __stop___verbose; iter++) {
-                       if (strcmp(modname, iter->modname)) {
-                               ret = ddebug_add_module(iter_start, n, modname);
-                               if (ret)
-                                       goto out_free;
-                               n = 0;
-                               modname = iter->modname;
-                               iter_start = iter;
-                       }
-                       n++;
+       if (__start___verbose == __stop___verbose) {
+               pr_warn("_ddebug table is empty in a "
+                       "CONFIG_DYNAMIC_DEBUG build");
+               return 1;
+       }
+       iter = __start___verbose;
+       modname = iter->modname;
+       iter_start = iter;
+       for (; iter < __stop___verbose; iter++) {
+               if (strcmp(modname, iter->modname)) {
+                       ret = ddebug_add_module(iter_start, n, modname);
+                       if (ret)
+                               goto out_free;
+                       n = 0;
+                       modname = iter->modname;
+                       iter_start = iter;
                }
-               ret = ddebug_add_module(iter_start, n, modname);
+               n++;
        }
+       ret = ddebug_add_module(iter_start, n, modname);
+       if (ret)
+               goto out_free;
 
        /* ddebug_query boot param got passed -> set it up */
        if (ddebug_setup_string[0] != '\0') {
-               ret = ddebug_exec_query(ddebug_setup_string);
-               if (ret)
+               ret = ddebug_exec_queries(ddebug_setup_string);
+               if (ret < 0)
                        pr_warn("Invalid ddebug boot param %s",
                                ddebug_setup_string);
                else
-                       pr_info("ddebug initialized with string %s",
-                               ddebug_setup_string);
+                       pr_info("%d changes by ddebug_query\n", ret);
+
+               /* keep tables even on ddebug_query parse error */
+               ret = 0;
        }
 
 out_free:
index e66e9b6..75cbdb5 100644 (file)
 
 u64 uevent_seqnum;
 char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
-static DEFINE_SPINLOCK(sequence_lock);
 #ifdef CONFIG_NET
 struct uevent_sock {
        struct list_head list;
        struct sock *sk;
 };
 static LIST_HEAD(uevent_sock_list);
-static DEFINE_MUTEX(uevent_sock_mutex);
 #endif
 
+/* This lock protects uevent_seqnum and uevent_sock_list */
+static DEFINE_MUTEX(uevent_sock_mutex);
+
 /* the strings here must match the enum in include/linux/kobject.h */
 static const char *kobject_actions[] = {
        [KOBJ_ADD] =            "add",
@@ -136,7 +137,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        struct kobject *top_kobj;
        struct kset *kset;
        const struct kset_uevent_ops *uevent_ops;
-       u64 seq;
        int i = 0;
        int retval = 0;
 #ifdef CONFIG_NET
@@ -243,17 +243,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        else if (action == KOBJ_REMOVE)
                kobj->state_remove_uevent_sent = 1;
 
+       mutex_lock(&uevent_sock_mutex);
        /* we will send an event, so request a new sequence number */
-       spin_lock(&sequence_lock);
-       seq = ++uevent_seqnum;
-       spin_unlock(&sequence_lock);
-       retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
-       if (retval)
+       retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
+       if (retval) {
+               mutex_unlock(&uevent_sock_mutex);
                goto exit;
+       }
 
 #if defined(CONFIG_NET)
        /* send netlink message */
-       mutex_lock(&uevent_sock_mutex);
        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
                struct sock *uevent_sock = ue_sk->sk;
                struct sk_buff *skb;
@@ -290,8 +289,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                } else
                        retval = -ENOMEM;
        }
-       mutex_unlock(&uevent_sock_mutex);
 #endif
+       mutex_unlock(&uevent_sock_mutex);
 
        /* call uevent_helper, usually only enabled during early boot */
        if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
index b89efe6..8e730cc 100644 (file)
@@ -1029,6 +1029,31 @@ static int do_amba_entry(const char *filename,
 }
 ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry);
 
+/* LOOKS like x86cpu:vendor:VVVV:family:FFFF:model:MMMM:feature:*,FEAT,*
+ * All fields are numbers. It would be nicer to use strings for vendor
+ * and feature, but getting those out of the build system here is too
+ * complicated.
+ */
+
+static int do_x86cpu_entry(const char *filename, struct x86_cpu_id *id,
+                          char *alias)
+{
+       id->feature = TO_NATIVE(id->feature);
+       id->family = TO_NATIVE(id->family);
+       id->model = TO_NATIVE(id->model);
+       id->vendor = TO_NATIVE(id->vendor);
+
+       strcpy(alias, "x86cpu:");
+       ADD(alias, "vendor:",  id->vendor != X86_VENDOR_ANY, id->vendor);
+       ADD(alias, ":family:", id->family != X86_FAMILY_ANY, id->family);
+       ADD(alias, ":model:",  id->model  != X86_MODEL_ANY,  id->model);
+       strcat(alias, ":feature:*");
+       if (id->feature != X86_FEATURE_ANY)
+               sprintf(alias + strlen(alias), "%04X*", id->feature);
+       return 1;
+}
+ADD_TO_DEVTABLE("x86cpu", struct x86_cpu_id, do_x86cpu_entry);
+
 /* Does namelen bytes of name exactly match the symbol? */
 static bool sym_is(const char *name, unsigned namelen, const char *symbol)
 {
index 11224ed..146fd61 100644 (file)
 #include <errno.h>
 #include <arpa/inet.h>
 #include <linux/connector.h>
+#include <linux/hyperv.h>
 #include <linux/netlink.h>
 #include <ifaddrs.h>
 #include <netdb.h>
 #include <syslog.h>
-
-/*
- * KYS: TODO. Need to register these in the kernel.
- *
- * The following definitions are shared with the in-kernel component; do not
- * change any of this without making the corresponding changes in
- * the KVP kernel component.
- */
-#define CN_KVP_IDX             0x9     /* MSFT KVP functionality */
-#define CN_KVP_VAL             0x1 /* This supports queries from the kernel */
-#define CN_KVP_USER_VAL                0x2 /* This supports queries from the user  */
+#include <sys/stat.h>
+#include <fcntl.h>
 
 /*
  * KVP protocol: The user mode component first registers with the
  * We use this infrastructure for also supporting queries from user mode
  * application for state that may be maintained in the KVP kernel component.
  *
- * XXXKYS: Have a shared header file between the user and kernel (TODO)
  */
 
-enum kvp_op {
-       KVP_REGISTER = 0, /* Register the user mode component*/
-       KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/
-       KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/
-       KVP_USER_GET, /*User is requesting the value for the specified key*/
-       KVP_USER_SET /*User is providing the value for the specified key*/
-};
-
-#define HV_KVP_EXCHANGE_MAX_KEY_SIZE   512
-#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048
-
-struct hv_ku_msg {
-       __u32   kvp_index;
-       __u8  kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
-       __u8  kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key  value */
-};
 
 enum key_index {
        FullyQualifiedDomainName = 0,
@@ -93,10 +68,6 @@ enum key_index {
        ProcessorArchitecture
 };
 
-/*
- * End of shared definitions.
- */
-
 static char kvp_send_buffer[4096];
 static char kvp_recv_buffer[4096];
 static struct sockaddr_nl addr;
@@ -109,6 +80,345 @@ static char *os_build;
 static char *lic_version;
 static struct utsname uts_buf;
 
+
+#define MAX_FILE_NAME 100
+#define ENTRIES_PER_BLOCK 50
+
+struct kvp_record {
+       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+       __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+};
+
+struct kvp_file_state {
+       int fd;
+       int num_blocks;
+       struct kvp_record *records;
+       int num_records;
+       __u8 fname[MAX_FILE_NAME];
+};
+
+static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
+
+static void kvp_acquire_lock(int pool)
+{
+       struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
+       fl.l_pid = getpid();
+
+       if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
+               syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
+               exit(-1);
+       }
+}
+
+static void kvp_release_lock(int pool)
+{
+       struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
+       fl.l_pid = getpid();
+
+       if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
+               perror("fcntl");
+               syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
+               exit(-1);
+       }
+}
+
+static void kvp_update_file(int pool)
+{
+       FILE *filep;
+       size_t bytes_written;
+
+       /*
+        * We are going to write our in-memory registry out to
+        * disk; acquire the lock first.
+        */
+       kvp_acquire_lock(pool);
+
+       filep = fopen(kvp_file_info[pool].fname, "w");
+       if (!filep) {
+               kvp_release_lock(pool);
+               syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
+               exit(-1);
+       }
+
+       bytes_written = fwrite(kvp_file_info[pool].records,
+                               sizeof(struct kvp_record),
+                               kvp_file_info[pool].num_records, filep);
+
+       fflush(filep);
+       kvp_release_lock(pool);
+}
+
+static void kvp_update_mem_state(int pool)
+{
+       FILE *filep;
+       size_t records_read = 0;
+       struct kvp_record *record = kvp_file_info[pool].records;
+       struct kvp_record *readp;
+       int num_blocks = kvp_file_info[pool].num_blocks;
+       int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
+
+       kvp_acquire_lock(pool);
+
+       filep = fopen(kvp_file_info[pool].fname, "r");
+       if (!filep) {
+               kvp_release_lock(pool);
+               syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
+               exit(-1);
+       }
+       while (!feof(filep)) {
+               readp = &record[records_read];
+               records_read += fread(readp, sizeof(struct kvp_record),
+                                       ENTRIES_PER_BLOCK * num_blocks,
+                                       filep);
+
+               if (!feof(filep)) {
+                       /*
+                        * We have more data to read.
+                        */
+                       num_blocks++;
+                       record = realloc(record, alloc_unit * num_blocks);
+
+                       if (record == NULL) {
+                               syslog(LOG_ERR, "malloc failed");
+                               exit(-1);
+                       }
+                       continue;
+               }
+               break;
+       }
+
+       kvp_file_info[pool].num_blocks = num_blocks;
+       kvp_file_info[pool].records = record;
+       kvp_file_info[pool].num_records = records_read;
+
+       kvp_release_lock(pool);
+}
+static int kvp_file_init(void)
+{
+       int ret, fd;
+       FILE *filep;
+       size_t records_read;
+       __u8 *fname;
+       struct kvp_record *record;
+       struct kvp_record *readp;
+       int num_blocks;
+       int i;
+       int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
+
+       if (access("/var/opt/hyperv", F_OK)) {
+               if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
+                       syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
+                       exit(-1);
+               }
+       }
+
+       for (i = 0; i < KVP_POOL_COUNT; i++) {
+               fname = kvp_file_info[i].fname;
+               records_read = 0;
+               num_blocks = 1;
+               sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
+               fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
+
+               if (fd == -1)
+                       return 1;
+
+
+               filep = fopen(fname, "r");
+               if (!filep)
+                       return 1;
+
+               record = malloc(alloc_unit * num_blocks);
+               if (record == NULL) {
+                       fclose(filep);
+                       return 1;
+               }
+               while (!feof(filep)) {
+                       readp = &record[records_read];
+                       records_read += fread(readp, sizeof(struct kvp_record),
+                                       ENTRIES_PER_BLOCK,
+                                       filep);
+
+                       if (!feof(filep)) {
+                               /*
+                                * We have more data to read.
+                                */
+                               num_blocks++;
+                               record = realloc(record, alloc_unit *
+                                               num_blocks);
+                               if (record == NULL) {
+                                       fclose(filep);
+                                       return 1;
+                               }
+                               continue;
+                       }
+                       break;
+               }
+               kvp_file_info[i].fd = fd;
+               kvp_file_info[i].num_blocks = num_blocks;
+               kvp_file_info[i].records = record;
+               kvp_file_info[i].num_records = records_read;
+               fclose(filep);
+
+       }
+
+       return 0;
+}
+
+static int kvp_key_delete(int pool, __u8 *key, int key_size)
+{
+       int i;
+       int j, k;
+       int num_records;
+       struct kvp_record *record;
+
+       /*
+        * First update the in-memory state.
+        */
+       kvp_update_mem_state(pool);
+
+       num_records = kvp_file_info[pool].num_records;
+       record = kvp_file_info[pool].records;
+
+       for (i = 0; i < num_records; i++) {
+               if (memcmp(key, record[i].key, key_size))
+                       continue;
+               /*
+                * Found a match; just move the remaining
+                * entries up.
+                */
+               if (i == num_records) {
+                       kvp_file_info[pool].num_records--;
+                       kvp_update_file(pool);
+                       return 0;
+               }
+
+               j = i;
+               k = j + 1;
+               for (; k < num_records; k++) {
+                       strcpy(record[j].key, record[k].key);
+                       strcpy(record[j].value, record[k].value);
+                       j++;
+               }
+
+               kvp_file_info[pool].num_records--;
+               kvp_update_file(pool);
+               return 0;
+       }
+       return 1;
+}
+
+static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
+                       int value_size)
+{
+       int i;
+       int j, k;
+       int num_records;
+       struct kvp_record *record;
+       int num_blocks;
+
+       if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
+               (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+               return 1;
+
+       /*
+        * First update the in-memory state.
+        */
+       kvp_update_mem_state(pool);
+
+       num_records = kvp_file_info[pool].num_records;
+       record = kvp_file_info[pool].records;
+       num_blocks = kvp_file_info[pool].num_blocks;
+
+       for (i = 0; i < num_records; i++) {
+               if (memcmp(key, record[i].key, key_size))
+                       continue;
+               /*
+                * Found a match; just update the value -
+                * this is the modify case.
+                */
+               memcpy(record[i].value, value, value_size);
+               kvp_update_file(pool);
+               return 0;
+       }
+
+       /*
+        * Need to add a new entry;
+        */
+       if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
+               /* Need to allocate a larger array for reg entries. */
+               record = realloc(record, sizeof(struct kvp_record) *
+                        ENTRIES_PER_BLOCK * (num_blocks + 1));
+
+               if (record == NULL)
+                       return 1;
+               kvp_file_info[pool].num_blocks++;
+
+       }
+       memcpy(record[i].value, value, value_size);
+       memcpy(record[i].key, key, key_size);
+       kvp_file_info[pool].records = record;
+       kvp_file_info[pool].num_records++;
+       kvp_update_file(pool);
+       return 0;
+}
+
+static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
+                       int value_size)
+{
+       int i;
+       int num_records;
+       struct kvp_record *record;
+
+       if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
+               (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+               return 1;
+
+       /*
+        * First update the in-memory state.
+        */
+       kvp_update_mem_state(pool);
+
+       num_records = kvp_file_info[pool].num_records;
+       record = kvp_file_info[pool].records;
+
+       for (i = 0; i < num_records; i++) {
+               if (memcmp(key, record[i].key, key_size))
+                       continue;
+               /*
+                * Found a match; just copy the value out.
+                */
+               memcpy(value, record[i].value, value_size);
+               return 0;
+       }
+
+       return 1;
+}
+
+static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
+                               __u8 *value, int value_size)
+{
+       struct kvp_record *record;
+
+       /*
+        * First update our in-memory database.
+        */
+       kvp_update_mem_state(pool);
+       record = kvp_file_info[pool].records;
+
+       if (index >= kvp_file_info[pool].num_records) {
+               /*
+                * This is an invalid index; terminate enumeration;
+                * - a NULL value will do the trick.
+                */
+               strcpy(value, "");
+               return;
+       }
+
+       memcpy(key, record[index].key, key_size);
+       memcpy(value, record[index].value, value_size);
+}
+
+
 void kvp_get_os_info(void)
 {
        FILE    *file;
@@ -332,7 +642,7 @@ int main(void)
        struct pollfd pfd;
        struct nlmsghdr *incoming_msg;
        struct cn_msg   *incoming_cn_msg;
-       struct hv_ku_msg *hv_msg;
+       struct hv_kvp_msg *hv_msg;
        char    *p;
        char    *key_value;
        char    *key_name;
@@ -345,6 +655,11 @@ int main(void)
         */
        kvp_get_os_info();
 
+       if (kvp_file_init()) {
+               syslog(LOG_ERR, "Failed to initialize the pools");
+               exit(-1);
+       }
+
        fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
        if (fd < 0) {
                syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
@@ -370,9 +685,11 @@ int main(void)
        message = (struct cn_msg *)kvp_send_buffer;
        message->id.idx = CN_KVP_IDX;
        message->id.val = CN_KVP_VAL;
-       message->seq = KVP_REGISTER;
+
+       hv_msg = (struct hv_kvp_msg *)message->data;
+       hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
        message->ack = 0;
-       message->len = 0;
+       message->len = sizeof(struct hv_kvp_msg);
 
        len = netlink_send(fd, message);
        if (len < 0) {
@@ -398,14 +715,15 @@ int main(void)
 
                incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
                incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
+               hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
 
-               switch (incoming_cn_msg->seq) {
-               case KVP_REGISTER:
+               switch (hv_msg->kvp_hdr.operation) {
+               case KVP_OP_REGISTER:
                        /*
                         * Driver is registering with us; stash away the version
                        &