aboutsummaryrefslogtreecommitdiff
path: root/drivers/ide/ide-floppy.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ide/ide-floppy.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/ide/ide-floppy.c')
-rw-r--r--drivers/ide/ide-floppy.c2211
1 files changed, 2211 insertions, 0 deletions
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
new file mode 100644
index 00000000000..36c0b74a4e4
--- /dev/null
+++ b/drivers/ide/ide-floppy.c
@@ -0,0 +1,2211 @@
+/*
+ * linux/drivers/ide/ide-floppy.c Version 0.99 Feb 24 2002
+ *
+ * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
+ * Copyright (C) 2000 - 2002 Paul Bristow <paul@paulbristow.net>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * This driver supports the following IDE floppy drives:
+ *
+ * LS-120/240 SuperDisk
+ * Iomega Zip 100/250
+ * Iomega PC Card Clik!/PocketZip
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1 Oct 17 96 Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2 Oct 31 96 Minor changes.
+ * Ver 0.3 Dec 2 96 Fixed error recovery bug.
+ * Ver 0.4 Jan 26 97 Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5 Feb 21 97 Add partitions support.
+ * Use the minimum of the LBA and CHS capacities.
+ * Avoid hwgroup->rq == NULL on the last irq.
+ * Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.8 Dec 7 97 Increase irq timeout from 10 to 50 seconds.
+ * Add media write-protect detection.
+ * Issue START command only if TEST UNIT READY fails.
+ * Add work-around for IOMEGA ZIP revision 21.D.
+ * Remove idefloppy_get_capabilities().
+ * Ver 0.9 Jul 4 99 Fix a bug which might have caused the number of
+ * bytes requested on each interrupt to be zero.
+ * Thanks to <shanos@es.co.nz> for pointing this out.
+ * Ver 0.9.sv Jan 6 01 Sam Varshavchik <mrsam@courier-mta.com>
+ * Implement low level formatting. Reimplemented
+ * IDEFLOPPY_CAPABILITIES_PAGE, since we need the srfp
+ * bit. My LS-120 drive barfs on
+ * IDEFLOPPY_CAPABILITIES_PAGE, but maybe it's just me.
+ * Compromise by not reporting a failure to get this
+ * mode page. Implemented four IOCTLs in order to
+ * implement formatting. IOCTls begin with 0x4600,
+ * 0x46 is 'F' as in Format.
+ * Jan 9 01 Userland option to select format verify.
+ * Added PC_SUPPRESS_ERROR flag - some idefloppy drives
+ * do not implement IDEFLOPPY_CAPABILITIES_PAGE, and
+ * return a sense error. Suppress error reporting in
+ * this particular case in order to avoid spurious
+ * errors in syslog. The culprit is
+ * idefloppy_get_capability_page(), so move it to
+ * idefloppy_begin_format() so that it's not used
+ * unless absolutely necessary.
+ * If drive does not support format progress indication
+ * monitor the dsc bit in the status register.
+ * Also, O_NDELAY on open will allow the device to be
+ * opened without a disk available. This can be used to
+ * open an unformatted disk, or get the device capacity.
+ * Ver 0.91 Dec 11 99 Added IOMEGA Clik! drive support by
+ * <paul@paulbristow.net>
+ * Ver 0.92 Oct 22 00 Paul Bristow became official maintainer for this
+ * driver. Included Powerbook internal zip kludge.
+ * Ver 0.93 Oct 24 00 Fixed bugs for Clik! drive
+ * no disk on insert and disk change now works
+ * Ver 0.94 Oct 27 00 Tidied up to remove strstr(Clik) everywhere
+ * Ver 0.95 Nov 7 00 Brought across to kernel 2.4
+ * Ver 0.96 Jan 7 01 Actually in line with release version of 2.4.0
+ * including set_bit patch from Rusty Russell
+ * Ver 0.97 Jul 22 01 Merge 0.91-0.96 onto 0.9.sv for ac series
+ * Ver 0.97.sv Aug 3 01 Backported from 2.4.7-ac3
+ * Ver 0.98 Oct 26 01 Split idefloppy_transfer_pc into two pieces to
+ * fix a lost interrupt problem. It appears the busy
+ * bit was being deasserted by my IOMEGA ATAPI ZIP 100
+ * drive before the drive was actually ready.
+ * Ver 0.98a Oct 29 01 Expose delay value so we can play.
+ * Ver 0.99 Feb 24 02 Remove duplicate code, modify clik! detection code
+ * to support new PocketZip drives
+ */
+
+#define IDEFLOPPY_VERSION "0.99.newide"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/cdrom.h>
+#include <linux/ide.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+
+/*
+ * The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG 0
+#define IDEFLOPPY_DEBUG_INFO 0
+#define IDEFLOPPY_DEBUG_BUGS 1
+
+/* #define IDEFLOPPY_DEBUG(fmt, args...) printk(KERN_INFO fmt, ## args) */
+#define IDEFLOPPY_DEBUG( fmt, args... )
+
+#if IDEFLOPPY_DEBUG_LOG
+#define debug_log printk
+#else
+#define debug_log(fmt, args... ) do {} while(0)
+#endif
+
+
+/*
+ * Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD)
+
+/*
+ * After each failed packet command we issue a request sense command
+ * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES 3
+
+/*
+ * With each packet command, we allocate a buffer of
+ * IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE 256
+
+/*
+ * In various places in the driver, we need to allocate storage
+ * for packet commands and requests, which will remain valid while
+ * we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ * Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+ u8 c[12]; /* Actual packet bytes */
+ int retries; /* On each retry, we increment retries */
+ int error; /* Error code */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ int b_count; /* Missing/Available data on the current buffer */
+ struct request *rq; /* The corresponding request */
+ u8 *buffer; /* Data buffer */
+ u8 *current_position; /* Pointer into the above buffer */
+ void (*callback) (ide_drive_t *); /* Called when this packet command is completed */
+ u8 pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */
+ unsigned long flags; /* Status/Action bit flags: long for set_bit */
+} idefloppy_pc_t;
+
+/*
+ * Packet command flag bits.
+ */
+#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */
+#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */
+#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */
+#define PC_WRITING 5 /* Data direction */
+
+#define PC_SUPPRESS_ERROR 6 /* Suppress error reporting */
+
+/*
+ * Removable Block Access Capabilities Page
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x1b */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* Should be 0 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1; /* Should be 0 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned page_code :6; /* Page code - Should be 0x1b */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 page_length; /* Page Length - Should be 0xa */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved2 :6;
+ unsigned srfp :1; /* Supports reporting progress of format */
+ unsigned sflp :1; /* System floppy type device */
+ unsigned tlun :3; /* Total logical units supported by the device */
+ unsigned reserved3 :3;
+ unsigned sml :1; /* Single / Multiple lun supported */
+ unsigned ncd :1; /* Non cd optical device */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned sflp :1; /* System floppy type device */
+ unsigned srfp :1; /* Supports reporting progress of format */
+ unsigned reserved2 :6;
+ unsigned ncd :1; /* Non cd optical device */
+ unsigned sml :1; /* Single / Multiple lun supported */
+ unsigned reserved3 :3;
+ unsigned tlun :3; /* Total logical units supported by the device */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ * Flexible disk page.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned page_code :6; /* Page code - Should be 0x5 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* The device is capable of saving the page */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned ps :1; /* The device is capable of saving the page */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned page_code :6; /* Page code - Should be 0x5 */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 page_length; /* Page Length - Should be 0x1e */
+ u16 transfer_rate; /* In kilobits per second */
+ u8 heads, sectors; /* Number of heads, Number of sectors per track */
+ u16 sector_size; /* Byes per sector */
+ u16 cyls; /* Number of cylinders */
+ u8 reserved10[10];
+ u8 motor_delay; /* Motor off delay */
+ u8 reserved21[7];
+ u16 rpm; /* Rotations per minute */
+ u8 reserved30[2];
+} idefloppy_flexible_disk_page_t;
+
+/*
+ * Format capacity
+ */
+typedef struct {
+ u8 reserved[3];
+ u8 length; /* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+ u32 blocks; /* Number of blocks */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned dc :2; /* Descriptor Code */
+ unsigned reserved :6;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved :6;
+ unsigned dc :2; /* Descriptor Code */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 length_msb; /* Block Length (MSB)*/
+ u16 length; /* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID 0x00
+#define CAPACITY_UNFORMATTED 0x01
+#define CAPACITY_CURRENT 0x02
+#define CAPACITY_NO_CARTRIDGE 0x03
+
+/*
+ * Most of our global data which we need to save even as we leave the
+ * driver due to an interrupt or a timer event is stored in a variable
+ * of type idefloppy_floppy_t, defined below.
+ */
+typedef struct ide_floppy_obj {
+ ide_drive_t *drive;
+ ide_driver_t *driver;
+ struct gendisk *disk;
+ struct kref kref;
+
+ /* Current packet command */
+ idefloppy_pc_t *pc;
+ /* Last failed packet command */
+ idefloppy_pc_t *failed_pc;
+ /* Packet command stack */
+ idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];
+ /* Next free packet command storage space */
+ int pc_stack_index;
+ struct request rq_stack[IDEFLOPPY_PC_STACK];
+ /* We implement a circular array */
+ int rq_stack_index;
+
+ /*
+ * Last error information
+ */
+ u8 sense_key, asc, ascq;
+ /* delay this long before sending packet command */
+ u8 ticks;
+ int progress_indication;
+
+ /*
+ * Device information
+ */
+ /* Current format */
+ int blocks, block_size, bs_factor;
+ /* Last format capacity */
+ idefloppy_capacity_descriptor_t capacity;
+ /* Copy of the flexible disk page */
+ idefloppy_flexible_disk_page_t flexible_disk_page;
+ /* Write protect */
+ int wp;
+ /* Supports format progress report */
+ int srfp;
+ /* Status/Action flags */
+ unsigned long flags;
+} idefloppy_floppy_t;
+
+#define IDEFLOPPY_TICKS_DELAY 3 /* default delay for ZIP 100 */
+
+/*
+ * Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */
+#define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */
+#define IDEFLOPPY_FORMAT_IN_PROGRESS 3 /* Format in progress */
+#define IDEFLOPPY_CLIK_DRIVE 4 /* Avoid commands not supported in Clik drive */
+#define IDEFLOPPY_ZIP_DRIVE 5 /* Requires BH algorithm for packets */
+
+/*
+ * ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD 0x04
+#define IDEFLOPPY_INQUIRY_CMD 0x12
+#define IDEFLOPPY_MODE_SELECT_CMD 0x55
+#define IDEFLOPPY_MODE_SENSE_CMD 0x5a
+#define IDEFLOPPY_READ10_CMD 0x28
+#define IDEFLOPPY_READ12_CMD 0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD 0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD 0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD 0x1e
+#define IDEFLOPPY_SEEK_CMD 0x2b
+#define IDEFLOPPY_START_STOP_CMD 0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD 0x00
+#define IDEFLOPPY_VERIFY_CMD 0x2f
+#define IDEFLOPPY_WRITE10_CMD 0x2a
+#define IDEFLOPPY_WRITE12_CMD 0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD 0x2e
+
+/*
+ * Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT 0x00
+#define MODE_SENSE_CHANGEABLE 0x01
+#define MODE_SENSE_DEFAULT 0x02
+#define MODE_SENSE_SAVED 0x03
+
+/*
+ * IOCTLs used in low-level formatting.
+ */
+
+#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600
+#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601
+#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602
+#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603
+
+#if 0
+/*
+ * Special requests for our block device strategy routine.
+ */
+#define IDEFLOPPY_FIRST_RQ 90
+
+/*
+ * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDEFLOPPY_PC_RQ 90
+
+#define IDEFLOPPY_LAST_RQ 90
+
+/*
+ * A macro which can be used to check if a given request command
+ * originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+#endif
+
+/*
+ * Error codes which are returned in rq->errors to the higher part
+ * of the driver.
+ */
+#define IDEFLOPPY_ERROR_GENERAL 101
+
+/*
+ * The following is used to format the general configuration word of
+ * the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned packet_size :2; /* Packet Size */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned removable :1; /* Removable media */
+ unsigned device_type :5; /* Device type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned protocol :2; /* Protocol type */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned protocol :2; /* Protocol type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned device_type :5; /* Device type */
+ unsigned removable :1; /* Removable media */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned packet_size :2; /* Packet Size */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+};
+
+/*
+ * INQUIRY packet command - Data Format
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned response_format :4; /* Response Data Format */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 additional_length; /* Additional Length (total_length-4) */
+ u8 rsv5, rsv6, rsv7; /* Reserved */
+ u8 vendor_id[8]; /* Vendor Identification */
+ u8 product_id[16]; /* Product Identification */
+ u8 revision_level[4]; /* Revision Level */
+ u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned error_code :7; /* Current error (0x70) */
+ unsigned valid :1; /* The information field conforms to SFF-8070i */
+ u8 reserved1 :8; /* Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned reserved2_67 :2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned valid :1; /* The information field conforms to SFF-8070i */
+ unsigned error_code :7; /* Current error (0x70) */
+ u8 reserved1 :8; /* Reserved */
+ unsigned reserved2_67 :2;
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned sense_key :4; /* Sense Key */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u32 information __attribute__ ((packed));
+ u8 asl; /* Additional sense length (n-7) */
+ u32 command_specific; /* Additional command specific information */
+ u8 asc; /* Additional Sense Code */
+ u8 ascq; /* Additional Sense Code Qualifier */
+ u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ u8 sksv[3];
+ u8 pad[2]; /* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ * Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ u16 mode_data_length; /* Length of the following data transfer */
+ u8 medium_type; /* Medium Type */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned reserved3 :7;
+ unsigned wp :1; /* Write protect */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned wp :1; /* Write protect */
+ unsigned reserved3 :7;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+static DECLARE_MUTEX(idefloppy_ref_sem);
+
+#define to_ide_floppy(obj) container_of(obj, struct ide_floppy_obj, kref)
+
+#define ide_floppy_g(disk) \
+ container_of((disk)->private_data, struct ide_floppy_obj, driver)
+
+static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk)
+{
+ struct ide_floppy_obj *floppy = NULL;
+
+ down(&idefloppy_ref_sem);
+ floppy = ide_floppy_g(disk);
+ if (floppy)
+ kref_get(&floppy->kref);
+ up(&idefloppy_ref_sem);
+ return floppy;
+}
+
+static void ide_floppy_release(struct kref *);
+
+static void ide_floppy_put(struct ide_floppy_obj *floppy)
+{
+ down(&idefloppy_ref_sem);
+ kref_put(&floppy->kref, ide_floppy_release);
+ up(&idefloppy_ref_sem);
+}
+
+/*
+ * Too bad. The drive wants to send us data which we are not ready to accept.
+ * Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ (void) HWIF(drive)->INB(IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ HWIF(drive)->OUTB(0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+
+/*
+ * idefloppy_do_end_request is used to finish servicing a request.
+ *
+ * For read/write requests, we will call ide_end_request to pass to the
+ * next buffer.
+ */
+static int idefloppy_do_end_request(ide_drive_t *drive, int uptodate, int nsecs)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+ int error;
+
+ debug_log(KERN_INFO "Reached idefloppy_end_request\n");
+
+ switch (uptodate) {
+ case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+ case 1: error = 0; break;
+ default: error = uptodate;
+ }
+ if (error)
+ floppy->failed_pc = NULL;
+ /* Why does this happen? */
+ if (!rq)
+ return 0;
+ if (!(rq->flags & REQ_SPECIAL)) { //if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+ /* our real local end request function */
+ ide_end_request(drive, uptodate, nsecs);
+ return 0;
+ }
+ rq->errors = error;
+ /* fixme: need to move this local also */
+ ide_end_drive_cmd(drive, 0, 0);
+ return 0;
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct bio_vec *bvec;
+ struct bio *bio;
+ unsigned long flags;
+ char *data;
+ int count, i, done = 0;
+
+ rq_for_each_bio(bio, rq) {
+ bio_for_each_segment(bvec, bio, i) {
+ if (!bcount)
+ break;
+
+ count = min(bvec->bv_len, bcount);
+
+ data = bvec_kmap_irq(bvec, &flags);
+ drive->hwif->atapi_input_bytes(drive, data, count);
+ bvec_kunmap_irq(data, &flags);
+
+ bcount -= count;
+ pc->b_count += count;
+ done += count;
+ }
+ }
+
+ idefloppy_do_end_request(drive, 1, done >> 9);
+
+ if (bcount) {
+ printk(KERN_ERR "%s: leftover data in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_discard_data(drive, bcount);
+ }
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct bio *bio;
+ struct bio_vec *bvec;
+ unsigned long flags;
+ int count, i, done = 0;
+ char *data;
+
+ rq_for_each_bio(bio, rq) {
+ bio_for_each_segment(bvec, bio, i) {
+ if (!bcount)
+ break;
+
+ count = min(bvec->bv_len, bcount);
+
+ data = bvec_kmap_irq(bvec, &flags);
+ drive->hwif->atapi_output_bytes(drive, data, count);
+ bvec_kunmap_irq(data, &flags);
+
+ bcount -= count;
+ pc->b_count += count;
+ done += count;
+ }
+ }
+
+ idefloppy_do_end_request(drive, 1, done >> 9);
+
+ if (bcount) {
+ printk(KERN_ERR "%s: leftover data in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_write_zeros(drive, bcount);
+ }
+}
+
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+ struct request *rq = pc->rq;
+ struct bio *bio = rq->bio;
+
+ while ((bio = rq->bio) != NULL)
+ idefloppy_do_end_request(drive, 1, 0);
+}
+
+/*
+ * idefloppy_queue_pc_head generates a new packet command request in front
+ * of the request queue, before the current request, so that it will be
+ * processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+ struct ide_floppy_obj *floppy = drive->driver_data;
+
+ ide_init_drive_cmd(rq);
+ rq->buffer = (char *) pc;
+ rq->flags = REQ_SPECIAL; //rq->cmd = IDEFLOPPY_PC_RQ;
+ rq->rq_disk = floppy->disk;
+ (void) ide_do_drive_cmd(drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->pc_stack_index == IDEFLOPPY_PC_STACK)
+ floppy->pc_stack_index=0;
+ return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->rq_stack_index == IDEFLOPPY_PC_STACK)
+ floppy->rq_stack_index = 0;
+ return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ * idefloppy_analyze_error is called on each failed packet command retry
+ * to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ floppy->sense_key = result->sense_key;
+ floppy->asc = result->asc;
+ floppy->ascq = result->ascq;
+ floppy->progress_indication = result->sksv[0] & 0x80 ?
+ (u16)get_unaligned((u16 *)(result->sksv+1)):0x10000;
+ if (floppy->failed_pc)
+ debug_log(KERN_INFO "ide-floppy: pc = %x, sense key = %x, "
+ "asc = %x, ascq = %x\n", floppy->failed_pc->c[0],
+ result->sense_key, result->asc, result->ascq);
+ else
+ debug_log(KERN_INFO "ide-floppy: sense key = %x, asc = %x, "
+ "ascq = %x\n", result->sense_key,
+ result->asc, result->ascq);
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ debug_log(KERN_INFO "ide-floppy: Reached %s\n", __FUNCTION__);
+
+ if (!floppy->pc->error) {
+ idefloppy_analyze_error(drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+ idefloppy_do_end_request(drive, 1, 0);
+ } else {
+ printk(KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+ idefloppy_do_end_request(drive, 0, 0);
+ }
+}
+
+/*
+ * General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ debug_log(KERN_INFO "ide-floppy: Reached %s\n", __FUNCTION__);
+
+ idefloppy_do_end_request(drive, floppy->pc->error ? 0 : 1, 0);
+}
+
+/*
+ * idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+ memset(pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->request_transfer = 0;
+ pc->buffer = pc->pc_buffer;
+ pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+ pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc(pc);
+ pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+ pc->c[4] = 255;
+ pc->request_transfer = 18;
+ pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ * idefloppy_retry_pc is called when an error was detected during the
+ * last packet command. We queue a request sense packet command in
+ * the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+ idefloppy_pc_t *pc;
+ struct request *rq;
+ atapi_error_t error;
+
+ error.all = HWIF(drive)->INB(IDE_ERROR_REG);
+ pc = idefloppy_next_pc_storage(drive);
+ rq = idefloppy_next_rq_storage(drive);
+ idefloppy_create_request_sense_cmd(pc);
+ idefloppy_queue_pc_head(drive, pc, rq);
+}
+
+/*
+ * idefloppy_pc_intr is the usual interrupt handler which will be called
+ * during a packet command.
+ */
+static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ atapi_status_t status;
+ atapi_bcount_t bcount;
+ atapi_ireason_t ireason;
+ idefloppy_pc_t *pc = floppy->pc;
+ struct request *rq = pc->rq;
+ unsigned int temp;
+
+ debug_log(KERN_INFO "ide-floppy: Reached %s interrupt handler\n",
+ __FUNCTION__);
+
+ if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) {
+ if (HWIF(drive)->ide_dma_end(drive)) {
+ set_bit(PC_DMA_ERROR, &pc->flags);
+ } else {
+ pc->actually_transferred = pc->request_transfer;
+ idefloppy_update_buffers(drive, pc);
+ }
+ debug_log(KERN_INFO "ide-floppy: DMA finished\n");
+ }
+
+ /* Clear the interrupt */
+ status.all = HWIF(drive)->INB(IDE_STATUS_REG);
+
+ if (!status.b.drq) { /* No more interrupts */
+ debug_log(KERN_INFO "Packet command completed, %d bytes "
+ "transferred\n", pc->actually_transferred);
+ clear_bit(PC_DMA_IN_PROGRESS, &pc->flags);
+
+ local_irq_enable();
+
+ if (status.b.check || test_bit(PC_DMA_ERROR, &pc->flags)) {
+ /* Error detected */
+ debug_log(KERN_INFO "ide-floppy: %s: I/O error\n",
+ drive->name);
+ rq->errors++;
+ if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+ printk(KERN_ERR "ide-floppy: I/O error in "
+ "request sense command\n");
+ return ide_do_reset(drive);
+ }
+ /* Retry operation */
+ idefloppy_retry_pc(drive);
+ /* queued, but not started */
+ return ide_stopped;
+ }
+ pc->error = 0;
+ if (floppy->failed_pc == pc)
+ floppy->failed_pc = NULL;
+ /* Command finished - Call the callback function */
+ pc->callback(drive);
+ return ide_stopped;
+ }
+
+ if (test_and_clear_bit(PC_DMA_IN_PROGRESS, &pc->flags)) {
+ printk(KERN_ERR "ide-floppy: The floppy wants to issue "
+ "more interrupts in DMA mode\n");
+ (void)__ide_dma_off(drive);
+ return ide_do_reset(drive);
+ }
+
+ /* Get the number of bytes to transfer */
+ bcount.b.high = HWIF(drive)->INB(IDE_BCOUNTH_REG);
+ bcount.b.low = HWIF(drive)->INB(IDE_BCOUNTL_REG);
+ /* on this interrupt */
+ ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk(KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+ return ide_do_reset(drive);
+ }
+ if (ireason.b.io == test_bit(PC_WRITING, &pc->flags)) {
+ /* Hopefully, we will never get here */
+ printk(KERN_ERR "ide-floppy: We wanted to %s, ",
+ ireason.b.io ? "Write":"Read");
+ printk(KERN_ERR "but the floppy wants us to %s !\n",
+ ireason.b.io ? "Read":"Write");
+ return ide_do_reset(drive);
+ }
+ if (!test_bit(PC_WRITING, &pc->flags)) {
+ /* Reading - Check that we have enough space */
+ temp = pc->actually_transferred + bcount.all;
+ if (temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk(KERN_ERR "ide-floppy: The floppy wants "
+ "to send us more data than expected "
+ "- discarding data\n");
+ idefloppy_discard_data(drive,bcount.all);
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ ide_set_handler(drive,
+ &idefloppy_pc_intr,
+ IDEFLOPPY_WAIT_CMD,
+ NULL);
+ return ide_started;
+ }
+ debug_log(KERN_NOTICE "ide-floppy: The floppy wants to "
+ "send us more data than expected - "
+ "allowing transfer\n");
+ }
+ }
+ if (test_bit(PC_WRITING, &pc->flags)) {
+ if (pc->buffer != NULL)
+ /* Write the current buffer */
+ HWIF(drive)->atapi_output_bytes(drive,
+ pc->current_position,
+ bcount.all);
+ else
+ idefloppy_output_buffers(drive, pc, bcount.all);
+ } else {
+ if (pc->buffer != NULL)
+ /* Read the current buffer */
+ HWIF(drive)->atapi_input_bytes(drive,
+ pc->current_position,
+ bcount.all);
+ else
+ idefloppy_input_buffers(drive, pc, bcount.all);
+ }
+ /* Update the current position */
+ pc->actually_transferred += bcount.all;
+ pc->current_position += bcount.all;
+
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* And set the interrupt handler again */
+ return ide_started;
+}
+
+/*
+ * This is the original routine that did the packet transfer.
+ * It fails at high speeds on the Iomega ZIP drive, so there's a slower version
+ * for that drive below. The algorithm is chosen based on drive type
+ */
+static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive)
+{
+ ide_startstop_t startstop;
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ atapi_ireason_t ireason;
+
+ if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
+ printk(KERN_ERR "ide-floppy: Strange, packet command "
+ "initiated yet DRQ isn't asserted\n");
+ return startstop;
+ }
+ ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while "
+ "issuing a packet command\n");
+ return ide_do_reset(drive);
+ }
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ /* Set the interrupt routine */
+ ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);
+ /* Send the actual packet */
+ HWIF(drive)->atapi_output_bytes(drive, floppy->pc->c, 12);
+ return ide_started;
+}
+
+
+/*
+ * What we have here is a classic case of a top half / bottom half
+ * interrupt service routine. In interrupt mode, the device sends
+ * an interrupt to signal it's ready to receive a packet. However,
+ * we need to delay about 2-3 ticks before issuing the packet or we
+ * gets in trouble.
+ *
+ * So, follow carefully. transfer_pc1 is called as an interrupt (or
+ * directly). In either case, when the device says it's ready for a
+ * packet, we schedule the packet transfer to occur about 2-3 ticks
+ * later in transfer_pc2.
+ */
+static int idefloppy_transfer_pc2 (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ /* Send the actual packet */
+ HWIF(drive)->atapi_output_bytes(drive, floppy->pc->c, 12);
+ /* Timeout for the packet command */
+ return IDEFLOPPY_WAIT_CMD;
+}
+
+static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ ide_startstop_t startstop;
+ atapi_ireason_t ireason;
+
+ if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
+ printk(KERN_ERR "ide-floppy: Strange, packet command "
+ "initiated yet DRQ isn't asserted\n");
+ return startstop;
+ }
+ ireason.all = HWIF(drive)->INB(IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) "
+ "while issuing a packet command\n");
+ return ide_do_reset(drive);
+ }
+ /*
+ * The following delay solves a problem with ATAPI Zip 100 drives
+ * where the Busy flag was apparently being deasserted before the
+ * unit was ready to receive data. This was happening on a
+ * 1200 MHz Athlon system. 10/26/01 25msec is too short,
+ * 40 and 50msec work well. idefloppy_pc_intr will not be actually
+ * used until after the packet is moved in about 50 msec.
+ */
+ if (HWGROUP(drive)->handler != NULL)
+ BUG();
+ ide_set_handler(drive,
+ &idefloppy_pc_intr, /* service routine for packet command */
+ floppy->ticks, /* wait this long before "failing" */
+ &idefloppy_transfer_pc2); /* fail == transfer_pc2 */
+ return ide_started;
+}
+
+/**
+ * idefloppy_should_report_error()
+ *
+ * Supresses error messages resulting from Medium not present
+ */
+static inline int idefloppy_should_report_error(idefloppy_floppy_t *floppy)
+{
+ if (floppy->sense_key == 0x02 &&
+ floppy->asc == 0x3a &&
+ floppy->ascq == 0x00)
+ return 0;
+ return 1;
+}
+
+/*
+ * Issue a packet command
+ */
+static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)