@@ -1147,6 +1148,11 @@ static void ata_dev_identify(struct ata_
return;
}
+ /* Necessary if we had an LBA48 drive in, we pulled it out, and put a
+ * non-LBA48 drive on the same ap.
+ */
+ dev->flags &= ~ATA_DFLAG_LBA48;
+
@@ -3692,6 +3698,102 @@ idle_irq:
return 0; /* irq not handled */
}
+void ata_check_kill_qc(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
+
+ if (unlikely(qc)) {
+ /* This is SO bad. But we can't just run
+ * ata_qc_complete without doing this, because
+ * ata_scsi_qc_complete wants to talk to the device,
+ * and we can't let it do that since it doesn't exist
+ * anymore.
+ */
+ ata_scsi_prepare_qc_abort(qc);
+ ata_qc_complete(qc, ATA_ERR);
+ }
+}
+
+static void ata_hotplug_task(void *_data)
+{
+ struct ata_port *ap = (struct ata_port *)_data;
+ enum hotplug_states hotplug_todo;
+
+ spin_lock(&ap->host_set->lock);
+ hotplug_todo = ap->plug; // Make sure we don't modify while reading!
+ spin_unlock(&ap->host_set->lock);
+ if (hotplug_todo == ATA_HOTPLUG_PLUG) {
+ DPRINTK("Got a plug request on port %d\n", ap->id);
+
+ /* This might be necessary if we unplug and plug in a drive
+ * within ATA_TMOUT_HOTSWAP / HZ seconds... due to the debounce
+ * timer, one event is generated. Since the last event was a
+ * plug, the unplug routine never gets called, so we need to
+ * clean up the mess first. If there was never a drive here in
+ * the first place, this will just do nothing. Otherwise, it
+ * basically prevents 'plug' from being called twice with no
+ * unplug in between.
+ */
+ ata_scsi_handle_unplug(ap);
+
+ // The following flag is necessary on some Seagate drives.
+ ap->flags |= ATA_FLAG_SATA_RESET;
+ ap->udma_mask = ap->orig_udma_mask;
+ if (!ata_bus_probe(ap)) //Drive found! Tell the SCSI layer!
+ ata_scsi_handle_plug(ap);
+ } else if (hotplug_todo == ATA_HOTPLUG_UNPLUG) {
+ DPRINTK("Got an unplug request on port %d\n", ap->id);
+
+ ata_scsi_handle_unplug(ap);
+ } else /* FIXME: Some kind of error condition? */
+ BUG();
+}
+
+static void ata_hotplug_timer_func(unsigned long _data)
+{
+ struct ata_port *ap = (struct ata_port *)_data;
+
+ queue_work(ata_hotplug_wq, &ap->hotplug_task);
+}
+void ata_hotplug_plug(struct ata_port *ap)
+{
+ del_timer(&ap->hotplug_timer);
+
+ if (ap->ops->hotplug_plug_janitor)
+ ap->ops->hotplug_plug_janitor(ap);
+
+ /* This line should be protected by a host_set->lock. If we follow the
+ * call chain up from this, it should be called from ata_hotplug_unplug
+ * or ata_hotplug_plug, which should be called from an interrupt
+ * handler. Make sure the call to either of those functions is
+ * protected.
+ */
+ ap->plug = ATA_HOTPLUG_PLUG;
+
+ ap->hotplug_timer.expires = jiffies + ATA_TMOUT_HOTSWAP;
+ add_timer(&ap->hotplug_timer);
+}
+
+void ata_hotplug_unplug(struct ata_port *ap)
+{
+ ata_port_disable(ap); //Gone gone gone!
+
+ del_timer(&ap->hotplug_timer);
+
+ if (ap->ops->hotplug_unplug_janitor)
+ ap->ops->hotplug_unplug_janitor(ap);
+
+ /* See comment near similar line in ata_hotplug_plug function.
+ */
+ ap->plug = ATA_HOTPLUG_UNPLUG;
+
+ ap->hotplug_timer.expires = jiffies + ATA_TMOUT_HOTSWAP;
+ add_timer(&ap->hotplug_timer);
+}
+
/**
* ata_interrupt - Default ATA host interrupt handler
* @irq: irq line (unused)
@@ -3925,7 +4027,12 @@ static void ata_host_init(struct ata_por
ap->cbl = ATA_CBL_NONE;
ap->active_tag = ATA_TAG_POISON;
ap->last_ctl = 0xFF;
+ ap->orig_udma_mask = ent->udma_mask;
+ ap->hotplug_timer.function = ata_hotplug_timer_func;
+ ap->hotplug_timer.data = (unsigned int)ap;
+ init_timer(&ap->hotplug_timer);
+ INIT_WORK(&ap->hotplug_task, ata_hotplug_task, ap);
INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
INIT_WORK(&ap->pio_task, ata_pio_task, ap);
@@ -4541,6 +4648,11 @@ static int __init ata_init(void)
ata_wq = create_workqueue("ata");
if (!ata_wq)
return -ENOMEM;
+ ata_hotplug_wq = create_workqueue("ata_hotplug");
+ if (!ata_hotplug_wq) {
+ destroy_workqueue(ata_wq);
+ return -ENOMEM;
+ }
printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0;
@@ -4549,6 +4661,7 @@ static int __init ata_init(void)
static void __exit ata_exit(void)
{
destroy_workqueue(ata_wq);
+ destroy_workqueue(ata_hotplug_wq);
}
module_init(ata_init);
@@ -4604,6 +4717,8 @@ EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_id_string);
EXPORT_SYMBOL_GPL(ata_dev_config);
EXPORT_SYMBOL_GPL(ata_scsi_simulate);
+EXPORT_SYMBOL_GPL(ata_hotplug_plug);
+EXPORT_SYMBOL_GPL(ata_hotplug_unplug);
#ifdef CONFIG_PCI
EXPORT_SYMBOL_GPL(pci_test_config_bits);
diff -rpuN linux-2.6.14-rc1/drivers/scsi/libata.h linux-2.6.14-rc1-new/drivers/scsi/libata.h
--- linux-2.6.14-rc1/drivers/scsi/libata.h 2005-09-12 23:12:09.000000000 -0400
+++ linux-2.6.14-rc1-new/drivers/scsi/libata.h 2005-09-14 19:57:53.000000000 -0400
@@ -44,6 +44,7 @@ extern struct ata_queued_cmd *ata_qc_new
extern void ata_qc_free(struct ata_queued_cmd *qc);
extern int ata_qc_issue(struct ata_queued_cmd *qc);
extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
+extern void ata_check_kill_qc(struct ata_port *ap);
extern void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep);
extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf);
@@ -79,6 +80,9 @@ extern void ata_scsi_badcmd(struct scsi_
extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
unsigned int (*actor) (struct ata_scsi_args *args,
u8 *rbuf, unsigned int buflen));
+extern void ata_scsi_prepare_qc_abort(struct ata_queued_cmd *qc);
+extern void ata_scsi_handle_plug(struct ata_port *ap);
+extern void ata_scsi_handle_unplug(struct ata_port *ap);
static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
diff -rpuN linux-2.6.14-rc1/drivers/scsi/libata-scsi.c linux-2.6.14-rc1-new/drivers/scsi/libata-scsi.c
--- linux-2.6.14-rc1/drivers/scsi/libata-scsi.c 2005-09-12 23:12:09.000000000 -0400
+++ linux-2.6.14-rc1-new/drivers/scsi/libata-scsi.c 2005-09-14 19:57:53.000000000 -0400
@@ -716,6 +716,53 @@ static int ata_scsi_qc_complete(struct a
return 0;
}
+static int ata_scsi_qc_abort(struct ata_queued_cmd *qc, u8 drv_stat)
+{
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ cmd->result = SAM_STAT_TASK_ABORTED; //FIXME: Is this what we want?
+ qc->scsidone(cmd);
+
+ return 0;
+}
+
+void ata_scsi_prepare_qc_abort(struct ata_queued_cmd *qc)
+{
+ /* For some reason or another, we can't allow a normal scsi_qc_complete.
+ * Note that at this point, we must be SURE the command is going to time
+ * out, or else we might be changing this as it's being called. Never a
+ * good thing.
+ */
+ if (qc->complete_fn == ata_scsi_qc_complete);
+ qc->complete_fn = ata_scsi_qc_abort;
+}
+
+void ata_scsi_handle_plug(struct ata_port *ap)
+{
+ //Currently SATA only
+ scsi_add_device(ap->host, 0, 0, 0);
+}
+
+void ata_scsi_handle_unplug(struct ata_port *ap)
+{
+ //SATA only, no PATA