diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/scsi/sd.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 953773c..a587252 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -52,6 +52,9 @@ #include <linux/slab.h> #include <asm/uaccess.h> #include <asm/unaligned.h> +#ifdef CONFIG_USB_HOST_NOTIFY +#include <linux/kthread.h> +#endif #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -2381,6 +2384,9 @@ static int sd_revalidate_disk(struct gendisk *disk) * react badly if we do. */ if (sdkp->media_present) { +#ifdef CONFIG_USB_HOST_NOTIFY + disk->media_present = 1; +#endif sd_read_capacity(sdkp, buffer); if (sd_try_extended_inquiry(sdp)) { @@ -2481,6 +2487,105 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) return 0; } +#ifdef CONFIG_USB_HOST_NOTIFY +static void sd_media_state_emit(struct scsi_disk *sdkp) +{ + struct gendisk *gd = sdkp->disk; + struct device *ddev = disk_to_dev(gd); + int idx = 0; + char *envp[3]; + + envp[idx++] = "DISC_MEDIA_CHANGE=1"; + envp[idx++] = NULL; + + kobject_uevent_env(&ddev->kobj, KOBJ_CHANGE, envp); +} + +static void sd_scanpartition_async(void *data, async_cookie_t cookie) +{ + struct scsi_disk *sdkp = data; + struct block_device *bdev; + struct gendisk *gd = sdkp->disk; + struct device *ddev = disk_to_dev(gd); + struct disk_part_iter piter; + struct hd_struct *part; + int err; + + /* delay uevents, until we scanned partition table */ + dev_set_uevent_suppress(ddev, 1); + + /* No minors to use for partitions */ + if (!disk_partitionable(gd)) { + sd_printk(KERN_NOTICE, sdkp, "No disc partitions\n"); + goto exit; + } + bdev = bdget_disk(gd, 0); + if (!bdev) { + sd_printk(KERN_NOTICE, sdkp, "bdget_disk, bdev is NULL\n"); + goto exit; + } + bdev->bd_invalidated = 1; + err = blkdev_get(bdev, FMODE_READ, NULL); + if (err < 0) { + sd_printk(KERN_NOTICE, sdkp, "no media, delete partition\n"); + disk_part_iter_init(&piter, gd, DISK_PITER_INCL_EMPTY); + while ((part = disk_part_iter_next(&piter))) + delete_partition(gd, part->partno); + disk_part_iter_exit(&piter); + + check_disk_size_change(gd, bdev); + bdev->bd_invalidated = 0; + goto exit; + } + blkdev_put(bdev, FMODE_READ); + +exit: + /* announce disk after possible partitions are created */ + dev_set_uevent_suppress(ddev, 0); + + /* announce disk change state */ + sd_media_state_emit(sdkp); + + /* announce possible partitions */ + disk_part_iter_init(&piter, gd, 0); + while ((part = disk_part_iter_next(&piter))) + kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); + disk_part_iter_exit(&piter); + + sdkp->async_end = 1; + wake_up_interruptible(&sdkp->delay_wait); +} + +static int sd_media_scan_thread(void *__sdkp) +{ + struct scsi_disk *sdkp = __sdkp; + int ret; + sdkp->async_end = 1; + sdkp->device->changed = 0; + while (!kthread_should_stop()) { + wait_event_interruptible_timeout(sdkp->delay_wait, + (sdkp->thread_remove && sdkp->async_end), 3*HZ); + if (sdkp->thread_remove && sdkp->async_end) + break; + ret = sd_check_events(sdkp->disk, 0); + + if (sdkp->prv_media_present + != sdkp->media_present) { + sd_printk(KERN_NOTICE, sdkp, + "sd_check_ret=%d prv_media=%d media=%d\n", + ret, sdkp->prv_media_present + , sdkp->media_present); + sdkp->disk->media_present = 0; + sdkp->async_end = 0; + async_schedule(sd_scanpartition_async, sdkp); + sdkp->prv_media_present = sdkp->media_present; + } + } + sd_printk(KERN_NOTICE, sdkp, "sd_media_scan_thread exit\n"); + complete_and_exit(&sdkp->scanning_done, 0); +} +#endif + /* * The asynchronous part of sd_probe */ @@ -2526,8 +2631,16 @@ static void sd_probe_async(void *data, async_cookie_t cookie) gd->flags |= GENHD_FL_REMOVABLE; gd->events |= DISK_EVENT_MEDIA_CHANGE; } +#ifdef CONFIG_USB_HOST_NOTIFY + if (sdp->host->by_usb) + gd->interfaces = GENHD_IF_USB; + msleep(500); +#endif add_disk(gd); +#ifdef CONFIG_USB_HOST_NOTIFY + sdkp->prv_media_present = sdkp->media_present; +#endif sd_dif_config_host(sdkp); sd_revalidate_disk(gd); @@ -2536,6 +2649,12 @@ static void sd_probe_async(void *data, async_cookie_t cookie) sdp->removable ? "removable " : ""); scsi_autopm_put_device(sdp); put_device(&sdkp->dev); +#ifdef CONFIG_USB_HOST_NOTIFY + if (sdp->host->by_usb) { + if (!IS_ERR(sdkp->th)) + wake_up_process(sdkp->th); + } +#endif } /** @@ -2627,6 +2746,20 @@ static int sd_probe(struct device *dev) get_device(dev); dev_set_drvdata(dev, sdkp); +#ifdef CONFIG_USB_HOST_NOTIFY + if (sdp->host->by_usb) { + init_waitqueue_head(&sdkp->delay_wait); + init_completion(&sdkp->scanning_done); + sdkp->thread_remove = 0; + sdkp->th = kthread_create(sd_media_scan_thread, + sdkp, "sd-media-scan"); + if (IS_ERR(sdkp->th)) { + pr_err("Unable to start the device-scanning thread\n"); + complete(&sdkp->scanning_done); + } + } +#endif + get_device(&sdkp->dev); /* prevent release before async_schedule */ async_schedule(sd_probe_async, sdkp); @@ -2662,6 +2795,16 @@ static int sd_remove(struct device *dev) sdkp = dev_get_drvdata(dev); scsi_autopm_get_device(sdkp->device); +#ifdef CONFIG_USB_HOST_NOTIFY + sdkp->disk->media_present = 0; + if (sdkp->device->host->by_usb) { + sdkp->thread_remove = 1; + wake_up_interruptible(&sdkp->delay_wait); + wait_for_completion(&sdkp->scanning_done); + sd_printk(KERN_NOTICE, sdkp, "scan thread kill success\n"); + } +#endif + async_synchronize_full(); blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn); blk_queue_unprep_rq(sdkp->device->request_queue, NULL); |