aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/scsi/sd.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_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.c143
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);