V4L/DVB (11159): pvrusb2: Providing means to stop tracking an old i2c module
[deliverable/linux.git] / drivers / media / video / pvrusb2 / pvrusb2-hdw.c
index 4358079f1966f4efa4a60eaded89e9c75e1866fd..0e0d086bb2785f62c7365024f903f76fe699f601 100644 (file)
@@ -29,6 +29,7 @@
 #include "pvrusb2-util.h"
 #include "pvrusb2-hdw.h"
 #include "pvrusb2-i2c-core.h"
+#include "pvrusb2-i2c-track.h"
 #include "pvrusb2-tuner.h"
 #include "pvrusb2-eeprom.h"
 #include "pvrusb2-hdw-internal.h"
@@ -104,6 +105,20 @@ MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
 /* size of a firmware chunk */
 #define FIRMWARE_CHUNK_SIZE 0x2000
 
+static const char *module_names[] = {
+       [PVR2_CLIENT_ID_MSP3400] = "msp3400",
+       [PVR2_CLIENT_ID_CX25840] = "cx25840",
+       [PVR2_CLIENT_ID_SAA7115] = "saa7115",
+       [PVR2_CLIENT_ID_TUNER] = "tuner",
+       [PVR2_CLIENT_ID_CS53132A] = "cs53132a",
+};
+
+
+static const unsigned char *module_i2c_addresses[] = {
+       [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
+};
+
+
 /* Define the list of additional controls we'll dynamically construct based
    on query of the cx2341x module. */
 struct pvr2_mpeg_ids {
@@ -642,7 +657,7 @@ static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
        unsigned long fv;
        struct pvr2_hdw *hdw = cptr->hdw;
        if (hdw->tuner_signal_stale) {
-               pvr2_i2c_core_status_poll(hdw);
+               pvr2_hdw_status_poll(hdw);
        }
        fv = hdw->tuner_signal_info.rangehigh;
        if (!fv) {
@@ -664,7 +679,7 @@ static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
        unsigned long fv;
        struct pvr2_hdw *hdw = cptr->hdw;
        if (hdw->tuner_signal_stale) {
-               pvr2_i2c_core_status_poll(hdw);
+               pvr2_hdw_status_poll(hdw);
        }
        fv = hdw->tuner_signal_info.rangelow;
        if (!fv) {
@@ -858,7 +873,7 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
 static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
 {
        struct pvr2_hdw *hdw = cptr->hdw;
-       pvr2_i2c_core_status_poll(hdw);
+       pvr2_hdw_status_poll(hdw);
        *vp = hdw->tuner_signal_info.signal;
        return 0;
 }
@@ -868,7 +883,7 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
        int val = 0;
        unsigned int subchan;
        struct pvr2_hdw *hdw = cptr->hdw;
-       pvr2_i2c_core_status_poll(hdw);
+       pvr2_hdw_status_poll(hdw);
        subchan = hdw->tuner_signal_info.rxsubchans;
        if (subchan & V4L2_TUNER_SUB_MONO) {
                val |= (1 << V4L2_TUNER_MODE_MONO);
@@ -1283,6 +1298,12 @@ const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
 }
 
 
+const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
+{
+       return hdw->identifier;
+}
+
+
 unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
 {
        return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
@@ -1927,6 +1948,111 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
 }
 
 
+static unsigned int pvr2_copy_i2c_addr_list(
+       unsigned short *dst, const unsigned char *src,
+       unsigned int dst_max)
+{
+       unsigned int cnt;
+       if (!src) return 0;
+       while (src[cnt] && (cnt + 1) < dst_max) {
+               dst[cnt] = src[cnt];
+               cnt++;
+       }
+       dst[cnt] = I2C_CLIENT_END;
+       return cnt;
+}
+
+
+static void pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
+                                const struct pvr2_device_client_desc *cd)
+{
+       const char *fname;
+       unsigned char mid;
+       struct v4l2_subdev *sd;
+       unsigned int i2ccnt;
+       const unsigned char *p;
+       /* Arbitrary count - max # i2c addresses we will probe */
+       unsigned short i2caddr[25];
+
+       mid = cd->module_id;
+       fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
+       if (!fname) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Module ID %u for device %s is unknown"
+                          " (this is probably a bad thing...)",
+                          mid,
+                          hdw->hdw_desc->description);
+               return;
+       }
+
+       i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
+                                        ARRAY_SIZE(i2caddr));
+       if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
+                        module_i2c_addresses[mid] : NULL) != NULL)) {
+               /* Second chance: Try default i2c address list */
+               i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
+                                                ARRAY_SIZE(i2caddr));
+       }
+
+       if (!i2ccnt) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Module ID %u for device %s:"
+                          " No i2c addresses"
+                          " (this is probably a bad thing...)",
+                          mid, hdw->hdw_desc->description);
+               return;
+       }
+
+       /* Note how the 2nd and 3rd arguments are the same for both
+        * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev().  Why?
+        * Well the 2nd argument is the module name to load, while the 3rd
+        * argument is documented in the framework as being the "chipid" -
+        * and every other place where I can find examples of this, the
+        * "chipid" appears to just be the module name again.  So here we
+        * just do the same thing. */
+       if (i2ccnt == 1) {
+               sd = v4l2_i2c_new_subdev(&hdw->i2c_adap,
+                                        fname, fname,
+                                        i2caddr[0]);
+       } else {
+               sd = v4l2_i2c_new_probed_subdev(&hdw->i2c_adap,
+                                               fname, fname,
+                                               i2caddr);
+       }
+
+       /* If we have both old and new i2c layers enabled, make sure that
+          old layer isn't also tracking this module.  This is a debugging
+          aid, in normal situations there's no reason for both mechanisms
+          to be enabled. */
+       pvr2_i2c_untrack_subdev(hdw, sd);
+
+       // ?????
+       /* Based on module ID, we should remember subdev pointers
+          so that we can send certain custom commands where
+          needed. */
+       // ?????
+
+}
+
+
+static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
+{
+       unsigned int idx;
+       const struct pvr2_string_table *cm;
+       const struct pvr2_device_client_table *ct;
+
+       cm = &hdw->hdw_desc->client_modules;
+       for (idx = 0; idx < cm->cnt; idx++) {
+               request_module(cm->lst[idx]);
+       }
+
+       ct = &hdw->hdw_desc->client_table;
+       for (idx = 0; idx < ct->cnt; idx++) {
+               pvr2_hdw_load_subdev(hdw,&ct->lst[idx]);
+       }
+}
+
+
 static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 {
        int ret;
@@ -1966,10 +2092,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
-       for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
-               request_module(hdw->hdw_desc->client_modules.lst[idx]);
-       }
-
        if (!hdw->hdw_desc->flag_no_powerup) {
                pvr2_hdw_cmd_powerup(hdw);
                if (!pvr2_hdw_dev_ok(hdw)) return;
@@ -1984,9 +2106,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
        }
 
        // This step MUST happen after the earlier powerup step.
+       pvr2_i2c_track_init(hdw);
        pvr2_i2c_core_init(hdw);
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
+       pvr2_hdw_load_modules(hdw);
+
        for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
                cptr = hdw->controls + idx;
                if (cptr->info->skip_init) continue;
@@ -2024,6 +2149,19 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
                hdw->std_mask_eeprom = V4L2_STD_ALL;
        }
 
+       if (hdw->serial_number) {
+               idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+                               "sn-%lu", hdw->serial_number);
+       } else if (hdw->unit_number >= 0) {
+               idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+                               "unit-%c",
+                               hdw->unit_number + 'a');
+       } else {
+               idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+                               "unit-??");
+       }
+       hdw->identifier[idx] = 0;
+
        pvr2_hdw_setup_std(hdw);
 
        if (!get_default_tuner_type(hdw)) {
@@ -2171,11 +2309,14 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        struct pvr2_hdw *hdw = NULL;
        int valid_std_mask;
        struct pvr2_ctrl *cptr;
+       struct usb_device *usb_dev;
        const struct pvr2_device_desc *hdw_desc;
        __u8 ifnum;
        struct v4l2_queryctrl qctrl;
        struct pvr2_ctl_info *ciptr;
 
+       usb_dev = interface_to_usbdev(intf);
+
        hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
 
        if (hdw_desc == NULL) {
@@ -2360,6 +2501,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
        if (!hdw->ctl_read_urb) goto fail;
 
+       if (v4l2_device_register(&usb_dev->dev, &hdw->v4l2_dev) != 0) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Error registering with v4l core, giving up");
+               goto fail;
+       }
        mutex_lock(&pvr2_unit_mtx); do {
                for (idx = 0; idx < PVR_NUM; idx++) {
                        if (unit_pointers[idx]) continue;
@@ -2391,12 +2537,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        hdw->flag_ok = !0;
 
        hdw->usb_intf = intf;
-       hdw->usb_dev = interface_to_usbdev(intf);
+       hdw->usb_dev = usb_dev;
 
-       scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
-                 "usb %s address %d",
-                 dev_name(&hdw->usb_dev->dev),
-                 hdw->usb_dev->devnum);
+       usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
 
        ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
        usb_set_interface(hdw->usb_dev,ifnum,0);
@@ -2454,6 +2597,13 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
                hdw->ctl_write_buffer = NULL;
        }
        hdw->flag_disconnected = !0;
+       /* If we don't do this, then there will be a dangling struct device
+          reference to our disappearing device persisting inside the V4L
+          core... */
+       if (hdw->v4l2_dev.dev) {
+               dev_set_drvdata(hdw->v4l2_dev.dev, NULL);
+               hdw->v4l2_dev.dev = NULL;
+       }
        hdw->usb_dev = NULL;
        hdw->usb_intf = NULL;
        pvr2_hdw_render_useless(hdw);
@@ -2485,6 +2635,8 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
                hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt);
        }
        pvr2_i2c_core_done(hdw);
+       pvr2_i2c_track_done(hdw);
+       v4l2_device_unregister(&hdw->v4l2_dev);
        pvr2_hdw_remove_usb_stuff(hdw);
        mutex_lock(&pvr2_unit_mtx); do {
                if ((hdw->unit_number >= 0) &&
@@ -2973,7 +3125,7 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
 void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
 {
        LOCK_TAKE(hdw->big_lock); do {
-               pvr2_i2c_core_status_poll(hdw);
+               pvr2_hdw_status_poll(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
 }
 
@@ -2983,7 +3135,7 @@ static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
        if (!hdw->cropcap_stale) {
                return 0;
        }
-       pvr2_i2c_core_status_poll(hdw);
+       pvr2_hdw_status_poll(hdw);
        if (hdw->cropcap_stale) {
                return -EIO;
        }
@@ -3010,7 +3162,7 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
 {
        LOCK_TAKE(hdw->big_lock); do {
                if (hdw->tuner_signal_stale) {
-                       pvr2_i2c_core_status_poll(hdw);
+                       pvr2_hdw_status_poll(hdw);
                }
                memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
        } while (0); LOCK_GIVE(hdw->big_lock);
@@ -3032,8 +3184,8 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
                hdw->log_requested = !0;
                printk(KERN_INFO "pvrusb2: =================  START STATUS CARD #%d  =================\n", nr);
                pvr2_i2c_core_check_stale(hdw);
-               hdw->log_requested = 0;
                pvr2_i2c_core_sync(hdw);
+               hdw->log_requested = 0;
                pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
                cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
                pvr2_hdw_state_log_state(hdw);
@@ -3655,7 +3807,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
        int ret;
        pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
        ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
-       if (ret == 1) {
+       if (ret == 0) {
                ret = usb_reset_device(hdw->usb_dev);
                usb_unlock_device(hdw->usb_dev);
        } else {
@@ -4641,6 +4793,12 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
 }
 
 
+void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
+{
+       pvr2_i2c_core_status_poll(hdw);
+}
+
+
 unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
 {
        return hdw->input_avail_mask;
@@ -4732,26 +4890,25 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
 
 
 int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
-                            u32 match_type, u32 match_chip, u64 reg_id,
-                            int setFl,u64 *val_ptr)
+                            struct v4l2_dbg_match *match, u64 reg_id,
+                            int setFl, u64 *val_ptr)
 {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        struct pvr2_i2c_client *cp;
-       struct v4l2_register req;
+       struct v4l2_dbg_register req;
        int stat = 0;
        int okFl = 0;
 
        if (!capable(CAP_SYS_ADMIN)) return -EPERM;
 
-       req.match_type = match_type;
-       req.match_chip = match_chip;
+       req.match = *match;
        req.reg = reg_id;
        if (setFl) req.val = *val_ptr;
        mutex_lock(&hdw->i2c_list_lock); do {
                list_for_each_entry(cp, &hdw->i2c_clients, list) {
                        if (!v4l2_chip_match_i2c_client(
                                    cp->client,
-                                   req.match_type, req.match_chip)) {
+                                   &req.match)) {
                                continue;
                        }
                        stat = pvr2_i2c_client_cmd(
This page took 0.033186 seconds and 5 git commands to generate.