media: usb: em28xx: resolve WinTV dualHD plugging/unplugging crash
Sungtak Lee [Thu, 9 Feb 2017 18:02:35 +0000 (10:02 -0800)]
The second adapter has to be removed first because the data structure of
the first adapter contains a pointer to the second. If memory of the
first adapter is freed before the second adapter is removed the pointer
could be corrupted and kernel crashes could follow.
SHA1 commit:d86686b5879b5fba57a0fac47d94982cafaa65bb

Bug: 32669837
Bug 1904252

Change-Id: Ie3e8d726261e7d88822a5875e89b31e04bef9fce
Signed-off-by: Jean Huang <jeanh@nvidia.com>
Signed-off-by: Patrick Horng<phorng@nvidia.com>
Reviewed-on: http://git-master/r/1460865
Reviewed-by: Manish Tuteja <mtuteja@nvidia.com>
Tested-by: Manish Tuteja <mtuteja@nvidia.com>

drivers/media/usb/em28xx/em28xx-audio.c
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/em28xx/em28xx-core.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-i2c.c
drivers/media/usb/em28xx/em28xx-input.c
drivers/media/usb/em28xx/em28xx-video.c
drivers/media/usb/em28xx/em28xx.h

index 2fdb66e..d7eea27 100644 (file)
@@ -60,6 +60,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 
 static int em28xx_deinit_isoc_audio(struct em28xx *dev)
 {
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int i;
 
        dprintk("Stopping isoc\n");
@@ -272,6 +273,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
 {
        struct em28xx *dev = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int ret = 0;
 
        dprintk("opening device and trying to acquire exclusive lock\n");
@@ -291,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
 
                dprintk("changing alternate number on interface %d to %d\n",
                        dev->audio_ifnum, dev->alt);
-               usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
+               usb_set_interface(udev, dev->audio_ifnum, dev->alt);
 
                /* Sets volume, mute, etc */
                dev->mute = 0;
@@ -637,6 +639,7 @@ static struct snd_pcm_ops snd_em28xx_pcm_capture = {
 static int em28xx_audio_init(struct em28xx *dev)
 {
        struct em28xx_audio *adev = &dev->adev;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        struct snd_pcm      *pcm;
        struct snd_card     *card;
        static int          devnr;
@@ -671,7 +674,7 @@ static int em28xx_audio_init(struct em28xx *dev)
        pcm->private_data = dev;
        strcpy(pcm->name, "Empia 28xx Capture");
 
-       snd_card_set_dev(card, &dev->udev->dev);
+       snd_card_set_dev(card, &udev->dev);
        strcpy(card->driver, "Em28xx-Audio");
        strcpy(card->shortname, "Em28xx Audio");
        strcpy(card->longname, "Empia Em28xx Audio");
@@ -700,7 +703,7 @@ static int em28xx_audio_init(struct em28xx *dev)
                return err;
        }
        adev->sndcard = card;
-       adev->udev = dev->udev;
+       adev->udev = udev;
 
        return 0;
 }
index 681839f..1c6fca2 100644 (file)
@@ -2890,6 +2890,8 @@ static void flush_request_modules(struct em28xx *dev)
 */
 void em28xx_release_resources(struct em28xx *dev)
 {
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
+
        /*FIXME: I2C IR should be disconnected */
 
        em28xx_release_analog_resources(dev);
@@ -2903,7 +2905,7 @@ void em28xx_release_resources(struct em28xx *dev)
        v4l2_device_unregister(&dev->v4l2_dev);
 
        if (dev->ts == PRIMARY_TS)
-               usb_put_dev(dev->udev);
+               usb_put_dev(udev);
 
        /* Mark device as unused */
        clear_bit(dev->devno, &em28xx_devused);
@@ -2919,10 +2921,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
 {
        struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
        int retval;
-       static const char *default_chip_name = "em28xx";
-       const char *chip_name = default_chip_name;
+       const char *chip_name = "em28xx";
 
-       dev->udev = udev;
+       dev->intf = interface;
        mutex_init(&dev->vb_queue_lock);
        mutex_init(&dev->vb_vbi_queue_lock);
        mutex_init(&dev->ctrl_urb_lock);
@@ -2965,14 +2966,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                        dev->eeprom_addrwidth_16bit = 1;
                        break;
                case CHIP_ID_EM2820:
-                       chip_name = "em2710/2820";
-                       if (le16_to_cpu(dev->udev->descriptor.idVendor)
-                                                                   == 0xeb1a) {
-                               __le16 idProd = dev->udev->descriptor.idProduct;
+                       if (le16_to_cpu(udev->descriptor.idVendor) == 0xeb1a) {
+                               __le16 idProd = udev->descriptor.idProduct;
                                if (le16_to_cpu(idProd) == 0x2710)
                                        chip_name = "em2710";
                                else if (le16_to_cpu(idProd) == 0x2820)
                                        chip_name = "em2820";
+                       } else {
+                               chip_name = "em2710/2820";
                        }
                        /* NOTE: the em2820 is used in webcams, too ! */
                        break;
@@ -3014,9 +3015,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
                }
        }
 
-       if (chip_name != default_chip_name)
-               printk(KERN_INFO DRIVER_NAME
-                      ": chip ID is %s\n", chip_name);
+       printk(KERN_INFO DRIVER_NAME ": chip ID is %s\n", chip_name);
 
        /*
         * For em2820/em2710, the name may change latter, after checking
@@ -3173,16 +3172,29 @@ unregister_dev:
 
 int em28xx_duplicate_dev(struct em28xx *dev)
 {
-       struct em28xx *sec_dev = NULL;
        int nr;
-       sec_dev = kzalloc(sizeof(struct em28xx), GFP_KERNEL);
-       memcpy(sec_dev, dev, sizeof(struct em28xx));
-       nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
-       test_and_set_bit(nr, &em28xx_devused);
+       struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL);
+       if (sec_dev == NULL) {
+               dev->dev_next = NULL;
+               return -ENOMEM;
+       }
+       memcpy(sec_dev, dev, sizeof(sizeof(*sec_dev)));
+       /* Check to see next free device and mark as used */
+       do {
+               nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+               if (nr >= EM28XX_MAXBOARDS) {
+                       /* No free device slots */
+                       printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+                                       EM28XX_MAXBOARDS);
+                       kfree(sec_dev);
+                       dev->dev_next = NULL;
+                       return -ENOMEM;
+               }
+       } while (test_and_set_bit(nr, &em28xx_devused));
        sec_dev->devno = nr;
        snprintf(sec_dev->name, 28, "em28xx #%d", nr);
-       dev->dev_next = sec_dev;
        sec_dev->dev_next = NULL;
+       dev->dev_next = sec_dev;
        return 0;
 }
 
@@ -3470,8 +3482,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
                            dev->dvb_xfer_bulk ? "bulk" : "isoc");
        }
 
-       if (dev->board.has_dual_ts) {
-               em28xx_duplicate_dev(dev);
+       if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {
                dev->dev_next->ts = SECONDARY_TS;
                dev->dev_next->alt = -1;
                dev->dev_next->is_audio_only = has_audio && !(has_video || has_dvb);
index 5bbfc47..14bceea 100644 (file)
@@ -75,7 +75,8 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
                                   char *buf, int len)
 {
        int ret;
-       int pipe = usb_rcvctrlpipe(dev->udev, 0);
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
+       int pipe = usb_rcvctrlpipe(udev, 0);
 
        if (dev->disconnected)
                return -ENODEV;
@@ -94,7 +95,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
        }
 
        mutex_lock(&dev->ctrl_urb_lock);
-       ret = usb_control_msg(dev->udev, pipe, req,
+       ret = usb_control_msg(udev, pipe, req,
                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              0x0000, reg, dev->urb_buf, len, HZ);
        if (ret < 0) {
@@ -151,7 +152,8 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
                                 int len)
 {
        int ret;
-       int pipe = usb_sndctrlpipe(dev->udev, 0);
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
+       int pipe = usb_sndctrlpipe(udev, 0);
 
        if (dev->disconnected)
                return -ENODEV;
@@ -177,7 +179,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
 
        mutex_lock(&dev->ctrl_urb_lock);
        memcpy(dev->urb_buf, buf, len);
-       ret = usb_control_msg(dev->udev, pipe, req,
+       ret = usb_control_msg(udev, pipe, req,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              0x0000, reg, dev->urb_buf, len, HZ);
        mutex_unlock(&dev->ctrl_urb_lock);
@@ -883,6 +885,7 @@ int em28xx_resolution_set(struct em28xx *dev)
 /* Set USB alternate setting for analog video */
 int em28xx_set_alternate(struct em28xx *dev)
 {
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int errCode;
        int i;
        unsigned int min_pkt_size = dev->width * 2 + 4;
@@ -934,7 +937,7 @@ set_alt:
        }
        em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
                       dev->alt, dev->max_pkt_size);
-       errCode = usb_set_interface(dev->udev, 0, dev->alt);
+       errCode = usb_set_interface(udev, 0, dev->alt);
        if (errCode < 0) {
                em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
                              dev->alt, errCode);
@@ -1052,6 +1055,7 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode)
 {
        struct urb *urb;
        struct em28xx_usb_bufs *usb_bufs;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int i;
 
        em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n",
@@ -1071,7 +1075,7 @@ void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode)
                                usb_unlink_urb(urb);
 
                        if (usb_bufs->transfer_buffer[i]) {
-                               usb_free_coherent(dev->udev,
+                               usb_free_coherent(udev,
                                        urb->transfer_buffer_length,
                                        usb_bufs->transfer_buffer[i],
                                        urb->transfer_dma);
@@ -1125,9 +1129,10 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
                      int num_bufs, int max_pkt_size, int packet_multiplier)
 {
        struct em28xx_usb_bufs *usb_bufs;
+       struct urb *urb;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int i;
        int sb_size, pipe;
-       struct urb *urb;
        int j, k;
 
        em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
@@ -1194,7 +1199,7 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
                }
                usb_bufs->urb[i] = urb;
 
-               usb_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+               usb_bufs->transfer_buffer[i] = usb_alloc_coherent(udev,
                        sb_size, GFP_KERNEL, &urb->transfer_dma);
                if (!usb_bufs->transfer_buffer[i]) {
                        em28xx_err("unable to allocate %i bytes for transfer"
@@ -1207,20 +1212,20 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
                memset(usb_bufs->transfer_buffer[i], 0, sb_size);
 
                if (xfer_bulk) { /* bulk */
-                       pipe = usb_rcvbulkpipe(dev->udev,
+                       pipe = usb_rcvbulkpipe(udev,
                                               mode == EM28XX_ANALOG_MODE ?
                                               dev->analog_ep_bulk :
                                               dev->dvb_ep_bulk);
-                       usb_fill_bulk_urb(urb, dev->udev, pipe,
+                       usb_fill_bulk_urb(urb, udev, pipe,
                                          usb_bufs->transfer_buffer[i], sb_size,
                                          em28xx_irq_callback, dev);
                        urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
                } else { /* isoc */
-                       pipe = usb_rcvisocpipe(dev->udev,
+                       pipe = usb_rcvisocpipe(udev,
                                               mode == EM28XX_ANALOG_MODE ?
                                               dev->analog_ep_isoc :
                                               dev->dvb_ep_isoc);
-                       usb_fill_int_urb(urb, dev->udev, pipe,
+                       usb_fill_int_urb(urb, udev, pipe,
                                         usb_bufs->transfer_buffer[i], sb_size,
                                         em28xx_irq_callback, dev, 1);
                        urb->transfer_flags = URB_ISO_ASAP |
@@ -1252,6 +1257,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
        struct em28xx_dmaqueue *dma_q = &dev->vidq;
        struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
        struct em28xx_usb_bufs *usb_bufs;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int i;
        int rc;
        int alloc;
@@ -1278,7 +1284,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
        }
 
        if (xfer_bulk) {
-               rc = usb_clear_halt(dev->udev, usb_bufs->urb[0]->pipe);
+               rc = usb_clear_halt(udev, usb_bufs->urb[0]->pipe);
                if (rc < 0) {
                        em28xx_err("failed to clear USB bulk endpoint stall/halt condition (error=%i)\n",
                                   rc);
@@ -1372,10 +1378,11 @@ void em28xx_init_extension(struct em28xx *dev)
        mutex_lock(&em28xx_devlist_mutex);
        list_add_tail(&dev->devlist, &em28xx_devlist);
        list_for_each_entry(ops, &em28xx_extension_devlist, next) {
-               if (ops->init)
+               if (ops->init) {
                        ops->init(dev);
-               if (dev->dev_next != NULL)
-                       ops->init(dev->dev_next);
+                       if (dev->dev_next != NULL)
+                               ops->init(dev->dev_next);
+               }
        }
        mutex_unlock(&em28xx_devlist_mutex);
 }
@@ -1386,10 +1393,11 @@ void em28xx_close_extension(struct em28xx *dev)
 
        mutex_lock(&em28xx_devlist_mutex);
        list_for_each_entry(ops, &em28xx_extension_devlist, next) {
-               if (ops->fini)
+               if (ops->fini) {
+                       if (dev->dev_next != NULL)
+                               ops->fini(dev->dev_next);
                        ops->fini(dev);
-               if (dev->dev_next != NULL)
-                       ops->fini(dev->dev_next);
+               }
        }
        list_del(&dev->devlist);
        mutex_unlock(&em28xx_devlist_mutex);
index 5818546..bf5fc6d 100644 (file)
@@ -700,7 +700,8 @@ static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe)
 
        return ret;
 #else
-       dev_warn(&dev->udev->dev, "%s: LNA control is disabled (lna=%u)\n",
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
+       dev_warn(&udev->dev, "%s: LNA control is disabled (lna=%u)\n",
                        KBUILD_MODNAME, c->lna);
        return 0;
 #endif
@@ -966,6 +967,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
 {
        int result = 0, mfe_shared = 0, dvb_alt = 0;
        struct em28xx_dvb *dvb;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
 
        if (!dev->board.has_dvb) {
                /* This device does not support the extension */
@@ -1102,7 +1104,8 @@ static int em28xx_dvb_init(struct em28xx *dev)
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
        case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
                dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
-                                          &dev->i2c_adap[dev->def_i2c_bus], &dev->udev->dev);
+                                       &dev->i2c_adap[dev->def_i2c_bus],
+                                       &dev->intf->dev);
                if (em28xx_attach_xc3028(0x61, dev) < 0) {
                        result = -EINVAL;
                        goto out_free;
@@ -1487,7 +1490,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
                dvb->fe[1]->callback = em28xx_tuner_callback;
 
        /* register everything */
-       result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+       result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &udev->dev);
 
        if (result < 0)
                goto out_free;
@@ -1500,7 +1503,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
        } else { /* isoc */
                dvb_alt = dev->dvb_alt_isoc;
        }
-       usb_set_interface(dev->udev, dev->ifnum, dvb_alt);
+       usb_set_interface(udev, dev->ifnum, dvb_alt);
        em28xx_info("Successfully loaded em28xx-dvb\n");
 ret:
        em28xx_set_mode(dev, EM28XX_SUSPEND);
index c4ff973..5c814a2 100644 (file)
@@ -871,6 +871,7 @@ void em28xx_do_i2c_scan(struct em28xx *dev, unsigned bus)
 int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
                        enum em28xx_i2c_algo_type algo_type)
 {
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int retval;
 
        BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
@@ -880,7 +881,7 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
                return -ENODEV;
 
        dev->i2c_adap[bus] = em28xx_adap_template;
-       dev->i2c_adap[bus].dev.parent = &dev->udev->dev;
+       dev->i2c_adap[bus].dev.parent = &udev->dev;
        strcpy(dev->i2c_adap[bus].name, dev->name);
 
        dev->i2c_bus[bus].bus = bus;
index 466b19d..e16e2bc 100644 (file)
@@ -668,7 +668,7 @@ static int em28xx_ir_init(struct em28xx *dev)
        rc->input_id.version = 1;
        rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
        rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
-       rc->dev.parent = &dev->udev->dev;
+       rc->dev.parent = &dev->intf->dev;
        rc->driver_name = MODULE_NAME;
 
        /* all done */
index fe1ee75..7b7efbd 100644 (file)
@@ -1462,10 +1462,11 @@ static int vidioc_querycap(struct file *file, void  *priv,
        struct video_device *vdev = video_devdata(file);
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
 
        strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
        strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
-       usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+       usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info));
 
        if (vdev->vfl_type == VFL_TYPE_GRABBER)
                cap->device_caps = V4L2_CAP_READWRITE |
@@ -1716,6 +1717,7 @@ static int em28xx_v4l2_close(struct file *filp)
 {
        struct em28xx_fh *fh  = filp->private_data;
        struct em28xx    *dev = fh->dev;
+       struct usb_device *udev = interface_to_usbdev(dev->intf);
        int              errCode;
 
        em28xx_videodbg("users=%d\n", dev->users);
@@ -1744,10 +1746,10 @@ static int em28xx_v4l2_close(struct file *filp)
                 if (!dev->board.has_dvb) {
                        dev->alt = 0;
                        em28xx_videodbg("setting alternate 0\n");
-                       errCode = usb_set_interface(dev->udev, 0, 0);
+                       errCode = usb_set_interface(udev, 0, 0);
                        if (errCode < 0) {
                                em28xx_errdev("cannot change alternate number to "
-                                               "0 (error=%i)\n", errCode);
+                                       "0 (error=%i)\n", errCode);
                        }
                 }
        }
index 6f86ec0..e9d7ece 100644 (file)
@@ -612,7 +612,7 @@ struct em28xx {
        unsigned int vbi_field_count;
 
        /* usb transfer */
-       struct usb_device *udev;        /* the usb device */
+       struct usb_interface *intf;     /* the usb interface */
        u8 ifnum;                       /* number of the assigned usb interface */
        u8 analog_ep_isoc;      /* address of isoc endpoint for analog */
        u8 analog_ep_bulk;      /* address of bulk endpoint for analog */