LS120 enhancements

Paul Flinders (paul@dawa.demon.co.uk)
30 Dec 1998 13:14:24 +0000


--Multipart_Wed_Dec_30_13:14:24_1998-1
Content-Type: text/plain; charset=US-ASCII

Here are some patches to the ide-floppy driver to support formatting
of standard floppies and a (very slightly) modified copy of fdformat.

The patch also includes a small fix from Gadi to avoid an oops in
idefloppy_end_request when using DMA.

Some points
- The format unit ioctl isn't much use at the moment as it locks
my machine (presumably) because no ide commands can be issued
until it is complete - I couldn't get the status reporting during
format to work. Format track also suffers from this but isn't
_quite_ as antisocial.

- (For my LS120 at least) the drives don't appear to want to do
a low level format of an LS120 - they just ignore the command
(LS120 disks are pre-formatted anyway)

- No alternative format densities are supported. My drive reported
a couple of alternatives but they were all lower than 1.44M so
there didn't seem to be much point.

--Multipart_Wed_Dec_30_13:14:24_1998-1
Content-Type: application/octet-stream; type=patch
Content-Disposition: attachment; filename="ide-floppy.patch"
Content-Transfer-Encoding: 8bit

--- linux-2.1.132-ac2/include/linux/fd.h.orig Wed Jun 24 22:30:11 1998
+++ linux-2.1.132-ac2/include/linux/fd.h Tue Dec 29 14:21:15 1998
@@ -71,6 +71,8 @@
/* format the specified track */
#define FDFMTEND _IO(2,0x49)
/* end formatting a disk */
+#define FDFMTUNIT _IO(2,0x4a)
+/* Format whole disk (ide-floppy) */


/*
--- linux-2.1.132-ac2/drivers/block/ide-floppy.c.orig Mon Dec 28 17:17:49 1998
+++ linux-2.1.132-ac2/drivers/block/ide-floppy.c Wed Dec 30 12:32:40 1998
@@ -26,9 +26,17 @@
* 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 Dec 30 98 Avoid rq == NULL in idefloppy_end_request when
+ * DMA enabled
+ * Allow read of multicount for hdparm
+ * Copy current geometry to id struct for hdparm
+ * Add FDGETPRM ioctl to get geometry in a manner
+ * compatible with "normal floppies"
+ * Add FDFMT{BEG,TRK,END} ioctls
+ * Allow timeout to be specified per command
*/

-#define IDEFLOPPY_VERSION "0.8"
+#define IDEFLOPPY_VERSION "0.9"

#include <linux/config.h>
#include <linux/module.h>
@@ -44,6 +52,7 @@
#include <linux/genhd.h>
#include <linux/malloc.h>
#include <linux/cdrom.h>
+#include <linux/fd.h>

#include <asm/byteorder.h>
#include <asm/irq.h>
@@ -67,7 +76,8 @@
/*
* Some drives require a longer irq timeout.
*/
-#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD)
+#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD) /* 50 secs */
+#define IDEFLOPPY_FORMAT_WAIT (10 * WAIT_CMD) /* 100 secs */

/*
* After each failed packet command we issue a request sense command
@@ -92,7 +102,9 @@
* Our view of a packet command.
*/
typedef struct idefloppy_packet_command_s {
- u8 c[12]; /* Actual packet bytes */
+ u8 c[12]; /* Actual packet bytes */
+ u8 p[12]; /* parameter block */
+ int pl; /* parameter block len */
int retries; /* On each retry, we increment retries */
int error; /* Error code */
int request_transfer; /* Bytes to transfer */
@@ -106,6 +118,7 @@
void (*callback) (ide_drive_t *); /* Called when this packet command is completed */
byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */
unsigned int flags; /* Status/Action bit flags */
+ unsigned int timeout; /* timeout */
} idefloppy_pc_t;

/*
@@ -173,7 +186,7 @@
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 sector_size; /* Bytes per sector */
u16 cyls; /* Number of cylinders */
u8 reserved10[10];
u8 motor_delay; /* Motor off delay */
@@ -183,7 +196,7 @@
} idefloppy_flexible_disk_page_t;

/*
- * Format capacity
+ * Format capacity as read from drive
*/
typedef struct {
u8 reserved[3];
@@ -210,6 +223,46 @@
#define CAPACITY_CURRENT 0x02
#define CAPACITY_NO_CARTRIDGE 0x03

+typedef struct {
+ u8 reserved; /* */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned side :1;
+ unsigned immed :1;
+ unsigned reserved2 :2;
+ unsigned dcrt :1;
+ unsigned singletrack :1;
+ unsigned reserved3 :1;
+ unsigned fov :1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned fov :1;
+ unsigned reserved3 :1;
+ unsigned singletrack :1;
+ unsigned dcrt :1;
+ unsigned reserved2 :2;
+ unsigned immed :1;
+ unsigned side :1;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
+ u8 dll_msb; /* Defect List Length */
+ u8 dll_lsb; /* Defect List Length */
+} idefloppy_format_list_hdr_t;
+
+/*
+ * Format capacity as written to drive for FORMAT UNIT
+ */
+typedef struct {
+ u32 blocks; /* Number of blocks */
+ u8 reserved;
+ u8 length_msb; /* Block Length (MSB)*/
+ u16 length; /* Block Length */
+} idefloppy_format_capacity_descriptor_t;
+
+typedef struct {
+ idefloppy_format_list_hdr_t flist;
+ idefloppy_format_capacity_descriptor_t capacity;
+} idefloppy_fmt_unit_params_t;
+
/*
* 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
@@ -696,11 +749,10 @@
#ifdef CONFIG_BLK_DEV_IDEDMA
static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
{
- struct request *rq = pc->rq;
- struct buffer_head *bh = rq->bh;
-
- while ((bh = rq->bh) != NULL)
- idefloppy_end_request (1, HWGROUP(drive));
+ struct request *rq = pc->rq;
+
+ while (rq->bh->b_reqnext != NULL)
+ idefloppy_end_request(1, HWGROUP(drive));
}
#endif /* CONFIG_BLK_DEV_IDEDMA */

@@ -787,7 +839,8 @@
*/
static void idefloppy_init_pc (idefloppy_pc_t *pc)
{
- memset (pc->c, 0, 12);
+ memset (pc->c, 0, sizeof(pc->c));
+ pc->pl = 0;
pc->retries = 0;
pc->flags = 0;
pc->request_transfer = 0;
@@ -795,6 +848,7 @@
pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
pc->b_data = NULL;
pc->callback = &idefloppy_pc_callback;
+ pc->timeout = IDEFLOPPY_WAIT_CMD;
}

static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
@@ -806,6 +860,53 @@
pc->callback = &idefloppy_request_sense_callback;
}

+static void idefloppy_create_format_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc)
+{
+ idefloppy_fmt_unit_params_t *params = (idefloppy_fmt_unit_params_t *)pc->p;
+ memset(params, '\0', sizeof(*params));
+
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_FORMAT_UNIT_CMD;
+ pc->c[1] = 0x17;
+ params->flist.fov = 1;
+ /* params->flist.immed = 1; */
+ params->flist.dll_lsb = 8;
+
+ put_unaligned (htons (floppy->block_size), (unsigned short *) &params->capacity.length);
+ put_unaligned (htonl (floppy->blocks), (unsigned long *) &params->capacity.blocks);
+ pc->pl = 12;
+ set_bit (PC_WRITING, &pc->flags);
+ /* format takes about 60 secs */
+ pc->timeout = IDEFLOPPY_FORMAT_WAIT;
+}
+
+static void idefloppy_create_format_track_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct format_descr *f)
+{
+ idefloppy_fmt_unit_params_t *params = (idefloppy_fmt_unit_params_t *)pc->p;
+ memset(params, '\0', sizeof(*params));
+
+#if IDEFLOPPY_DEBUG_INFO
+ printk(KERN_INFO "ide-floppy: format track %d head %d\n", f->track, f->head);
+#endif
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_FORMAT_UNIT_CMD;
+ pc->c[1] = 0x17;
+ pc->c[2] = f->track;
+ params->flist.fov = 1;
+ /* params->flist.immed = 1; */
+ params->flist.dcrt = 1;
+ params->flist.singletrack = 1;
+ if (f->head)
+ params->flist.side = 1;
+ params->flist.dll_lsb = 8;
+
+ put_unaligned (htons (floppy->block_size), (unsigned short *) &params->capacity.length);
+ put_unaligned (htonl (floppy->blocks), (unsigned long *) &params->capacity.blocks);
+ pc->pl = 12;
+ set_bit (PC_WRITING, &pc->flags);
+ /* default timeout should be OK for single track w/o check */
+}
+
/*
* idefloppy_retry_pc is called when an error was detected during the
* last packet command. We queue a request sense packet command in
@@ -914,7 +1015,7 @@
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);
- ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
+ ide_set_handler (drive,&idefloppy_pc_intr,pc->timeout);
return;
}
#if IDEFLOPPY_DEBUG_LOG
@@ -923,10 +1024,13 @@
}
}
if (test_bit (PC_WRITING, &pc->flags)) {
- if (pc->buffer != NULL)
+ if (pc->pl != 0) {
+ atapi_output_bytes (drive,pc->p, pc->pl); /* Write the parameter block */
+ } else if (pc->buffer != NULL) {
atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */
- else
+ } else {
idefloppy_output_buffers (drive, pc, bcount.all);
+ }
} else {
if (pc->buffer != NULL)
atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */
@@ -936,7 +1040,7 @@
pc->actually_transferred+=bcount.all; /* Update the current position */
pc->current_position+=bcount.all;

- ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */
+ ide_set_handler (drive,&idefloppy_pc_intr,pc->timeout); /* And set the interrupt handler again */
}

static void idefloppy_transfer_pc (ide_drive_t *drive)
@@ -954,7 +1058,7 @@
ide_do_reset (drive);
return;
}
- ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */
+ ide_set_handler (drive, &idefloppy_pc_intr, floppy->pc->timeout); /* Set the interrupt routine */
atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
}

@@ -1223,6 +1327,14 @@
drive->bios_cyl = page->cyls;
drive->bios_head = page->heads;
drive->bios_sect = page->sectors;
+ /* Copy to identity struct for HDIO_GET_IDENITY */
+ if (drive->id)
+ {
+ drive->id->cur_sectors = drive->bios_sect;
+ drive->id->cur_heads = drive->bios_head;
+ drive->id->cur_cyls = drive->bios_cyl;
+ drive->id->sector_bytes = page->sector_size;
+ }
lba_capacity = floppy->blocks * floppy->block_size;
if (capacity != lba_capacity) {
printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
@@ -1294,8 +1406,11 @@
unsigned int cmd, unsigned long arg)
{
idefloppy_pc_t pc;
+ idefloppy_floppy_t *floppy = (idefloppy_floppy_t *)drive->driver_data;

- if (cmd == CDROMEJECT) {
+ switch (cmd) {
+ case FDEJECT:
+ case CDROMEJECT:
if (drive->usage > 1)
return -EBUSY;
idefloppy_create_prevent_cmd (&pc, 0);
@@ -1303,6 +1418,46 @@
idefloppy_create_start_stop_cmd (&pc, 2);
(void) idefloppy_queue_pc_tail (drive, &pc);
return 0;
+ case FDGETPRM:
+ {
+ struct floppy_struct params;
+ memset(&params, '\0', sizeof(params));
+ params.size = floppy->blocks;
+ params.sect = drive->id->cur_sectors;
+ params.head = drive->id->cur_heads;
+ params.track = drive->id->cur_cyls;
+ return copy_to_user((void *)arg,&params, sizeof(params)) ? -EFAULT : 0;
+ }
+ case FDFMTBEG:
+ case FDFMTTRK:
+ case FDFMTUNIT:
+ if (drive->usage > 1)
+ return -EBUSY;
+ if (!(file->f_mode & 2))
+ return -EBADF;
+ if (floppy->wp)
+ /* Shouldn't get here as check in write or
+ above should prevent trying to format write
+ protected media */
+ return -EROFS;
+ switch (cmd) {
+ case FDFMTBEG:
+ return 0;
+ case FDFMTTRK:
+ {
+ struct format_descr f;
+ if (copy_from_user(&f, (void *)arg, sizeof(f)))
+ return -EFAULT;
+ idefloppy_create_format_track_cmd (floppy, &pc, &f);
+ return idefloppy_queue_pc_tail (drive, &pc);
+ }
+ case FDFMTUNIT:
+ idefloppy_create_format_cmd (floppy, &pc);
+ idefloppy_queue_pc_tail (drive, &pc);
+ return idefloppy_create_request_sense_cmd (&pc);
+ }
+ case FDFMTEND:
+ return 0;
}
return -EIO;
}
@@ -1501,6 +1656,7 @@
ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+ ide_add_setting(drive, "multcount", SETTING_READ, HDIO_GET_MULTCOUNT, -1, TYPE_BYTE, 0, 0, 1, 1, &drive->mult_count, NULL);

}

--Multipart_Wed_Dec_30_13:14:24_1998-1
Content-Type: text/plain; charset=US-ASCII

--Multipart_Wed_Dec_30_13:14:24_1998-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="fdformat.c"
Content-Transfer-Encoding: 8bit

/* fdformat.c - Low-level formats a floppy disk. */

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fd.h>
#include <linux/fs.h>

static int ctrl;
struct floppy_struct param;

#define FLOPPY_MAJOR 2
#define SECTOR_SIZE 512
#define PERROR(msg) { perror(msg); exit(1); }

static void format_disk(char *name)
{
struct format_descr descr;
int track;

printf("Formatting ... ");
fflush(stdout);
if (ioctl(ctrl,FDFMTBEG,NULL) < 0) PERROR("\nioctl(FDFMTBEG)");
for (track = 0; track < param.track; track++) {
descr.track = track;
descr.head = 0;
if (ioctl(ctrl,FDFMTTRK,(long) &descr) < 0)
PERROR("\nioctl(FDFMTTRK)");

printf("%3d\b\b\b",track);
fflush(stdout);
if (param.head == 2) {
descr.head = 1;
if (ioctl(ctrl,FDFMTTRK,(long) &descr) < 0)
PERROR("\nioctl(FDFMTTRK)");
}
}
if (ioctl(ctrl,FDFMTEND,NULL) < 0) PERROR("\nioctl(FDFMTEND)");
printf("done\n");
}

static void verify_disk(char *name)
{
unsigned char *data;
int fd,cyl_size,cyl,count;

cyl_size = param.sect*param.head*512;
if ((data = (unsigned char *) malloc(cyl_size)) == NULL) PERROR("malloc");
printf("Verifying ... ");
fflush(stdout);
if ((fd = open(name,O_RDONLY)) < 0) PERROR(name);
for (cyl = 0; cyl < param.track; cyl++) {
printf("%3d\b\b\b",cyl);
fflush(stdout);
if (read(fd,data,cyl_size) != cyl_size) PERROR("read");
for (count = 0; count < cyl_size; count++)
if (data[count] != FD_FILL_BYTE) {
printf("bad data in cyl %d\nContinuing ... ",cyl);
fflush(stdout);
break;
}
}
printf("done\n");
if (close(fd) < 0) PERROR("close");
}

static void usage(char *name)
{
char *this;

if ((this = strrchr(name,'/')) != NULL) name = this+1;
fprintf(stderr,"usage: %s [ -n ] device\n",name);
exit(1);
}

int main(int argc,char **argv)
{
int verify;
char *name;
struct stat st;

name = argv[0];
verify = 1;
if (argc > 1 && argv[1][0] == '-') {
if (argv[1][1] != 'n') usage(name);
verify = 0;
argc--;
argv++;
}
if (argc != 2) usage(name);
if (stat(argv[1],&st) < 0) PERROR(argv[1]);
if (!S_ISBLK(st.st_mode) /* || MAJOR(st.st_rdev) != FLOPPY_MAJOR */) {
fprintf(stderr,"%s: not a floppy device\n",argv[1]);
exit(1);
}
if (access(argv[1],W_OK) < 0) PERROR(argv[1]);
if ((ctrl = open(argv[1],O_RDWR)) < 0) PERROR(argv[1]);
if (ioctl(ctrl,FDGETPRM,(long) &param) < 0)
PERROR("Could not determine current format type");
printf("%sle-sided, %d tracks, %d sec/track. Total capacity %d kB.\n",
param.head ? "Doub" : "Sing",param.track,param.sect,param.size >> 1);
format_disk(argv[1]);
if (verify) verify_disk(argv[1]);
return 0;
}

--Multipart_Wed_Dec_30_13:14:24_1998-1
Content-Type: text/plain; charset=US-ASCII

--Multipart_Wed_Dec_30_13:14:24_1998-1--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/