--- /dev/null
+/*
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2007 Mike Isely <isely@pobox.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+
+This source file should encompass ALL per-device type information for the
+driver.  To define a new device, add elements to the pvr2_device_table and
+pvr2_device_desc structures.
+
+*/
+
+#include "pvrusb2-devattr.h"
+#include <linux/usb.h>
+
+/* Known major hardware variants, keyed from device ID */
+#define PVR2_HDW_TYPE_29XXX 0
+#define PVR2_HDW_TYPE_24XXX 1
+
+struct usb_device_id pvr2_device_table[] = {
+       [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
+       [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
+       { }
+};
+
+/* Names of other client modules to request for 24xxx model hardware */
+static const char *pvr2_client_24xxx[] = {
+       "cx25840",
+       "tuner",
+       "wm8775",
+};
+
+/* Names of other client modules to request for 29xxx model hardware */
+static const char *pvr2_client_29xxx[] = {
+       "msp3400",
+       "saa7115",
+       "tuner",
+};
+
+/* Firmware file name(s) for 29xxx devices */
+static const char *pvr2_fw1_names_29xxx[] = {
+               "v4l-pvrusb2-29xxx-01.fw",
+};
+
+/* Firmware file name(s) for 29xxx devices */
+static const char *pvr2_fw1_names_24xxx[] = {
+               "v4l-pvrusb2-24xxx-01.fw",
+};
+
+const struct pvr2_device_desc pvr2_device_descriptions[] = {
+       [PVR2_HDW_TYPE_29XXX] = {
+               .description = "WinTV PVR USB2 Model Category 29xxxx",
+               .shortname = "29xxx",
+               .client_modules.lst = pvr2_client_29xxx,
+               .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx),
+               .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+               .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+       },
+       [PVR2_HDW_TYPE_24XXX] = {
+               .description = "WinTV PVR USB2 Model Category 24xxxx",
+               .shortname = "24xxx",
+               .client_modules.lst = pvr2_client_24xxx,
+               .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx),
+               .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+               .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+               .flag_has_cx25840 = !0,
+               .flag_has_wm8775 = !0,
+       },
+};
+
+const unsigned int pvr2_device_count = ARRAY_SIZE(pvr2_device_descriptions);
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+
+
+/*
+  Stuff for Emacs to see, in order to encourage consistent editing style:
+  *** Local Variables: ***
+  *** mode: c ***
+  *** fill-column: 75 ***
+  *** tab-width: 8 ***
+  *** c-basic-offset: 8 ***
+  *** End: ***
+  */
 
--- /dev/null
+/*
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef __PVRUSB2_DEVATTR_H
+#define __PVRUSB2_DEVATTR_H
+
+#include <linux/mod_devicetable.h>
+
+/*
+
+  This header defines structures used to describe attributes of a device.
+
+*/
+
+
+struct pvr2_string_table {
+       const char **lst;
+       unsigned int cnt;
+};
+
+
+/* This describes a particular hardware type (except for the USB device ID
+   which must live in a separate structure due to environmental
+   constraints).  See the top of pvrusb2-hdw.c for where this is
+   instantiated. */
+struct pvr2_device_desc {
+       /* Single line text description of hardware */
+       const char *description;
+
+       /* Single token identifier for hardware */
+       const char *shortname;
+
+       /* List of additional client modules we need to load */
+       struct pvr2_string_table client_modules;
+
+       /* List of FX2 firmware file names we should search; if empty then
+          FX2 firmware check / load is skipped and we assume the device
+          was initialized from internal ROM. */
+       struct pvr2_string_table fx2_firmware;
+
+       /* If set, we don't bother trying to load cx23416 firmware. */
+       char flag_skip_cx23416_firmware;
+
+       /* Device does not require a powerup command to be issued. */
+       char flag_no_powerup;
+
+       /* Device has a cx25840 - this enables special additional logic to
+          handle it. */
+       char flag_has_cx25840;
+
+       /* Device has a wm8775 - this enables special additional logic to
+          ensure that it is found. */
+       char flag_has_wm8775;
+};
+
+extern const struct pvr2_device_desc pvr2_device_descriptions[];
+extern struct usb_device_id pvr2_device_table[];
+extern const unsigned int pvr2_device_count;
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+  Stuff for Emacs to see, in order to encourage consistent editing style:
+  *** Local Variables: ***
+  *** mode: c ***
+  *** fill-column: 75 ***
+  *** tab-width: 8 ***
+  *** c-basic-offset: 8 ***
+  *** End: ***
+  */
 
 
        /* This ENC_MISC(3,encMisc3Arg) command is critical - without
           it there will eventually be video corruption.  Also, the
-          29xxx case is strange - the Windows driver is passing 1
-          regardless of device type but if we have 1 for 29xxx device
-          the video turns sluggish.  */
-       switch (hdw->hdw_type) {
-       case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break;
-       case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break;
-       default: break;
+          saa7115 case is strange - the Windows driver is passing 1
+          regardless of device type but if we have 1 for saa7115
+          devices the video turns sluggish.  */
+       if (hdw->hdw_desc->flag_has_cx25840) {
+               encMisc3Arg = 1;
+       } else {
+               encMisc3Arg = 0;
        }
        ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
                                 encMisc3Arg,0,0);
 
        /* saa7115: 0xf0 */
        val = 0xf0;
-       if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+       if (hdw->hdw_desc->flag_has_cx25840) {
                /* ivtv cx25840: 0x140 */
                val = 0x140;
        }
 
 #include "pvrusb2-hdw.h"
 #include "pvrusb2-io.h"
 #include <media/cx2341x.h>
+#include "pvrusb2-devattr.h"
 
 /* Legal values for PVR2_CID_HSM */
 #define PVR2_CVAL_HSM_FAIL 0
 #define FW1_STATE_RELOAD 3
 #define FW1_STATE_OK 4
 
-/* Known major hardware variants, keyed from device ID */
-#define PVR2_HDW_TYPE_29XXX 0
-#define PVR2_HDW_TYPE_24XXX 1
-
 typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
 #define PVR2_I2C_FUNC_CNT 128
 
 
        /* Device type, one of PVR2_HDW_TYPE_xxxxx */
        unsigned int hdw_type;
+       const struct pvr2_device_desc *hdw_desc;
 
        /* Kernel worker thread handling */
        struct workqueue_struct *workqueue;
 
 #define TV_MIN_FREQ     55250000L
 #define TV_MAX_FREQ    850000000L
 
-struct usb_device_id pvr2_device_table[] = {
-       [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
-       [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
-       { }
-};
-
-MODULE_DEVICE_TABLE(usb, pvr2_device_table);
-
-static const char *pvr2_device_names[] = {
-       [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
-       [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
-};
-
-struct pvr2_string_table {
-       const char **lst;
-       unsigned int cnt;
-};
-
-// Names of other client modules to request for 24xxx model hardware
-static const char *pvr2_client_24xxx[] = {
-       "cx25840",
-       "tuner",
-       "wm8775",
-};
-
-// Names of other client modules to request for 29xxx model hardware
-static const char *pvr2_client_29xxx[] = {
-       "msp3400",
-       "saa7115",
-       "tuner",
-};
-
-static struct pvr2_string_table pvr2_client_lists[] = {
-       [PVR2_HDW_TYPE_29XXX] = {
-               pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx)
-       },
-       [PVR2_HDW_TYPE_24XXX] = {
-               pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx)
-       },
-};
-
 static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
 static DEFINE_MUTEX(pvr2_unit_mtx);
 
 
 static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
 {
-       /* Actual minimum depends on device type. */
-       if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+       /* Actual minimum depends on device digitizer type. */
+       if (cptr->hdw->hdw_desc->flag_has_cx25840) {
                *vp = 75;
        } else {
                *vp = 17;
        unsigned int pipe;
        int ret;
        u16 address;
-       static const char *fw_files_29xxx[] = {
-               "v4l-pvrusb2-29xxx-01.fw",
-       };
-       static const char *fw_files_24xxx[] = {
-               "v4l-pvrusb2-24xxx-01.fw",
-       };
-       static const struct pvr2_string_table fw_file_defs[] = {
-               [PVR2_HDW_TYPE_29XXX] = {
-                       fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx)
-               },
-               [PVR2_HDW_TYPE_24XXX] = {
-                       fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx)
-               },
-       };
 
-       if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) ||
-           (!fw_file_defs[hdw->hdw_type].lst)) {
+       if (!hdw->hdw_desc->fx2_firmware.cnt) {
                hdw->fw1_state = FW1_STATE_OK;
                return 0;
        }
        trace_firmware("pvr2_upload_firmware1");
 
        ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
-                                  fw_file_defs[hdw->hdw_type].cnt,
-                                  fw_file_defs[hdw->hdw_type].lst);
+                                  hdw->hdw_desc->fx2_firmware.cnt,
+                                  hdw->hdw_desc->fx2_firmware.lst);
        if (ret < 0) {
                if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
                return ret;
                CX2341X_FIRM_ENC_FILENAME,
        };
 
-       if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) &&
-           (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) {
+       if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
                return 0;
        }
 
        unsigned int idx;
        struct pvr2_ctrl *cptr;
        int reloadFl = 0;
-       if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
-           (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+       if (hdw->hdw_desc->fx2_firmware.cnt) {
                if (!reloadFl) {
                        reloadFl =
                                (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
        }
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
-       if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) {
-               for (idx = 0;
-                    idx < pvr2_client_lists[hdw->hdw_type].cnt;
-                    idx++) {
-                       request_module(
-                               pvr2_client_lists[hdw->hdw_type].lst[idx]);
-               }
+       for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
+               request_module(hdw->hdw_desc->client_modules.lst[idx]);
        }
 
-       if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
-           (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+       if (!hdw->hdw_desc->flag_no_powerup) {
                pvr2_hdw_cmd_powerup(hdw);
                if (!pvr2_hdw_dev_ok(hdw)) return;
        }
        unsigned int hdw_type;
        int valid_std_mask;
        struct pvr2_ctrl *cptr;
+       const struct pvr2_device_desc *hdw_desc;
        __u8 ifnum;
        struct v4l2_queryctrl qctrl;
        struct pvr2_ctl_info *ciptr;
 
        hdw_type = devid - pvr2_device_table;
-       if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) {
+       if (hdw_type >= pvr2_device_count) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                           "Bogus device type of %u reported",hdw_type);
                return NULL;
        }
+       hdw_desc = pvr2_device_descriptions + hdw_type;
 
        hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
-                  hdw,pvr2_device_names[hdw_type]);
+                  hdw,hdw_desc->description);
        if (!hdw) goto fail;
 
        init_timer(&hdw->quiescent_timer);
                                GFP_KERNEL);
        if (!hdw->controls) goto fail;
        hdw->hdw_type = hdw_type;
+       hdw->hdw_desc = hdw_desc;
        for (idx = 0; idx < hdw->control_cnt; idx++) {
                cptr = hdw->controls + idx;
                cptr->hdw = hdw;
 
    a debugging aid. */
 int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
 
-/* List of device types that we can match */
-extern struct usb_device_id pvr2_device_table[];
-
 #endif /* __PVRUSB2_HDW_H */
 
 /*
 
                printk(KERN_INFO "%s: IR disabled\n",hdw->name);
                hdw->i2c_func[0x18] = i2c_black_hole;
        } else if (ir_mode[hdw->unit_number] == 1) {
-               if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+               if (hdw->hdw_desc->flag_has_cx25840) {
                        hdw->i2c_func[0x18] = i2c_24xxx_ir;
                }
        }
-       if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
-               hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+       if (hdw->hdw_desc->flag_has_cx25840) {
                hdw->i2c_func[0x44] = i2c_hack_cx25840;
        }
+       if (hdw->hdw_desc->flag_has_wm8775) {
+               hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+       }
 
        // Configure the adapter and set up everything else related to it.
        memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
 
 #include <linux/videodev2.h>
 
 #include "pvrusb2-hdw.h"
+#include "pvrusb2-devattr.h"
 #include "pvrusb2-context.h"
 #include "pvrusb2-debug.h"
 #include "pvrusb2-v4l2.h"