]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Mar 2015 11:57:18 +0000 (12:57 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Mar 2015 11:57:18 +0000 (12:57 +0100)
added patches:
dm-fix-a-race-condition-in-dm_get_md.patch
dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch
dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch
dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch
firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch
firmware-dmi_scan-fix-dmi_len-type.patch
staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch

queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch [new file with mode: 0644]
queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch [new file with mode: 0644]
queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch [new file with mode: 0644]
queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch [new file with mode: 0644]
queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch [new file with mode: 0644]
queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch [new file with mode: 0644]
queue-3.19/series
queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch [new file with mode: 0644]

diff --git a/queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch b/queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch
new file mode 100644 (file)
index 0000000..5543604
--- /dev/null
@@ -0,0 +1,84 @@
+From 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 Mon Sep 17 00:00:00 2001
+From: Mikulas Patocka <mpatocka@redhat.com>
+Date: Tue, 17 Feb 2015 14:30:53 -0500
+Subject: dm: fix a race condition in dm_get_md
+
+From: Mikulas Patocka <mpatocka@redhat.com>
+
+commit 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 upstream.
+
+The function dm_get_md finds a device mapper device with a given dev_t,
+increases the reference count and returns the pointer.
+
+dm_get_md calls dm_find_md, dm_find_md takes _minor_lock, finds the
+device, tests that the device doesn't have DMF_DELETING or DMF_FREEING
+flag, drops _minor_lock and returns pointer to the device. dm_get_md then
+calls dm_get. dm_get calls BUG if the device has the DMF_FREEING flag,
+otherwise it increments the reference count.
+
+There is a possible race condition - after dm_find_md exits and before
+dm_get is called, there are no locks held, so the device may disappear or
+DMF_FREEING flag may be set, which results in BUG.
+
+To fix this bug, we need to call dm_get while we hold _minor_lock. This
+patch renames dm_find_md to dm_get_md and changes it so that it calls
+dm_get while holding the lock.
+
+Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/md/dm.c |   27 ++++++++++-----------------
+ 1 file changed, 10 insertions(+), 17 deletions(-)
+
+--- a/drivers/md/dm.c
++++ b/drivers/md/dm.c
+@@ -2462,7 +2462,7 @@ int dm_setup_md_queue(struct mapped_devi
+       return 0;
+ }
+-static struct mapped_device *dm_find_md(dev_t dev)
++struct mapped_device *dm_get_md(dev_t dev)
+ {
+       struct mapped_device *md;
+       unsigned minor = MINOR(dev);
+@@ -2473,12 +2473,15 @@ static struct mapped_device *dm_find_md(
+       spin_lock(&_minor_lock);
+       md = idr_find(&_minor_idr, minor);
+-      if (md && (md == MINOR_ALLOCED ||
+-                 (MINOR(disk_devt(dm_disk(md))) != minor) ||
+-                 dm_deleting_md(md) ||
+-                 test_bit(DMF_FREEING, &md->flags))) {
+-              md = NULL;
+-              goto out;
++      if (md) {
++              if ((md == MINOR_ALLOCED ||
++                   (MINOR(disk_devt(dm_disk(md))) != minor) ||
++                   dm_deleting_md(md) ||
++                   test_bit(DMF_FREEING, &md->flags))) {
++                      md = NULL;
++                      goto out;
++              }
++              dm_get(md);
+       }
+ out:
+@@ -2486,16 +2489,6 @@ out:
+       return md;
+ }
+-
+-struct mapped_device *dm_get_md(dev_t dev)
+-{
+-      struct mapped_device *md = dm_find_md(dev);
+-
+-      if (md)
+-              dm_get(md);
+-
+-      return md;
+-}
+ EXPORT_SYMBOL_GPL(dm_get_md);
+ void *dm_get_mdptr(struct mapped_device *md)
diff --git a/queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch b/queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch
new file mode 100644 (file)
index 0000000..b06e742
--- /dev/null
@@ -0,0 +1,62 @@
+From 37527b869207ad4c208b1e13967d69b8bba1fbf9 Mon Sep 17 00:00:00 2001
+From: "Darrick J. Wong" <darrick.wong@oracle.com>
+Date: Fri, 13 Feb 2015 11:05:37 -0800
+Subject: dm io: reject unsupported DISCARD requests with EOPNOTSUPP
+
+From: "Darrick J. Wong" <darrick.wong@oracle.com>
+
+commit 37527b869207ad4c208b1e13967d69b8bba1fbf9 upstream.
+
+I created a dm-raid1 device backed by a device that supports DISCARD
+and another device that does NOT support DISCARD with the following
+dm configuration:
+
+ #  echo '0 2048 mirror core 1 512 2 /dev/sda 0 /dev/sdb 0' | dmsetup create moo
+ # lsblk -D
+ NAME         DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
+ sda                 0        4K       1G         0
+ `-moo (dm-0)        0        4K       1G         0
+ sdb                 0        0B       0B         0
+ `-moo (dm-0)        0        4K       1G         0
+
+Notice that the mirror device /dev/mapper/moo advertises DISCARD
+support even though one of the mirror halves doesn't.
+
+If I issue a DISCARD request (via fstrim, mount -o discard, or ioctl
+BLKDISCARD) through the mirror, kmirrord gets stuck in an infinite
+loop in do_region() when it tries to issue a DISCARD request to sdb.
+The problem is that when we call do_region() against sdb, num_sectors
+is set to zero because q->limits.max_discard_sectors is zero.
+Therefore, "remaining" never decreases and the loop never terminates.
+
+To fix this: before entering the loop, check for the combination of
+REQ_DISCARD and no discard and return -EOPNOTSUPP to avoid hanging up
+the mirror device.
+
+This bug was found by the unfortunate coincidence of pvmove and a
+discard operation in the RHEL 6.5 kernel; upstream is also affected.
+
+Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
+Acked-by: "Martin K. Petersen" <martin.petersen@oracle.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/md/dm-io.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/md/dm-io.c
++++ b/drivers/md/dm-io.c
+@@ -290,6 +290,12 @@ static void do_region(int rw, unsigned r
+       unsigned short logical_block_size = queue_logical_block_size(q);
+       sector_t num_sectors;
++      /* Reject unsupported discard requests */
++      if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) {
++              dec_count(io, region, -EOPNOTSUPP);
++              return;
++      }
++
+       /*
+        * where->count may be zero if rw holds a flush and we need to
+        * send a zero-sized flush.
diff --git a/queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch b/queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch
new file mode 100644 (file)
index 0000000..36766eb
--- /dev/null
@@ -0,0 +1,47 @@
+From f2ed51ac64611d717d1917820a01930174c2f236 Mon Sep 17 00:00:00 2001
+From: Mikulas Patocka <mpatocka@redhat.com>
+Date: Thu, 12 Feb 2015 10:09:20 -0500
+Subject: dm mirror: do not degrade the mirror on discard error
+
+From: Mikulas Patocka <mpatocka@redhat.com>
+
+commit f2ed51ac64611d717d1917820a01930174c2f236 upstream.
+
+It may be possible that a device claims discard support but it rejects
+discards with -EOPNOTSUPP.  It happens when using loopback on ext2/ext3
+filesystem driven by the ext4 driver.  It may also happen if the
+underlying devices are moved from one disk on another.
+
+If discard error happens, we reject the bio with -EOPNOTSUPP, but we do
+not degrade the array.
+
+This patch fixes failed test shell/lvconvert-repair-transient.sh in the
+lvm2 testsuite if the testsuite is extracted on an ext2 or ext3
+filesystem and it is being driven by the ext4 driver.
+
+Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/md/dm-raid1.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/md/dm-raid1.c
++++ b/drivers/md/dm-raid1.c
+@@ -604,6 +604,15 @@ static void write_callback(unsigned long
+               return;
+       }
++      /*
++       * If the bio is discard, return an error, but do not
++       * degrade the array.
++       */
++      if (bio->bi_rw & REQ_DISCARD) {
++              bio_endio(bio, -EOPNOTSUPP);
++              return;
++      }
++
+       for (i = 0; i < ms->nr_mirrors; i++)
+               if (test_bit(i, &error))
+                       fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR);
diff --git a/queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch b/queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch
new file mode 100644 (file)
index 0000000..3f4b3df
--- /dev/null
@@ -0,0 +1,52 @@
+From 22aa66a3ee5b61e0f4a0bfeabcaa567861109ec3 Mon Sep 17 00:00:00 2001
+From: Mikulas Patocka <mpatocka@redhat.com>
+Date: Tue, 17 Feb 2015 14:34:00 -0500
+Subject: dm snapshot: fix a possible invalid memory access on unload
+
+From: Mikulas Patocka <mpatocka@redhat.com>
+
+commit 22aa66a3ee5b61e0f4a0bfeabcaa567861109ec3 upstream.
+
+When the snapshot target is unloaded, snapshot_dtr() waits until
+pending_exceptions_count drops to zero.  Then, it destroys the snapshot.
+Therefore, the function that decrements pending_exceptions_count
+should not touch the snapshot structure after the decrement.
+
+pending_complete() calls free_pending_exception(), which decrements
+pending_exceptions_count, and then it performs up_write(&s->lock) and it
+calls retry_origin_bios() which dereferences  s->origin.  These two
+memory accesses to the fields of the snapshot may touch the dm_snapshot
+struture after it is freed.
+
+This patch moves the call to free_pending_exception() to the end of
+pending_complete(), so that the snapshot will not be destroyed while
+pending_complete() is in progress.
+
+Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/md/dm-snap.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/md/dm-snap.c
++++ b/drivers/md/dm-snap.c
+@@ -1432,8 +1432,6 @@ out:
+               full_bio->bi_private = pe->full_bio_private;
+               atomic_inc(&full_bio->bi_remaining);
+       }
+-      free_pending_exception(pe);
+-
+       increment_pending_exceptions_done_count();
+       up_write(&s->lock);
+@@ -1450,6 +1448,8 @@ out:
+       }
+       retry_origin_bios(s, origin_bios);
++
++      free_pending_exception(pe);
+ }
+ static void commit_callback(void *context, int success)
diff --git a/queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch b/queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch
new file mode 100644 (file)
index 0000000..4f68756
--- /dev/null
@@ -0,0 +1,57 @@
+From ce204e9a4bd82e9e6e7479bca8057e45aaac5c42 Mon Sep 17 00:00:00 2001
+From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+Date: Wed, 18 Feb 2015 15:51:41 +0200
+Subject: firmware: dmi_scan: Fix dmi scan to handle "End of Table"
+ structure
+
+From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+
+commit ce204e9a4bd82e9e6e7479bca8057e45aaac5c42 upstream.
+
+The dmi-sysfs should create "End of Table" entry, that is type 127. But
+after adding initial SMBIOS v3 support fc43026278b2 ("dmi: add support
+for SMBIOS 3.0 64-bit entry point") the 127-0 entry is not handled any
+more, as result it's not created in dmi sysfs for instance. This is
+important because the size of whole DMI table must correspond to sum of
+all DMI entry sizes.
+
+So move the end-of-table check after it's handled by dmi_table.
+
+Reviewed-by: Ard Biesheuvel <ard@linaro.org>
+Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+Signed-off-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/dmi_scan.c |   13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+--- a/drivers/firmware/dmi_scan.c
++++ b/drivers/firmware/dmi_scan.c
+@@ -93,12 +93,6 @@ static void dmi_table(u8 *buf, u32 len,
+               const struct dmi_header *dm = (const struct dmi_header *)data;
+               /*
+-               * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
+-               */
+-              if (dm->type == DMI_ENTRY_END_OF_TABLE)
+-                      break;
+-
+-              /*
+                *  We want to know the total length (formatted area and
+                *  strings) before decoding to make sure we won't run off the
+                *  table in dmi_decode or dmi_string
+@@ -108,6 +102,13 @@ static void dmi_table(u8 *buf, u32 len,
+                       data++;
+               if (data - buf < len - 1)
+                       decode(dm, private_data);
++
++              /*
++               * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
++               */
++              if (dm->type == DMI_ENTRY_END_OF_TABLE)
++                      break;
++
+               data += 2;
+               i++;
+       }
diff --git a/queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch b/queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch
new file mode 100644 (file)
index 0000000..259d2d6
--- /dev/null
@@ -0,0 +1,47 @@
+From 6d9ff473317245e3e5cd9922b4520411c2296388 Mon Sep 17 00:00:00 2001
+From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+Date: Wed, 18 Feb 2015 13:33:19 +0200
+Subject: firmware: dmi_scan: Fix dmi_len type
+
+From: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+
+commit 6d9ff473317245e3e5cd9922b4520411c2296388 upstream.
+
+According to SMBIOSv3 specification the length of DMI table can be
+up to 32bits wide. So use appropriate type to avoid overflow.
+
+It's obvious that dmi_num theoretically can be more than u16 also,
+so it's can be changed to u32 or at least it's better to use int
+instead of u16, but on that moment I cannot imagine dmi structure
+count more than 65535 and it can require changing type of vars that
+work with it. So I didn't correct it.
+
+Acked-by: Ard Biesheuvel <ard@linaro.org>
+Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
+Signed-off-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/dmi_scan.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/firmware/dmi_scan.c
++++ b/drivers/firmware/dmi_scan.c
+@@ -78,7 +78,7 @@ static const char * __init dmi_string(co
+  *    We have to be cautious here. We have seen BIOSes with DMI pointers
+  *    pointing to completely the wrong place for example
+  */
+-static void dmi_table(u8 *buf, int len, int num,
++static void dmi_table(u8 *buf, u32 len, int num,
+                     void (*decode)(const struct dmi_header *, void *),
+                     void *private_data)
+ {
+@@ -114,7 +114,7 @@ static void dmi_table(u8 *buf, int len,
+ }
+ static phys_addr_t dmi_base;
+-static u16 dmi_len;
++static u32 dmi_len;
+ static u16 dmi_num;
+ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
index 4dead3e4912f3903d355ff95dddafd3bb22ab681..fdfc416a2da9e29ea9cee26e7422ce8f915b14ca 100644 (file)
@@ -131,3 +131,10 @@ clk-zynq-force-cpu_2x-clock-to-be-ungated.patch
 clk-fix-debugfs-clk-removal-before-inited.patch
 sunxi-clk-set-sun6i-pll1-n_start-1.patch
 staging-comedi-comedi_compat32.c-fix-comedi_cmd-copy-back.patch
+dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch
+dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch
+dm-fix-a-race-condition-in-dm_get_md.patch
+dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch
+firmware-dmi_scan-fix-dmi_len-type.patch
+firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch
+staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch
diff --git a/queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch b/queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch
new file mode 100644 (file)
index 0000000..9dc3d28
--- /dev/null
@@ -0,0 +1,388 @@
+From be8e89087ec2d2c8a1ad1e3db64bf4efdfc3c298 Mon Sep 17 00:00:00 2001
+From: Ian Abbott <abbotti@mev.co.uk>
+Date: Mon, 19 Jan 2015 14:47:27 +0000
+Subject: staging: comedi: cb_pcidas64: fix incorrect AI range code handling
+
+From: Ian Abbott <abbotti@mev.co.uk>
+
+commit be8e89087ec2d2c8a1ad1e3db64bf4efdfc3c298 upstream.
+
+The hardware range code values and list of valid ranges for the AI
+subdevice is incorrect for several supported boards.  The hardware range
+code values for all boards except PCI-DAS4020/12 is determined by
+calling `ai_range_bits_6xxx()` based on the maximum voltage of the range
+and whether it is bipolar or unipolar, however it only returns the
+correct hardware range code for the PCI-DAS60xx boards.  For
+PCI-DAS6402/16 (and /12) it returns the wrong code for the unipolar
+ranges.  For PCI-DAS64/Mx/16 it returns the wrong code for all the
+ranges and the comedi range table is incorrect.
+
+Change `ai_range_bits_6xxx()` to use a look-up table pointed to by new
+member `ai_range_codes` of `struct pcidas64_board` to map the comedi
+range table indices to the hardware range codes.  Use a new comedi range
+table for the PCI-DAS64/Mx/16 boards (and the commented out variants).
+
+Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/staging/comedi/drivers/cb_pcidas64.c |  124 ++++++++++++++++-----------
+ 1 file changed, 76 insertions(+), 48 deletions(-)
+
+--- a/drivers/staging/comedi/drivers/cb_pcidas64.c
++++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
+@@ -439,6 +439,29 @@ static const struct comedi_lrange ai_ran
+       }
+ };
++static const uint8_t ai_range_code_64xx[8] = {
++      0x0, 0x1, 0x2, 0x3,     /* bipolar 10, 5, 2,5, 1.25 */
++      0x8, 0x9, 0xa, 0xb      /* unipolar 10, 5, 2.5, 1.25 */
++};
++
++/* analog input ranges for 64-Mx boards */
++static const struct comedi_lrange ai_ranges_64_mx = {
++      7, {
++              BIP_RANGE(5),
++              BIP_RANGE(2.5),
++              BIP_RANGE(1.25),
++              BIP_RANGE(0.625),
++              UNI_RANGE(5),
++              UNI_RANGE(2.5),
++              UNI_RANGE(1.25)
++      }
++};
++
++static const uint8_t ai_range_code_64_mx[7] = {
++      0x0, 0x1, 0x2, 0x3,     /* bipolar 5, 2.5, 1.25, 0.625 */
++      0x9, 0xa, 0xb           /* unipolar 5, 2.5, 1.25 */
++};
++
+ /* analog input ranges for 60xx boards */
+ static const struct comedi_lrange ai_ranges_60xx = {
+       4, {
+@@ -449,6 +472,10 @@ static const struct comedi_lrange ai_ran
+       }
+ };
++static const uint8_t ai_range_code_60xx[4] = {
++      0x0, 0x1, 0x4, 0x7      /* bipolar 10, 5, 0.5, 0.05 */
++};
++
+ /* analog input ranges for 6030, etc boards */
+ static const struct comedi_lrange ai_ranges_6030 = {
+       14, {
+@@ -469,6 +496,11 @@ static const struct comedi_lrange ai_ran
+       }
+ };
++static const uint8_t ai_range_code_6030[14] = {
++      0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */
++      0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf  /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */
++};
++
+ /* analog input ranges for 6052, etc boards */
+ static const struct comedi_lrange ai_ranges_6052 = {
+       15, {
+@@ -490,6 +522,11 @@ static const struct comedi_lrange ai_ran
+       }
+ };
++static const uint8_t ai_range_code_6052[15] = {
++      0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */
++      0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf       /* unipolar 10 ... 0.1 */
++};
++
+ /* analog input ranges for 4020 board */
+ static const struct comedi_lrange ai_ranges_4020 = {
+       2, {
+@@ -593,6 +630,7 @@ struct pcidas64_board {
+       int ai_bits;            /*  analog input resolution */
+       int ai_speed;           /*  fastest conversion period in ns */
+       const struct comedi_lrange *ai_range_table;
++      const uint8_t *ai_range_code;
+       int ao_nchan;           /*  number of analog out channels */
+       int ao_bits;            /*  analog output resolution */
+       int ao_scan_speed;      /*  analog output scan speed */
+@@ -651,6 +689,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+               .ai_range_table = &ai_ranges_64xx,
++              .ai_range_code  = ai_range_code_64xx,
+               .ao_range_table = &ao_ranges_64xx,
+               .ao_range_code  = ao_range_code_64xx,
+               .ai_fifo        = &ai_fifo_64xx,
+@@ -666,6 +705,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+               .ai_range_table = &ai_ranges_64xx,
++              .ai_range_code  = ai_range_code_64xx,
+               .ao_range_table = &ao_ranges_64xx,
+               .ao_range_code  = ao_range_code_64xx,
+               .ai_fifo        = &ai_fifo_64xx,
+@@ -680,7 +720,8 @@ static const struct pcidas64_board pcida
+               .ao_bits        = 16,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ao_range_table = &ao_ranges_64xx,
+               .ao_range_code  = ao_range_code_64xx,
+               .ai_fifo        = &ai_fifo_64xx,
+@@ -695,7 +736,8 @@ static const struct pcidas64_board pcida
+               .ao_bits        = 16,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ao_range_table = &ao_ranges_64xx,
+               .ao_range_code  = ao_range_code_64xx,
+               .ai_fifo        = &ai_fifo_64xx,
+@@ -710,7 +752,8 @@ static const struct pcidas64_board pcida
+               .ao_bits        = 16,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ao_range_table = &ao_ranges_64xx,
+               .ao_range_code  = ao_range_code_64xx,
+               .ai_fifo        = &ai_fifo_64xx,
+@@ -725,6 +768,7 @@ static const struct pcidas64_board pcida
+               .ao_bits        = 16,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -740,6 +784,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 100000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -754,6 +799,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 100000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -769,6 +815,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 100000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -784,6 +831,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6030,
++              .ai_range_code  = ai_range_code_6030,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -799,6 +847,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6030,
++              .ai_range_code  = ai_range_code_6030,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -812,6 +861,7 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 0,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6030,
++              .ai_range_code  = ai_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+               .has_8255       = 0,
+       },
+@@ -823,6 +873,7 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 0,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6030,
++              .ai_range_code  = ai_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+               .has_8255       = 0,
+       },
+@@ -835,6 +886,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 0,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+               .has_8255       = 0,
+       },
+@@ -848,6 +900,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 100000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -863,6 +916,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 100000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_60xx,
++              .ai_range_code  = ai_range_code_60xx,
+               .ao_range_table = &range_bipolar10,
+               .ao_range_code  = ao_range_code_60xx,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -878,6 +932,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 1000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6052,
++              .ai_range_code  = ai_range_code_6052,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -893,6 +948,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 3333,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6052,
++              .ai_range_code  = ai_range_code_6052,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -908,6 +964,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 1000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6052,
++              .ai_range_code  = ai_range_code_6052,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -923,6 +980,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 1000,
+               .layout         = LAYOUT_60XX,
+               .ai_range_table = &ai_ranges_6052,
++              .ai_range_code  = ai_range_code_6052,
+               .ao_range_table = &ao_ranges_6030,
+               .ao_range_code  = ao_range_code_6030,
+               .ai_fifo        = &ai_fifo_60xx,
+@@ -957,6 +1015,7 @@ static const struct pcidas64_board pcida
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+               .ai_range_table = &ai_ranges_64xx,
++              .ai_range_code  = ai_range_code_64xx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -968,7 +1027,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 0,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -980,7 +1040,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 0,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -992,7 +1053,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 0,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -1004,7 +1066,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 2,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -1016,7 +1079,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 2,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -1028,7 +1092,8 @@ static const struct pcidas64_board pcida
+               .ao_nchan       = 2,
+               .ao_scan_speed  = 10000,
+               .layout         = LAYOUT_64XX,
+-              .ai_range_table = &ai_ranges_64xx,
++              .ai_range_table = &ai_ranges_64_mx,
++              .ai_range_code  = ai_range_code_64_mx,
+               .ai_fifo        = ai_fifo_64xx,
+               .has_8255       = 1,
+       },
+@@ -1115,45 +1180,8 @@ static unsigned int ai_range_bits_6xxx(c
+                                      unsigned int range_index)
+ {
+       const struct pcidas64_board *thisboard = dev->board_ptr;
+-      const struct comedi_krange *range =
+-              &thisboard->ai_range_table->range[range_index];
+-      unsigned int bits = 0;
+-
+-      switch (range->max) {
+-      case 10000000:
+-              bits = 0x000;
+-              break;
+-      case 5000000:
+-              bits = 0x100;
+-              break;
+-      case 2000000:
+-      case 2500000:
+-              bits = 0x200;
+-              break;
+-      case 1000000:
+-      case 1250000:
+-              bits = 0x300;
+-              break;
+-      case 500000:
+-              bits = 0x400;
+-              break;
+-      case 200000:
+-      case 250000:
+-              bits = 0x500;
+-              break;
+-      case 100000:
+-              bits = 0x600;
+-              break;
+-      case 50000:
+-              bits = 0x700;
+-              break;
+-      default:
+-              dev_err(dev->class_dev, "bug! in %s\n", __func__);
+-              break;
+-      }
+-      if (range->min == 0)
+-              bits += 0x900;
+-      return bits;
++
++      return thisboard->ai_range_code[range_index] << 8;
+ }
+ static unsigned int hw_revision(const struct comedi_device *dev,