N: Hennus Bergman
E: hennus@sky.nl.mugnet.org [My uucp-fed Linux box at home]
-E: csg279@wing.rug.nl [Alternate address]
D: Author and maintainer of the QIC-02 tape driver
S: The Netherlands
VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 34
+SUBLEVEL = 35
all: Version zImage
bool 'Second extended fs support' CONFIG_EXT2_FS y
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
+bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
bool '/proc filesystem support' CONFIG_PROC_FS y
if [ "$CONFIG_INET" = "y" ]; then
bool 'NFS filesystem support' CONFIG_NFS_FS y
bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n
-bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n
+
+bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
+if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
+bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
+if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+
+comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
+
+else
+
+comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
+comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
+
+fi
+fi
+
bool 'QIC-117 tape support' CONFIG_FTAPE n
if [ "$CONFIG_FTAPE" = "y" ]; then
int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
SRCS := $(SRCS) psaux.c
endif
-ifdef CONFIG_TAPE_QIC02
+ifdef CONFIG_QIC02_TAPE
OBJS := $(OBJS) tpqic02.o
SRCS := $(SRCS) tpqic02.c
endif
/* misc */
unsigned long vc_ques : 1;
unsigned long vc_need_wrap : 1;
- unsigned long vc_report_mouse : 1;
+ unsigned long vc_report_mouse : 2;
unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */
unsigned char * vc_translate;
unsigned char * vc_G0_charset;
clr_kbd(decarm);
break;
case 9:
- report_mouse = on_off;
+ report_mouse = on_off ? 1 : 0;
break;
case 25: /* Cursor on/off */
deccm = on_off;
set_cursor(currcons);
break;
+ case 1000:
+ report_mouse = on_off ? 2 : 0;
+ break;
} else switch(par[i]) { /* ANSI modes set/reset */
case 4: /* Insert Mode on/off */
decim = on_off;
}
/* invoked via ioctl(TIOCLINUX) */
-int mouse_reporting_p(void)
+int mouse_reporting(void)
{
int currcons = fg_console;
- return ((report_mouse) ? 0 : -EINVAL);
+ return report_mouse;
}
/* set the current selection. Invoked by ioctl(). */
#ifdef CONFIG_SOUND
mem_start = soundcard_init(mem_start);
#endif
-#if CONFIG_TAPE_QIC02
- mem_start = tape_qic02_init(mem_start);
+#if CONFIG_QIC02_TAPE
+ mem_start = qic02_tape_init(mem_start);
#endif
/*
* Rude way to allocate kernel memory buffer for tape device
-/* $Id: tpqic02.c,v 0.2.1.21 1993/06/18 19:04:33 root Exp root $
+/* $Id: tpqic02.c,v 0.4.1.4 1994/07/21 02:15:45 root Exp root $
*
- * Driver for tape drive support for Linux-i386 0.99.12.
+ * Driver for tape drive support for Linux-i386 1.1.30
*
- * Copyright (c) 1993 by H. H. Bergman. All rights reserved.
- * Current e-mail address: csg279@wing.rug.nl
+ * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
+ * Current e-mail address: hennus@sky.nl.mugnet.org [This is a UUCP link.]
+ * Secondary e-mail address: csg279@wing.rug.nl [IP connected, but flaky]
* [If you are unable to reach me directly, try the TAPE mailing list
* channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as
* the first line in your message.]
* Use this code at your own risk. Don't blame me if it destroys your data!
* Make sure you have a backup before you try this code.
*
+ * If you make changes to my code and redistribute it in source or binary
+ * form you must make it clear to even casual users of your code that you
+ * have modified my code, clearly point out what the changes exactly are
+ * (preferably in the form of a context diff file), how to undo your changes,
+ * where the original can be obtained, and that complaints/requests about the
+ * modified code should be directed to you instead of me.
+ *
* This driver was partially inspired by the 'wt' driver in the 386BSD
* source distribution, which carries the following copyright notice:
*
* You are not allowed to change this line nor the text above.
*
* $Log: tpqic02.c,v $
+ * Revision 0.4.1.4 1994/07/21 02:15:45 root
+ * ifdef'd DDI. Exception masks.
+ *
+ * Revision 0.4.1.3 1994/05/03 01:49:09 root
+ * Initial attempt at Mountain support for the Mountain 7150.
+ * Based on patches provided by Erik Jacobson.
+ *
+ * Revision 0.4.1.2 1994/03/18 21:16:50 root
+ * Many driver messages can now be turned off (runtime selectable).
+ *
+ * Revision 0.4.1.1 1994/02/16 19:47:22 root
+ * First stab at runtime debug-variable.
+ *
+ * Revision 0.4 1994/02/15 01:53:16 root
+ * DYNCONF mark II.
+ * Minor cleanups.
+ *
+ * Revision 0.3 1994/02/07 01:23:16 root
+ * More improved DYNCONF.
+ * Archive changes & some cleanups by Eddy Olk.
+ * Removed status_open, more cleanups, misc other.
+ *
+ * Revision 0.2.1.25 1994/01/24 02:01:33 root
+ * Changed tape_qic02 to QIC02_TAPE.
+ * Changes to prepare for DYNCONF.
+ *
+ * Revision 0.2.1.24 1994/01/23 07:27:18 root
+ * Attempt to remove compilation warnings, G++ bug,
+ * Linus changed TAPE_QIC02 to QIC02_TAPE.
+ *
+ * Revision 0.2.1.23 1994/01/20 23:49:28 root
+ * Changed some exception decoding stuff.
+ * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global.
+ * Attempt to fix cartridge-changed-problem for 2150L.
+ * Release irq and dma reservations if initial reset fails.
+ *
+ * Revision 0.2.1.22 1994/01/19 20:56:55 root
+ * Speed measuring stuff moved from aperf.h to delay.h.
+ * BogoMips (tm) introduced by Linus.
+ *
* Revision 0.2.1.21 1993/06/18 19:04:33 root
* minor fixes for 0.99.10.
*
#include <linux/config.h>
/* skip this driver if not required for this configuration */
-#if CONFIG_TAPE_QIC02
+#if CONFIG_QIC02_TAPE
/*
#define TDEBUG
#include <asm/segment.h>
/* check existence of required configuration parameters */
-#if !defined(TAPE_QIC02_PORT) || \
- !defined(TAPE_QIC02_IRQ) || \
- !defined(TAPE_QIC02_DMA)
-#error tape_qic02 configuration error
+#if !defined(QIC02_CMD_PORT) || \
+ !defined(QIC02_TAPE_IRQ) || \
+ !defined(QIC02_TAPE_DMA)
+#error qic02_tape configuration error
#endif
-#define TPQIC_NAME "tpqic02"
+#define TPQIC02_NAME "tpqic02"
/* Linux outb() commands have (value,port) as parameters.
* One might expect (port,value) instead, so beware!
*/
+#ifdef CONFIG_QIC02_DYNCONF
+/* This may hold the dynamic configuration info for the interface
+ * card+drive info in future versions.
+ */
+struct mtconfiginfo qic02_tape_dynconf = { 0, }; /* user settable */
+struct qic02_ccb qic02_tape_ccb = { 0, }; /* private stuff */
+
+#else
+
+unsigned long qic02_tape_debug;
+
+# if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN))
+# error No valid interface card specified
+# endif
+#endif
+
static volatile int ctlbits = 0; /* control reg bits for tape interface */
-static struct wait_queue *tape_qic02_transfer = NULL; /* sync rw with interrupts */
+static struct wait_queue *qic02_tape_transfer = NULL; /* sync rw with interrupts */
static volatile struct mtget ioctl_status; /* current generic status */
static volatile struct tpstatus tperror; /* last drive status */
-static char rcs_revision[] = "$Revision: 0.2.1.21 $";
-static char rcs_date[] = "$Date: 1993/06/18 19:04:33 $";
+static char rcs_revision[] = "$Revision: 0.4.1.4 $";
+static char rcs_date[] = "$Date: 1994/07/21 02:15:45 $";
/* Flag bits for status and outstanding requests.
* (Could all be put in one bit-field-struct.)
* by an interrupt.
*/
static volatile flag status_dead = YES; /* device is legally dead until proven alive */
-static flag status_open = NO; /* in use or not */
+static flag status_zombie = YES; /* it's `zombie' until irq/dma allocated */
static volatile flag status_bytes_wr = NO; /* write FM at close or not */
static volatile flag status_bytes_rd = NO; /* (rd|wr) used for rewinding */
static flag reported_write_eof = NO;
-#ifdef TP_HAVE_SEEK
/* This is for doing `mt seek <blocknr>' */
-static char seek_addr_buf[SEEK_BUF_SIZE];
-#endif
+static char seek_addr_buf[AR_SEEK_BUF_SIZE];
/* In write mode, we have to write a File Mark after the last block written,
/* This is the actual kernel buffer where the interrupt routines read
* from/write to. It is needed because the DMA channels 1 and 3 cannot
- * access the user buffers. [The kernel buffer must reside in the lower
- * 1MBytes of system memory because of the DMA controller.]
+ * always access the user buffers. [The kernel buffer must reside in the
+ * lower 16MBytes of system memory because of the DMA controller.]
* The user must ensure that a large enough buffer is passed to the
* kernel, in order to reduce tape repositioning.
*
* at 512 bytes, to prevent problems with 64k boundaries.
*/
-static volatile char tape_qic02_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
+static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
/* A really good compiler would be able to align this at 512 bytes... :-( */
static unsigned long buffaddr; /* aligned physical address of buffer */
* "Exception Status Summary" in QIC-02 rev F, but some changes
* were required to make it work with real-world drives.
*
- * Exception 1 (CNI) is changed to also cover status 0x00e0 (mask USL),
+ * Exception 2 (CNI) is changed to also cover status 0x00e0 (mask USL),
* Exception 4 (EOM) is changed to also cover status 0x8288 (mask EOR),
* Exception 11 (FIL) is changed to also cover status 0x0089 (mask EOM).
* Exception 15 (EOR) is added for seek-to-end-of-data (catch EOR),
* Exception 16 (BOM) is added for beginning-of-media (catch BOM).
+ *
+ * Had to swap EXC_NDRV and EXC_NCART to ensure that extended EXC_NCART
+ * (because of the incorrect Wangtek status code) doesn't catch the
+ * EXC_NDRV first.
*/
static struct exception_list_type {
unsigned short mask, code;
char *msg;
+ /* EXC_nr attribute should match with tpqic02.h */
} exception_list[] = {
{0, 0,
"Unknown exception status code", /* extra: 0 */},
+ {~(0), TP_ST0|TP_CNI|TP_USL|TP_WRP,
+ "Drive not online" /* 1 */},
+ /* Drive presence goes before cartridge presence. */
{~(TP_WRP|TP_USL), TP_ST0|TP_CNI,
/* My Wangtek 5150EQ sometimes reports a status code
* of 0x00e0, which is not a valid exception code, but
* I think it should be recognized as "NO CARTRIDGE".
*/
- "Cartridge not in place" /* 1 */},
- {-1, TP_ST0|TP_CNI|TP_USL|TP_WRP,
- "Drive not online" /* 2 */},
- {~(TP_ST1|TP_BOM), TP_ST0|TP_WRP,
+ "Cartridge not in place" /* 2 */},
+ {(unsigned short) ~(TP_ST1|TP_BOM), (TP_ST0|TP_WRP),
"Write protected cartridge" /* 3 */},
- {~(TP_ST1|TP_EOR), TP_ST0|TP_EOM,
+ {(unsigned short) ~(TP_ST1|TP_EOR), (TP_ST0|TP_EOM),
"End of media" /* 4 */},
{~TP_WRP, TP_ST0|TP_UDA| TP_ST1|TP_BOM,
"Read or Write abort. Rewind tape." /* 5 */},
/* 15 is returned when SEEKEOD completes successfully */
{~(TP_WRP|TP_ST0), TP_ST1|TP_BOM,
"Beginning of media" /* extra: 16 */}
-#ifdef CYPHER_BUG
- /* Perhaps the Cypher driver clears the TP_BOM bit after the
- * status has been read? The QIC-02 specs explicitly state that
- * the BOM bit should remain set as long as the tape is logically
- * at the beginning of the tape.
- */
- ,{-1, TP_ST1,
- "Hmm, this must be Cypher drive... Aaargh" /* extra */}
-#endif
};
#define NR_OF_EXC (sizeof(exception_list)/sizeof(struct exception_list_type))
-static void tpqputs(char *s)
+static void tpqputs(unsigned long flags, char *s)
{
- printk(TPQIC_NAME ": %s\n", s);
+ if ((flags & TPQD_ALWAYS) || (flags & QIC02_TAPE_DEBUG))
+ printk(TPQIC02_NAME ": %s\n", s);
} /* tpqputs */
+
+
/* Perform byte order swapping for a 16-bit word.
*
* [FIXME] This should probably be in include/asm/
*/
static void ifc_init(void)
{
-#if TAPE_QIC02_IFC == WANGTEK
- ctlbits = WT_CTL_ONLINE; /* online */
- outb_p(ctlbits, QIC_CTL_PORT);
-
-#elif TAPE_QIC02_IFC == ARCHIVE
- ctlbits = 0; /* no interrupts yet */
- outb_p(ctlbits, QIC_CTL_PORT);
- outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */
-#else
-# error No valid interface card specified
-#endif
+ if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ {
+ ctlbits = WT_CTL_ONLINE; /* online */
+ outb_p(ctlbits, QIC02_CTL_PORT);
+
+ } else if (QIC02_TAPE_IFC == ARCHIVE) {
+ ctlbits = 0; /* no interrupts yet */
+ outb_p(ctlbits, QIC02_CTL_PORT);
+ outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */
+
+ } else /* MOUNTAIN */ {
+ ctlbits = MTN_CTL_ONLINE; /* online, and logic enabled */
+ outb_p(ctlbits, QIC02_CTL_PORT);
+ }
} /* ifc_init */
static void report_exception(unsigned n)
{
- if (n >= NR_OF_EXC) { tpqputs("Oops -- report_exception"); n = 0; }
- printk(TPQIC_NAME ": sense: %s\n", exception_list[n].msg);
+ if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_exception"); n = 0; }
+ if (TPQDBG(SENSE_TEXT) || n==0)
+ printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg);
} /* report_exception */
* exception table (`exception_list[]').
* It is assumed that s!=0.
*/
-static int decode_exception_nr(short s) /* s must be short, because of sign-extension */
+static int decode_exception_nr(unsigned s)
{
int i;
if ((s & exception_list[i].mask)==exception_list[i].code)
return i;
}
- tpqputs("decode_exception_nr: exception not recognized");
+ printk(TPQIC02_NAME ": decode_exception_nr: exception(%x) not recognized\n", s);
return 0;
} /* decode_exception_nr */
else /* 8: Read error. No data detected. CONTINUABLE */
n = 8;
} else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */
- tpqputs("[Bad block -- filler data transferred.]");
+ tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
n = 7;
}
}
* This is why some people prefer not to
* use compression on backups...
*/
- tpqputs("[CRC failed!]");
+ tpqputs(TPQD_ALWAYS, "[CRC failed!]");
n = 6;
}
}
}
else if (s & TP_FIL) {
if (s & TP_MBD) { /* 14: Marginal block detected. CONTINUABLE */
- tpqputs("[Marginal block]");
+ tpqputs(TPQD_ALWAYS, "[Marginal block]");
n = 14;
} else /* 11: File mark detected. CONTINUABLE */
n = 11;
status_eom_detected = NO;
}
else if (exnr==EXC_XFILLER)
- tpqputs("[Bad block -- filler data transferred.]");
+ tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
else if (exnr==EXC_XBAD)
- tpqputs("[CRC failed!]");
+ tpqputs(TPQD_ALWAYS, "[CRC failed!]");
else if (exnr==EXC_MARGINAL) {
/* A marginal block behaves much like a FM.
* User may continue reading, if desired.
*/
- tpqputs("[Marginal block]");
+ tpqputs(TPQD_ALWAYS, "[Marginal block]");
doing_read = NO;
} else if (exnr==EXC_FM)
doing_read = NO;
static inline int is_exception(void)
{
- return (inb(QIC_STAT_PORT) & QIC_STAT_EXCEPTION) == 0;
+ return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0;
} /* is_exception */
{
ifc_init(); /* reset interface card */
- outb_p(ctlbits | QIC_CTL_RESET, QIC_CTL_PORT); /* assert reset */
+ /* assert reset */
+ if (QIC02_TAPE_IFC == MOUNTAIN)
+ outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+ else /* WANGTEK, ARCHIVE */
+ outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT);
/* Next, we need to wait >=25 usec. */
udelay(30);
doing_read = doing_write = NO;
ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0;
- outb_p(ctlbits & ~QIC_CTL_RESET, QIC_CTL_PORT); /* de-assert reset */
+ /* de-assert reset */
+ if (QIC02_TAPE_IFC == MOUNTAIN)
+ outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+ else
+ outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT);
+
/* KLUDGE FOR G++ BUG */
- { int stat = inb_p(QIC_STAT_PORT);
- status_dead = ((stat & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL); }
+ { int stat = inb_p(QIC02_STAT_PORT);
+ status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); }
/* if successful, inb(STAT) returned RESETVAL */
- if (status_dead)
- printk(TPQIC_NAME ": reset failed!\n");
+ if (status_dead == YES)
+ printk(TPQIC02_NAME ": reset failed!\n");
else if (verbose)
- printk(TPQIC_NAME ": reset successful\n");
+ printk(TPQIC02_NAME ": reset successful\n");
- return (status_dead)? TE_DEAD : TE_OK;
+ return (status_dead == YES)? TE_DEAD : TE_OK;
} /* tape_reset */
/* Notify tape drive of a new command. It only waits for the
* command to be accepted, not for the actual command to complete.
*
- * Before calling this routine, QIC_CMD_PORT must have been loaded
+ * Before calling this routine, QIC02_CMD_PORT must have been loaded
* with the command to be executed.
* After this routine, the exception bit must be checked.
* This routine is also used by rdstatus(), so in that case, any exception
{
int i;
- outb_p(cmd, QIC_CMD_PORT); /* output the command */
+ outb_p(cmd, QIC02_CMD_PORT); /* output the command */
/* wait 1 usec before asserting /REQUEST */
udelay(1);
if ((!ignore_ex) && is_exception()) {
- tpqputs("*** exception detected in notify_cmd");
+ tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd");
/** force a reset here **/
if (tape_reset(1)==TE_DEAD)
return TE_DEAD;
if (is_exception()) {
- tpqputs("exception persists after reset.");
- tpqputs(" ^ exception ignored.");
+ tpqputs(TPQD_ALWAYS, "exception persists after reset.");
+ tpqputs(TPQD_ALWAYS, " ^ exception ignored.");
}
}
- outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request bit */
+ outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request bit */
i = TAPE_NOTIFY_TIMEOUT;
/* The specs say this takes about 500 usec, but there is no upper limit!
* If the drive were busy retensioning or something like that,
* it could be *much* longer!
*/
- while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) && (--i>0))
+ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0))
/*skip*/; /* wait for ready */
if (i==0) {
- tpqputs("timed out waiting for ready in notify_cmd");
+ tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd");
status_dead = YES;
return TE_TIM;
}
- outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* reset request bit */
+ outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */
i = TAPE_NOTIFY_TIMEOUT;
/* according to the specs this one should never time-out */
- while (((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0) && (--i>0))
+ while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0))
/*skip*/; /* wait for not ready */
if (i==0) {
- tpqputs("timed out waiting for !ready in notify_cmd");
+ tpqputs(TPQD_ALWAYS, "timed out waiting for !ready in notify_cmd");
status_dead = YES;
return TE_TIM;
}
* First, busy wait a few usec:
*/
spin_t = 50;
- while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (--spin_t>0))
+ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0))
/*SKIP*/;
- if ((stat & QIC_STAT_READY) == 0)
+ if ((stat & QIC02_STAT_READY) == 0)
return TE_OK; /* covers 99.99% of all calls */
/* Then use schedule() a few times */
timeout -= spin_t;
spin_t += jiffies;
- while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t))
+ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t))
schedule(); /* don't waste all the CPU time */
- if ((stat & QIC_STAT_READY) == 0)
+ if ((stat & QIC02_STAT_READY) == 0)
return TE_OK;
/* If we reach this point, we probably need to wait much longer, or
TPQDEB({printk("wait_for_ready: additional timeout: %d\n", spin_t);})
/* not ready and no exception && timeout not expired yet */
- while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t)) {
+ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) {
/* be `nice` to other processes on long operations... */
current->timeout = jiffies + 30; /* nap 0.30 sec between checks, */
current->state = TASK_INTERRUPTIBLE;
}
/* don't use jiffies for this test because it may have changed by now */
- if ((stat & QIC_STAT_MASK) == QIC_STAT_MASK) {
- tpqputs("wait_for_ready() timed out");
+ if ((stat & QIC02_STAT_MASK) == QIC02_STAT_MASK) {
+ tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out");
return TE_TIM;
}
- if ((stat & QIC_STAT_EXCEPTION) == 0) {
- tpqputs("exception detected after waiting_for_ready");
+ if ((stat & QIC02_STAT_EXCEPTION) == 0) {
+ tpqputs(TPQD_ALWAYS, "exception detected after waiting_for_ready");
return TE_EX;
} else {
return TE_OK;
} /* wait_for_ready */
+
/* Send some data to the drive */
static int send_qic02_data(char sb[], unsigned size, int ignore_ex)
{
{
int stat;
- stat = inb_p(QIC_STAT_PORT);
- if ((stat & QIC_STAT_EXCEPTION) == 0) { /* if exception */
- tpqputs("send_qic02_cmd: Exception!");
+ stat = inb_p(QIC02_STAT_PORT);
+ if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* if exception */
+ tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!");
return TE_EX;
}
- if (stat & QIC_STAT_READY) { /* if not ready */
- tpqputs("send_qic02_cmd: not Ready!");
+ if (stat & QIC02_STAT_READY) { /* if not ready */
+ tpqputs(TPQD_ALWAYS, "send_qic02_cmd: not Ready!");
return TE_ERR;
}
stat = notify_cmd(cmd, ignore_ex); /* tell drive new command was loaded, */
/* inherit exception check. */
- if (cmd == QCMDV_SEEK_BLK) {
+ if (TP_HAVE_SEEK && (cmd == AR_QCMDV_SEEK_BLK)) {
/* This one needs to send 3 more bytes, MSB first */
stat = send_qic02_data(seek_addr_buf, sizeof(seek_addr_buf), ignore_ex);
}
+
if (stat != TE_OK) {
- tpqputs("send_qic02_cmd failed");
+ tpqputs(TPQD_ALWAYS, "send_qic02_cmd failed");
}
return stat;
} /* send_qic02_cmd */
* task switch is much longer than we usually have to wait here.
*/
n = 1000; /* 500 is not enough on a 486/33 */
- while ((n>0) && ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK))
+ while ((n>0) && ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK))
n--; /* wait for ready or exception or timeout */
if (n==0) {
/* n (above) should be chosen such that on your machine
* you rarely ever see the message below, and it should
* be small enough to give reasonable response time.]
*/
- tpqputs("waiting looong in rdstatus() -- drive dead?");
- while ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK)
+ tpqputs(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?");
+ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK)
schedule();
- tpqputs("finished waiting in rdstatus()");
+ tpqputs(TPQD_ALWAYS, "finished waiting in rdstatus()");
}
(void) notify_cmd(qcmd, 1); /* send read status command */
*/
if (TP_DIAGS(current_tape_dev))
- printk(TPQIC_NAME ": reading status bytes: ");
+ printk(TPQIC02_NAME ": reading status bytes: ");
for (q=stp; q<stp+size; q++)
{
- do s = inb_p(QIC_STAT_PORT);
- while ((s & QIC_STAT_MASK) == QIC_STAT_MASK); /* wait for ready or exception */
+ do s = inb_p(QIC02_STAT_PORT);
+ while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK); /* wait for ready or exception */
- if ((s & QIC_STAT_EXCEPTION) == 0) { /* if exception */
- tpqputs("rdstatus: exception error");
+ if ((s & QIC02_STAT_EXCEPTION) == 0) { /* if exception */
+ tpqputs(TPQD_ALWAYS, "rdstatus: exception error");
ioctl_status.mt_erreg = 0; /* dunno... */
return TE_NS; /* error, shouldn't happen... */
}
- *q = inb_p(QIC_DATA_PORT); /* read status byte */
+ *q = inb_p(QIC02_DATA_PORT); /* read status byte */
if (TP_DIAGS(current_tape_dev))
printk("[%1d]=0x%x ", q-stp, (unsigned) (*q) & 0xff);
- outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request */
+ outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request */
- while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0); /* wait for not ready */
+ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0); /* wait for not ready */
udelay(22); /* delay >20 usec */
- outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* un-set request */
+ outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* un-set request */
}
/* Specs say we should wait for READY here.
* My drive doesn't seem to need it here yet, but others do?
*/
- while (inb_p(QIC_STAT_PORT) & QIC_STAT_READY)
+ while (inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY)
/*skip*/; /* wait for ready */
if (TP_DIAGS(current_tape_dev))
* The `.dec' and `.urc' fields are in MSB-first byte-order,
* so they have to be swapped first.
*/
-static int get_status(char *stp)
+static int get_status(volatile struct tpstatus *stp)
{
- int stat = rdstatus(stp, TPSTATSIZE, QCMD_RD_STAT);
+ int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT);
#if defined(i386) || defined(i486)
- byte_swap_w(&tperror.dec);
- byte_swap_w(&tperror.urc);
+ byte_swap_w(&(stp->dec));
+ byte_swap_w(&(stp->urc));
#else
/* should probably swap status bytes #definition */
#endif
char vus[64]; /* vendor unique status */
int stat, i;
- tpqputs("Attempting to read Extended Status 3...");
+ tpqputs(TPQD_ALWAYS, "Attempting to read Extended Status 3...");
stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3);
if (stat != TE_OK)
return stat;
- tpqputs("Returned status bytes:");
+ tpqputs(TPQD_ALWAYS, "Returned status bytes:");
for (i=0; i<sizeof(vus); i++) {
if ( i % 8 == 0 )
- printk("\n" TPQIC_NAME ": %2d:");
+ printk("\n" TPQIC02_NAME ": %2d:");
printk(" %2x", vus[i] & 0xff);
}
printk("\n");
unsigned err = 0, exnr = 0, gs = 0;
static void finish_rw(int cmd);
- printk(TPQIC_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
+ if (TPQDBG(SENSE_TEXT))
+ printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
/* sense() is not allowed during a read or write cycle */
if (doing_write == YES)
- tpqputs("Warning: File Mark inserted because of sense() request");
+ tpqputs(TPQD_ALWAYS, "Warning: File Mark inserted because of sense() request");
/* The extra test is to avoid calling finish_rw during booting */
if ((doing_read!=NO) || (doing_write!=NO))
finish_rw(QCMD_RD_STAT);
- if (get_status((char *) &tperror) != TE_OK) {
- tpqputs("tp_sense: could not read tape drive status");
+ if (get_status(&tperror) != TE_OK) {
+ tpqputs(TPQD_ALWAYS, "tp_sense: could not read tape drive status");
return TE_ERR;
}
err = tperror.exs; /* get exception status bits */
if (err & (TP_ST0|TP_ST1))
- printk(TPQIC_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
+ printk(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
tperror.exs, tperror.dec, tperror.urc);
- else
- printk(TPQIC_NAME ": tp_sense: no errors at all, soft error count: %d, underruns: %d\n",
+ else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS))
+ printk(TPQIC02_NAME ": tp_sense: no hard errors, soft error count: %d, underruns: %d\n",
tperror.dec, tperror.urc);
/* Set generic status. HP-UX defines these, but some extra would
ioctl_status.mt_dsreg = tperror.exs; /* "drive status" */
ioctl_status.mt_erreg = tperror.dec; /* "sense key error" */
- if (err!=0) {
+ if (err & (TP_ST0|TP_ST1)) {
+ /* My Wangtek occasionally reports `status' 1212 which should be ignored. */
exnr = decode_exception_nr(err);
handle_exception(exnr, err); /* update driver state wrt drive status */
report_exception(exnr);
{
int stat;
- stat = inb(QIC_STAT_PORT) & QIC_STAT_MASK;
- printk(TPQIC_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
+ stat = inb(QIC02_STAT_PORT) & QIC02_STAT_MASK;
+ if (TPQDBG(REWIND))
+ printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
stat = wait_for_ready(timeout);
if (stat != TE_OK) {
- tpqputs("(re-) winding failed\n");
+ tpqputs(TPQD_ALWAYS, "(re-) winding failed\n");
}
return stat;
} /* wait_for_rewind */
{
int stat;
- if (status_dead) {
- tpqputs("Drive is dead. Do a `mt reset`.");
+ if (status_dead == YES) {
+ tpqputs(TPQD_ALWAYS, "Drive is dead. Do a `mt reset`.");
return -ENXIO; /* User should do an MTRESET. */
}
stat = TE_OK;
}
if (stat != TE_OK) {
- printk(TPQIC_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, timeout);
+ printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout);
return -EIO;
}
#if OBSOLETE
/* wait for ready since it may not be active immediately after reading status */
- while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) != 0);
+ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0);
#endif
stat = send_qic02_cmd(cmd, timeout, 0); /* (checks for exceptions) */
/* sense() will set eof/eom as required */
if (stat==TE_EX) {
if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) {
- printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, timeout);
+ printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout);
status_dead = YES;
return -ENXIO;
/* if rdstatus fails too, we're in trouble */
}
}
else if (stat!=TE_OK) {
- printk(TPQIC_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
+ printk(TPQIC02_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
return -EIO; /*** -EIO is probably not always appropriate */
}
if (tp_sense((cmd==QCMD_SEEK_EOD ? /*****************************/
TP_EOR|TP_NDT|TP_UDA|TP_BNL|TP_WRP|TP_BOM|TP_EOM|TP_FIL :
TP_WRP|TP_BOM|TP_EOM|TP_FIL))!=TE_OK) {
- printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, timeout);
+ printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) timeout);
if (cmd!=QCMD_RD_FM)
status_dead = YES;
return -ENXIO;
}
}
else if (stat!=TE_OK) {
- printk(TPQIC_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
+ printk(TPQIC02_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
return -EIO;
}
return 0;
/* if the command is a RFM, there is no need to do this
* because a RFM will legally terminate the read-cycle.
*/
- tpqputs("terminating pending read-cycle");
+ tpqputs(TPQD_ALWAYS, "terminating pending read-cycle");
+
+ /* I'm not too sure about this part -- hhb */
+ if (QIC02_TAPE_IFC == MOUNTAIN) {
+ /* Mountain reference says can terminate by de-asserting online */
+ ctlbits &= ~MTN_QIC02_CTL_ONLINE;
+ }
+
if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) {
- tpqputs("finish_rw[read1]: ignore the 2 lines above");
+ tpqputs(TPQD_ALWAYS, "finish_rw[read1]: ignore the 2 lines above");
if (is_exception()) {
if (tp_sense(TP_ILL|TP_FIL|TP_EOM|TP_WRP) != TE_OK)
- tpqputs("finish_rw[read2]: read cycle error");
+ tpqputs(TPQD_ALWAYS, "finish_rw[read2]: read cycle error");
}
}
}
/* finish off write cycle */
stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M);
if (stat != TE_OK)
- tpqputs("Couldn't finish write cycle properly");
+ tpqputs(TPQD_ALWAYS, "Couldn't finish write cycle properly");
(void) tp_sense(0);
}
/* If there is an EOF token waiting to be returned to
static void finish_rw(int cmd)
{
if (wait_for_ready(TIM_S) != TE_OK) {
- tpqputs("error: drive not ready in finish_rw() !");
+ tpqputs(TPQD_ALWAYS, "error: drive not ready in finish_rw() !");
return;
}
terminate_read(cmd);
finish_rw(cmd);
if (need_rewind) {
- tpqputs("Rewinding tape...");
+ tpqputs(TPQD_REWIND, "Rewinding tape...");
stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R);
if (stat != 0) {
- printk(TPQIC_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
+ printk(TPQIC02_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
return stat;
}
need_rewind = NO;
return (tape_reset(1)==TE_OK)? 0 : -EIO;
case MTFSF:
- tpqputs("MTFSF forward searching filemark");
+ tpqputs(TPQD_IOCTLS, "MTFSF forward searching filemark");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return do_qic_cmd(QCMD_RD_FM, TIM_F);
case MTBSF:
-#ifdef TP_HAVE_BSF
- tpqputs("MTBSF backward searching filemark -- optional command");
- if ((mode_access==WRITE) && status_bytes_wr)
- return -EACCES;
- stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
-#else
- tpqputs("MTBSF not supported");
- stat = -ENXIO;
-#endif
+ if (TP_HAVE_BSF) {
+ tpqputs(TPQD_IOCTLS, "MTBSF backward searching filemark -- optional command");
+ if ((mode_access==WRITE) && status_bytes_wr)
+ return -EACCES;
+ stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
+ } else {
+ stat = -ENXIO;
+ }
status_eom_detected = status_eof_detected = NO;
return stat;
case MTFSR:
-#ifdef TP_HAVE_FSR /* This is an optional QIC-02 command */
- tpqputs("MTFSR forward space record");
- if ((mode_access==WRITE) && status_bytes_wr)
- return -EACCES;
- stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
-#else
- /**** fake it by doing a read data block command? ******/
- tpqputs("MTFSR not supported");
- stat = -ENXIO;
-#endif
+ if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */
+ tpqputs(TPQD_IOCTLS, "MTFSR forward space record");
+ if ((mode_access==WRITE) && status_bytes_wr)
+ return -EACCES;
+ stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
+ } else {
+ /**** fake it by doing a read data block command? ******/
+ tpqputs(TPQD_IOCTLS, "MTFSR not supported");
+ stat = -ENXIO;
+ }
return stat;
case MTBSR:
-#ifdef TP_HAVE_BSR /* This is an optional QIC-02 command */
- /* we need this for appending files with GNU tar!! */
- tpqputs("MTFSR backward space record");
- if ((mode_access==WRITE) && status_bytes_wr)
- return -EACCES;
- stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
-#else
- tpqputs("MTBSR not supported");
- stat = -ENXIO;
-#endif
+ if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */
+ /* we need this for appending files with GNU tar!! */
+ tpqputs(TPQD_IOCTLS, "MTFSR backward space record");
+ if ((mode_access==WRITE) && status_bytes_wr)
+ return -EACCES;
+ stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
+ } else {
+ tpqputs(TPQD_IOCTLS, "MTBSR not supported");
+ stat = -ENXIO;
+ }
status_eom_detected = status_eof_detected = NO;
return stat;
case MTWEOF:
- tpqputs("MTWEOF write eof mark");
+ tpqputs(TPQD_IOCTLS, "MTWEOF write eof mark");
/* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */
if (mode_access==READ)
return -EACCES;
/* not sure what to do with status_bytes when WFM should fail */
case MTREW:
- tpqputs("MTREW rewinding tape");
+ tpqputs(TPQD_IOCTLS, "MTREW rewinding tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_REWIND, TIM_R);
case MTOFFL:
- tpqputs("MTOFFL rewinding & going offline"); /*---*/
+ tpqputs(TPQD_IOCTLS, "MTOFFL rewinding & going offline"); /*---*/
/******* What exactly are we supposed to do, to take it offline????
*****/
/* Doing a drive select will clear (unlock) the current drive.
return stat;
case MTNOP:
- tpqputs("MTNOP setting status only");
+ tpqputs(TPQD_IOCTLS, "MTNOP setting status only");
/********** should do `read position' for drives that support it **********/
return (tp_sense(-1)==TE_OK)? 0 : -EIO; /**** check return codes ****/
case MTRETEN:
- tpqputs("MTRETEN retension tape");
+ tpqputs(TPQD_IOCTLS, "MTRETEN retension tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
* we shouldn't skip the FM. Tricky.
* Maybe use RD_FM_BCK, then do a SPACE_FWD?
*/
- tpqputs("MTBSFM not supported");
+ tpqputs(TPQD_IOCTLS, "MTBSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
* Maybe use RD_FM, then RD_FM_BCK, but not all
* drives will support that!
*/
- tpqputs("MTFSFM not supported");
+ tpqputs(TPQD_IOCTLS, "MTFSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
* another file to the end, such that it would append
* after the last FM on tape.
*/
- tpqputs("MTEOM search for End Of recorded Media");
+ tpqputs(TPQD_IOCTLS, "MTEOM search for End Of recorded Media");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
-#ifdef TP_HAVE_EOD
- /* Use faster seeking when possible.
- * This requires the absence of data beyond the EOM.
- */
-# if TAPE_QIC02_DRIVE == MT_ISWT5150
- /* It seems that my drive does not always perform the
- * SEEK_EOD correctly, unless it is preceded by a
- * rewind command.
- */
+ if (TP_HAVE_EOD) {
+ /* Use faster seeking when possible.
+ * This requires the absence of data beyond the EOM.
+ * It seems that my drive does not always perform the
+ * SEEK_EOD correctly, unless it is preceded by a
+ * rewind command.
+ */
# if 0
- status_eom_detected = status_eof_detected = NO;
-# endif
- stat = do_qic_cmd(QCMD_REWIND, TIM_R);
- if (stat)
- return stat;
-
+ status_eom_detected = status_eof_detected = NO;
# endif
- stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
- /* After a successful seek, TP_EOR should be returned */
-#else
- /* else just seek until the drive returns exception "No Data" */
- stat = 0;
- while ((stat==0) && (!status_eom_detected)) {
- stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
+ stat = do_qic_cmd(QCMD_REWIND, TIM_R);
+ if (stat)
+ return stat;
+ stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
+ /* After a successful seek, TP_EOR should be returned */
+ } else {
+ /* else just seek until the drive returns exception "No Data" */
+ stat = 0;
+ while ((stat==0) && (!status_eom_detected)) {
+ stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
+ }
+ if (tperror.exs & TP_NDT)
+ return 0;
}
- if (tperror.exs & TP_NDT)
- return 0;
-#endif
return stat;
case MTERASE:
- tpqputs("MTERASE -- ERASE TAPE !");
+ tpqputs(TPQD_IOCTLS, "MTERASE -- ERASE TAPE !");
if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
- tpqputs("Cartridge is write-protected.");
+ tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
return -EACCES;
} else {
time_t t = jiffies;
+
+ /* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
+ if (mode_access==READ)
+ return -EACCES;
+
/* give user a few seconds to pull out tape */
- while (jiffies - t < 3*HZ)
+ while (jiffies - t < 4*HZ)
schedule();
}
- /* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
- if (mode_access==READ)
- return -EACCES;
-
- /* don't bother writing filemark */
+ /* don't bother writing filemark first */
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_ERASE, TIM_R);
-
case MTRAS1:
-#ifdef TP_HAVE_RAS1
- tpqputs("MTRAS1: non-destructive self test");
- stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
- if (stat != 0) {
- tpqputs("RAS1 failed");
- return stat;
+ if (TP_HAVE_RAS1) {
+ tpqputs(TPQD_IOCTLS, "MTRAS1: non-destructive self test");
+ stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
+ if (stat != 0) {
+ tpqputs(TPQD_ALWAYS, "RAS1 failed");
+ return stat;
+ }
+ return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
}
- return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
-#else
- tpqputs("RAS1 not supported");
+ tpqputs(TPQD_IOCTLS, "RAS1 not supported");
return -ENXIO;
-#endif
case MTRAS2:
-#ifdef TP_HAVE_RAS2
- tpqputs("MTRAS2: destructive self test");
- stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
- if (stat != 0) {
- tpqputs("RAS2 failed");
- return stat;
+ if (TP_HAVE_RAS2) {
+ tpqputs(TPQD_IOCTLS, "MTRAS2: destructive self test");
+ stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
+ if (stat != 0) {
+ tpqputs(TPQD_ALWAYS, "RAS2 failed");
+ return stat;
+ }
+ return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
}
- return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
-#else
- tpqputs("RAS2 not supported");
+ tpqputs(TPQD_IOCTLS, "RAS2 not supported");
return -ENXIO;
-#endif
-#ifdef TP_HAVE_SEEK
case MTSEEK:
- tpqputs("MTSEEK seeking block");
- if ((mode_access==WRITE) && status_bytes_wr)
- return -EACCES;
- /* NOTE: address (24 bits) is in seek_addr_buf[] */
- return do_qic_cmd(QCMDV_SEEK_BLK, TIM_F);
-#endif
+ if (TP_HAVE_SEEK && (QIC02_TAPE_IFC==ARCHIVE)) {
+ tpqputs(TPQD_IOCTLS, "MTSEEK seeking block");
+ if ((mode_access==WRITE) && status_bytes_wr)
+ return -EACCES;
+ /* NOTE: address (24 bits) is in seek_addr_buf[] */
+ return do_qic_cmd(AR_QCMDV_SEEK_BLK, TIM_F);
+ }
+ else
+ return -ENOTTY;
default:
return -ENOTTY;
static inline void dma_transfer(void)
{
-#if TAPE_QIC02_IFC == WANGTEK
- outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
-#elif TAPE_QIC02_IFC == ARCHIVE
- outb_p(0, AR_RESET_DMA_PORT);
-#endif
+ if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */
+ else if (QIC02_TAPE_IFC == ARCHIVE)
+ outb_p(0, AR_RESET_DMA_PORT);
+ else /* QIC02_TAPE_IFC == MOUNTAIN */
+ outb_p(ctlbits, QIC02_CTL_PORT);
- clear_dma_ff(TAPE_QIC02_DMA);
- set_dma_mode(TAPE_QIC02_DMA, dma_mode);
- set_dma_addr(TAPE_QIC02_DMA, buffaddr+dma_bytes_done); /* full address */
- set_dma_count(TAPE_QIC02_DMA, TAPE_BLKSIZE);
+
+ clear_dma_ff(QIC02_TAPE_DMA);
+ set_dma_mode(QIC02_TAPE_DMA, dma_mode);
+ set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done); /* full address */
+ set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE);
/* start tape DMA controller */
-#if TAPE_QIC02_IFC == WANGTEK
- outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC_CTL_PORT); /* trigger DMA transfer */
-#elif TAPE_QIC02_IFC == ARCHIVE
- outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC_CTL_PORT); /* enable interrupts again */
- outb_p(0, AR_START_DMA_PORT); /* start DMA transfer */
- /* In dma_end() AR_RESET_DMA_PORT is written too. */
-#endif
+ if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+ outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC02_CTL_PORT); /* trigger DMA transfer */
+
+ else if (QIC02_TAPE_IFC == ARCHIVE) {
+ outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC02_CTL_PORT); /* enable interrupts again */
+ outb_p(0, AR_START_DMA_PORT); /* start DMA transfer */
+ /* In dma_end() AR_RESET_DMA_PORT is written too. */
+
+ } else /* QIC02_TAPE_IFC == MOUNTAIN */ {
+ inb(MTN_R_DESELECT_DMA_PORT);
+ outb_p(ctlbits | (MTN_CTL_EXC_IEN | MTN_CTL_DNIEN), QIC02_CTL_PORT);
+ outb_p(0, MTN_W_SELECT_DMA_PORT); /* start DMA transfer */
+ if (dma_mode == DMA_MODE_WRITE)
+ outb_p(0, MTN_W_DMA_WRITE_PORT); /* start DMA transfer */
+ }
/* start computer DMA controller */
- enable_dma(TAPE_QIC02_DMA);
+ enable_dma(QIC02_TAPE_DMA);
/* block transfer should start now, jumping to the
* interrupt routine when done or an exception was detected.
*/
/* start_dma() sets a DMA transfer up between the tape controller and
- * the kernel tape_qic02_buf buffer.
+ * the kernel qic02_tape_buf buffer.
* Normally bytes_todo==dma_bytes_done at the end of a DMA transfer. If not,
* a filemark was read, or an attempt to write beyond the End Of Tape
* was made. [Or some other bad thing happened.]
{
int stat;
- TPQPUTS("start_dma() enter");
- TPQDEB({printk(TPQIC_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
+ tpqputs(TPQD_DEBUG, "start_dma() enter");
+ TPQDEB({printk(TPQIC02_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
dma_bytes_done = 0;
dma_bytes_todo = bytes_todo;
since we're only just starting a read/write it doesn't
matter some exceptions are cleared by reading the status;
we're only interested in CNI and WRP. -Eddy */
- get_status((char *) &tperror);
+ get_status(&tperror);
#else
/* TP_CNI should now be handled in open(). -Hennus */
#endif
#if OBSOLETE
/************* not needed iff rd_status() would wait for ready!!!!!! **********/
if (wait_for_ready(TIM_S) != TE_OK) { /*** not sure this is needed ***/
- tpqputs("wait_for_ready failed in start_dma");
+ tpqputs(TPQD_ALWAYS, "wait_for_ready failed in start_dma");
return -EIO;
}
#endif
+ if (QIC02_TAPE_IFC == MOUNTAIN) {
+ /* Set control bits to select ONLINE during command */
+ ctlbits |= MTN_QIC02_CTL_ONLINE;
+ }
+
/* Tell the controller the data direction */
/* r/w, timeout medium, check exceptions, sets status_cmd_pending. */
stat = send_qic02_cmd((mode == WRITE)? QCMD_WRT_DATA : QCMD_RD_DATA, TIM_M, 0);
if (stat!=TE_OK) {
- printk(TPQIC_NAME ": start_dma: init %s failed\n",
+ printk(TPQIC02_NAME ": start_dma: init %s failed\n",
(mode == WRITE)? "write" : "read");
(void) tp_sense(0);
return stat;
doing_write = YES;
break;
default:
- printk(TPQIC_NAME ": requested unknown mode %d\n", mode);
- panic(TPQIC_NAME ": invalid mode in start_dma()");
+ printk(TPQIC02_NAME ": requested unknown mode %d\n", mode);
+ panic(TPQIC02_NAME ": invalid mode in start_dma()");
}
} else if (is_exception()) {
*
* ******** this also affects EOF/EOT handling! ************
*/
- tpqputs("detected exception in start_dma() while transfer in progress");
+ tpqputs(TPQD_ALWAYS, "detected exception in start_dma() while transfer in progress");
status_error = YES;
return TE_END;
}
TPQPUTS("end_dma() enter");
- disable_dma(TAPE_QIC02_DMA);
- clear_dma_ff(TAPE_QIC02_DMA);
+ disable_dma(QIC02_TAPE_DMA);
+ clear_dma_ff(QIC02_TAPE_DMA);
-#if TAPE_QIC02_IFC == WANGTEK
- outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
-#elif TAPE_QIC02_IFC == ARCHIVE
- outb_p(0, AR_RESET_DMA_PORT);
-#endif
+ if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */
+ else if (QIC02_TAPE_IFC == ARCHIVE)
+ outb_p(0, AR_RESET_DMA_PORT);
+ else /* QIC02_TAPE_IFC == MOUNTAIN */ {
+ /* Clear control bits, de-select ONLINE during tp_sense */
+ ctlbits &= ~MTN_QIC02_CTL_ONLINE;
+ }
stat = wait_for_ready(TIM_M);
if (status_error || (stat!=TE_OK)) {
- tpqputs("DMA transfer exception");
+ tpqputs(TPQD_DMAX, "DMA transfer exception");
stat = tp_sense((dma_mode==READ)? TP_WRP : 0);
/* no return here -- got to clean up first! */
+ } else /* if (QIC02_TAPE_IFC == MOUNTAIN) */ {
+ outb_p(ctlbits, QIC02_CTL_PORT);
}
- /* take the tape controller offline */
+
+ if (QIC02_TAPE_IFC == MOUNTAIN)
+ inb(MTN_R_DESELECT_DMA_PORT);
+
+ /* take the tape controller offline */
/* finish off DMA stuff */
/*********** Below are the (public) OS-interface procedures ***********/
-/* tape_qic02_times_out() is called when a DMA transfer doesn't complete
+/* qic02_tape_times_out() is called when a DMA transfer doesn't complete
* quickly enough. Usually this means there is something seriously wrong
* with the hardware/software, but it could just be that the controller
* has decided to do a long rewind, just when I didn't expect it.
* Just try again.
*/
-static void tape_qic02_times_out(void)
+static void qic02_tape_times_out(void)
{
- printk("time-out in %s driver\n", TPQIC_NAME);
+ printk("time-out in %s driver\n", TPQIC02_NAME);
if ((status_cmd_pending>0) || dma_mode) {
/* takes tooo long, shut it down */
status_dead = YES;
status_error = YES;
if (dma_mode) {
dma_mode = 0; /* signal end to read/write routine */
- wake_up(&tape_qic02_transfer);
+ wake_up(&qic02_tape_transfer);
}
}
-} /* tape_qic02_times_out */
+} /* qic02_tape_times_out */
/*
* Interrupt handling:
*/
-/* tape_qic02_interrupt() is called when the tape controller completes
+/* qic02_tape_interrupt() is called when the tape controller completes
* a DMA transfer.
* We are not allowed to sleep here!
*
* When we are finished, set flags to indicate end, disable timer.
* NOTE: This *must* be fast!
*/
-static void tape_qic02_interrupt(int unused)
+static void qic02_tape_interrupt(int unused)
{
int stat, r, i;
TIMEROFF;
if (status_expect_int) {
+#ifdef WANT_EXTRA_FULL_DEBUGGING
if (TP_DIAGS(current_tape_dev))
printk("@");
-
- stat = inb(QIC_STAT_PORT); /* Knock, knock */
-#if TAPE_QIC02_IFC == ARCHIVE /* "Who's there?" */
- if (((stat & (AR_STAT_DMADONE)) == 0) &&
- ((stat & (QIC_STAT_EXCEPTION)) != 0)) {
- TIMERCONT;
- return; /* "Linux with IRQ sharing" */
- }
#endif
- if ((stat & QIC_STAT_EXCEPTION) == 0) { /* exception occurred */
+ stat = inb(QIC02_STAT_PORT); /* Knock, knock */
+ if (QIC02_TAPE_IFC == ARCHIVE) { /* "Who's there?" */
+ if (((stat & (AR_STAT_DMADONE)) == 0) &&
+ ((stat & (QIC02_STAT_EXCEPTION)) != 0)) {
+ TIMERCONT;
+ return; /* "Linux with IRQ sharing" */
+ }
+ }
+
+ if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* exception occurred */
/* Possible causes for an exception during a transfer:
* - during a write-cycle: end of tape (EW) hole detected.
* - during a read-cycle: filemark or EOD detected.
* - something went wrong
* So don't continue with the next block.
*/
- tpqputs("isr: exception on tape controller");
+ tpqputs(TPQD_ALWAYS, "isr: exception on tape controller");
printk(" status %02x\n", stat);
status_error = TE_EX;
dma_mode = 0; /* wake up rw() */
status_expect_int = NO;
- wake_up(&tape_qic02_transfer);
+ wake_up(&qic02_tape_transfer);
return;
}
/* return if tape controller not ready, or
/* Skip next ready check for Archive controller because
* it may be busy reading ahead. Weird. --hhb
*/
-#if TAPE_QIC02_IFC != ARCHIVE /* I think this is a drive-dependency, not IFC -- hhb */
- if (stat & QIC_STAT_READY) { /* not ready */
- tpqputs("isr: ? Tape controller not ready");
- r = 1;
- }
-#endif
+ if (QIC02_TAPE_IFC == WANGTEK) /* I think this is a drive-dependency, not IFC -- hhb */
+ if (stat & QIC02_STAT_READY) { /* not ready */
+ tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready");
+ r = 1;
+ }
- if ( (i = get_dma_residue(TAPE_QIC02_DMA)) != 0 ) {
- printk(TPQIC_NAME ": dma_residue == %x !!!\n", i);
+ if ( (i = get_dma_residue(QIC02_TAPE_DMA)) != 0 ) {
+ printk(TPQIC02_NAME ": dma_residue == %x !!!\n", i);
r = 1; /* big trouble, but can't do much about it... */
}
dma_mode = 0;
status_expect_int = NO;
TPQPUTS("isr: dma_bytes_done");
- wake_up(&tape_qic02_transfer);
+ wake_up(&qic02_tape_transfer);
} else {
/* start next transfer, account for track-switching time */
- timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ;
+ timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ;
dma_transfer();
}
} else {
- printk(TPQIC_NAME ": Unexpected interrupt, stat == %x\n",
- inb(QIC_STAT_PORT));
+ printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n",
+ inb(QIC02_STAT_PORT));
}
-} /* tape_qic02_interrupt */
+} /* qic02_tape_interrupt */
-static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
+static int qic02_tape_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
{
return -EINVAL; /* not supported */
-} /* tape_qic02_lseek */
+} /* qic02_tape_lseek */
/* read/write routines:
* request would return the EOF flag for the previous file.
*/
-static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count)
+static int qic02_tape_read(struct inode * inode, struct file * filp, char * buf, int count)
{
int error;
dev_t dev = inode->i_rdev;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
int stat;
+ if (status_zombie==YES) {
+ tpqputs(TPQD_ALWAYS, "configs not set");
+ return -ENXIO;
+ }
+
if (TP_DIAGS(current_tape_dev))
- printk(TPQIC_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
+ printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
MINOR(dev), buf, count, filp->f_pos, flags);
if (count % TAPE_BLKSIZE) { /* Only allow mod 512 bytes at a time. */
- tpqputs("Wrong block size");
+ tpqputs(TPQD_BLKSZ, "Wrong block size");
return -EINVAL;
}
/* Make sure buffer is safe to write into. */
error = verify_area(VERIFY_WRITE, buf, count);
- if (error) {
- printk(TPQIC_NAME ": read: verify_area(WRITE, %p, %x) failed\n", buf, count);
+ if (error)
return error;
- }
/* This is rather ugly because it has to implement a finite state
* machine in order to handle the EOF situations properly.
/* Must ensure that user program sees exactly one EOF token (==0) */
if (return_read_eof==YES) {
- printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%ld\n", return_read_eof, reported_read_eof, total_bytes_done);
+ if (TPQDBG(DEBUG))
+ printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done);
if (reported_read_eof==NO) {
/* have not yet returned EOF to user program */
if (bytes_todo>0) {
/* start reading data */
if (is_exception()) /****************************************/
- tpqputs("is_exception() before start_dma()!");
+ tpqputs(TPQD_DMAX, "is_exception() before start_dma()!");
/******************************************************************
***** if start_dma() fails because the head is positioned 0 bytes
***** before the FM, (causing EXCEPTION to be set) return_read_eof should
if (stat == TE_OK) {
/* Wait for transfer to complete, interrupt should wake us */
while (dma_mode != 0) {
- sleep_on(&tape_qic02_transfer);
+ sleep_on(&qic02_tape_transfer);
}
if (status_error)
return_read_eof = YES;
}
end_dma(&bytes_done);
if (bytes_done>bytes_todo) {
- tpqputs("read: Oops, read more bytes than requested");
+ tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested");
return -EIO;
}
/* copy buffer to user-space in one go */
#if 1
/* Checks Ton's patch below */
if ((return_read_eof == NO) && (status_eof_detected == YES)) {
- printk(TPQIC_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
+ printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
}
#endif
if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
count -= bytes_done;
}
}
- tpqputs("read request for <0 bytes");
+ tpqputs(TPQD_ALWAYS, "read request for <0 bytes");
return -EINVAL;
-} /* tape_qic02_read */
+} /* qic02_tape_read */
* tape device again. The driver will detect an exception status in (No Cartridge)
* and force a rewind. After that tar may continue writing.
*/
-static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
+static int qic02_tape_write(struct inode * inode, struct file * filp, char * buf, int count)
{
int error;
dev_t dev = inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
+ if (status_zombie==YES) {
+ tpqputs(TPQD_ALWAYS, "configs not set");
+ return -ENXIO;
+ }
+
if (TP_DIAGS(current_tape_dev))
- printk(TPQIC_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
+ printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
MINOR(dev), buf, count, filp->f_pos, flags);
if (count % TAPE_BLKSIZE) { /* only allow mod 512 bytes at a time */
- tpqputs("Wrong block size");
+ tpqputs(TPQD_BLKSZ, "Wrong block size");
return -EINVAL;
}
if (mode_access==READ) {
- tpqputs("Not in write mode");
+ tpqputs(TPQD_ALWAYS, "Not in write mode");
return -EACCES;
}
* be valid.
*/
if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
- tpqputs("Cartridge is write-protected.");
+ tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
return -EACCES; /* don't even try when write protected */
}
/* Make sure buffer is safe to read from. */
error = verify_area(VERIFY_READ, buf, count);
- if (error) {
- printk(TPQIC_NAME ": write: verify_area(READ, %p, %x) failed\n", buf, count);
+ if (error)
return error;
- }
if (doing_read == YES)
terminate_read(0);
if (reported_write_eof==NO) {
if (bytes_todo>0) {
- tpqputs("partial write");
+ tpqputs(TPQD_ALWAYS, "partial write");
/* partial write signals EOF to user program */
}
reported_write_eof = YES;
***** fail and write() will return ENXIO error
*****/
if (start_dma(WRITE, bytes_todo) != TE_OK) {
- tpqputs("write: start_dma() failed");
+ tpqputs(TPQD_ALWAYS, "write: start_dma() failed");
/* should do sense() on error here */
return -ENXIO; /*********** FIXTHIS **************/
}
/* Wait for write to complete, interrupt should wake us. */
while ((status_error == 0) && (dma_mode != 0)) {
- sleep_on(&tape_qic02_transfer);
+ sleep_on(&qic02_tape_transfer);
}
end_dma(&bytes_done);
if (bytes_done>bytes_todo) {
- tpqputs("write: Oops, wrote more bytes than requested");
+ tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested");
return -EIO;
}
/* If the dma-transfer was aborted because of an exception,
*/
if (status_error) {
if (status_eom_detected == YES) {
- tpqputs("write: EW detected");
+ tpqputs(TPQD_ALWAYS, "write: EW detected");
return_write_eof = YES;
} else {
/* probably EXC_RWA */
- tpqputs("write: dma: error in writing");
+ tpqputs(TPQD_ALWAYS, "write: dma: error in writing");
return -EIO;
}
}
count -= bytes_done;
}
}
- tpqputs("write request for <0 bytes");
- printk(TPQIC_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count);
+ tpqputs(TPQD_ALWAYS, "write request for <0 bytes");
+ if (TPQDBG(DEBUG))
+ printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count);
return -EINVAL;
-} /* tape_qic02_write */
+} /* qic02_tape_write */
-/* tape_qic02_open()
+/* qic02_tape_open()
* We allow the device to be opened, even if it is marked 'dead' because
* we want to be able to reset the tape device without rebooting.
* Only one open tape file at a time, except when minor=255.
* remembered values, rewind the tape and set the required density.
* Don't rewind if the minor bits specify density 0.
*/
-static int tape_qic02_open(struct inode * inode, struct file * filp)
+static int qic02_tape_open(struct inode * inode, struct file * filp)
{
dev_t dev = inode->i_rdev;
unsigned short flags = filp->f_flags;
- unsigned short dens;
+ unsigned short dens = 0;
int s;
- status_open = NO;
if (TP_DIAGS(dev)) {
- printk("tape_qic02_open: dev=%x, flags=%x ", dev, flags);
+ printk("qic02_tape_open: dev=%x, flags=%x ", dev, flags);
}
if (MINOR(dev)==255) /* special case for resetting */
else
return -EPERM;
- if (status_open==YES) {
- return -EBUSY; /* only one at a time... */
+ if (status_dead==YES)
+ /* Allow `mt reset' ioctl() even when already open()ed. */
+ return 0;
+
+ /* Only one at a time from here on... */
+ if (filp->f_count>1) { /* filp->f_count==1 for the first open() */
+ return -EBUSY;
}
- status_open = YES;
+
+ if (status_zombie==YES)
+ /* no irq/dma/port stuff allocated yet, no reset done
+ * yet, so return until MTSETCONFIG has been done.
+ */
+ return 0;
status_bytes_rd = NO;
status_bytes_wr = NO;
mode_access = READ;
break;
case O_WRONLY: /* Fallthru... Strictly speaking this is not correct... */
- case O_RDWR: /* reads are allowed as long as nothing is written */
+ case O_RDWR: /* Reads are allowed as long as nothing is written */
mode_access = WRITE;
break;
}
+ /* This is to avoid tape-changed problems (TP_CNI exception).
+ *
+ * Since removing the cartridge will not raise an exception,
+ * we always do a tp_sense() to make sure we have the proper
+ * CNI status, the 2150L may need an additional sense.... - Eddy
+ */
+ s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
+
+ if (s == TE_OK)
+ /* Try to clear cartridge-changed status for Archive-2150L */
+ if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
+ s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
- /* This is to avoid tape-changed problems (TP_CNI exception). */
- if (is_exception()) {
- s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
- if (s != TE_OK) {
- status_open = NO;
- tpqputs("open: sense() failed after exception was detected");
- return -EIO;
- }
+ if (s != TE_OK) {
+ tpqputs(TPQD_ALWAYS, "open: sense() failed");
+ return -EIO;
}
+ /* exception bits should be up-to-date now, so check for
+ * tape presence and exit if absent.
+ * Even `mt stat' will fail without a tape.
+ */
+ if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) {
+ tpqputs(TPQD_ALWAYS, "No tape present.");
+ return -EIO;
+ }
+
+ /* At this point we can assume that a tape is present and
+ * that it will remain present until release() is called.
+ */
/* not allowed to do QCMD_DENS_* unless tape is rewound */
if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) {
* i.e. user wants to use tape in different format.
* [assuming single drive operation]
*/
- tpqputs("Density minor bits have changed. Forcing rewind.");
- need_rewind = YES;
+ if (TP_HAVE_DENS) {
+ tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind.");
+ need_rewind = YES;
+ }
} else {
/* density bits still the same, but TP_DIAGS bit
* may have changed.
current_tape_dev = dev;
}
- if (need_rewind == YES) {
+ if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/
s = do_qic_cmd(QCMD_REWIND, TIM_R);
if (s != 0) {
- tpqputs("open: rewind failed");
- status_open = NO;
+ tpqputs(TPQD_ALWAYS, "open: rewind failed");
return -EIO;
}
}
/* Note: After a reset command, the controller will rewind the tape
- * just before performing any tape movement operation!
+ * just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!!
*/
- if (status_dead) {
- tpqputs("open: tape dead, attempting reset");
+ if (status_dead==YES) {
+ tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
if (tape_reset(1)!=TE_OK) {
- status_open = NO;
return -ENXIO;
} else {
status_dead = NO;
if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
- tpqputs("open: tp_sense() failed\n");
+ tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
status_dead = YES; /* try reset next time */
- status_open = NO;
return -EIO;
}
}
current_tape_dev = dev;
need_rewind = NO;
- dens = TP_DENS(dev);
+ if (TP_HAVE_DENS)
+ dens = TP_DENS(dev);
+
if (dens < sizeof(format_names)/sizeof(char *))
- printk(TPQIC_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
+ printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
else
- tpqputs("Wait for retensioning...");
+ tpqputs(TPQD_REWIND, "Wait for retensioning...");
switch (TP_DENS(dev)) {
- case 0: /* This one's for Eddy ;-) */
+ case 0: /* Minor 0 is for drives without set-density support */
s = 0;
break;
case 1:
s = do_qic_cmd(QCMD_RETEN, TIM_R);
}
if (s != 0) {
- status_open = NO; /* fail if fault occurred */
status_dead = YES; /* force reset */
current_tape_dev = 0xff80;
return -EIO;
}
return 0;
-} /* tape_qic02_open */
+} /* qic02_tape_open */
+
-static int tape_qic02_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count)
+static int qic02_tape_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count)
{
return -ENOTDIR; /* not supported */
-} /* tape_qic02_readdir */
+} /* qic02_tape_readdir */
+
-static void tape_qic02_release(struct inode * inode, struct file * filp)
+static void qic02_tape_release(struct inode * inode, struct file * filp)
{
dev_t dev = inode->i_rdev;
if (TP_DIAGS(dev))
- printk("tape_qic02_release: dev=%x\n", dev);
+ printk("qic02_tape_release: dev=%x\n", dev);
+
+ if (status_zombie==YES) /* don't rewind in zombie mode */
+ return;
/* Terminate any pending write cycle. Terminating the read-cycle
* is delayed until it is required to do so for a new command.
terminate_write(-1);
if (status_dead==YES)
- tpqputs("release: device dead!?");
+ tpqputs(TPQD_ALWAYS, "release: device dead!?");
- if (status_open==NO) {
- tpqputs("release: device not open");
- return;
- }
/* Rewind only if minor number requires it AND
- * read/writes have been done.
+ * read/writes have been done. ************* IS THIS CORRECT??????????
*/
if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) {
- tpqputs("release: Doing rewind...");
+ tpqputs(TPQD_REWIND, "release: Doing rewind...");
(void) do_qic_cmd(QCMD_REWIND, TIM_R);
}
- status_open = NO;
return;
-} /* tape_qic02_release */
+} /* qic02_tape_release */
+
+
+#ifdef CONFIG_QIC02_DYNCONF
+/* Set masks etc. based on the interface card type. */
+int update_ifc_masks(int ifc)
+{
+ QIC02_TAPE_IFC = ifc;
+
+ if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) {
+ QIC02_STAT_PORT = QIC02_TAPE_PORT;
+ QIC02_CTL_PORT = QIC02_TAPE_PORT;
+ QIC02_CMD_PORT = QIC02_TAPE_PORT+1;
+ QIC02_DATA_PORT = QIC02_TAPE_PORT+1;
+ QIC02_STAT_READY = WT_QIC02_STAT_READY;
+ QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION;
+ QIC02_STAT_MASK = WT_QIC02_STAT_MASK;
+
+ QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK;
+ QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL;
+
+ QIC02_CTL_RESET = WT_QIC02_CTL_RESET;
+ QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST;
+
+ if (QIC02_TAPE_DMA == 3)
+ WT_CTL_DMA = WT_CTL_DMA3;
+ else if (QIC02_TAPE_DMA == 1)
+ WT_CTL_DMA = WT_CTL_DMA1;
+ else {
+ tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+ return -EIO;
+ }
+ if (QIC02_TAPE_IFC == EVEREX) {
+ /* Everex is a special case for Wangtek (actually
+ * it's the other way 'round, but I saw Wangtek first)
+ */
+ if (QIC02_TAPE_DMA==3)
+ WT_CTL_DMA = WT_CTL_DMA1;
+ /* Fixup the kernel copy of the IFC type to that
+ * we don't have to distinguish between Wangtek and
+ * and Everex at runtime.
+ */
+ QIC02_TAPE_IFC = WANGTEK;
+ }
+ } else if (QIC02_TAPE_IFC == ARCHIVE) {
+ QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+ QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+ QIC02_CMD_PORT = QIC02_TAPE_PORT;
+ QIC02_DATA_PORT = QIC02_TAPE_PORT;
+ QIC02_STAT_READY = AR_QIC02_STAT_READY;
+ QIC02_STAT_EXCEPTION = AR_QIC02_STAT_EXCEPTION;
+ QIC02_STAT_MASK = AR_QIC02_STAT_MASK;
+
+ QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK;
+ QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL;
+
+ QIC02_CTL_RESET = AR_QIC02_CTL_RESET;
+ QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST;
+
+ if (QIC02_TAPE_DMA > 3) {
+ tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+ return -EIO;
+ }
+ } else if (QIC02_TAPE_IFC == MOUNTAIN) {
+ QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+ QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+ QIC02_CMD_PORT = QIC02_TAPE_PORT;
+ QIC02_DATA_PORT = QIC02_TAPE_PORT;
+
+ QIC02_STAT_READY = MTN_QIC02_STAT_READY;
+ QIC02_STAT_EXCEPTION = MTN_QIC02_STAT_EXCEPTION;
+ QIC02_STAT_MASK = MTN_QIC02_STAT_MASK;
+
+ QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK;
+ QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL;
+
+ QIC02_CTL_RESET = MTN_QIC02_CTL_RESET;
+ QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST;
+
+ if (QIC02_TAPE_DMA > 3) {
+ tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+ return -EIO;
+ }
+ } else {
+ tpqputs(TPQD_ALWAYS, "Invalid interface type");
+ return -ENXIO;
+ }
+ return 0;
+} /* update_ifc-masks */
+#endif
/* ioctl allows user programs to rewind the tape and stuff like that */
-static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
+static int qic02_tape_ioctl(struct inode * inode, struct file * filp,
unsigned int iocmd, unsigned long ioarg)
{
int error;
int c;
struct mtop operation;
char *stp, *argp;
-
-#ifdef TP_HAVE_TELL
unsigned char blk_addr[6];
struct mtpos ioctl_tell;
-#endif
+
if (TP_DIAGS(current_tape_dev))
- printk(TPQIC_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
+ printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
if (!inode || !ioarg)
return -EINVAL;
/* check iocmd first */
if (dev_maj != QIC02_TAPE_MAJOR) {
- printk(TPQIC_NAME ": Oops! Wrong device?\n");
+ printk(TPQIC02_NAME ": Oops! Wrong device?\n");
/* A panic() would be appropriate here */
return -ENODEV;
}
c = iocmd & IOCCMD_MASK;
+
+#ifdef DDIOCSDBG
+ /* Check for DDI Debug Control, contributed by FvK, edited by HHB. */
+ if (c == DDIOCSDBG) {
+ if (!suser())
+ return -EPERM;
+ verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
+ c = get_fs_long((int *) ioarg);
+ if (c==0) {
+ QIC02_TAPE_DEBUG = 0;
+ return 0;
+ }
+ if ((c>=1) && (c<=32)) {
+ QIC02_TAPE_DEBUG |= (1 << (c-1));
+ return 0;
+ }
+ if (c >= 128) {
+ QIC02_TAPE_DEBUG &= ~(1 << (c - 128));
+ return 0;
+ }
+ return -EINVAL;
+ }
+#endif
+
+#ifdef CONFIG_QIC02_DYNCONF
+ if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) {
+ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
+ tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
+ return -EFAULT;
+ }
+
+ /* check for valid user address */
+ error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf));
+ if (error)
+ return error;
+ /* copy current settings to user space */
+ stp = (char *) &qic02_tape_dynconf;
+ argp = (char *) ioarg;
+ for (i=0; i<sizeof(qic02_tape_dynconf); i++)
+ put_fs_byte(*stp++, argp++);
+ return 0;
+
+ } else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
+ static int qic02_get_resources(void), qic02_release_resources(void);
+
+ /* One should always do a MTIOCGETCONFIG first, then update
+ * user-settings, then write back with MTIOCSETCONFIG.
+ * Re-open() the device before actual use to make sure
+ * everything is initialized.
+ */
+ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
+ tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
+ return -EFAULT;
+ }
+ if (!suser())
+ return -EPERM;
+ if ((doing_read!=NO) || (doing_write!=NO))
+ return -EBUSY;
+ error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf));
+ if (error)
+ return error;
+
+ /* copy struct from user space to kernel space */
+ stp = (char *) &qic02_tape_dynconf;
+ argp = (char *) ioarg;
+ for (i=0; i<sizeof(qic02_tape_dynconf); i++)
+ *stp++ = get_fs_byte(argp++);
+ if (status_zombie==NO)
+ qic02_release_resources(); /* and go zombie */
+ if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
+ return -EIO;
+ if (qic02_get_resources())
+ return -ENXIO;
+ return 0;
+
+ }
+ if (status_zombie==YES) {
+ tpqputs(TPQD_ALWAYS, "Configs not set");
+ return -ENXIO;
+ }
+#endif
if (c == (MTIOCTOP & IOCCMD_MASK)) {
/* Compare expected struct size and actual struct size. This
* is useful to catch programs compiled with old #includes.
*/
if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) {
- tpqputs("sizeof(struct mtop) does not match!");
+ tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!");
return -EFAULT;
}
error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation));
printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
if (operation.mt_count < 0)
- tpqputs("Warning: negative mt_count ignored");
+ tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored");
ioctl_status.mt_resid = operation.mt_count;
if (operation.mt_op == MTSEEK) {
+ if (!TP_HAVE_SEEK)
+ return -ENOTTY;
seek_addr_buf[0] = (operation.mt_count>>16)&0xff;
seek_addr_buf[1] = (operation.mt_count>>8)&0xff;
seek_addr_buf[2] = (operation.mt_count)&0xff;
if (operation.mt_count>>24)
return -EINVAL;
-
if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
return error;
ioctl_status.mt_resid = 0;
/* compare expected struct size and actual struct size */
if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) {
- tpqputs("sizeof(struct mtget) does not match!");
+ tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!");
return -EFAULT;
}
put_fs_byte(*stp++, argp++);
return 0;
-#ifdef TP_HAVE_TELL
- } else if (c == (MTIOCPOS & IOCCMD_MASK)) {
+
+ } else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) {
if (TP_DIAGS(current_tape_dev))
printk("POS ");
/* compare expected struct size and actual struct size */
if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) {
- tpqputs("sizeof(struct mtpos) does not match!");
+ tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!");
return -EFAULT;
}
if (error)
return error;
- tpqputs("MTTELL reading block address");
+ tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
if ((doing_read==YES) || (doing_write==YES))
- finish_rw(QCMDV_TELL_BLK);
+ finish_rw(AR_QCMDV_TELL_BLK);
- c = rdstatus((char *) blk_addr, sizeof(blk_addr), QCMDV_TELL_BLK);
+ c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK);
if (c!=TE_OK)
return -EIO;
for (i=0; i<sizeof(ioctl_tell); i++)
put_fs_byte(*stp++, argp++);
return 0;
-#endif
+
} else
return -ENOTTY; /* Other cmds not supported. */
-} /* tape_qic02_ioctl */
+} /* qic02_tape_ioctl */
/* These are (most) of the interface functions: */
-static struct file_operations tape_qic02_fops = {
- tape_qic02_lseek, /* not allowed */
- tape_qic02_read, /* read */
- tape_qic02_write, /* write */
- tape_qic02_readdir, /* not allowed */
+static struct file_operations qic02_tape_fops = {
+ qic02_tape_lseek, /* not allowed */
+ qic02_tape_read, /* read */
+ qic02_tape_write, /* write */
+ qic02_tape_readdir, /* not allowed */
NULL, /* select ??? */
- tape_qic02_ioctl, /* ioctl */
+ qic02_tape_ioctl, /* ioctl */
NULL, /* mmap not allowed */
- tape_qic02_open, /* open */
- tape_qic02_release, /* release */
+ qic02_tape_open, /* open */
+ qic02_tape_release, /* release */
NULL /* fsync */
};
* then dma_transfer() would have to disable interrupts explicitly.
* System load is high enough as it is :-(
*/
-static struct sigaction tape_qic02_sigaction = {
- tape_qic02_interrupt,
+static struct sigaction qic02_tape_sigaction = {
+ qic02_tape_interrupt,
0,
SA_INTERRUPT,
NULL
}
-/* init() is called from chr_dev_init() in kernel/chr_drv/mem.c */
-long tape_qic02_init(long kmem_start)
- /* Shouldn't this be a caddr_t ? */
+
+static void qic02_release_resources(void)
{
+ free_irq(QIC02_TAPE_IRQ);
+ free_dma(QIC02_TAPE_DMA);
+ status_zombie = YES;
+} /* qic02_release_resources */
+
- printk(TPQIC_NAME ": IRQ %d, DMA %d, IO %xh, IFC %s, %s, %s\n",
- TAPE_QIC02_IRQ, TAPE_QIC02_DMA, TAPE_QIC02_PORT,
-#if TAPE_QIC02_IFC == WANGTEK
- "Wangtek",
-#elif TAPE_QIC02_IFC == ARCHIVE
- "Archive",
-#else
-# error
-#endif
- rcs_revision, rcs_date);
+
+static int qic02_get_resources(void)
+{
/* First perform some checks. If one of them fails,
* the tape driver will not be registered to the system.
*/
+ if (QIC02_TAPE_IRQ>16) {
+ tpqputs(TPQD_ALWAYS, "Bogus interrupt number.");
+ return -1;
+ }
- /* Should do IRQ/DMA allocation in open(). Use free_irq() in release()
- * return -EBUSY, if allocation fails in open().
- * Make IRQ settable/readable through ioctls. DMA is trickier because
- * some bits change for different DMA numbers. Also, this would add
- * runtime overhead for having the dma channel number be a variable
- * rather than a constant. Probably need to make the DMA stuff #ifdef'd
- * to choose between hardcoded and changeable DMA channel.
- *
- * Also, Linux needs a more generic way to set DMA/IRQ and other stuff.
- * Right now, this is done differently for every device. (setserial,
- * ctrlaltdel, setdfprm, setfdthr, tunelp, ((setterm))...)
- *
- * #include <hint.h>
+ /* for DYNCONF, allocating DMA & IRQ should not be done until
+ * the config parameters have been set using MTSETCONFIG.
*/
/* get IRQ */
- if (irqaction(TAPE_QIC02_IRQ, &tape_qic02_sigaction)) {
- printk(TPQIC_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
- TAPE_QIC02_IRQ);
- return kmem_start;
+ if (irqaction(QIC02_TAPE_IRQ, &qic02_tape_sigaction)) {
+ printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
+ QIC02_TAPE_IRQ);
+ status_zombie = YES;
+ return -1;
}
/* After IRQ, allocate DMA channel */
- if (request_dma(TAPE_QIC02_DMA)) {
- printk(TPQIC_NAME ": can't allocate DMA%d for QIC-02 tape\n",
- TAPE_QIC02_DMA);
- free_irq(TAPE_QIC02_IRQ);
- return kmem_start;
+ if (request_dma(QIC02_TAPE_DMA)) {
+ printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n",
+ QIC02_TAPE_DMA);
+ free_irq(QIC02_TAPE_IRQ);
+ status_zombie = YES;
+ return -1;
+ }
+
+ printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
+ QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+ ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))?
+ QIC02_CMD_PORT : QIC02_STAT_PORT,
+ (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" :
+ ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek"));
+
+ if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
+ /* No drive detected, so vanish */
+ tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma.");
+ status_dead = YES;
+ qic02_release_resources();
+ return -1;
}
+ /* All should be ok now */
+ status_zombie = NO;
+ return 0;
+} /* qic02_get_resources */
+
+
+
+/* init() is called from chr_dev_init() in drivers/char/mem.c */
+long qic02_tape_init(long kmem_start)
+ /* Shouldn't this be a caddr_t ? */
+{
+
if (TPSTATSIZE != 6) {
- printk(TPQIC_NAME ": internal error: tpstatus struct incorrect!\n");
+ printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n");
return kmem_start;
}
if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) {
- printk(TPQIC_NAME ": internal error: DMA buffer size out of range\n");
+ printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n");
return kmem_start;
}
- printk(TPQIC_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
+ QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS;
+
+#ifndef CONFIG_QIC02_DYNCONF
+ printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n",
+ QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+# if QIC02_TAPE_IFC == WANGTEK
+ QIC02_STAT_PORT, "Wangtek",
+# elif QIC02_TAPE_IFC == ARCHIVE
+ QIC02_CMD_PORT, "Archive",
+# elif QIC02_TAPE_IFC == MOUNTAIN
+ QIC02_CMD_PORT, "Mountain",
+# else
+# error
+# endif
+ rcs_revision, rcs_date);
+ if (qic02_get_resources())
+ return kmem_start;
+#else
+ printk(TPQIC02_NAME ": Runtime config, %s, %s\n",
+ rcs_revision, rcs_date);
+
+ QIC02_TAPE_IRQ = BOGUS_IRQ; /* invalid value */
+#endif
+
+ printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
/* Setup the page-address for the dma transfer.
* This assumes a one-to-one identity mapping between
* kernel addresses and physical memory.
*/
- buffaddr = align_buffer((unsigned long) &tape_qic02_buf, TAPE_BLKSIZE);
- printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &tape_qic02_buf);
+ buffaddr = align_buffer((unsigned long) &qic02_tape_buf, TAPE_BLKSIZE);
+ printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf);
#ifndef CONFIG_MAX_16M
if (buffaddr+TPQBUF_SIZE>=0x1000000) {
- printk(TPQIC_NAME ": DMA buffer *must* be in lower 16MB\n");
+ printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n");
return kmem_start;
}
#endif
/* If we got this far, install driver functions */
- if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC_NAME, &tape_qic02_fops)) {
- printk(TPQIC_NAME ": Unable to get chrdev major %d\n",
- QIC02_TAPE_MAJOR);
+ if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) {
+ printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
+#ifndef CONFIG_QIC02_DYNCONF
+ free_irq(QIC02_TAPE_IRQ);
+ free_dma(QIC02_TAPE_DMA);
+#endif
return kmem_start;
}
/* prepare timer */
TIMEROFF;
- timer_table[TAPE_QIC02_TIMER].expires = 0;
- timer_table[TAPE_QIC02_TIMER].fn = tape_qic02_times_out;
+ timer_table[QIC02_TAPE_TIMER].expires = 0;
+ timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out;
+#ifndef CONFIG_QIC02_DYNCONF
if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
+ /* No drive detected, so vanish */
+ tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
status_dead = YES;
+ free_irq(QIC02_TAPE_IRQ);
+ free_dma(QIC02_TAPE_DMA);
+ unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+ return kmem_start;
} else {
if (is_exception()) {
- tpqputs("exception detected\n");
+ tpqputs(TPQD_ALWAYS, "exception detected\n");
(void) tp_sense(TP_WRP|TP_POR|TP_CNI);
}
}
+#endif
/* initialize generic status for ioctl requests */
- ioctl_status.mt_type = TAPE_QIC02_DRIVE; /* MT_IS* id nr */
+ ioctl_status.mt_type = QIC02_TAPE_DRIVE; /* MT_IS* id nr */
ioctl_status.mt_resid = 0; /* ---residual count */
ioctl_status.mt_gstat = 0; /* ---generic status */
ioctl_status.mt_blkno = 0; /* number of current (logical) block */
return kmem_start;
-} /* tape_qic02_init */
+} /* qic02_tape_init */
-#endif /* CONFIG_TAPE_QIC02 */
+#endif /* CONFIG_QIC02_TAPE */
extern int set_selection(const int arg, struct tty_struct *tty);
extern int paste_selection(struct tty_struct *tty);
extern int sel_loadlut(const int arg);
-extern int mouse_reporting_p(void);
+extern int mouse_reporting(void);
extern int shift_state;
#endif /* CONFIG_SELECTION */
extern int do_screendump(int arg);
put_fs_byte(shift_state,arg);
return 0;
case 7:
- return mouse_reporting_p();
+ put_fs_byte(mouse_reporting(),arg);
+ return 0;
#endif /* CONFIG_SELECTION */
default:
return -EINVAL;
int i, mask;
int timeout = jiffies+waittime;
- irq_number = 0;
- irq_bitmap = 0;
irq_handled = 0;
for (i = 0; i < 16; i++) {
if (!irqaction(i, &autoirq_sigaction))
}
/* Update our USED lists. */
irqs_used |= ~irq_handled;
+ irq_number = 0;
+ irq_bitmap = 0;
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
while (timeout > jiffies)
volatile unsigned char *mem)
{
asm("xchgb %0,%1" :
- "=r" (reg), "=m" (*(unsigned char *)mem) :
+ "=q" (reg), "=m" (*(unsigned char *)mem) :
"0" (reg), "1" (*(unsigned char *)mem));
return reg;
}
ifdef CONFIG_XIA_FS
FS_SUBDIRS := $(FS_SUBDIRS) xiafs
endif
+ifdef CONFIG_UMSDOS_FS
+FS_SUBDIRS := $(FS_SUBDIRS) umsdos
+endif
ifdef CONFIG_SYSV_FS
FS_SUBDIRS := $(FS_SUBDIRS) sysv
endif
#include <linux/ext2_fs.h>
#include <linux/xia_fs.h>
#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
#include <linux/proc_fs.h>
#include <linux/nfs_fs.h>
#include <linux/iso_fs.h>
register_filesystem(&(struct file_system_type)
{xiafs_read_super, "xiafs", 1, NULL});
#endif
+#ifdef CONFIG_UMSDOS_FS
+ register_filesystem(&(struct file_system_type)
+ {UMSDOS_read_super, "umsdos", 1, NULL});
+#endif
#ifdef CONFIG_MSDOS_FS
register_filesystem(&(struct file_system_type)
.s.o:
$(AS) -o $*.o $<
-OBJS= namei.o inode.o file.o dir.o misc.o fat.o
+OBJS= namei.o inode.o file.o dir.o misc.o fat.o mmap.o
msdos.o: $(OBJS)
$(LD) -r -o msdos.o $(OBJS)
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
+#include <linux/string.h>
static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
return -EISDIR;
}
-static int msdos_readdir(struct inode *inode,struct file *filp,
+int msdos_readdir(struct inode *inode,struct file *filp,
struct dirent *dirent,int count);
NULL /* permission */
};
-static int msdos_readdir(struct inode *inode,struct file *filp,
- struct dirent *dirent,int count)
+int msdos_readdir(
+ struct inode *inode,
+ struct file *filp,
+ struct dirent *dirent, /* dirent in user space */
+ int count)
{
int ino,i,i2,last;
char c,*walk;
/* Fake . and .. for the root directory. */
if (filp->f_pos == 2) filp->f_pos = 0;
else if (filp->f_pos < 2) {
- walk = filp->f_pos++ ? ".." : ".";
- for (i = 0; *walk; walk++)
- put_fs_byte(*walk,dirent->d_name+i++);
- put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
- put_fs_byte(0,dirent->d_name+i);
- put_fs_word(i,&dirent->d_reclen);
- return i;
- }
+ walk = filp->f_pos++ ? ".." : ".";
+ for (i = 0; *walk; walk++)
+ put_fs_byte(*walk,dirent->d_name+i++);
+ put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
+ put_fs_byte(0,dirent->d_name+i);
+ put_fs_word(i,&dirent->d_reclen);
+ return i;
+ }
}
if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT;
bh = NULL;
while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+ char bufname[13];
+ char *ptname = bufname;
for (i = last = 0; i < 8; i++) {
if (!(c = de->name[i])) break;
if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ') last = i+1;
- put_fs_byte(c,i+dirent->d_name);
+ if (c != ' '){
+ last = i+1;
+ *ptname++ = c;
+ }
}
i = last;
- put_fs_byte('.',i+dirent->d_name);
+ *ptname++ = '.';
i++;
for (i2 = 0; i2 < 3; i2++) {
if (!(c = de->ext[i2])) break;
if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ') last = i+1;
- put_fs_byte(c,i+dirent->d_name);
+ if (c != ' '){
+ last = i+1;
+ *ptname++ = c;
+ }
i++;
}
if ((i = last) != 0) {
ino = inode->i_ino;
else if (!strcmp(de->name,MSDOS_DOTDOT))
ino = msdos_parent_ino(inode,0);
+ bufname[i] = '\0';
put_fs_long(ino,&dirent->d_ino);
- put_fs_byte(0,i+dirent->d_name);
+ memcpy_tofs(dirent->d_name,bufname,i+1);
put_fs_word(i,&dirent->d_reclen);
brelse(bh);
return i;
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
+#include <linux/string.h>
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
-static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
- int count);
-static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
- int count);
-
-
static struct file_operations msdos_file_operations = {
NULL, /* lseek - default */
msdos_file_read, /* read */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- NULL, /* mmap */
+ msdos_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
NULL, /* follow_link */
msdos_bmap, /* bmap */
msdos_truncate, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ msdos_smap /* smap */
};
/* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */
NULL, /* follow_link */
NULL, /* bmap */
msdos_truncate, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ msdos_smap /* smap */
};
-static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
- int count)
+/*
+ Read a file into user space
+*/
+int msdos_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
{
char *start;
int left,offset,size,sector,cnt;
printk("msdos_file_read: inode = NULL\n");
return -EINVAL;
}
- if (!S_ISREG(inode->i_mode)) {
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
printk("msdos_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
return buf-start;
}
-
-static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
- int count)
+/*
+ Write to a file either from user space
+*/
+int msdos_file_write(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
{
int sector,offset,size,left,written;
int error,carry;
printk("msdos_file_write: inode = NULL\n");
return -EINVAL;
}
- if (!S_ISREG(inode->i_mode)) {
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
printk("msdos_file_write: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
return buf-start;
}
-
void msdos_truncate(struct inode *inode)
{
int cluster;
--- /dev/null
+/*
+ * fs/msdos/mmap.c
+ *
+ * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ * Inspired by fs/nfs/mmap.c (Jaon Tombs 15 Aug 1993)
+ *
+ * msdos mmap handling
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/msdos_fs.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long msdos_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ unsigned long page,
+ int error_code)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyong end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = msdos_file_read (inode,&filp,(char*)page
+ ,need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct msdos_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ msdos_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* share */
+ NULL, /* unmap */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int msdos_mmap(
+ struct inode * inode,
+ struct file * file,
+ unsigned long addr,
+ size_t len,
+ int prot,
+ unsigned long off)
+{
+ struct vm_area_struct * mpnt;
+
+ if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (off & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+ if (!mpnt)
+ return -ENOMEM;
+
+ unmap_page_range(addr, len);
+ mpnt->vm_task = current;
+ mpnt->vm_start = addr;
+ mpnt->vm_end = addr + len;
+ mpnt->vm_page_prot = prot;
+ mpnt->vm_share = NULL;
+ mpnt->vm_inode = inode;
+ inode->i_count++;
+ mpnt->vm_offset = off;
+ mpnt->vm_ops = &msdos_file_mmap;
+ insert_vm_struct (current,mpnt);
+ merge_segments (current->mm->mmap,NULL,NULL);
+ return 0;
+}
+
}
-int msdos_unlink(struct inode *dir,const char *name,int len)
+static int msdos_unlinkx(
+ struct inode *dir,
+ const char *name,
+ int len,
+ int nospc) /* Flag special file ? */
{
int res,ino;
struct buffer_head *bh;
res = -ENOENT;
goto unlink_done;
}
- if (!S_ISREG(inode->i_mode)) {
+ if (!S_ISREG(inode->i_mode) && nospc){
res = -EPERM;
goto unlink_done;
}
return res;
}
+int msdos_unlink(struct inode *dir,const char *name,int len)
+{
+ return msdos_unlinkx (dir,name,len,1);
+}
+/*
+ Special entry for umsdos
+*/
+int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+{
+ return msdos_unlinkx (dir,name,len,0);
+}
static int rename_same_dir(struct inode *old_dir,char *old_name,
struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
--- /dev/null
+#
+# Makefile for the umsdos unix-like filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\
+ rdir.o symlink.o #check.o
+
+umsdos.o: $(OBJS)
+ $(LD) -r -o umsdos.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a *.s
+
+dep:
+ $(CPP) -M *.c > .depend
+
+p:
+ proto *.c >/usr/include/linux/umsdos_fs.p
+
+doc:
+ nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
--- /dev/null
+Very short explanation for the impatient!!!
+
+Umsdos is a file system driver that run on top the MSDOS fs driver.
+It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
+
+Umsdos is not a file system per se, but a twist to make a boring
+one into a useful one.
+
+It gives you:
+
+ long file name
+ Permisssions and owner
+ Links
+ Special files (devices, pipe...)
+ All is need to be a linux root fs.
+
+There is plenty of documentation on it in the source. A formated document
+made from those comments is available from
+sunsite.unc.edu:/pub/Linux/ALPHA/umsdos
+
+Mostly...
+
+You mount a DOS partition like this
+
+mount -t umsdos /dev/hda3 /mnt
+ ^
+---------|
+
+All option are passed to the msdos drivers. Option like uid,gid etc are
+given to msdos.
+
+The default behavior of Umsdos is to do the same thing as the msdos driver
+mostly passing commands to it without much processing. Again, this is
+the default. After doing the mount on a DOS partition, nothing special
+happen. This is why all mount options are passed to the Msdos fs driver.
+
+Umsdos use a special DOS file --linux-.--- to store the information
+which can't be handle by the normal MsDOS file system. This is the trick.
+
+--linux-.--- is optionnal. There is one per directory.
+
+**** If --linux-.--- is missing, then Umsdos process the directory the
+ same way the msdos driver do. Short file name, no goodies, default
+ owner and permissions. So each directory may have or not this
+ --linux-.---
+
+Now, how to get those --linux-.---.
+
+\begin joke_section
+
+ Well send me a directory content
+ and I will send you one customised for you.
+ $5 per directory. Add any applicable taxes.
+\end joke_section
+
+A utility umssync creates those and maintain them. It is available
+from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz.
+A compiled version is available in umsdos-0.3a.bin.tar.gz.
+
+So in our example, after mounting mnt, we do
+
+umssync .
+
+This will promote this directory (a recursive option is available) to full
+umsdos capabilities (long name ...). A ls -l before and after won't show
+much difference however. The file which were there are still there. But now
+you can do all this:
+
+ chmod 644 *
+ chown you.your_groupe *
+ ls >THIS_IS.A.VERY.LONG.NAME
+ ln -s toto tata
+ ls -l
+
+Once a directory is promoted, all subdirectory created will inherit that
+promotion.
+
+What happen if you boot DOS and create files in those promoted directories ?
+Umsdos won't notice new files, but will signal removed file (it won't crash).
+Using umssync in /etc/rc will make sure the DOS directory is in sync with
+the --linux-.---.
+
+Hope this helps!
+
--- /dev/null
+#include <asm/system.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+
+extern unsigned long high_memory;
+
+static int check_one_table(unsigned long * page_dir)
+{
+ unsigned long pg_table = *page_dir;
+
+ if (!pg_table)
+ return 0;
+ if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * This function frees up all page tables of a process when it exits.
+ */
+void check_page_tables(void)
+{
+ unsigned long pg_dir;
+ static int err = 0;
+
+ int stack_level = (long)(&pg_dir)-current->kernel_stack_page;
+ if (stack_level < 1500) printk ("** %d ** ",stack_level);
+ pg_dir = current->tss.cr3;
+ if (mem_map[MAP_NR(pg_dir)] > 1) {
+ return;
+ }
+ if (err == 0){
+ unsigned long *page_dir = (unsigned long *) pg_dir;
+ unsigned long *base = page_dir;
+ int i;
+ for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){
+ int notok = check_one_table(page_dir);
+ if (notok){
+ err++;
+ printk ("|%d| ",page_dir-base);
+ }
+ }
+ if (err) printk ("Erreur MM %d\n",err);
+ }
+}
+
--- /dev/null
+/*
+ * linux/fs/umsdos/dir.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct inode *pseudo_root;
+/*
+ So grep * doesn't complain in the presence of directories.
+*/
+int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
+ int count)
+{
+ return -EISDIR;
+}
+/*
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ Return > 0 if success (the length of the file name).
+
+ This function is used by the normal readdir VFS entry point and by
+ some function who try to find out info on a file from a pure MSDOS
+ inode. See umsdos_locate_ancestor() below.
+*/
+static int umsdos_readdir_x(
+ struct inode *dir, /* Point to a description of the super block */
+ struct file *filp, /* Point to a directory which is read */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int dirent_in_fs, /* dirent point in user's space ? */
+ int count,
+ struct umsdos_dirent *u_entry, /* Optionnal umsdos entry */
+ int follow_hlink,
+ off_t *pt_f_pos) /* will hold the offset of the entry in EMD */
+{
+ int ret = 0;
+
+ umsdos_startlookup(dir);
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
+ && dir == pseudo_root
+ && dirent_in_fs){
+ /*
+ We don't need to simulate this pseudo directory
+ when umsdos_readdir_x is called for internal operation
+ of umsdos. This is why dirent_in_fs is tested
+ */
+ /* #Specification: pseudo root / directory /DOS
+ When umsdos operates in pseudo root mode (C:\linux is the
+ linux root), it simulate a directory /DOS which points to
+ the real root of the file system.
+ */
+ put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,"DOS",3);
+ put_fs_byte(0,dirent->d_name+3);
+ put_fs_word (3,&dirent->d_reclen);
+ if (u_entry != NULL) u_entry->flags = 0;
+ ret = 3;
+ filp->f_pos++;
+ }else if (filp->f_pos < 2
+ || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
+ /* #Specification: readdir / . and ..
+ The msdos filesystem manage the . and .. entry properly
+ so the EMD file won't hold any info about it.
+
+ In readdir, we assume that for the root directory
+ the read position will be 0 for ".", 1 for "..". For
+ a non root directory, the read position will be 0 for "."
+ and 32 for "..".
+ */
+ /*
+ This is a trick used by the msdos file system (fs/msdos/dir.c)
+ to manage . and .. for the root directory of a file system.
+ Since there is no such entry in the root, fs/msdos/dir.c
+ use the following:
+
+ if f_pos == 0, return ".".
+ if f_pos == 1, return "..".
+
+ So let msdos handle it
+
+ Since umsdos entries are much larger, we share the same f_pos.
+ if f_pos is 0 or 1 or 32, we are clearly looking at . and
+ ..
+
+ As soon as we get f_pos == 2 or f_pos == 64, then back to
+ 0, but this time we are reading the EMD file.
+
+ Well, not so true. The problem, is that UMSDOS_REC_SIZE is
+ also 64, so as soon as we read the first record in the
+ EMD, we are back at offset 64. So we set the offset
+ to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
+ .. entry from msdos.
+ */
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ if (u_entry != NULL) u_entry->flags = 0;
+ }else{
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+ if (emd_dir != NULL){
+ if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
+ PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size));
+ ret = 0;
+ while (filp->f_pos < emd_dir->i_size){
+ struct umsdos_dirent entry;
+ off_t cur_f_pos = filp->f_pos;
+ if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
+ ret = -EIO;
+ break;
+ }else if (entry.name_len != 0){
+ /* #Specification: umsdos / readdir
+ umsdos_readdir() should fill a struct dirent with
+ an inode number. The cheap way to get it is to
+ do a lookup in the MSDOS directory for each
+ entry processed by the readdir() function.
+ This is not very efficient, but very simple. The
+ other way around is to maintain a copy of the inode
+ number in the EMD file. This is a problem because
+ this has to be maintained in sync using tricks.
+ Remember that MSDOS (the OS) does not update the
+ modification time (mtime) of a directory. There is
+ no easy way to tell that a directory was modified
+ during a DOS session and synchronise the EMD file.
+
+ Suggestion welcome.
+
+ So the easy way is used!
+ */
+ struct umsdos_info info;
+ struct inode *inode;
+ int lret;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = cur_f_pos;
+ *pt_f_pos = cur_f_pos;
+ umsdos_manglename (&info);
+ lret = umsdos_real_lookup (dir,info.fake.fname
+ ,info.fake.len,&inode);
+ PRINTK (("Cherche inode de %s lret %d flags %d\n"
+ ,info.fake.fname,lret,entry.flags));
+ if (lret == 0
+ && (entry.flags & UMSDOS_HLINK)
+ && follow_hlink){
+ struct inode *rinode;
+ lret = umsdos_hlink2inode (inode,&rinode);
+ inode = rinode;
+ }
+ if (lret == 0){
+ /* #Specification: pseudo root / reading real root
+ The pseudo root (/linux) is logically
+ erased from the real root. This mean that
+ ls /DOS, won't show "linux". This avoids
+ infinite recursion /DOS/linux/DOS/linux while
+ walking the file system.
+ */
+ if (inode != pseudo_root){
+ PRINTK (("Trouve ino %d ",inode->i_ino));
+ if (dirent_in_fs){
+ put_fs_long(inode->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,entry.name
+ ,entry.name_len);
+ put_fs_byte(0,dirent->d_name+entry.name_len);
+ put_fs_word (entry.name_len
+ ,&dirent->d_reclen);
+ /* In this case, the caller only needs */
+ /* flags */
+ if (u_entry != NULL){
+ u_entry->flags = entry.flags;
+ }
+ }else{
+ dirent->d_ino = inode->i_ino;
+ memcpy (dirent->d_name,entry.name
+ ,entry.name_len);
+ dirent->d_name[entry.name_len] = '\0';
+ dirent->d_reclen = entry.name_len;
+ if (u_entry != NULL) *u_entry = entry;
+ }
+ ret = entry.name_len;
+ iput (inode);
+ break;
+ }
+ iput (inode);
+ }else{
+ /* #Specification: umsdos / readdir / not in MSDOS
+ During a readdir operation, if the file is not
+ in the MSDOS directory anymore, the entry is
+ removed from the EMD file silently.
+ */
+ ret = umsdos_writeentry (dir,emd_dir,&info,1);
+ if (ret != 0){
+ break;
+ }
+ }
+ }
+ }
+ iput(emd_dir);
+ }
+ }
+ umsdos_endlookup(dir);
+ PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret));
+ return ret;
+}
+/*
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ Return > 0 if success (the length of the file name).
+*/
+static int UMSDOS_readdir(
+ struct inode *dir, /* Point to a description of the super block */
+ struct file *filp, /* Point to a directory which is read */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int count)
+{
+ int ret = -ENOENT;
+ while (1){
+ struct umsdos_dirent entry;
+ off_t f_pos;
+ ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
+ if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+ }
+ return ret;
+}
+/*
+ Complete the inode content with info from the EMD file
+*/
+void umsdos_lookup_patch (
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry,
+ off_t emd_pos)
+{
+ /*
+ This function modify the state of a dir inode. It decides
+ if the dir is a umsdos dir or a dos dir. This is done
+ deeper in umsdos_patch_inode() called at the end of this function.
+
+ umsdos_patch_inode() may block because it is doing disk access.
+ At the same time, another process may get here to initialise
+ the same dir inode. There is 3 cases.
+
+ 1-The inode is already initialised. We do nothing.
+ 2-The inode is not initialised. We lock access and do it.
+ 3-Like 2 but another process has lock the inode, so we try
+ to lock it and right after check if initialisation is still
+ needed.
+
+
+ Thanks to the mem option of the kernel command line, it was
+ possible to consistently reproduce this problem by limiting
+ my mem to 4 meg and running X.
+ */
+ /*
+ Do this only if the inode is freshly read, because we will lose
+ the current (updated) content.
+ */
+ /*
+ A lookup of a mount point directory yield the inode into
+ the other fs, so we don't care about initialising it. iget()
+ does this automaticly.
+ */
+ if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
+ if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
+ if (!umsdos_isinit(inode)){
+ /* #Specification: umsdos / lookup / inode info
+ After successfully reading an inode from the MSDOS
+ filesystem, we use the EMD file to complete it.
+ We update the following field.
+
+ uid, gid, atime, ctime, mtime, mode.
+
+ We rely on MSDOS for mtime. If the file
+ was modified during an MSDOS session, at least
+ mtime will be meaningful. We do this only for regular
+ file.
+
+ We don't rely on MSDOS for mtime for directory because
+ the MSDOS directory date is creation time (strange
+ MSDOS behavior) which fit nowhere in the three UNIX
+ time stamp.
+ */
+ if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
+ inode->i_mode = entry->mode;
+ inode->i_rdev = entry->rdev;
+ inode->i_atime = entry->atime;
+ inode->i_ctime = entry->ctime;
+ inode->i_mtime = entry->mtime;
+ inode->i_uid = entry->uid;
+ inode->i_gid = entry->gid;
+ /* #Specification: umsdos / i_nlink
+ The nlink field of an inode is maintain by the MSDOS file system
+ for directory and by UMSDOS for other file. The logic is that
+ MSDOS is already figuring out what to do for directories and
+ does nothing for other files. For MSDOS, there are no hard link
+ so all file carry nlink==1. UMSDOS use some info in the
+ EMD file to plug the correct value.
+ */
+ if (!S_ISDIR(entry->mode)){
+ if (entry->nlink > 0){
+ inode->i_nlink = entry->nlink;
+ }else{
+ printk ("UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+ }
+ }
+ umsdos_patch_inode(inode,dir,emd_pos);
+ }
+ if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
+if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
+ }
+}
+/*
+ Locate entry of an inode in a directory.
+ Return 0 or a negative error code.
+
+ Normally, this function must succeed. It means a strange corruption
+ in the file system if not.
+*/
+int umsdos_inode2entry (
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry) /* Will hold the entry */
+{
+ int ret = -ENOENT;
+ if (inode == pseudo_root){
+ /*
+ Quick way to find the name.
+ Also umsdos_readdir_x won't show /linux anyway
+ */
+ memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
+ entry->name_len = UMSDOS_PSDROOT_LEN;
+ ret = 0;
+ }else{
+ struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
+ iput (emddir);
+ if (emddir == NULL){
+ /* This is a DOS directory */
+ struct file filp;
+ filp.f_pos = 0;
+ while (1){
+ struct dirent dirent;
+ if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
+ ,inode->i_ino);
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ memcpy (entry->name,dirent.d_name,dirent.d_reclen);
+ entry->name[dirent.d_reclen] = '\0';
+ entry->name_len = dirent.d_reclen;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode(inode);
+ break;
+ }
+ }
+ }else{
+ /* skip . and .. see umsdos_readdir_x() */
+ struct file filp;
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ while (1){
+ struct dirent dirent;
+ off_t f_pos;
+ if (umsdos_readdir_x(dir,&filp,&dirent
+ ,0,1,entry,0,&f_pos) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
+ ,inode->i_ino);
+ break;
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ umsdos_lookup_patch (dir,inode,entry,f_pos);
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+/*
+ Locate the parent of a directory and the info on that directory
+ Return 0 or a negative error code.
+*/
+static int umsdos_locate_ancestor (
+ struct inode *dir,
+ struct inode **result,
+ struct umsdos_dirent *entry)
+{
+ int ret;
+ umsdos_patch_inode (dir,NULL,0);
+ ret = umsdos_real_lookup (dir,"..",2,result);
+ PRINTK (("result %d %x ",ret,*result));
+ if (ret == 0){
+ struct inode *adir = *result;
+ ret = umsdos_inode2entry (adir,dir,entry);
+ }
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Build the path name of an inode (relative to the file system.
+ This function is need to set (pseudo) hard link.
+
+ It uses the same strategy as the standard getcwd().
+*/
+int umsdos_locate_path (
+ struct inode *inode,
+ char *path)
+{
+ int ret = 0;
+ struct inode *dir = inode;
+ char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (bpath == NULL){
+ ret = -ENOMEM;
+ }else{
+ struct umsdos_dirent entry;
+ char *ptbpath = bpath+PATH_MAX-1;
+ *ptbpath = '\0';
+ PRINTK (("locate_path mode %x ",inode->i_mode));
+ if (!S_ISDIR(inode->i_mode)){
+ ret = umsdos_get_dirowner (inode,&dir);
+ PRINTK (("locate_path ret %d ",ret));
+ if (ret == 0){
+ ret = umsdos_inode2entry (dir,inode,&entry);
+ if (ret == 0){
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ PRINTK (("ptbpath :%s: ",ptbpath));
+ }
+ }
+ }else{
+ dir->i_count++;
+ }
+ if (ret == 0){
+ while (dir != dir->i_sb->s_mounted){
+ struct inode *adir;
+ ret = umsdos_locate_ancestor (dir,&adir,&entry);
+ iput (dir);
+ dir = NULL;
+ PRINTK (("ancestor %d ",ret));
+ if (ret == 0){
+ *--ptbpath = '/';
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ dir = adir;
+ PRINTK (("ptbpath :%s: ",ptbpath));
+ }else{
+ break;
+ }
+ }
+ }
+ strcpy (path,ptbpath);
+ kfree (bpath);
+ }
+ PRINTK (("\n"));
+ iput (dir);
+ return ret;
+}
+
+/*
+ Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
+*/
+int umsdos_is_pseudodos (
+ struct inode *dir,
+ const char *name,
+ int len)
+{
+ /* #Specification: pseudo root / DOS hard coded
+ The pseudo sub-directory DOS in the pseudo root is hard coded.
+ The name is DOS. This is done this way to help standardised
+ the umsdos layout. The idea is that from now on /DOS is
+ a reserved path and nobody will think of using such a path
+ for a package.
+ */
+ return dir == pseudo_root
+ && len == 3
+ && name[0] == 'D' && name[1] == 'O' && name[2] == 'S';
+}
+/*
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+static int umsdos_lookup_x (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result, /* Will hold inode of the file, if successful */
+ int nopseudo) /* Don't care about pseudo root mode */
+{
+ int ret = -ENOENT;
+ *result = NULL;
+ umsdos_startlookup(dir);
+ if (len == 1 && name[0] == '.'){
+ *result = dir;
+ dir->i_count++;
+ ret = 0;
+ }else if (len == 2 && name[0] == '.' && name[1] == '.'){
+ if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){
+ /* #Specification: pseudo root / .. in real root
+ Whenever a lookup is those in the real root for
+ the directory .., and pseudo root is active, the
+ pseudo root is returned.
+ */
+ ret = 0;
+ *result = pseudo_root;
+ pseudo_root->i_count++;
+ }else{
+ /* #Specification: locating .. / strategy
+ We use the msdos filesystem to locate the parent directory.
+ But it is more complicated than that.
+
+ We have to step back even further to
+ get the parent of the parent, so we can get the EMD
+ of the parent of the parent. Using the EMD file, we can
+ locate all the info on the parent, such a permissions
+ and owner.
+ */
+ ret = umsdos_real_lookup (dir,"..",2,result);
+ PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result));
+ if (ret == 0
+ && *result != dir->i_sb->s_mounted
+ && *result != pseudo_root){
+ struct inode *aadir;
+ struct umsdos_dirent entry;
+ ret = umsdos_locate_ancestor (*result,&aadir,&entry);
+ iput (aadir);
+ }
+ }
+ }else if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / lookup(DOS)
+ A lookup of DOS in the pseudo root will always succeed
+ and return the inode of the real root.
+ */
+ *result = dir->i_sb->s_mounted;
+ (*result)->i_count++;
+ ret = 0;
+ }else{
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ if (ret == 0) ret = umsdos_findentry (dir,&info,0);
+ PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret
+ ,info.fake.len));
+ if (ret == 0){
+ /* #Specification: umsdos / lookup
+ A lookup for a file is done in two step. First, we locate
+ the file in the EMD file. If not present, we return
+ an error code (-ENOENT). If it is there, we repeat the
+ operation on the msdos file system. If this fails, it means
+ that the file system is not in sync with the emd file.
+ We silently remove this entry from the emd file,
+ and return ENOENT.
+ */
+ struct inode *inode;
+ ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result);
+ inode = *result;
+ if (inode == NULL){
+ printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n"
+ ,info.fake.fname);
+ umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
+ }else{
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ PRINTK (("lookup ino %d flags %d\n",inode->i_ino
+ ,info.entry.flags));
+ if (info.entry.flags & UMSDOS_HLINK){
+ ret = umsdos_hlink2inode (inode,result);
+ }
+ if (*result == pseudo_root && !nopseudo){
+ /* #Specification: pseudo root / dir lookup
+ For the same reason as readdir, a lookup in /DOS for
+ the pseudo root directory (linux) will fail.
+ */
+ /*
+ This has to be allowed for resolving hard link
+ which are recorded independantly of the pseudo-root
+ mode.
+ */
+ iput (pseudo_root);
+ *result = NULL;
+ ret = -ENOENT;
+ }
+ }
+ }
+ }
+ umsdos_endlookup(dir);
+ iput (dir);
+ return ret;
+}
+/*
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+int UMSDOS_lookup (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ return umsdos_lookup_x(dir,name,len,result,0);
+}
+/*
+ Locate the inode pointed by a (pseudo) hard link
+ Return 0 if ok, a negative error code if not.
+*/
+int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
+{
+ int ret = -EIO;
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ *result = NULL;
+ if (path == NULL){
+ ret = -ENOMEM;
+ iput (hlink);
+ }else{
+ struct file filp;
+ filp.f_pos = 0;
+ PRINTK (("hlink2inode "));
+ if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
+ ==hlink->i_size){
+ struct inode *dir;
+ char *pt = path;
+ dir = hlink->i_sb->s_mounted;
+ path[hlink->i_size] = '\0';
+ iput (hlink);
+ dir->i_count++;
+ while (1){
+ char *start = pt;
+ int len;
+ while (*pt != '\0' && *pt != '/') pt++;
+ len = (int)(pt - start);
+ if (*pt == '/') *pt++ = '\0';
+ if (dir->u.umsdos_i.i_emd_dir == 0){
+ /* This is a DOS directory */
+ ret = msdos_lookup(dir,start,len,result);
+ }else{
+ ret = umsdos_lookup_x(dir,start,len,result,1);
+ }
+ PRINTK (("h2n lookup :%s: -> %d ",start,ret));
+ if (ret == 0 && *pt != '\0'){
+ dir = *result;
+ }else{
+ break;
+ }
+ }
+ }else{
+ iput (hlink);
+ }
+ PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
+ kfree (path);
+ }
+ return ret;
+}
+
+static struct file_operations umsdos_dir_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_readdir, /* readdir */
+ NULL, /* select - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_dir_inode_operations = {
+ &umsdos_dir_operations, /* default directory file-ops */
+ UMSDOS_create, /* create */
+ UMSDOS_lookup, /* lookup */
+ UMSDOS_link, /* link */
+ UMSDOS_unlink, /* unlink */
+ UMSDOS_symlink, /* symlink */
+ UMSDOS_mkdir, /* mkdir */
+ UMSDOS_rmdir, /* rmdir */
+ UMSDOS_mknod, /* mknod */
+ UMSDOS_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/emd.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory handling functions
+ */
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+int umsdos_readdir_kmem(
+ struct inode *inode,
+ struct file *filp,
+ struct dirent *dirent,
+ int count)
+{
+ int ret;
+ set_fs (KERNEL_DS);
+ ret = msdos_readdir(inode,filp,dirent,count);
+ set_fs (USER_DS);
+ return ret;
+}
+/*
+ Read a file into kernel space memory
+*/
+int umsdos_file_read_kmem(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int ret;
+ set_fs (KERNEL_DS);
+ ret = msdos_file_read(inode,filp,buf,count);
+ set_fs (USER_DS);
+ return ret;
+}
+/*
+ Write to a file from kernel space
+*/
+int umsdos_file_write_kmem(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int ret;
+ set_fs (KERNEL_DS);
+ ret = msdos_file_write(inode,filp,buf,count);
+ set_fs (USER_DS);
+ return ret;
+}
+
+
+/*
+ Write a block of bytes into one EMD file.
+ The block of data is NOT in user space.
+
+ Return 0 if ok, a negative error code if not.
+*/
+int umsdos_emd_dir_write (
+ struct inode *emd_dir,
+ struct file *filp,
+ char *buf, /* buffer in kernel memory, not in user space */
+ int count)
+{
+ int written;
+ filp->f_flags = 0;
+ written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
+ return written != count ? -EIO : 0;
+}
+/*
+ Read a block of bytes from one EMD file.
+ The block of data is NOT in user space.
+ Retourne 0 if ok, -EIO if any error.
+*/
+int umsdos_emd_dir_read (
+ struct inode *emd_dir,
+ struct file *filp,
+ char *buf, /* buffer in kernel memory, not in user space */
+ int count)
+{
+ int ret = 0;
+ int sizeread;
+ filp->f_flags = 0;
+ sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
+ if (sizeread != count){
+ printk ("UMSDOS: problem with EMD file. Can't read\n");
+ ret = -EIO;
+ }
+ return ret;
+
+}
+/*
+ Locate the EMD file in a directory and optionnally, creates it.
+
+ Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
+*/
+struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
+{
+ struct inode *ret = NULL;
+ if (dir->u.umsdos_i.i_emd_dir != 0){
+ ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
+ PRINTK (("deja trouve %d %x [%d] "
+ ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
+ }else{
+ umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
+ PRINTK (("emd_dir_lookup "));
+ if (ret != NULL){
+ PRINTK (("Find --linux "));
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else if (creat){
+ int code;
+ PRINTK (("avant create "));
+ dir->i_count++;
+ code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
+ ,S_IFREG|0777,&ret);
+ PRINTK (("Creat EMD code %d ret %x ",code,ret));
+ if (ret != NULL){
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else{
+ printk ("UMSDOS: Can't create EMD file\n");
+ }
+ }
+ }
+ if (ret != NULL){
+ /* Disable UMSDOS_notify_change() for EMD file */
+ ret->u.umsdos_i.i_emd_owner = 0xffffffff;
+ }
+ return ret;
+}
+
+/*
+ Read an entry from the EMD file.
+ Support variable length record.
+ Return -EIO if error, 0 if ok.
+*/
+int umsdos_emd_dir_readentry (
+ struct inode *emd_dir,
+ struct file *filp,
+ struct umsdos_dirent *entry)
+{
+ int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
+ if (ret == 0){
+ /* Variable size record. Maybe, we have to read some more */
+ int recsize = umsdos_evalrecsize (entry->name_len);
+ if (recsize > UMSDOS_REC_SIZE){
+ ret = umsdos_emd_dir_read(emd_dir,filp
+ ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
+
+ }
+ }
+ return ret;
+}
+/*
+ Write an entry in the EMD file.
+ Return 0 if ok, -EIO if some error.
+*/
+int umsdos_writeentry (
+ struct inode *dir,
+ struct inode *emd_dir,
+ struct umsdos_info *info,
+ int free_entry) /* This entry is deleted, so Write all 0's */
+{
+ int ret = 0;
+ struct file filp;
+ struct umsdos_dirent *entry = &info->entry;
+ struct umsdos_dirent entry0;
+ if (free_entry){
+ /* #Specification: EMD file / empty entries
+ Unused entry in the EMD file are identify
+ by the name_len field equal to 0. However to
+ help future extension (or bug corretion :-( ),
+ empty entries are filled with 0.
+ */
+ memset (&entry0,0,sizeof(entry0));
+ entry = &entry0;
+ }else if (entry->name_len > 0){
+ memset (entry->name+entry->name_len,'\0'
+ ,sizeof(entry->name)-entry->name_len);
+ /* #Specification: EMD file / spare bytes
+ 10 bytes are unused in each record of the EMD. They
+ are set to 0 all the time. So it will be possible
+ to do new stuff and rely on the state of those
+ bytes in old EMD file around.
+ */
+ memset (entry->spare,0,sizeof(entry->spare));
+ }
+ filp.f_pos = info->f_pos;
+ ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
+ if (ret != 0){
+ printk ("UMSDOS: problem with EMD file. Can't write\n");
+ }else{
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ }
+ return ret;
+}
+
+#define CHUNK_SIZE (16*UMSDOS_REC_SIZE)
+struct find_buffer{
+ char buffer[CHUNK_SIZE];
+ int pos; /* read offset in buffer */
+ int size; /* Current size of buffer */
+ struct file filp;
+};
+
+/*
+ Fill the read buffer and take care of the byte remaining inside.
+ Unread bytes are simply move to the beginning.
+
+ Return -ENOENT if EOF, 0 if ok, a negativ error code if any problem.
+*/
+static int umsdos_fillbuf (
+ struct inode *inode,
+ struct find_buffer *buf)
+{
+ int ret = -ENOENT;
+ int mustmove = buf->size - buf->pos;
+ int mustread;
+ int remain;
+ if (mustmove > 0){
+ memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
+ }
+ buf->pos = 0;
+ mustread = CHUNK_SIZE - mustmove;
+ remain = inode->i_size - buf->filp.f_pos;
+ if (remain < mustread) mustread = remain;
+ if (mustread > 0){
+ ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
+ ,mustread);
+ if (ret == 0) buf->size = mustmove + mustread;
+ }else if (mustmove){
+ buf->size = mustmove;
+ ret = 0;
+ }
+ return ret;
+}
+
+/*
+ General search, locate a name in the EMD file or an empty slot to
+ store it. if info->entry.name_len == 0, search the first empty
+ slot (of the proper size).
+
+ Caller must do iput on *pt_emd_dir.
+
+ Return 0 if found, -ENOENT if not found, another error code if
+ other problem.
+
+ So this routine is used to either find an existing entry or to
+ create a new one, while making sure it is a new one. After you
+ get -ENOENT, you make sure the entry is stuffed correctly and
+ call umsdos_writeentry().
+
+ To delete an entry, you find it, zero out the entry (memset)
+ and call umsdos_writeentry().
+
+ All this to say that umsdos_writeentry must be call after this
+ function since it rely on the f_pos field of info.
+*/
+static int umsdos_find (
+ struct inode *dir,
+ struct umsdos_info *info, /* Hold name and name_len */
+ /* Will hold the entry found */
+ struct inode **pt_emd_dir) /* Will hold the emd_dir inode */
+ /* or NULL if not found */
+{
+ /* #Specification: EMD file structure
+ The EMD file uses a fairly simple layout. It is made of records
+ (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
+ record, multiple contiguous record are allocated.
+ */
+ int ret = -ENOENT;
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
+ if (emd_dir != NULL){
+ struct umsdos_dirent *entry = &info->entry;
+ int recsize = info->recsize;
+ struct {
+ off_t posok; /* Position available to store the entry */
+ int found; /* A valid empty position has been found */
+ off_t one; /* One empty position -> maybe <- large enough */
+ int onesize; /* size of empty region starting at one */
+ }empty;
+ /* Read several entries at a time to speed up the search */
+ struct find_buffer buf;
+ buf.pos = 0;
+ buf.size = 0;
+ buf.filp.f_pos = 0;
+ empty.found = 0;
+ empty.posok = emd_dir->i_size;
+ empty.onesize = 0;
+ while (1){
+ struct umsdos_dirent *rentry = (struct umsdos_dirent*)
+ (buf.buffer + buf.pos);
+ int file_pos = buf.filp.f_pos - buf.size + buf.pos;
+ if (buf.pos == buf.size){
+ ret = umsdos_fillbuf (emd_dir,&buf);
+ if (ret < 0){
+ /* Not found, so note where it can be added */
+ info->f_pos = empty.posok;
+ break;
+ }
+ }else if (rentry->name_len == 0){
+ /* We are looking for an empty section at least */
+ /* recsize large */
+ if (entry->name_len == 0){
+ info->f_pos = file_pos;
+ ret = 0;
+ break;
+ }else if (!empty.found){
+ if (empty.onesize == 0){
+ /* This is the first empty record of a section */
+ empty.one = file_pos;
+ }
+ /* grow the empty section */
+ empty.onesize += UMSDOS_REC_SIZE;
+ if (empty.onesize == recsize){
+ /* here is a large enough section */
+ empty.posok = empty.one;
+ empty.found = 1;
+ }
+ }
+ buf.pos += UMSDOS_REC_SIZE;
+ }else{
+ int entry_size = umsdos_evalrecsize(rentry->name_len);
+ if (buf.pos+entry_size > buf.size){
+ ret = umsdos_fillbuf (emd_dir,&buf);
+ if (ret < 0){
+ /* Not found, so note where it can be added */
+ info->f_pos = empty.posok;
+ break;
+ }
+ }else{
+ empty.onesize = 0; /* Reset the free slot search */
+ if (entry->name_len == rentry->name_len
+ && memcmp(entry->name,rentry->name,rentry->name_len)
+ ==0){
+ info->f_pos = file_pos;
+ *entry = *rentry;
+ ret = 0;
+ break;
+ }else{
+ buf.pos += entry_size;
+ }
+ }
+ }
+ }
+ umsdos_manglename(info);
+ }
+ *pt_emd_dir = emd_dir;
+ return ret;
+}
+/*
+ Add a new entry in the emd file
+ Return 0 if ok or a negative error code.
+ Return -EEXIST if the entry already exist.
+
+ Complete the information missing in info.
+*/
+int umsdos_newentry (
+ struct inode *dir,
+ struct umsdos_info *info)
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ ret = -EEXIST;
+ }else if (ret == -ENOENT){
+ ret = umsdos_writeentry(dir,emd_dir,info,0);
+ PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
+ }
+ iput (emd_dir);
+ return ret;
+}
+/*
+ Create a new hidden link.
+ Return 0 if ok, an error code if not.
+*/
+int umsdos_newhidden (
+ struct inode *dir,
+ struct umsdos_info *info)
+{
+ struct inode *emd_dir;
+ int ret;
+ umsdos_parse ("..LINK",6,info);
+ info->entry.name_len = 0;
+ ret = umsdos_find (dir,info,&emd_dir);
+ iput (emd_dir);
+ if (ret == -ENOENT || ret == 0){
+ /* #Specification: hard link / hidden name
+ When a hard link is created, the original file is renamed
+ to a hidden name. The name is "..LINKNNN" where NNN is a
+ number define from the entry offset in the EMD file.
+ */
+ info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
+ ,info->f_pos);
+ ret = 0;
+ }
+ return ret;
+}
+/*
+ Remove an entry from the emd file
+ Return 0 if ok, a negative error code otherwise.
+
+ Complete the information missing in info.
+*/
+int umsdos_delentry (
+ struct inode *dir,
+ struct umsdos_info *info,
+ int isdir)
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ if (info->entry.name_len != 0){
+ if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
+ if (S_ISDIR(info->entry.mode)){
+ ret = -EISDIR;
+ }else{
+ ret = -ENOTDIR;
+ }
+ }else{
+ ret = umsdos_writeentry(dir,emd_dir,info,1);
+ }
+ }
+ }
+ iput(emd_dir);
+ return ret;
+}
+
+
+/*
+ Verify is a EMD directory is empty.
+ Return 0 if not empty
+ 1 if empty
+ 2 if empty, no EMD file.
+*/
+int umsdos_isempty (struct inode *dir)
+{
+ int ret = 2;
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+ /* If the EMD file does not exist, it is certainly empty :-) */
+ if (emd_dir != NULL){
+ struct file filp;
+ /* Find an empty slot */
+ filp.f_pos = 0;
+ filp.f_flags = O_RDONLY;
+ ret = 1;
+ while (filp.f_pos < emd_dir->i_size){
+ struct umsdos_dirent entry;
+ if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
+ ret = 0;
+ break;
+ }else if (entry.name_len != 0){
+ ret = 0;
+ break;
+ }
+ }
+ iput (emd_dir);
+ }
+ return ret;
+}
+
+/*
+ Locate an entry in a EMD directory.
+ Return 0 if ok, errcod if not, generally -ENOENT.
+*/
+int umsdos_findentry (
+ struct inode *dir,
+ struct umsdos_info *info,
+ int expect) /* 0: anything */
+ /* 1: file */
+ /* 2: directory */
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ if (expect != 0){
+ if (S_ISDIR(info->entry.mode)){
+ if (expect != 2) ret = -EISDIR;
+ }else if (expect == 2){
+ ret = -ENOTDIR;
+ }
+ }
+ }
+ iput (emd_dir);
+ return ret;
+}
+
--- /dev/null
+/*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+
+#define PRINTK(x)
+#define Printk(x) printk x
+/*
+ Read a file into user space memory
+*/
+static int UMSDOS_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ /* We have to set the access time because msdos don't care */
+ int ret = msdos_file_read(inode,filp,buf,count);
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return ret;
+}
+/*
+ Write a file from user space memory
+*/
+static int UMSDOS_file_write(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ return msdos_file_write(inode,filp,buf,count);
+}
+/*
+ Truncate a file to 0 length.
+*/
+static void UMSDOS_truncate(struct inode *inode)
+{
+ PRINTK (("UMSDOS_truncate\n"));
+ msdos_truncate (inode);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}
+/*
+ See inode.c
+
+ Some entry point are filled dynamicly with function pointers
+ from the msdos file_operations and file_inode_operations.
+
+ The idea is to have the code as independant as possible from
+ the msdos file system.
+*/
+
+struct file_operations umsdos_file_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_file_read, /* read */
+ UMSDOS_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ msdos_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_file_inode_operations = {
+ &umsdos_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ UMSDOS_truncate,/* truncate */
+ NULL, /* permission */
+ msdos_smap /* smap */
+};
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/inode.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ */
+
+#include <stdlib.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+
+struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
+ /* directory. See UMSDOS_readdir_x() */
+
+/* #Specification: convention / PRINTK Printk and printk
+ Here is the convention for the use of printk inside fs/umsdos
+
+ printk carry important message (error or status).
+ Printk is for debugging (it is a macro defined at the beginning of
+ most source.
+ PRINTK is a nulled Printk macro.
+
+ This convention makes the source easier to read, and Printk easier
+ to shut off.
+*/
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+void UMSDOS_put_inode(struct inode *inode)
+{
+ PRINTK (("put inode %x owner %x pos %d dir %x\n",inode
+ ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
+ ,inode->u.umsdos_i.i_emd_dir));
+ msdos_put_inode(inode);
+}
+
+
+void UMSDOS_put_super(struct super_block *sb)
+{
+ msdos_put_super(sb);
+}
+
+
+void UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+{
+ msdos_statfs(sb,buf);
+}
+
+
+/*
+ Call msdos_lookup, but set back the original msdos function table.
+ Retourne 0 if ok, or a negative error code if not.
+*/
+int umsdos_real_lookup (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ int ret;
+ dir->i_count++;
+ ret = msdos_lookup (dir,name,len,result);
+ return ret;
+}
+/*
+ Complete the setup of an directory inode.
+ First, it completes the function pointers, then
+ it locates the EMD file. If the EMD is there, then plug the
+ umsdos function table. If not, use the msdos one.
+*/
+void umsdos_setup_dir_inode (struct inode *inode)
+{
+ inode->u.umsdos_i.i_emd_dir = 0;
+ {
+ struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0);
+ extern struct inode_operations umsdos_rdir_inode_operations;
+ inode->i_op = emd_dir != NULL
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ iput (emd_dir);
+ }
+}
+/*
+ Add some info into an inode so it can find its owner quickly
+*/
+void umsdos_set_dirinfo(
+ struct inode *inode,
+ struct inode *dir,
+ off_t f_pos)
+{
+ struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ iput (emd_owner);
+ inode->u.umsdos_i.pos = f_pos;
+}
+/*
+ Tells if an Umsdos inode has been "patched" once.
+ Returne != 0 if so.
+*/
+int umsdos_isinit (struct inode *inode)
+{
+#if 1
+ return inode->u.umsdos_i.i_emd_owner != 0;
+#elif 0
+ return inode->i_atime != 0;
+#else
+ return inode->i_count > 1;
+#endif
+}
+/*
+ Connect the proper tables in the inode and add some info.
+*/
+void umsdos_patch_inode (
+ struct inode *inode,
+ struct inode *dir, /* May be NULL */
+ off_t f_pos)
+{
+ /*
+ This function is called very early to setup the inode, somewhat
+ too early (called by UMSDOS_read_inode). At this point, we can't
+ do to much, such as lookup up EMD files and so on. This causes
+ confusion in the kernel. This is why some initialisation
+ will be done when dir != NULL only.
+
+ UMSDOS do run piggy back on top of msdos fs. It looks like something
+ is missing in the VFS to accomodate stacked fs. Still unclear what
+ (quite honestly).
+
+ Well, maybe one! A new entry "may_unmount" which would allow
+ the stacked fs to allocate some inode permanently and release
+ them at the end. Doing that now introduce a problem. unmount
+ always fail because some inodes are in use.
+ */
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
+ static char is_init = 0;
+ if (!is_init){
+ /*
+ I don't want to change the msdos file system code
+ so I get the adress of some subroutine dynamicly
+ once.
+ */
+ umsdos_file_inode_operations.bmap = inode->i_op->bmap;
+ inode->i_op = &umsdos_file_inode_operations;
+ is_init = 1;
+ }
+ inode->i_op = &umsdos_file_inode_operations;
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+ }
+ }else if (S_ISLNK(inode->i_mode)){
+ inode->i_op = &umsdos_symlink_inode_operations;
+ }else if (S_ISCHR(inode->i_mode)){
+ inode->i_op = &chrdev_inode_operations;
+ }else if (S_ISBLK(inode->i_mode)){
+ inode->i_op = &blkdev_inode_operations;
+ }else if (S_ISFIFO(inode->i_mode)){
+ init_fifo(inode);
+ }
+ if (dir != NULL){
+ /* #Specification: inode / umsdos info
+ The first time an inode is seen (inode->i_count == 1),
+ the inode number of the EMD file which control this inode
+ is tagged to this inode. It allows operation such
+ as notify_change to be handled.
+ */
+ /*
+ This is done last because it also control the
+ status of umsdos_isinit()
+ */
+ umsdos_set_dirinfo (inode,dir,f_pos);
+ }
+ }else if (dir != NULL){
+ /*
+ Test to see if the info is maintained.
+ This should be removed when the file system will be proven.
+ */
+ struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+ iput (emd_owner);
+ if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
+ printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
+ ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
+ }
+ }
+}
+/*
+ Get the inode of the directory which owns this inode.
+ Return 0 if ok, -EIO if error.
+*/
+int umsdos_get_dirowner(
+ struct inode *inode,
+ struct inode **result) /* Hold NULL if any error */
+ /* else, the inode of the directory */
+{
+ int ret = -EIO;
+ unsigned long ino = inode->u.umsdos_i.i_dir_owner;
+ *result = NULL;
+ if (ino == 0){
+ printk ("UMSDOS: umsdos_get_dirowner ino == 0\n");
+ }else{
+ struct inode *dir = *result = iget(inode->i_sb,ino);
+ if (dir != NULL){
+ umsdos_patch_inode (dir,NULL,0);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+/*
+ Load an inode from disk.
+*/
+void UMSDOS_read_inode(struct inode *inode)
+{
+ PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
+ msdos_read_inode(inode);
+ PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count));
+ if (S_ISDIR(inode->i_mode)
+ && (inode->u.umsdos_i.u.dir_info.creating != 0
+ || inode->u.umsdos_i.u.dir_info.looking != 0
+ || inode->u.umsdos_i.u.dir_info.p != NULL)){
+ Printk (("read inode %d %d %p\n"
+ ,inode->u.umsdos_i.u.dir_info.creating
+ ,inode->u.umsdos_i.u.dir_info.looking
+ ,inode->u.umsdos_i.u.dir_info.p));
+ }
+ /* #Specification: Inode / post initialisation
+ To completly initialise an inode, we need access to the owner
+ directory, so we can locate more info in the EMD file. This is
+ not available the first time the inode is access, we use
+ a value in the inode to tell if it has been finally initialised.
+
+ At first, we have tried testing i_count but it was causing
+ problem. It is possible that two or more process use the
+ newly accessed inode. While the first one block during
+ the initialisation (probably while reading the EMD file), the
+ others believe all is well because i_count > 1. They go banana
+ with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+ */
+ umsdos_patch_inode(inode,NULL,0);
+}
+
+/*
+ Update the disk with the inode content
+*/
+void UMSDOS_write_inode(struct inode *inode)
+{
+ PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
+ msdos_write_inode(inode);
+ UMSDOS_notify_change (NOTIFY_TIME,inode);
+}
+int UMSDOS_notify_change (int flags, struct inode *inode)
+{
+ int ret = 0;
+ if (inode->i_nlink > 0){
+ /* #Specification: notify_change / i_nlink > 0
+ notify change is only done for inode with nlink > 0. An inode
+ with nlink == 0 is no longer associated with any entry in
+ the EMD file, so there is nothing to update.
+ */
+ unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ if (inode == inode->i_sb->s_mounted){
+ /* #Specification: root inode / attributes
+ I don't know yet how this should work. Normally
+ the attributes (permissions bits, owner, times) of
+ a directory are stored in the EMD file of its parent.
+
+ One thing we could do is store the attributes of the root
+ inode in its own EMD file. A simple entry named "." could
+ be used for this special case. It would be read once
+ when the file system is mounted and update in
+ UMSDOS_notify_change() (right here).
+
+ I am not sure of the behavior of the root inode for
+ a real UNIX file system. For now, this is a nop.
+ */
+ }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
+ /* This inode is not a EMD file nor an inode used internally
+ by MSDOS, so we can update its status.
+ See emd.c
+ */
+ struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
+ PRINTK (("notify change %p ",inode));
+ if (emd_owner == NULL){
+ printk ("UMSDOS: emd_owner = NULL ???");
+ ret = -EPERM;
+ }else{
+ struct file filp;
+ struct umsdos_dirent entry;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ PRINTK (("pos = %d ",filp.f_pos));
+ /* Read only the start of the entry since we don't touch */
+ /* the name */
+ ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry
+ ,UMSDOS_REC_SIZE);
+ if (ret == 0){
+ if (flags & NOTIFY_UIDGID){
+ entry.uid = inode->i_uid;
+ entry.gid = inode->i_gid;
+ /* Remove those flags msdos don't like */
+ flags &= ~NOTIFY_UIDGID;
+ }
+ if (flags & NOTIFY_MODE){
+ entry.mode = inode->i_mode;
+ flags &= ~NOTIFY_MODE;
+ }
+ if (flags & NOTIFY_TIME){
+ entry.atime = inode->i_atime;
+ entry.mtime = inode->i_mtime;
+ entry.ctime = inode->i_ctime;
+ }
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry
+ ,UMSDOS_REC_SIZE);
+ PRINTK (("notify pos %d ret %d nlink %d "
+ ,inode->u.umsdos_i.pos
+ ,ret,entry.nlink));
+ /* #Specification: notify_change / msdos fs
+ notify_change operation are done only on the
+ EMD file. The msdos fs is not even called.
+ */
+ #if 0
+ if (ret == 0
+ && (S_ISDIR(inode->i_mode)
+ || S_ISREG(inode->i_mode))){
+ ret = msdos_notify_change(flags, inode);
+ printk ("msdos_notify %x %d",inode,ret);
+ }
+ #endif
+ }
+ iput (emd_owner);
+ }
+ PRINTK (("\n"));
+ }
+ }
+ return ret;
+}
+
+/* #Specification: function name / convention
+ A simple convention for function name has been used in
+ the UMSDOS file system. First all function use the prefix
+ umsdos_ to avoid name clash with other part of the kernel.
+
+ And standard VFS entry point use the prefix UMSDOS (upper case)
+ so it's easier to tell them apart.
+*/
+
+static struct super_operations umsdos_sops = {
+ UMSDOS_read_inode,
+ UMSDOS_notify_change,
+ UMSDOS_write_inode,
+ UMSDOS_put_inode,
+ UMSDOS_put_super,
+ NULL, /* added in 0.96c */
+ UMSDOS_statfs,
+ NULL
+};
+
+/*
+ Read the super block of an Extended MS-DOS FS.
+*/
+struct super_block *UMSDOS_read_super(
+ struct super_block *s,
+ void *data,
+ int silent)
+{
+ /* #Specification: mount / options
+ Umsdos run on top of msdos. Currently, it supports no
+ mount option, but happily pass all option received to
+ the msdos driver. I am not sure if all msdos mount option
+ make sens with Umsdos. Here are at least those who
+ are useful.
+ uid=
+ gid=
+
+ These options affect the operation of umsdos in directories
+ which do not have an EMD file. They behave like normal
+ msdos directory, with all limitation of msdos.
+ */
+ struct super_block *sb = msdos_read_super(s,data,silent);
+ printk ("UMSDOS Alpha 0.4 (compatibility level %d.%d)\n"
+ ,UMSDOS_VERSION,UMSDOS_RELEASE);
+ if (sb != NULL){
+ sb->s_op = &umsdos_sops;
+ PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
+ umsdos_setup_dir_inode (sb->s_mounted);
+ PRINTK (("End umsdos_read_super\n"));
+ if (s == super_blocks){
+ /* #Specification: pseudo root / mount
+ When a umsdos fs is mounted, a special handling is done
+ if it is the root partition. We check for the presence
+ of the file /linux/etc/init or /linux/etc/rc.
+ If one is there, we do a chroot("/linux").
+
+ We check both because (see init/main.c) the kernel
+ try to exec init at different place and if it fails
+ it tries /bin/sh /etc/rc. To be consistent with
+ init/main.c, many more test would have to be done
+ to locate init. Any complain ?
+
+ The chroot is done manually in init/main.c but the
+ info (the inode) is located at mount time and store
+ in a global variable (pseudo_root) which is used at
+ different place in the umsdos driver. There is no
+ need to store this variable elsewhere because it
+ will always be one, not one per mount.
+
+ This feature allows the installation
+ of a linux system within a DOS system in a subdirectory.
+
+ A user may install its linux stuff in c:\linux
+ avoiding any clash with existing DOS file and subdirectory.
+ When linux boots, it hides this fact, showing a normal
+ root directory with /etc /bin /tmp ...
+
+ The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
+ in the macro UMSDOS_PSDROOT_NAME.
+ */
+
+ struct inode *pseudo;
+ Printk (("Mounting root\n"));
+ if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
+ ,UMSDOS_PSDROOT_LEN,&pseudo)==0
+ && S_ISDIR(pseudo->i_mode)){
+ struct inode *etc = NULL;
+ struct inode *rc = NULL;
+ Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
+ if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
+ && S_ISDIR(etc->i_mode)){
+ struct inode *init;
+ Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
+ if ((umsdos_real_lookup (etc,"init",4,&init)==0
+ && S_ISREG(init->i_mode))
+ || (umsdos_real_lookup (etc,"rc",2,&rc)==0
+ && S_ISREG(rc->i_mode))){
+ umsdos_setup_dir_inode (pseudo);
+ Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+ pseudo_root = pseudo;
+ pseudo->i_count++;
+ pseudo = NULL;
+ }
+ iput (init);
+ iput (rc);
+ }
+ iput (etc);
+ }
+ iput (pseudo);
+ }
+ }
+ return sb;
+}
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/ioctl.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS ioctl directory handling functions
+ */
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+/*
+ Perform special function on a directory
+*/
+int UMSDOS_ioctl_dir (
+ struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data)
+{
+ int ret = -EPERM;
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+ */
+ if (current->euid == 0
+ || cmd == UMSDOS_GETVERSION){
+ struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
+ ret = -EINVAL;
+ /* #Specification: ioctl / prototypes
+ The official prototype for the umsdos ioctl on directory
+ is:
+
+ int ioctl (
+ int fd, // File handle of the directory
+ int cmd, // command
+ struct umsdos_ioctl *data)
+
+ The struct and the commands are defined in linux/umsdos_fs.h.
+
+ umsdos_progs/umsdosio.c provide an interface in C++ to all
+ these ioctl. umsdos_progs/udosctl is a small utility showing
+ all this.
+
+ These ioctl generally allow one to work on the EMD or the
+ DOS directory independantly. These are essential to implement
+ the synchroniser.
+ */
+ PRINTK (("ioctl %d ",cmd));
+ if (cmd == UMSDOS_GETVERSION){
+ /* #Specification: ioctl / UMSDOS_GETVERSION
+ The field version and release of the structure
+ umsdos_ioctl are filled with the version and release
+ number of the fs code in the kernel. This will allow
+ some form of checking. Users won't be able to run
+ incompatible utility such as the synchroniser (umssync).
+ umsdos_progs/umsdosio.c enforce this checking.
+
+ Return always 0.
+ */
+ put_fs_byte (UMSDOS_VERSION,&idata->version);
+ put_fs_byte (UMSDOS_RELEASE,&idata->release);
+ ret = 0;
+ }else if (cmd == UMSDOS_READDIR_DOS){
+ /* #Specification: ioctl / UMSDOS_READDIR_DOS
+ One entry is read from the DOS directory at the current
+ file position. The entry is put as is in the dos_dirent
+ field of struct umsdos_ioctl.
+
+ Return > 0 if success.
+ */
+ ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+ }else if (cmd == UMSDOS_READDIR_EMD){
+ /* #Specification: ioctl / UMSDOS_READDIR_EMD
+ One entry is read from the EMD at the current
+ file position. The entry is put as is in the umsdos_dirent
+ field of struct umsdos_ioctl. The corresponding mangled
+ DOS entry name is put in the dos_dirent field.
+
+ All entries are read including hidden links. Blank
+ entries are skipped.
+
+ Return > 0 if success.
+ */
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
+ if (emd_dir != NULL){
+ while (1){
+ if (filp->f_pos >= emd_dir->i_size){
+ ret = 0;
+ break;
+ }else{
+ struct umsdos_dirent entry;
+ off_t f_pos = filp->f_pos;
+ ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
+ if (ret < 0){
+ break;
+ }else if (entry.name_len > 0){
+ struct umsdos_info info;
+ ret = entry.name_len;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = f_pos;
+ umsdos_manglename(&info);
+ memcpy_tofs(&idata->umsdos_dirent,&entry
+ ,sizeof(entry));
+ memcpy_tofs(&idata->dos_dirent.d_name
+ ,info.fake.fname,info.fake.len+1);
+ break;
+ }
+ }
+ }
+ iput (emd_dir);
+ }else{
+ /* The absence of the EMD is simply seen as an EOF */
+ ret = 0;
+ }
+ }else if (cmd == UMSDOS_INIT_EMD){
+ /* #Specification: ioctl / UMSDOS_INIT_EMD
+ The UMSDOS_INIT_EMD command make sure the EMD
+ exist for a directory. If it does not, it is
+ created. Also, it makes sure the directory functions
+ table (struct inode_operations) is set to the UMSDOS
+ semantic. This mean that umssync may be applied to
+ an "opened" msdos directory, and it will change behavior
+ on the fly.
+
+ Return 0 if success.
+ */
+ extern struct inode_operations umsdos_rdir_inode_operations;
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
+ ret = emd_dir != NULL;
+ iput (emd_dir);
+
+ dir->i_op = ret
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ }else{
+ struct umsdos_ioctl data;
+ memcpy_fromfs (&data,idata,sizeof(data));
+ if (cmd == UMSDOS_CREAT_EMD){
+ /* #Specification: ioctl / UMSDOS_CREAT_EMD
+ The umsdos_dirent field of the struct umsdos_ioctl is used
+ as is to create a new entry in the EMD of the directory.
+ The DOS directory is not modified.
+ No validation is done (yet).
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_newentry (dir,&info);
+ }else if (cmd == UMSDOS_UNLINK_EMD){
+ /* #Specification: ioctl / UMSDOS_UNLINK_EMD
+ The umsdos_dirent field of the struct umsdos_ioctl is used
+ as is to remove an entry from the EMD of the directory.
+ No validation is done (yet). The mode field is used
+ to validate S_ISDIR or S_ISREG.
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_delentry (dir,&info
+ ,S_ISDIR(data.umsdos_dirent.mode));
+ }else if (cmd == UMSDOS_UNLINK_DOS){
+ /* #Specification: ioctl / UMSDOS_UNLINK_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ ret = msdos_unlink (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen);
+ }else if (cmd == UMSDOS_RMDIR_DOS){
+ /* #Specification: ioctl / UMSDOS_RMDIR_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ ret = msdos_rmdir (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen);
+ }else if (cmd == UMSDOS_STAT_DOS){
+ /* #Specification: ioctl / UMSDOS_STAT_DOS
+ The dos_dirent field of the struct umsdos_ioctl is
+ used to execute a stat operation in the DOS directory.
+ The d_name and d_reclen fields are used.
+
+ The following field of umsdos_ioctl.stat are filled.
+
+ st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
+ Return 0 if success.
+ */
+ struct inode *inode;
+ ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen,&inode);
+ if (ret == 0){
+ data.stat.st_ino = inode->i_ino;
+ data.stat.st_mode = inode->i_mode;
+ data.stat.st_size = inode->i_size;
+ data.stat.st_atime = inode->i_atime;
+ data.stat.st_ctime = inode->i_ctime;
+ data.stat.st_mtime = inode->i_mtime;
+ memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
+ iput (inode);
+ }
+ }else if (cmd == UMSDOS_DOS_SETUP){
+ /* #Specification: ioctl / UMSDOS_DOS_SETUP
+ The UMSDOS_DOS_SETUP ioctl allow changing the
+ default permission of the MsDOS file system driver
+ on the fly. The MsDOS driver apply global permission
+ to every file and directory. Normally these permissions
+ are controlled by a mount option. This is not
+ available for root partition, so a special utility
+ (umssetup) is provided to do this, normally in
+ /etc/rc.local.
+
+ Be aware that this apply ONLY to MsDOS directory
+ (those without EMD --linux-.---). Umsdos directory
+ have independant (standard) permission for each
+ and every file.
+
+ The field umsdos_dirent provide the information needed.
+ umsdos_dirent.uid and gid sets the owner and group.
+ umsdos_dirent.mode set the permissions flags.
+ */
+ dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
+ ret = 0;
+ }
+ }
+ }
+ PRINTK (("ioctl return %d\n",ret));
+ return ret;
+}
+
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/mangle.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Control the mangling of file name to fit msdos name space.
+ * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID)
+*/
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/umsdos_fs.h>
+
+/*
+ Complete the mangling of the MSDOS fake name
+ based on the position of the entry in the EMD file.
+
+ Simply complete the job of umsdos_parse; fill the extension.
+
+ Beware that info->f_pos must be set.
+*/
+void umsdos_manglename (struct umsdos_info *info)
+{
+ if (info->msdos_reject){
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Each non MSDOS conforming file has a special extension
+ build from the entry position in the EMD file.
+
+ This number is then transform in a base 32 number, where
+ each digit is expressed like hexadecimal number, using
+ digit and letter, except it uses 22 letters from 'a' to 'v'.
+ The number 32 comes from 2**5. It is faster to split a binary
+ number using a base which is a power of two. And I was 32
+ when I started this project. Pick your answer :-) .
+
+ If the result is '0', it is replace with '_', simply
+ to make it odd.
+
+ This is true for the first two character of the extension.
+ The last one is taken from a list of odd character, which
+ are:
+
+ { } ( ) ! ` ^ & @
+
+ With this scheme, we can produce 9216 ( 9* 32 * 32)
+ different extensions which should not clash with any useful
+ extension already popular or meaningful. Since most directory
+ have much less than 32 * 32 files in it, the first character
+ of the extension of any mangle name will be {.
+
+ Here are the reason to do this (this kind of mangling).
+
+ -The mangling is deterministic. Just by the extension, we
+ are able to locate the entry in the EMD file.
+
+ -By keeping to beginning of the file name almost unchange,
+ we are helping the MSDOS user.
+
+ -The mangling produces names not too ugly, so an msdos user
+ may live with it (remember it, type it, etc...).
+
+ -The mangling produces names ugly enough so no one will
+ ever think of using such a name in real life. This is not
+ fool proof. I don't think there is a total solution to this.
+ */
+ union {
+ int entry_num;
+ struct {
+ unsigned num1:5,num2:5,num3:5;
+ }num;
+ } u;
+ char *pt = info->fake.fname + info->fake.len;
+ /* lookup for encoding the last character of the extension */
+ /* It contain valid character after the ugly one to make sure */
+ /* even if someone overflow the 32 * 32 * 9 limit, it still do */
+ /* something */
+ #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
+ static char lookup3[]={
+ SPECIAL_MANGLING,
+ /* This is the start of lookup12 */
+ '_','1','2','3','4','5','6','7','8','9',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+ 'p','q','r','s','t','u','v'
+ };
+ #define lookup12 (lookup3+9)
+ u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
+ if (u.entry_num > (9* 32 * 32)){
+ printk ("UMSDOS: More than 9216 file in a directory.\n"
+ "This may break the mangling strategy.\n"
+ "Not a killer problem. See doc.\n");
+ }
+ *pt++ = '.';
+ *pt++ = lookup3 [u.num.num3];
+ *pt++ = lookup12[u.num.num2];
+ *pt++ = lookup12[u.num.num1];
+ *pt = '\0'; /* help doing printk */
+ info->fake.len += 4;
+ info->msdos_reject = 0; /* Avoid mangling twice */
+ }
+}
+
+/*
+ Evaluate the record size needed to store of name of len character.
+ The value returned is a multiple of UMSDOS_REC_SIZE.
+*/
+int umsdos_evalrecsize (int len)
+{
+ struct umsdos_dirent dirent;
+ int nbrec = 1+((len-1+(dirent.name-(char*)&dirent))
+ / UMSDOS_REC_SIZE);
+ return nbrec * UMSDOS_REC_SIZE;
+ /*
+ GLU This should be inlined or something to speed it up to the max.
+ GLU nbrec is absolutely not needed to return the value.
+ */
+}
+#ifdef TEST
+int umsdos_evalrecsize_old (int len)
+{
+ struct umsdos_dirent dirent;
+ int size = len + (dirent.name-(char*)&dirent);
+ int nbrec = size / UMSDOS_REC_SIZE;
+ int extra = size % UMSDOS_REC_SIZE;
+ if (extra > 0) nbrec++;
+ return nbrec * UMSDOS_REC_SIZE;
+}
+#endif
+/*
+ Fill the struct info with the full and msdos name of a file
+ Return 0 if all is ok, a negative error code otherwise.
+*/
+int umsdos_parse (
+ const char *fname,
+ int len,
+ struct umsdos_info *info)
+{
+ int ret = -ENAMETOOLONG;
+ /* #Specification: file name / too long
+ If a file name exceed UMSDOS maxima, the file name is silently
+ truncated. This makes it conformant with the other file system
+ of Linux (minix and ext2 at least).
+ */
+ if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME;
+ {
+ const char *firstpt=NULL; /* First place we saw a . in fname */
+ /* #Specification: file name / non MSDOS conforming / base length 0
+ file name beginning with a period '.' are invalid for MsDOS.
+ It needs absolutly a base name. So the file name is mangled
+ */
+ int ivldchar = fname[0] == '.';/* At least one invalid character */
+ int msdos_len = len;
+ int base_len;
+ /*
+ cardinal_per_size tells if there exist at least one
+ DOS pseudo devices on length n. See the test below.
+ */
+ static const char cardinal_per_size[9]={
+ 0, 0, 0, 1, 1, 0, 1, 0, 1
+ };
+ /*
+ lkp translate all character to acceptable character (for DOS).
+ When lkp[n] == n, it means also it is an acceptable one.
+ So it serve both as a flag and as a translator.
+ */
+ static char lkp[256];
+ static char is_init=0;
+ if (!is_init){
+ /*
+ Initialisation of the array is easier and less error prone
+ like this.
+ */
+ int i;
+ static char *spc = "\"*+,/:;<=>?[\\]|~";
+ is_init = 1;
+ for (i=0; i<=32; i++) lkp[i] = '#';
+ for (i=33; i<'A'; i++) lkp[i] = (char)i;
+ for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A'));
+ for (i='Z'+1; i<127; i++) lkp[i] = (char)i;
+ for (i=128; i<256; i++) lkp[i] = '#';
+
+ lkp['.'] = '_';
+ while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#';
+ }
+ /* GLU
+ file name wich are longer than 8+'.'+3 are invalid for MsDOS.
+ So the file name is to be mangled no more test needed.
+ This Speed Up for long and very long name.
+ The position of the last point is no more necessary anyway.
+ */
+ if (len<=(8+1+3)){
+ const char *pt = fname;
+ const char *endpt = fname + len;
+ while (pt < endpt){
+ if (*pt == '.'){
+ if (firstpt != NULL){
+ /* 2 . in a file name. Reject */
+ ivldchar = 1;
+ break;
+ }else{
+ int extlen = (int)(endpt - pt);
+ firstpt = pt;
+ if (firstpt - fname > 8){
+ /* base name longer than 8: reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen > 4){
+ /* Extension longer than 4 (including .): reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 1){
+ /* #Specification: file name / non MSDOS conforming / last char == .
+ If the last character of a file name is
+ a period, mangling is applied. MsDOS do
+ not support those file name.
+ */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 4){
+ /* #Specification: file name / non MSDOS conforming / mangling clash
+ To avoid clash with the umsdos mangling, any file
+ with a special character as the first character
+ of the extension will be mangled. This solve the
+ following problem:
+
+ touch FILE
+ # FILE is invalid for DOS, so mangling is applied
+ # file.{_1 is created in the DOS directory
+ touch file.{_1
+ # To UMSDOS file point to a single DOS entry.
+ # So file.{_1 has to be mangled.
+ */
+ static char special[]={
+ SPECIAL_MANGLING,'\0'
+ };
+ if (strchr(special,firstpt[1])!= NULL){
+ ivldchar = 1;
+ break;
+ }
+ }
+ }
+ }else if (lkp[(unsigned char)(*pt)] != *pt){
+ ivldchar = 1;
+ break;
+ }
+ pt++;
+ }
+ }else{
+ ivldchar = 1;
+ }
+ if (ivldchar
+ || (firstpt == NULL && len > 8)
+ || (len == UMSDOS_EMD_NAMELEN
+ && memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){
+ /* #Specification: file name / --linux-.---
+ The name of the EMD file --linux-.--- is map to a mangled
+ name. So UMSDOS does not restrict its use.
+ */
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Non MSDOS conforming file name must use some alias to fit
+ in the MSDOS name space.
+
+ The strategy is simple. The name is simply truncated to
+ 8 char. points are replace with underscore and a
+ number is given as an extension. This number correspond
+ to the entry number in the EMD file. The EMD file
+ only need to carry the real name.
+
+ Upper case is also convert to lower case.
+ Control character are converted to #.
+ Space are converted to #.
+ The following character are also converted to #.
+ " * + , / : ; < = > ? [ \ ] | ~
+
+ Sometime, the problem is not in MsDOS itself but in
+ command.com.
+ */
+ int i;
+ char *pt = info->fake.fname;
+ base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len;
+ /*
+ There is no '.' any more so we know for a fact that
+ the base lenght is the lenght.
+ */
+ memcpy (info->fake.fname,fname,msdos_len);
+ for (i=0; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
+ *pt = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ info->msdos_reject = 1;
+ /*
+ The numeric extension is added only when we know
+ the position in the EMD file, in umsdos_newentry(),
+ umsdos_delentry(), and umsdos_findentry().
+ See umsdos_manglename().
+ */
+ }else{
+ /* Conforming MSDOS file name */
+ strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */
+ info->msdos_reject = 0;
+ base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
+ }
+ if (cardinal_per_size[base_len]){
+ /* #Specification: file name / MSDOS devices / mangling
+ To avoid unreachable file from MsDOS, any MsDOS conforming
+ file with a basename equal to one of the MsDOS pseudo
+ devices will be mangled.
+
+ If a file such as "prn" was created, it would be unreachable
+ under MsDOS because prn is assumed to be the printer, even
+ if the file does have an extension.
+
+ Since the extension is unimportant to MsDOS, we must patch
+ the basename also. We simply insert a minus '-'. To avoid
+ conflict with valid file with a minus in front (such as
+ "-prn"), we add an mangled extension like any other
+ mangled file name.
+
+ Here is the list of DOS pseudo devices:
+
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$"
+
+ and some standard ones for common DOS programs
+
+ "emmxxxx0","xmsxxxx0","setverxx"
+
+ (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
+ for pointing these to me).
+
+ Is there one missing ?
+ */
+ /* This table must be ordered by length */
+ static const char *tbdev[]={
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$",
+ "emmxxxx0","xmsxxxx0","setverxx"
+ };
+ /* Tell where to find in tbdev[], the first name of */
+ /* a certain length */
+ static const char start_ind_dev[9]={
+ 0, 0, 0, 4, 12, 12, 13, 13, 16
+ };
+ char basen[9];
+ int i;
+ for (i=start_ind_dev[base_len-1]; i<start_ind_dev[base_len]; i++){
+ if (memcmp(info->fake.fname,tbdev[i],base_len)==0){
+ memcpy (basen,info->fake.fname,base_len);
+ basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ /*
+ GLU On ne fait cela que si necessaire, on essaye d'etre le
+ GLU simple dans le cas general (le plus frequent).
+ */
+ info->fake.fname[0] = '-';
+ strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */
+ msdos_len = (base_len==8) ? 8 : base_len + 1;
+ info->msdos_reject = 1;
+ break;
+ }
+ }
+ }
+ info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
+ /* GLU Ce zero devrais deja y etre ! (invariant ?) */
+ info->fake.len = msdos_len;
+ /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
+ memcpy (info->entry.name,fname,len);
+ info->entry.name_len = len;
+ ret = 0;
+ }
+ /*
+ Evaluate how many record are needed to store this entry.
+ */
+ info->recsize = umsdos_evalrecsize (len);
+ return ret;
+}
+
+#ifdef TEST
+
+struct MANG_TEST{
+ char *fname; /* Name to validate */
+ int msdos_reject; /* Expected msdos_reject flag */
+ char *msname; /* Expected msdos name */
+};
+
+struct MANG_TEST tb[]={
+ "hello", 0, "hello",
+ "hello.1", 0, "hello.1",
+ "hello.1_", 0, "hello.1_",
+ "prm", 0, "prm",
+
+#ifdef PROPOSITION
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello.1",
+ "Hello.c", 1, "hello.c",
+#elseif
+/*
+ Je trouve les trois exemples ci-dessous tres "malheureux".
+ Je propose de mettre en minuscule dans un passe preliminaire,
+ et de tester apres si il y a d'autres caracters "mechants".
+ Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
+ modifiable que ca. Mais c'est pour le principe.
+ Evidemment cela augmente les chances de "Colision",
+ par exemple: entre "HELLO" et "Hello", mais ces problemes
+ peuvent etre traiter ailleur avec les autres colisions.
+*/
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello_1",
+ "Hello.c", 1, "hello_c",
+#endif
+
+ "hello.{_1", 1, "hello_{_",
+ "hello\t", 1, "hello#",
+ "hello.1.1", 1, "hello_1_",
+ "hel,lo", 1, "hel#lo",
+ "Salut.Tu.vas.bien?", 1, "salut_tu",
+ ".profile", 1, "_profile",
+ ".xv", 1, "_xv",
+ "toto.", 1, "toto_",
+ "clock$.x", 1, "-clock$",
+ "emmxxxx0", 1, "-emmxxxx",
+ "emmxxxx0.abcd", 1, "-emmxxxx",
+ "aux", 1, "-aux",
+ "prn", 1, "-prn",
+ "prn.abc", 1, "-prn",
+ "PRN", 1, "-prn",
+/*
+GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version
+GLU du mangle par rapport au mangle originale.
+GLU CAUSE: La maniere de calculer la variable baselen.
+GLU Pour toi c'est toujours 3
+GLU Pour moi c'est respectivement 7, 8 et 8
+*/
+ "PRN.abc", 1, "prn_abc",
+ "Prn.abcd", 1, "prn_abcd",
+ "prn.abcd", 1, "prn_abcd",
+ "Prn.abcdefghij", 1, "prn_abcd"
+};
+
+int main (int argc, char *argv[])
+{
+ int i,rold,rnew;
+ printf ("Testing the umsdos_parse.\n");
+ for (i=0; i<sizeof(tb)/sizeof(tb[0]); i++){
+ struct MANG_TEST *pttb = tb+i;
+ struct umsdos_info info;
+ int ok = umsdos_parse (pttb->fname,strlen(pttb->fname),&info);
+ if (strcmp(info.fake.fname,pttb->msname)!=0){
+ printf ("**** %s -> ",pttb->fname);
+ printf ("%s <> %s\n",info.fake.fname,pttb->msname);
+ }else if (info.msdos_reject != pttb->msdos_reject){
+ printf ("**** %s -> %s ",pttb->fname,pttb->msname);
+ printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject);
+ }else{
+ printf (" %s -> %s %d\n",pttb->fname,pttb->msname
+ ,pttb->msdos_reject);
+ }
+ }
+ printf ("Testing the new umsdos_evalrecsize.");
+ for (i=0; i<UMSDOS_MAXNAME ; i++){
+ rnew=umsdos_evalrecsize (i);
+ rold=umsdos_evalrecsize_old (i);
+ if (!(i%UMSDOS_REC_SIZE)){
+ printf ("\n%d:\t",i);
+ }
+ if (rnew!=rold){
+ printf ("**** %d newres: %d != %d \n", i, rnew, rold);
+ }else{
+ printf(".");
+ }
+ }
+ printf ("\nEnd of Testing.\n");
+
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ * linux/fs/umsdos/namei.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ * Maintain and access the --linux alternate directory file.
+*/
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+#if 1
+/*
+ Wait for creation exclusivity.
+ Return 0 if the dir was already available.
+ Return 1 if a wait was necessary.
+ When 1 is return, it means a wait was done. It does not
+ mean the directory is available.
+*/
+static int umsdos_waitcreate(struct inode *dir)
+{
+ int ret = 0;
+ if (dir->u.umsdos_i.u.dir_info.creating
+ && dir->u.umsdos_i.u.dir_info.pid != current->pid){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ ret = 1;
+ }
+ return ret;
+}
+/*
+ Wait for any lookup process to finish
+*/
+static void umsdos_waitlookup (struct inode *dir)
+{
+ while (dir->u.umsdos_i.u.dir_info.looking){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ }
+}
+/*
+ Lock all other process out of this directory.
+*/
+void umsdos_lockcreate (struct inode *dir)
+{
+ /* #Specification: file creation / not atomic
+ File creation is a two step process. First we create (allocate)
+ an entry in the EMD file and then (using the entry offset) we
+ build a unique name for MSDOS. We create this name in the msdos
+ space.
+
+ We have to use semaphore (sleep_on/wake_up) to prevent lookup
+ into a directory when we create a file or directory and to
+ prevent creation while a lookup is going on. Since many lookup
+ may happen at the same time, the semaphore is a counter.
+
+ Only one creation is allowed at the same time. This protection
+ may not be necessary. The problem arise mainly when a lookup
+ or a readdir is done while a file is partially created. The
+ lookup process see that as a "normal" problem and silently
+ erase the file from the EMD file. Normal because a file
+ may be erased during a MSDOS session, but not removed from
+ the EMD file.
+
+ The locking is done on a directory per directory basis. Each
+ directory inode has its wait_queue.
+
+ For some operation like hard link, things even get worse. Many
+ creation must occur at once (atomic). To simplify the design
+ a process is allowed to recursivly lock the directory for
+ creation. The pid of the locking process is kept along with
+ a counter so a second level of locking is granted or not.
+ */
+ /*
+ Wait for any creation process to finish except
+ if we (the process) own the lock
+ */
+ while (umsdos_waitcreate(dir)!=0);
+ dir->u.umsdos_i.u.dir_info.creating++;
+ dir->u.umsdos_i.u.dir_info.pid = current->pid;
+ umsdos_waitlookup (dir);
+}
+/*
+ Lock all other process out of those two directories.
+*/
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
+{
+ /*
+ We must check that both directory are available before
+ locking anyone of them. This is to avoid some deadlock.
+ Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
+ this to me.
+ */
+ while (1){
+ if (umsdos_waitcreate(dir1)==0
+ && umsdos_waitcreate(dir2)==0){
+ /* We own both now */
+ dir1->u.umsdos_i.u.dir_info.creating++;
+ dir1->u.umsdos_i.u.dir_info.pid = current->pid;
+ dir2->u.umsdos_i.u.dir_info.creating++;
+ dir2->u.umsdos_i.u.dir_info.pid = current->pid;
+ break;
+ }
+ }
+ umsdos_waitlookup(dir1);
+ umsdos_waitlookup(dir2);
+}
+/*
+ Wait until creation is finish in this directory.
+*/
+void umsdos_startlookup (struct inode *dir)
+{
+ while (umsdos_waitcreate (dir) != 0);
+ dir->u.umsdos_i.u.dir_info.looking++;
+}
+void check_page_tables(void);
+
+/*
+ Unlock the directory.
+*/
+void umsdos_unlockcreate (struct inode *dir)
+{
+ dir->u.umsdos_i.u.dir_info.creating--;
+ if (dir->u.umsdos_i.u.dir_info.creating < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.creating);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+/*
+ Tell directory lookup is over.
+*/
+void umsdos_endlookup (struct inode *dir)
+{
+ dir->u.umsdos_i.u.dir_info.looking--;
+ if (dir->u.umsdos_i.u.dir_info.looking < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.looking);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+#else
+static void umsdos_lockcreate (struct inode *dir){}
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){}
+void umsdos_startlookup (struct inode *dir){}
+static void umsdos_unlockcreate (struct inode *dir){}
+void umsdos_endlookup (struct inode *dir){}
+#endif
+static int umsdos_nevercreat(
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len,
+ int errcod) /* Length of the name */
+{
+ int ret = 0;
+ if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / any file creation /DOS
+ The pseudo sub-directory /DOS can't be created!
+ EEXIST is returned.
+
+ The pseudo sub-directory /DOS can't be removed!
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ ret = errcod;
+ }else if (name[0] == '.'
+ && (len == 1 || (len == 2 && name[1] == '.'))){
+ /* #Specification: create / . and ..
+ If one try to creates . or .., it always fail and return
+ EEXIST.
+
+ If one try to delete . or .., it always fail and return
+ EPERM.
+
+ This should be test at the VFS layer level to avoid
+ duplicating this in all file systems. Any comments ?
+ */
+ ret = errcod;
+ }
+ return ret;
+}
+
+/*
+ Add a new file (ordinary or special) into the alternate directory.
+ The file is added to the real MSDOS directory. If successfull, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
+*/
+static int umsdos_create_any (
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len, /* Length of the name */
+ int mode, /* Permission bit + file type ??? */
+ int rdev, /* major, minor or 0 for ordinary file */
+ /* and symlinks */
+ char flags,
+ struct inode **result) /* Will hold the inode of the newly created */
+ /* file */
+{
+ int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ *result = NULL;
+ if (ret == 0){
+ info.entry.mode = mode;
+ info.entry.rdev = rdev;
+ info.entry.flags = flags;
+ info.entry.uid = current->euid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->egid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.nlink = 1;
+ umsdos_lockcreate(dir);
+ ret = umsdos_newentry (dir,&info);
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_create (dir,info.fake.fname,info.fake.len
+ ,S_IFREG|0777,result);
+ if (ret == 0){
+ struct inode *inode = *result;
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ PRINTK (("inode %p[%d] ",inode,inode->i_count));
+ PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
+ ,info.fake.fname,current->pid,info.f_pos));
+ }else{
+ /* #Specification: create / file exist in DOS
+ Here is a situation. Trying to create a file with
+ UMSDOS. The file is unknown to UMSDOS but already
+ exist in the DOS directory.
+
+ Here is what we are NOT doing:
+
+ We could silently assume that everything is fine
+ and allows the creation to succeed.
+
+ It is possible not all files in the partition
+ are mean to be visible from linux. By trying to create
+ those file in some directory, one user may get access
+ to those file without proper permissions. Looks like
+ a security hole to me. Off course sharing a file system
+ with DOS is some kind of security hole :-)
+
+ So ?
+
+ We return EEXIST in this case.
+ The same is true for directory creation.
+ */
+ if (ret == -EEXIST){
+ printk ("UMSDOS: out of sync, Creation error [%ld], "
+ "deleting %s %d %d pos %ld\n",dir->i_ino
+ ,info.fake.fname,-ret,current->pid,info.f_pos);
+ }
+ umsdos_delentry (dir,&info,0);
+ }
+ PRINTK (("umsdos_create %s ret = %d pos %d\n"
+ ,info.fake.fname,ret,info.f_pos));
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ return ret;
+}
+/*
+ Initialise the new_entry from the old for a rename operation.
+ (Only useful for umsdos_rename_f() below).
+*/
+static void umsdos_ren_init(
+ struct umsdos_info *new_info,
+ struct umsdos_info *old_info,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
+{
+ new_info->entry.mode = old_info->entry.mode;
+ new_info->entry.rdev = old_info->entry.rdev;
+ new_info->entry.uid = old_info->entry.uid;
+ new_info->entry.gid = old_info->entry.gid;
+ new_info->entry.ctime = old_info->entry.ctime;
+ new_info->entry.atime = old_info->entry.atime;
+ new_info->entry.mtime = old_info->entry.mtime;
+ new_info->entry.flags = flags ? flags : old_info->entry.flags;
+ new_info->entry.nlink = old_info->entry.nlink;
+}
+
+#define chkstk() \
+ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
+ printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
+ , current->comm,STACK_MAGIC \
+ ,*(unsigned long *)current->kernel_stack_page \
+ ,__LINE__); \
+ }
+
+/*
+ Rename a file (move) in the file system.
+*/
+static int umsdos_rename_f(
+ struct inode * old_dir,
+ const char * old_name,
+ int old_len,
+ struct inode * new_dir,
+ const char * new_name,
+ int new_len,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
+{
+ int ret = EPERM;
+ struct umsdos_info old_info;
+ int old_ret = umsdos_parse (old_name,old_len,&old_info);
+ struct umsdos_info new_info;
+ int new_ret = umsdos_parse (new_name,new_len,&new_info);
+chkstk();
+ PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
+ if (old_ret == 0 && new_ret == 0){
+ umsdos_lockcreate2(old_dir,new_dir);
+chkstk();
+ PRINTK (("old findentry "));
+ ret = umsdos_findentry(old_dir,&old_info,0);
+chkstk();
+ PRINTK (("ret %d ",ret));
+ if (ret == 0){
+ PRINTK (("new newentry "));
+ umsdos_ren_init(&new_info,&old_info,flags);
+ ret = umsdos_newentry (new_dir,&new_info);
+chkstk();
+ PRINTK (("ret %d %d ",ret,new_info.fake.len));
+ if (ret == 0){
+ PRINTK (("msdos_rename "));
+ old_dir->i_count++;
+ new_dir->i_count++; /* Both inode are needed later */
+ ret = msdos_rename (old_dir
+ ,old_info.fake.fname,old_info.fake.len
+ ,new_dir
+ ,new_info.fake.fname,new_info.fake.len);
+chkstk();
+ PRINTK (("after m_rename ret %d ",ret));
+ if (ret != 0){
+ umsdos_delentry (new_dir,&new_info
+ ,S_ISDIR(new_info.entry.mode));
+chkstk();
+ }else{
+ ret = umsdos_delentry (old_dir,&old_info
+ ,S_ISDIR(old_info.entry.mode));
+chkstk();
+ if (ret == 0){
+ /*
+ This UMSDOS_lookup does not look very useful.
+ It makes sure that the inode of the file will
+ be correctly setup (umsdos_patch_inode()) in
+ case it is already in use.
+
+ Not very efficient ...
+ */
+ struct inode *inode;
+ new_dir->i_count++;
+ PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+ ret = UMSDOS_lookup (new_dir,new_name,new_len
+ ,&inode);
+chkstk();
+ if (ret != 0){
+ printk ("UMSDOS: partial rename for file %s\n"
+ ,new_info.entry.name);
+ }else{
+ /*
+ Update f_pos so notify_change will succeed
+ if the file was already in use.
+ */
+ umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+chkstk();
+ iput (inode);
+ }
+ }
+ }
+ }
+ }
+ umsdos_unlockcreate(old_dir);
+ umsdos_unlockcreate(new_dir);
+ }
+ iput (old_dir);
+ iput (new_dir);
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Setup un Symbolic link or a (pseudo) hard link
+ Return a negative error code or 0 if ok.
+*/
+static int umsdos_symlink_x(
+ struct inode * dir,
+ const char * name,
+ int len,
+ const char * symname, /* name will point to this path */
+ int mode,
+ char flags)
+{
+ /* #Specification: symbolic links / strategy
+ A symbolic link is simply a file which hold a path. It is
+ implemented as a normal MSDOS file (not very space efficient :-()
+
+ I see 2 different way to do it. One is to place the link data
+ in unused entry of the EMD file. The other is to have a separate
+ file dedicated to hold all symbolic links data.
+
+ Lets go for simplicity...
+ */
+ struct inode *inode;
+ int ret;
+ dir->i_count++; /* We keep the inode in case we need it */
+ /* later */
+ ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode);
+ PRINTK (("umsdos_symlink ret %d ",ret));
+ if (ret == 0){
+ int len = strlen(symname);
+ struct file filp;
+ filp.f_pos = 0;
+ /* Make the inode acceptable to MSDOS */
+ ret = umsdos_file_write_kmem (inode,&filp,(char*)symname,len);
+ iput (inode);
+ if (ret >= 0){
+ if (ret != len){
+ ret = -EIO;
+ printk ("UMSDOS: "
+ "Can't write symbolic link data\n");
+ }else{
+ ret = 0;
+ }
+ }
+ if (ret != 0){
+ UMSDOS_unlink (dir,name,len);
+ dir = NULL;
+ }
+ }
+ iput (dir);
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Setup un Symbolic link.
+ Return a negative error code or 0 if ok.
+*/
+int UMSDOS_symlink(
+ struct inode * dir,
+ const char * name,
+ int len,
+ const char * symname) /* name will point to this path */
+{
+ return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0);
+}
+/*
+ Add a link to an inode in a directory
+*/
+int UMSDOS_link (
+ struct inode * oldinode,
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ /* #Specification: hard link / strategy
+ Well ... hard link are difficult to implement on top of an
+ MsDOS fat file system. Unlike UNIX file systems, there are no
+ inode. A directory entry hold the functionnality of the inode
+ and the entry.
+
+ We will used the same strategy as a normal Unix file system
+ (with inode) except we will do it symbolicly (using paths).
+
+ Because anything can happen during a DOS session (defragment,
+ directory sorting, etc...), we can't rely on MsDOS pseudo
+ inode number to record the link. For this reason, the link
+ will be done using hidden symbolic links. The following
+ scenario illustrate how it work.
+
+ Given a file /foo/file
+
+ ln /foo/file /tmp/file2
+
+ become internally
+
+ mv /foo/file /foo/-LINK1
+ ln -s /foo/-LINK1 /foo/file
+ ln -s /foo/-LINK1 /tmp/file2
+
+ Using this strategy, we can operate on /foo/file or /foo/file2.
+ We can remove one and keep the other, like a normal Unix hard link.
+ We can rename /foo/file ou /tmp/file2 independantly.
+
+ The entry -LINK1 will be hidden. It will hold a link count.
+ When all link are erased, the hidden file is erased too.
+ */
+ /* #Specification: weakness / hard link
+ The strategy for hard link introduces a side effect that
+ may or may not be acceptable. Here is the sequence
+
+ mkdir subdir1
+ touch subdir1/file
+ mkdir subdir2
+ ln subdir1/file subdir2/file
+ rm subdir1/file
+ rmdir subdir1
+ rmdir: subdir1: Directory not empty
+
+ This happen because there is an invisible file (--link) in
+ subdir1 which is referenced by subdir2/file.
+
+ Any idea ?
+ */
+ /* #Specification: weakness / hard link / rename directory
+ Another weakness of hard link come from the fact that
+ it is based on hidden symbolic links. Here is an example.
+
+ mkdir /subdir1
+ touch /subdir1/file
+ mkdir /subdir2
+ ln /subdir1/file subdir2/file
+ mv /subdir1 subdir3
+ ls -l /subdir2/file
+
+ Since /subdir2/file is a hidden symbolic link
+ to /subdir1/..hlinkNNN, accessing it will fail since
+ /subdir1 does not exist anymore (has been renamed).
+ */
+ int ret = 0;
+ if (S_ISDIR(oldinode->i_mode)){
+ /* #Specification: hard link / directory
+ A hard link can't be made on a directory. EPERM is returned
+ in this case.
+ */
+ ret = -EPERM;
+ }else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
+ struct inode *olddir;
+ ret = umsdos_get_dirowner(oldinode,&olddir);
+ PRINTK (("umsdos_link dir_owner = %d -> %p [%d] "
+ ,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
+ if (ret == 0){
+ struct umsdos_dirent entry;
+ umsdos_lockcreate2(dir,olddir);
+ ret = umsdos_inode2entry (olddir,oldinode,&entry);
+ if (ret == 0){
+ PRINTK (("umsdos_link :%s: ino %d flags %d "
+ ,entry.name
+ ,oldinode->i_ino,entry.flags));
+ if (!(entry.flags & UMSDOS_HIDDEN)){
+ /* #Specification: hard link / first hard link
+ The first time a hard link is done on a file, this
+ file must be renamed and hidden. Then an internal
+ simbolic link must be done on the hidden file.
+
+ The second link is done after on this hidden file.
+
+ It is expected that the Linux MSDOS file system
+ keeps the same pseudo inode when a rename operation
+ is done on a file in the same directory.
+ */
+ struct umsdos_info info;
+ ret = umsdos_newhidden (olddir,&info);
+ if (ret == 0){
+ olddir->i_count+=2;
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_rename_f (olddir,entry.name
+ ,entry.name_len
+ ,olddir,info.entry.name,info.entry.name_len
+ ,UMSDOS_HIDDEN);
+ if (ret == 0){
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_locate_path (oldinode,path);
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ if (ret == 0){
+ olddir->i_count++;
+ ret = umsdos_symlink_x (olddir
+ ,entry.name
+ ,entry.name_len,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,name,len
+ ,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ }
+ }
+ kfree (path);
+ }
+ }
+ }
+ }else{
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ ret = umsdos_locate_path (oldinode,path);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,name,len,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ }
+ kfree (path);
+ }
+ }
+ }
+ umsdos_unlockcreate(olddir);
+ umsdos_unlockcreate(dir);
+ }
+ iput (olddir);
+ }
+ if (ret == 0){
+ oldinode->i_nlink++;
+ ret = UMSDOS_notify_change (0,oldinode);
+ }
+ iput (oldinode);
+ iput (dir);
+ PRINTK (("umsdos_link %d\n",ret));
+ return ret;
+}
+/*
+ Add a new file into the alternate directory.
+ The file is added to the real MSDOS directory. If successfull, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
+*/
+int UMSDOS_create (
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len, /* Length of the name */
+ int mode, /* Permission bit + file type ??? */
+ struct inode **result) /* Will hold the inode of the newly created */
+ /* file */
+{
+ return umsdos_create_any (dir,name,len,mode,0,0,result);
+}
+/*
+ Add a sub-directory in a directory
+*/
+int UMSDOS_mkdir(
+ struct inode * dir,
+ const char * name,
+ int len,
+ int mode)
+{
+ int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ PRINTK (("umsdos_mkdir %d\n",ret));
+ if (ret == 0){
+ info.entry.mode = mode | S_IFDIR;
+ info.entry.rdev = 0;
+ info.entry.uid = current->euid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->egid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.flags = 0;
+ umsdos_lockcreate(dir);
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dir,&info);
+ PRINTK (("newentry %d ",ret));
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode);
+ if (ret != 0){
+ umsdos_delentry (dir,&info,1);
+ /* #Specification: mkdir / Directory already exist in DOS
+ We do the same thing as for file creation.
+ For all user it is an error.
+ */
+ }else{
+ /* #Specification: mkdir / umsdos directory / create EMD
+ When we created a new sub-directory in a UMSDOS
+ directory (one with full UMSDOS semantic), we
+ create immediatly an EMD file in the new
+ sub-directory so it inherit UMSDOS semantic.
+ */
+ struct inode *subdir;
+ ret = umsdos_real_lookup (dir,info.fake.fname
+ ,info.fake.len,&subdir);
+ if (ret == 0){
+ struct inode *result;
+ ret = msdos_create (subdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result);
+ subdir = NULL;
+ iput (result);
+ }
+ if (ret < 0){
+ printk ("UMSDOS: Can't create empty --linux-.---\n");
+ }
+ iput (subdir);
+ }
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ PRINTK (("umsdos_mkdir %d\n",ret));
+ iput (dir);
+ return ret;
+}
+/*
+ Add a new device special file into a directory.
+*/
+int UMSDOS_mknod(
+ struct inode * dir,
+ const char * name,
+ int len,
+ int mode,
+ int rdev)
+{
+ /* #Specification: Special files / strategy
+ Device special file, pipes, etc ... are created like normal
+ file in the msdos file system. Of course they remain empty.
+
+ One strategy was to create thoses files only in the EMD file
+ since they were not important for MSDOS. The problem with
+ that, is that there were not getting inode number allocated.
+ The MSDOS filesystems is playing a nice game to fake inode
+ number, so why not use it.
+
+ The absence of inode number compatible with those allocated
+ for ordinary files was causing major trouble with hard link
+ in particular and other parts of the kernel I guess.
+ */
+ struct inode *inode;
+ int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode);
+ iput (inode);
+ return ret;
+}
+
+/*
+ Remove a sub-directory.
+*/
+int UMSDOS_rmdir(
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ /* #Specification: style / iput strategy
+ In the UMSDOS project, I am trying to apply a single
+ programming style regarding inode management. Many
+ entry point are receiving an inode to act on, and must
+ do an iput() as soon as they are finished with
+ the inode.
+
+ For simple case, there is no problem. When you introduce
+ error checking, you end up with many iput placed around the
+ code.
+
+ The coding style I use all around is one where I am trying
+ to provide independant flow logic (I don't know how to
+ name this). With this style, code is easier to understand
+ but you rapidly get iput() all around. Here is an exemple
+ of what I am trying to avoid.
+
+ if (a){
+ ...
+ if(b){
+ ...
+ }
+ ...
+ if (c){
+ // Complexe state. Was b true ?
+ ...
+ }
+ ...
+ }
+ // Weird state
+ if (d){
+ // ...
+ }
+ // Was iput finally done ?
+ return status;
+
+ Here is the style I am using. Still sometime I do the
+ first when things are very simple (or very complicated :-( )
+
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ // A single state gets here
+ }
+ }else if (d){
+ ...
+ }
+ return status;
+
+ Again, while this help clarifying the code, I often get a lot
+ of iput(), unlike the first style, where I can place few
+ "strategic" iput(). "strategic" also mean, more difficult
+ to place.
+
+ So here is the style I will be using from now on in this project.
+ There is always an iput() at the end of a function (which has
+ to do an iput()). One iput by inode. There is also one iput()
+ at the places where a successful operation is achieved. This
+ iput() is often done by a sub-function (often from the msdos
+ file system). So I get one too many iput() ? At the place
+ where an iput() is done, the inode is simply nulled, disabling
+ the last one.
+
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ msdos_rmdir(dir,...);
+ dir = NULL;
+ }
+ }else if (d){
+ ...
+ }
+ iput (dir);
+ return status;
+
+ Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
+ paire goes against this practice of "forgetting" the inode as soon
+ as possible.
+ */
+ int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+ if (ret == 0){
+ struct inode *sdir;
+ dir->i_count++;
+ ret = UMSDOS_lookup (dir,name,len,&sdir);
+ PRINTK (("rmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ umsdos_lockcreate(dir);
+ if ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+ if (empty == 1){
+ /* We have to removed the EMD file */
+ ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN);
+ sdir = NULL;
+ }
+ /* sdir must be free before msdos_rmdir() */
+ iput (sdir);
+ sdir = NULL;
+ PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
+ if (ret == 0){
+ struct umsdos_info info;
+ dir->i_count++;
+ umsdos_parse (name,len,&info);
+ /* The findentry is there only to complete */
+ /* the mangling */
+ umsdos_findentry (dir,&info,2);
+ ret = msdos_rmdir (dir,info.fake.fname
+ ,info.fake.len);
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,1);
+ }
+ }
+ }else{
+ /*
+ The subdirectory is not empty, so leave it there
+ */
+ ret = -ENOTEMPTY;
+ }
+ iput(sdir);
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ PRINTK (("umsdos_rmdir %d\n",ret));
+ return ret;
+}
+/*
+ Remove a file from the directory.
+*/
+int UMSDOS_unlink (
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ struct umsdos_info info;
+ int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+ if (ret == 0){
+ ret = umsdos_parse (name,len,&info);
+ if (ret == 0){
+ umsdos_lockcreate(dir);
+ ret = umsdos_findentry(dir,&info,1);
+ if (ret == 0){
+ PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
+ if (info.entry.flags & UMSDOS_HLINK){
+ /* #Specification: hard link / deleting a link
+ When we deletes a file, and this file is a link
+ we must substract 1 to the nlink field of the
+ hidden link.
+
+ If the count goes to 0, we delete this hidden
+ link too.
+ */
+ /*
+ First, get the inode of the hidden link
+ using the standard lookup function.
+ */
+ struct inode *inode;
+ dir->i_count++;
+ ret = UMSDOS_lookup (dir,name,len,&inode);
+ if (ret == 0){
+ PRINTK (("unlink nlink = %d ",inode->i_nlink));
+ inode->i_nlink--;
+ if (inode->i_nlink == 0){
+ struct inode *hdir = iget(inode->i_sb
+ ,inode->u.umsdos_i.i_dir_owner);
+ struct umsdos_dirent entry;
+ ret = umsdos_inode2entry (hdir,inode,&entry);
+ if (ret == 0){
+ ret = UMSDOS_unlink (hdir,entry.name
+ ,entry.name_len);
+ }else{
+ iput (hdir);
+ }
+ }else{
+ ret = UMSDOS_notify_change (0,inode);
+ }
+ iput (inode);
+ }
+ }
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,0);
+ if (ret == 0){
+ PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
+ dir->i_count++;
+ ret = msdos_unlink_umsdos (dir,info.fake.fname
+ ,info.fake.len);
+ PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
+ ,info.entry.mode,ret));
+ }
+ }
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ PRINTK (("umsdos_unlink %d\n",ret));
+ return ret;
+}
+
+/*
+ Rename a file (move) in the file system.
+*/
+int UMSDOS_rename(
+ struct inode * old_dir,
+ const char * old_name,
+ int old_len,
+ struct inode * new_dir,
+ const char * new_name,
+ int new_len)
+{
+ /* #Specification: weakness / rename
+ There is a case where UMSDOS rename has a different behavior
+ than normal UNIX file system. Renaming an open file across
+ directory boundary does not work. Renaming an open file within
+ a directory does work however.
+
+ The problem (not sure) is in the linux VFS msdos driver.
+ I believe this is not a bug but a design feature, because
+ an inode number represent some sort of directory address
+ in the MSDOS directory structure. So moving the file into
+ another directory does not preserve the inode number.
+ */
+ int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST);
+ if (ret == 0){
+ /* umsdos_rename_f eat the inode and we may need those later */
+ old_dir->i_count++;
+ new_dir->i_count++;
+ ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
+ ,new_len,0);
+ if (ret == -EEXIST){
+ /* #Specification: rename / new name exist
+ If the destination name already exist, it will
+ silently be removed. EXT2 does it this way
+ and this is the spec of SUNOS. So does UMSDOS.
+
+ If the destination is an empty directory it will
+ also be removed.
+ */
+ /* #Specification: rename / new name exist / possible flaw
+ The code to handle the deletion of the target (file
+ and directory) use to be in umsdos_rename_f, surrounded
+ by proper directory locking. This was insuring that only
+ one process could achieve a rename (modification) operation
+ in the source and destination directory. This was also
+ insuring the operation was "atomic".
+
+ This has been changed because this was creating a kernel
+ stack overflow (stack is only 4k in the kernel). To avoid
+ the code doing the deletion of the target (if exist) has
+ been moved to a upper layer. umsdos_rename_f is tried
+ once and if it fails with EEXIST, the target is removed
+ and umsdos_rename_f is done again.
+
+ This makes the code cleaner and (not sure) solve a
+ deadlock problem one tester was experiencing.
+
+ The point is to mention that possibly, the semantic of
+ "rename" may be wrong. Anyone dare to check that :-)
+ Be aware that IF it is wrong, to produce the problem you
+ will need two process trying to rename a file to the
+ same target at the same time. Again, I am not sure it
+ is a problem at all.
+ */
+ /* This is not super efficient but should work */
+ new_dir->i_count++;
+ ret = UMSDOS_unlink (new_dir,new_name,new_len);
+chkstk();
+ PRINTK (("rename unlink ret %d %d -- ",ret,new_len));
+ if (ret == -EISDIR){
+ new_dir->i_count++;
+ ret = UMSDOS_rmdir (new_dir,new_name,new_len);
+chkstk();
+ PRINTK (("rename rmdir ret %d -- ",ret));
+ }
+ if (ret == 0){
+ ret = umsdos_rename_f (old_dir,old_name,old_len
+ ,new_dir,new_name,new_len,0);
+ new_dir = old_dir = NULL;
+ }
+ }
+ }
+ iput (new_dir);
+ iput (old_dir);
+ return ret;
+}
+
--- /dev/null
+This file contain idea and things I don't want to forget
+
+Possible bug in fs/read_write.c
+Function sys_readdir()
+
+ There is a call the verify_area that does not take in account
+ the count parameter. I guess it should read
+
+ error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent));
+
+ instead of
+
+ error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
+
+ Of course, now , count is always 1
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/rdir.c
+ *
+ * Written 1994 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory pure MS-DOS handling functions
+ * (For directory without EMD file).
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+extern struct inode *pseudo_root;
+
+static int UMSDOS_rreaddir (
+ struct inode *dir,
+ struct file *filp,
+ struct dirent *dirent,
+ int count)
+{
+ int ret = 0;
+ while (1){
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (ret == 5
+ && pseudo_root != NULL
+ && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
+ /*
+ In pseudo root mode, we must eliminate logically
+ the directory linux from the real root.
+ */
+ char name[5];
+ memcpy_fromfs (name,dirent->d_name,5);
+ if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
+ }else{
+ if (pseudo_root != NULL
+ && ret == 2
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted){
+ char name[2];
+ memcpy_fromfs (name,dirent->d_name,2);
+ if (name[0] == '.' && name[1] == '.'){
+ put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
+ }
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+int UMSDOS_rlookup(
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ int ret;
+ if (pseudo_root != NULL
+ && len == 2
+ && name[0] == '.'
+ && name[1] == '.'
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted){
+ *result = pseudo_root;
+ pseudo_root->i_count++;
+ ret = 0;
+ /* #Specification: pseudo root / DOS/..
+ In the real root directory (c:\), the directory ..
+ is the pseudo root (c:\linux).
+ */
+ }else{
+ ret = umsdos_real_lookup (dir,name,len,result);
+ if (ret == 0){
+ struct inode *inode = *result;
+ if (inode == pseudo_root){
+ /* #Specification: pseudo root / DOS/linux
+ Even in the real root directory (c:\), the directory
+ /linux won't show
+ */
+ ret = -ENOENT;
+ iput (pseudo_root);
+ *result = NULL;
+ }else if (S_ISDIR(inode->i_mode)){
+ /* We must place the proper function table */
+ /* depending if this is a MsDOS directory or an UMSDOS directory */
+ umsdos_setup_dir_inode(inode);
+ }
+ }
+ }
+ iput (dir);
+ return ret;
+}
+
+static int UMSDOS_rrmdir (
+ struct inode *dir,
+ const char *name,
+ int len)
+{
+ /* #Specification: dual mode / rmdir in a DOS directory
+ In a DOS (not EMD in it) directory, we use a reverse strategy
+ compared with an Umsdos directory. We assume that a subdirectory
+ of a DOS directory is also a DOS directory. This is not always
+ true (umssync may be used anywhere), but make sense.
+
+ So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
+ then we check if it is a Umsdos directory. We check if it is
+ really empty (only . .. and --linux-.--- in it). If it is true
+ we remove the EMD and do a msdos_rmdir() again.
+
+ In a Umsdos directory, we assume all subdirectory are also
+ Umsdos directory, so we check the EMD file first.
+ */
+ int ret;
+ if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / rmdir /DOS
+ The pseudo sub-directory /DOS can't be removed!
+ This is done even if the pseudo root is not a Umsdos
+ directory anymore (very unlikely), but an accident (under
+ MsDOS) is always possible.
+
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ }else{
+ umsdos_lockcreate (dir);
+ dir->i_count++;
+ ret = msdos_rmdir (dir,name,len);
+ if (ret == -ENOTEMPTY){
+ struct inode *sdir;
+ dir->i_count++;
+ ret = UMSDOS_rlookup (dir,name,len,&sdir);
+ PRINTK (("rrmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ if ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+ if (empty == 2){
+ /*
+ Not a Umsdos directory, so the previous msdos_rmdir
+ was not lying :-)
+ */
+ ret = -ENOTEMPTY;
+ }else if (empty == 1){
+ /* We have to removed the EMD file */
+ ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN);
+ sdir = NULL;
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_rmdir (dir,name,len);
+ }
+ }
+ }else{
+ ret = -ENOTEMPTY;
+ }
+ iput (sdir);
+ }
+ }
+ umsdos_unlockcreate (dir);
+ }
+ iput (dir);
+ return ret;
+}
+
+/* #Specification: dual mode / introduction
+ One goal of UMSDOS is to allow a practical and simple coexistence
+ between MsDOS and Linux in a single partition. Using the EMD file
+ in each directory, UMSDOS add Unix semantics and capabilities to
+ normal DOS file system. To help and simplify coexistence, here is
+ the logic related to the EMD file.
+
+ If it is missing, then the directory is managed by the MsDOS driver.
+ The names are limited to DOS limits (8.3). No links, no device special
+ and pipe and so on.
+
+ If it is there, it is the directory. If it is there but empty, then
+ the directory looks empty. The utility umssync allows synchronisation
+ of the real DOS directory and the EMD.
+
+ Whenever umssync is applied to a directory without EMD, one is
+ created on the fly. The directory is promoted to full unix semantic.
+ Of course, the ls command will show exactly the same content as before
+ the umssync session.
+
+ It is believed that the user/admin will promote directories to unix
+ semantic as needed.
+
+ The strategy to implement this is to use two function table (struct
+ inode_operations). One for true UMSDOS directory and one for directory
+ with missing EMD.
+
+ Functions related to the DOS semantic (but aware of UMSDOS) generally
+ have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
+ from the one with full UMSDOS semantic.
+*/
+static struct file_operations umsdos_rdir_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_rreaddir, /* readdir */
+ NULL, /* select - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_rdir_inode_operations = {
+ &umsdos_rdir_operations, /* default directory file-ops */
+ msdos_create, /* create */
+ UMSDOS_rlookup, /* lookup */
+ NULL, /* link */
+ msdos_unlink, /* unlink */
+ NULL, /* symlink */
+ msdos_mkdir, /* mkdir */
+ UMSDOS_rrmdir, /* rmdir */
+ NULL, /* mknod */
+ msdos_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
--- /dev/null
+/*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1992 by Jacques Gelinas
+ * inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+/*
+ Read the data associate with the symlink.
+ Return lenght read in buffer or a negative error code.
+*/
+static int umsdos_readlink_x (
+ struct inode *inode,
+ char *buffer,
+ int (*msdos_read)(struct inode *, struct file *, char *, int),
+ int bufsiz)
+{
+ int ret = inode->i_size;
+ struct file filp;
+ filp.f_pos = 0;
+ if (ret > bufsiz) ret = bufsiz;
+ if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
+ ret = -EIO;
+ }
+ return ret;
+}
+/*
+ Follow a symbolic link chain by calling open_namei recursivly
+ until an inode is found.
+
+ Return 0 if ok, or a negative error code if not.
+*/
+static int UMSDOS_follow_link(
+ struct inode * dir,
+ struct inode * inode,
+ int flag,
+ int mode,
+ struct inode ** res_inode)
+{
+ int ret = -ELOOP;
+ *res_inode = NULL;
+ if (current->link_count < 5) {
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ if (!dir) {
+ dir = current->fs[1].root;
+ dir->i_count++;
+ }
+ if (!inode){
+ PRINTK (("symlink: inode = NULL\n"));
+ ret = -ENOENT;
+ }else if (!S_ISLNK(inode->i_mode)){
+ PRINTK (("symlink: Not ISLNK\n"));
+ *res_inode = inode;
+ inode = NULL;
+ ret = 0;
+ }else{
+ ret = umsdos_readlink_x (inode,path
+ ,umsdos_file_read_kmem,PATH_MAX-1);
+ if (ret > 0){
+ path[ret] = '\0';
+ PRINTK (("follow :%s: %d ",path,ret));
+ iput(inode);
+ inode = NULL;
+ current->link_count++;
+ ret = open_namei(path,flag,mode,res_inode,dir);
+ current->link_count--;
+ dir = NULL;
+ }else{
+ ret = -EIO;
+ }
+ }
+ kfree (path);
+ }
+ }
+ iput(inode);
+ iput(dir);
+ PRINTK (("follow_link ret %d\n",ret));
+ return ret;
+}
+
+static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ int ret = -EINVAL;
+ if (S_ISLNK(inode->i_mode)) {
+ ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen);
+ }
+ PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
+ iput(inode);
+ return ret;
+
+}
+
+static struct file_operations umsdos_symlink_operations = {
+ NULL, /* lseek - default */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_symlink_inode_operations = {
+ &umsdos_symlink_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ UMSDOS_readlink, /* readlink */
+ UMSDOS_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+
char name[EXT_NAME_LEN];
};
+#ifdef __KERNEL__
extern int ext_open(struct inode * inode, struct file * filp);
extern void ext_release(struct inode * inode, struct file * filp);
extern int ext_lookup(struct inode * dir,const char * name, int len,
extern struct inode_operations ext_dir_inode_operations;
extern struct inode_operations ext_symlink_inode_operations;
+#endif /*__KERNEL__ */
#endif
#include <linux/ext2_fs_i.h>
#include <linux/hpfs_fs_i.h>
#include <linux/msdos_fs_i.h>
+#include <linux/umsdos_fs_i.h>
#include <linux/iso_fs_i.h>
#include <linux/nfs_fs_i.h>
#include <linux/xia_fs_i.h>
#include <linux/sysv_fs_i.h>
+#ifdef __KERNEL__
+
struct inode {
dev_t i_dev;
unsigned long i_ino;
struct ext2_inode_info ext2_i;
struct hpfs_inode_info hpfs_i;
struct msdos_inode_info msdos_i;
+ struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct xiafs_inode_info xiafs_i;
int (*bmap) (struct inode *,int);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
+ int (*smap) (struct inode *,int);
};
struct super_operations {
struct file_system_type * next;
};
-#ifdef __KERNEL__
-
extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);
char name [0];
};
-extern int isonum_711(char *);
-extern int isonum_712(char *);
-extern int isonum_721(char *);
-extern int isonum_722(char *);
-extern int isonum_723(char *);
-extern int isonum_731(char *);
-extern int isonum_732(char *);
-extern int isonum_733(char *);
-extern int iso_date(char *, int);
-
-extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
-extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *);
-
-extern char * get_rock_ridge_symlink(struct inode *);
-extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
-
#define ISOFS_BLOCK_BITS 11
#define ISOFS_BLOCK_SIZE 2048
#define ISOFS_FILE_BINARY 2
#define ISOFS_FILE_TEXT_M 3
+#ifdef __KERNEL__
+extern int isonum_711(char *);
+extern int isonum_712(char *);
+extern int isonum_721(char *);
+extern int isonum_722(char *);
+extern int isonum_723(char *);
+extern int isonum_731(char *);
+extern int isonum_732(char *);
+extern int isonum_733(char *);
+extern int iso_date(char *, int);
+
+extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
+extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *);
+
+extern char * get_rock_ridge_symlink(struct inode *);
+extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
/* The stuff that follows may be totally unneeded. I have not checked to see
which prototypes we are still using. */
extern void leak_check_brelse(struct buffer_head * bh);
#endif /* LEAK_CHECK */
+#endif /* __KERNEL__ */
+
#endif
char name[0];
};
+#ifdef __KERNEL__
+
extern int minix_lookup(struct inode * dir,const char * name, int len,
struct inode ** result);
extern int minix_create(struct inode * dir,const char * name, int len, int mode,
extern struct inode_operations minix_dir_inode_operations;
extern struct inode_operations minix_symlink_inode_operations;
+#endif /* __KERNEL__ */
+
#endif
};
/* end of planning stage */
+#ifdef __KERNEL__
+
/*
* Free area management
*/
#define GFP_NOBUFFER 0x04
-/* vm_ops not present page codes */
+/*
+ * vm_ops not present page codes for shared memory.
+ *
+ * Will go away eventually..
+ */
#define SHM_SWP_TYPE 0x41
extern void shm_no_page (ulong *);
-/* swap cache stuff (in swap.c) */
+/*
+ * swap cache stuff (in swap.c)
+ */
+#define SWAP_CACHE_INFO
+
extern unsigned long * swap_cache;
+#ifdef SWAP_CACHE_INFO
+extern unsigned long swap_cache_add_total;
+extern unsigned long swap_cache_add_success;
+extern unsigned long swap_cache_del_total;
+extern unsigned long swap_cache_del_success;
+extern unsigned long swap_cache_find_total;
+extern unsigned long swap_cache_find_success;
+#endif
+
extern inline unsigned long in_swap_cache(unsigned long addr)
{
return swap_cache[addr >> PAGE_SHIFT];
}
-extern inline void swap_cache_invalidate(unsigned long addr)
+extern inline long find_in_swap_cache (unsigned long addr)
{
- swap_cache[addr >> PAGE_SHIFT] = 0;
+ unsigned long entry;
+
+#ifdef SWAP_CACHE_INFO
+ swap_cache_find_total++;
+#endif
+ __asm__ __volatile__("xchgl %0,%1"
+ :"=m" (swap_cache[addr >> PAGE_SHIFT]),
+ "=r" (entry)
+ :"0" (swap_cache[addr >> PAGE_SHIFT]),
+ "1" (0));
+#ifdef SWAP_CACHE_INFO
+ if (entry)
+ swap_cache_find_success++;
+#endif
+ return entry;
}
+extern inline int delete_from_swap_cache(unsigned long addr)
+{
+ unsigned long entry;
+
+#ifdef SWAP_CACHE_INFO
+ swap_cache_del_total++;
+#endif
+ __asm__ __volatile__("xchgl %0,%1"
+ :"=m" (swap_cache[addr >> PAGE_SHIFT]),
+ "=r" (entry)
+ :"0" (swap_cache[addr >> PAGE_SHIFT]),
+ "1" (0));
+ if (entry) {
+#ifdef SWAP_CACHE_INFO
+ swap_cache_del_success++;
+#endif
+ swap_free(entry);
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* __KERNEL__ */
+
#endif
/*
* The MS-DOS filesystem constants/structures
*/
-
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/fd.h>
#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
+#ifdef __KERNEL__
static inline struct buffer_head *msdos_sread(int dev,int sector,void **start)
{
extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
extern int msdos_rmdir(struct inode *dir,const char *name,int len);
extern int msdos_unlink(struct inode *dir,const char *name,int len);
+extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
struct inode *new_dir,const char *new_name,int new_len);
/* dir.c */
extern struct inode_operations msdos_dir_inode_operations;
-
+extern int msdos_readdir (struct inode *inode, struct file *filp,
+ struct dirent *dirent, int count);
/* file.c */
extern struct inode_operations msdos_file_inode_operations;
+extern int msdos_file_read(struct inode *, struct file *, char *, int);
+extern int msdos_file_write(struct inode *, struct file *, char *, int);
extern struct inode_operations msdos_file_inode_operations_no_bmap;
extern void msdos_truncate(struct inode *inode);
+/* mmap.c */
+extern int msdos_mmap (struct inode *, struct file *, unsigned long, size_t
+ ,int , unsigned long);
+
+#endif /* __KERNEL__ */
+
#endif
#ifndef _MSDOS_FS_I
#define _MSDOS_FS_I
+#ifndef _LINUX_CONFIG_H
+#include <linux/config.h>
+#endif
+#ifndef _LINUX_PIPE_FS_I_H
+#include <linux/pipe_fs_i.h>
+#endif
+
/*
* MS-DOS file system inode data in memory
*/
struct msdos_inode_info {
+ /*
+ UMSDOS manage special file and fifo as normal empty
+ msdos file. fifo inode processing conflict with msdos
+ processing. So I insert the pipe_inode_info so the
+ information does not overlap. This increases the size of
+ the msdos_inode_info, but the clear winner here is
+ the ext2_inode_info. So it does not change anything to
+ the total size of a struct inode.
+
+ I have not put it conditionnal. With the advent of loadable
+ file system drivers, it would be very easy to compile
+ a MsDOS FS driver unaware of UMSDOS and then later to
+ load a (then incompatible) UMSDOS FS driver.
+ */
+ struct pipe_inode_info reserved;
int i_start; /* first cluster or 0 */
int i_attrs; /* unused attribute bits */
int i_busy; /* file is either deleted but still open, or
-/* $Id: mtio.h,v 1.4 1992/11/18 01:32:03 root Exp root $
+/* $Id: mtio.h,v 1.13 1994/07/19 19:35:52 root Exp $
*
* linux/mtio.h header file for Linux. Written by H. Bergman
*/
#define MTSETBLK 20 /* set block length (SCSI) */
#define MTSETDENSITY 21 /* set tape density (SCSI) */
#define MTSEEK 22 /* seek to block (Tandberg, etc.) */
-#define MTTELL 23 /* tell block (Tandber, etc.) */
+#define MTTELL 23 /* tell block (Tandberg, etc.) */
#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
/* ordinary buffered operation with code 1 */
daddr_t mt_blkno; /* current block number */
};
+
+
/*
- * Constants for mt_type. Not all of these are supported.
+ * Constants for mt_type. Not all of these are supported,
+ * and these are not all of the ones that are supported.
*/
#define MT_ISUNKNOWN 0x01
#define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer */
#define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02 */
#define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L */
#define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L */
+#define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller */
#define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features */
#define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24 */
+#define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */
#define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */
#define MT_ISDDS1 0x51 /* DDS device without partitions */
#define MT_ISDDS2 0x52 /* DDS device with partitions */
/* QIC-40/QIC-80 ftape supported drives.
* 20bit vendor ID + 0x800000
*/
-#define MT_ISFTAPE_UNKNOWN 0x800000
-#define MT_ISCMSDJ10_DJ20 0x800047
-#define MT_ISCMSDJ10_DJ20_NEW 0x8011c4
-#define MT_ISARCHIVE_5580I 0x800005
-#define MT_ISARCHIVE_XL9250I 0x80014a
-#define MT_ISARCHIVE_31250Q 0x800146
-#define MT_ISINSIGHT_80 0x810005
-#define MT_ISCONNER_C250MQT 0x80014c
-#define MT_ISWANGTEK_2040F 0x8001c1
-#define MT_ISWANGTEK_2080F 0x8001c8
-#define MT_ISIOMEGA_250 0x808880
-#define MT_ISSUMMIT_SE150 0x800180
-#define MT_ISSUMMIT_SE250 0x800181
-#define MT_ISESCOM_IDTBU120E 0x800140
+#define MT_ISFTAPE_UNKNOWN 0x800000
+#define MT_ISCMSDJ10_DJ20 0x800047
+#define MT_ISCMSDJ10_DJ20_NEW 0x8011c4
+#define MT_ISARCHIVE_5580I 0x800005
+#define MT_ISARCHIVE_XL9250I 0x80014a
+#define MT_ISARCHIVE_31250Q 0x800146
+#define MT_ISINSIGHT_80 0x810005
+#define MT_ISCONNER_C250MQT 0x80014c
+#define MT_ISWANGTEK_2040F 0x8001c1
+#define MT_ISWANGTEK_2080F 0x8001c8
+#define MT_ISIOMEGA_250 0x808880
+#define MT_ISSUMMIT_SE150 0x800180
+#define MT_ISSUMMIT_SE250 0x800181
+#define MT_ISESCOM_IDTBU120E 0x800140
struct mt_tape_info {
long t_type; /* device type id (mt_type) */
{MT_ISARCHIVE_VP60I, "Archive VP60i, QIC-02"}, \
{MT_ISARCHIVE_2150L, "Archive Viper 2150L"}, \
{MT_ISARCHIVE_2060L, "Archive Viper 2060L"}, \
+ {MT_ISARCHIVESC499, "Archive SC-499 QIC-36 controller"}, \
+ {MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \
{MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \
+ {MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \
{MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
{MT_ISSCSI1, "Generic SCSI-1 tape"}, \
{MT_ISSCSI2, "Generic SCSI-2 tape"}, \
{MT_ISFTAPE_UNKNOWN, "Unknown floppy interface tape drive"},\
{MT_ISCMSDJ10_DJ20, "Colorado DJ-10/DJ-20"},\
- {MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\
+ {MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\
{MT_ISARCHIVE_5580I, "Archive 5580i"},\
- {MT_ISARCHIVE_XL9250I, "Archive XL9250i [Conner/Escom]"},\
+ {MT_ISARCHIVE_XL9250I, "Archive XL9250i [Conner/Escom]"},\
{MT_ISARCHIVE_31250Q, "Escom/Archive 31250Q"},\
- {MT_ISINSIGHT_80, "Insight 80 Mb"},\
- {MT_ISCONNER_C250MQT, "Conner C250MQT"},\
- {MT_ISWANGTEK_2040F, "Wangtek 3040F"},\
- {MT_ISWANGTEK_2080F, "Wangtek 3080F"},\
- {MT_ISIOMEGA_250, "Iomega 250"},\
- {MT_ISSUMMIT_SE150, "Summit SE 150"},\
- {MT_ISSUMMIT_SE250, "Summit SE 250/Mountain FS8000"},\
- {MT_ISESCOM_IDTBU120E, "Identity IDTBU120E, Escom?"},\
+ {MT_ISINSIGHT_80, "Insight 80 Mb"},\
+ {MT_ISCONNER_C250MQT, "Conner C250MQT"},\
+ {MT_ISWANGTEK_2040F, "Wangtek 3040F"},\
+ {MT_ISWANGTEK_2080F, "Wangtek 3080F"},\
+ {MT_ISIOMEGA_250, "Iomega 250"},\
+ {MT_ISSUMMIT_SE150, "Summit SE 150"},\
+ {MT_ISSUMMIT_SE250, "Summit SE 250/Mountain FS8000"},\
+ {MT_ISESCOM_IDTBU120E, "Identity IDTBU120E, Escom?"},\
{0, NULL} \
}
#define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */
#define MTIOCPOS _IOR('m', 3, struct mtpos) /* get tape position */
+/* The next two are used by the QIC-02 driver for runtime reconfiguration.
+ * See tpqic02.h for struct mtconfiginfo.
+ */
+#define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* get tape config */
+#define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* set tape config */
+
/* Generic Mag Tape (device independent) status macros for examining
* mt_gstat -- HP-UX compatible.
#define MT_ST_TWO_FM 0x10
#endif /* _LINUX_MTIO_H */
-
#define SO_ACCEPTCON (1<<16) /* performed a listen */
-
+#ifdef __KERNEL__
/*
* Internal representation of a socket. not all the fields are used by
* all configurations:
extern int sock_awaitconn(struct socket *mysock, struct socket *servsock);
extern int sock_register(int family, struct proto_ops *ops);
-
+#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
#define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server)
#define NFS_FH(inode) (&(inode)->u.nfs_i.fhandle)
+#ifdef __KERNEL__
+
/* linux/fs/nfs/proc.c */
extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
extern int nfs_mmap(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off);
+#endif /* __KERNEL__ */
+
#endif
/* mm */ { INIT_MM } \
}
+#ifdef __KERNEL__
+
extern struct task_struct init_task;
extern struct task_struct *task[NR_TASKS];
extern struct task_struct *last_task_used_math;
:"m" (current->debugreg[register]) \
:"dx");
+#endif /* __KERNEL__ */
+
#endif
*
* COPRO_TIMER 387 timeout for buggy hardware..
*
- * TAPE_QIC02_TIMER timer for QIC-02 tape driver (it's not hardcoded)
+ * QIC02_TAPE_TIMER timer for QIC-02 tape driver (it's not hardcoded)
*
* MCD_TIMER Mitsumi CD-ROM Timer
*/
#define SOUND_TIMER 20
#define COPRO_TIMER 21
-#define TAPE_QIC02_TIMER 22 /* hhb */
+#define QIC02_TAPE_TIMER 22 /* hhb */
#define MCD_TIMER 23
#define HD_TIMER2 24
-/* $Id: tpqic02.h,v 0.16 1993/04/19 23:15:39 root Exp root $
+/* $Id: tpqic02.h,v 0.25 1994/07/21 02:16:30 root Exp root $
*
* Include file for QIC-02 driver for Linux.
*
- * Copyright (c) 1992 by H. H. Bergman. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
*
- * ******* USER CONFIG SECTION BELOW *******
+ * ******* USER CONFIG SECTION BELOW (Near line 70) *******
*/
#ifndef _LINUX_TPQIC02_H
#include <linux/config.h>
-#if CONFIG_TAPE_QIC02
+#if CONFIG_QIC02_TAPE
-/* need to have TAPE_QIC02_DRIVE and TAPE_QIC02_IFC expand to something */
+/* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */
#include <linux/mtio.h>
-/* make TAPE_QIC02_IFC expand to something */
+/* Make QIC02_TAPE_IFC expand to something.
+ *
+ * The only difference between WANGTEK and EVEREX is in the
+ * handling of the DMA channel 3.
+ * Note that the driver maps EVEREX to WANGTEK internally for speed
+ * reasons. Externally WANGTEK==1, EVEREX==2, ARCHIVE==3.
+ * These must correspond to the values used in qic02config(1).
+ *
+ * Support for Mountain controllers was added by Erik Jacobson
+ * and severely hacked by me. -- hhb
+ */
#define WANGTEK 1 /* don't know about Wangtek QIC-36 */
-#define EVEREX WANGTEK /* I heard *some* of these are identical */
+#define EVEREX (WANGTEK+1) /* I heard *some* of these are identical */
#define EVEREX_811V EVEREX /* With TEAC MT 2ST 45D */
#define EVEREX_831V EVEREX
#define ARCHIVE 3
#define ARCHIVE_SC402 ARCHIVE /* don't know much about SC400 */
#define ARCHIVE_SC499 ARCHIVE /* SC402 and SC499R should be identical */
+#define MOUNTAIN 5 /* Mountain Computer Interface */
+
/*********** START OF USER CONFIGURABLE SECTION ************/
-/* Tape configuration:
+/* Tape configuration: Select DRIVE, IFC, PORT, IRQ and DMA below.
+ * Runtime (re)configuration is not supported yet.
*
- * Tape drive configuration: (MT_IS* constants are defined in sys/mtio.h)
+ * Tape drive configuration: (MT_IS* constants are defined in mtio.h)
*
- * TAPE_QIC02_DRIVE = MT_ISWT5150
+ * QIC02_TAPE_DRIVE = MT_ISWT5150
* - Wangtek 5150, format: up to QIC-150.
- * TAPE_QIC02_DRIVE = MT_ISQIC02_ALL_FEATURES
- * - Enables some optional QIC commands that some drives may lack.
+ * QIC02_TAPE_DRIVE = MT_ISQIC02_ALL_FEATURES
+ * - Enables some optional QIC02 commands that some drives may lack.
* It is provided so you can check which are supported by your drive.
* Refer to tpqic02.h for others.
*
- * Supported interface cards: TAPE_QIC02_IFC =
+ * Supported interface cards: QIC02_TAPE_IFC =
* WANGTEK,
* ARCHIVE_SC402, ARCHIVE_SC499. (both same programming interface)
*
* Make sure you have the I/O ports/DMA channels
* and IRQ stuff configured properly!
- * NOTE: Check for conflicts with TAPE_QIC02_TIMER in timer.h.
+ * NOTE: There may be other device drivers using the same major
+ * number. This must be avoided. Check for timer.h conflicts too.
+ *
+ * If you have an EVEREX EV-831 card and you are using DMA channel 3,
+ * you will probably have to ``#define QIC02_TAPE_DMA3_FIX'' below.
*/
-#define TAPE_QIC02_DRIVE MT_ISQIC02_ALL_FEATURES /* drive type */
-/* #define TAPE_QIC02_DRIVE MT_ISWT5150 */
-#define TAPE_QIC02_IFC WANGTEK /* interface card type */
-/* #define TAPE_QIC02_IFC ARCHIVE */
-#define TAPE_QIC02_PORT 0x300 /* controller port adress */
-#define TAPE_QIC02_IRQ 5 /* Muhammad, please don't use 2 here. -- Hennus */
-#define TAPE_QIC02_DMA 1 /* either 1 or 3, because 2 is used by the floppy */
+/* CONFIG_QIC02_DYNCONF can be defined in autoconf.h, by `make config' */
+
+/*** #undef CONFIG_QIC02_DYNCONF ***/
+
+#ifndef CONFIG_QIC02_DYNCONF
+#define QIC02_TAPE_DRIVE MT_ISQIC02_ALL_FEATURES /* drive type */
+/* #define QIC02_TAPE_DRIVE MT_ISWT5150 */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_5945L2 */
+/* #define QIC02_TAPE_DRIVE MT_ISTEAC_MT2ST */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_2150L */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVESC499 */
+
+/* Either WANGTEK, ARCHIVE or MOUNTAIN. Not EVEREX.
+ * If you have an EVEREX, use WANGTEK and try the DMA3_FIX below.
+ */
+#define QIC02_TAPE_IFC WANGTEK /* interface card type */
+/* #define QIC02_TAPE_IFC ARCHIVE */
+/* #define QIC02_TAPE_IFC MOUNTAIN */
+
+#define QIC02_TAPE_PORT 0x300 /* controller port adress */
+#define QIC02_TAPE_IRQ 5 /* For IRQ2, use 9 here, others normal. */
+#define QIC02_TAPE_DMA 1 /* either 1 or 3, because 2 is used by the floppy */
+
+/* If DMA3 doesn't work, but DMA1 does, and you have a
+ * Wangtek/Everex card, you can try #define-ing the flag
+ * below. Note that you should also change the DACK jumper
+ * for Wangtek/Everex cards when changing the DMA channel.
+ */
+#undef QIC02_TAPE_DMA3_FIX
/************ END OF USER CONFIGURABLE SECTION *************/
+/* I put the stuff above in config.in, but a few recompiles, to
+ * verify different configurations, and several days later I decided
+ * to change it back again.
+ */
+
+
-/* NOTE: TP_HAVE_DENS should distinguish between available densities
+/* NOTE: TP_HAVE_DENS should distinguish between available densities (?)
* NOTE: Drive select is not implemented -- I have only one tape streamer,
* so I'm unable and unmotivated to test and implement that. ;-) ;-)
*/
-#if TAPE_QIC02_DRIVE == MT_ISWT5150
-#define TP_HAVE_DENS
-#define TP_HAVE_BSF /* nope */
-#define TP_HAVE_FSR /* nope */
-#define TP_HAVE_BSR /* nope */
-#define TP_HAVE_EOD /* most of the time */
-#define TP_HAVE_RAS1
-#define TP_HAVE_RAS2
-
-#elif TAPE_QIC02_DRIVE == MT_ISARCHIVESC499 /* Archive SC-499 QIC-36 controller */
-#define TP_HAVE_DENS /* can do set density (QIC-11 / QIC-24) */
-#define TP_HAVE_FSR /* can skip one block forwards */
-#define TP_HAVE_BSR /* can skip one block backwards */
-#define TP_HAVE_EOD /* can seek to end of recorded data */
-#define TP_HAVE_RAS1 /* can run selftest 1 */
-#define TP_HAVE_RAS2 /* can run selftest 2 */
+#if QIC02_TAPE_DRIVE == MT_ISWT5150
+#define TP_HAVE_DENS 1
+#define TP_HAVE_BSF 0 /* nope */
+#define TP_HAVE_FSR 0 /* nope */
+#define TP_HAVE_BSR 0 /* nope */
+#define TP_HAVE_EOD 0 /* most of the time */
+#define TP_HAVE_SEEK 0
+#define TP_HAVE_TELL 0
+#define TP_HAVE_RAS1 1
+#define TP_HAVE_RAS2 1
+
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVESC499 /* Archive SC-499 QIC-36 controller */
+#define TP_HAVE_DENS 1 /* can do set density (QIC-11 / QIC-24) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 0
+#define TP_HAVE_TELL 0
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
/* These last two selftests shouldn't be used yet! */
-#elif (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2060L) || (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2150L)
-#define TP_HAVE_DENS /* can do set density (QIC-24 / QIC-120 / QIC-150) */
-#define TP_HAVE_FSR /* can skip one block forwards */
-#define TP_HAVE_BSR /* can skip one block backwards */
-#define TP_HAVE_EOD /* can seek to end of recorded data */
-#define TP_HAVE_TELL /* can read current block address */
-#define TP_HAVE_SEEK /* can seek to block */
-#define TP_HAVE_RAS1 /* can run selftest 1 */
-#define TP_HAVE_RAS2 /* can run selftest 2 */
+#elif (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2060L) || (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2150L)
+#define TP_HAVE_DENS 1 /* can do set density (QIC-24 / QIC-120 / QIC-150) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
/* These last two selftests shouldn't be used yet! */
-#elif TAPE_QIC02_DRIVE == MT_ISQIC02_ALL_FEATURES
-#define TP_HAVE_DENS /* can do set density */
-#define TP_HAVE_BSF /* can search filemark backwards */
-#define TP_HAVE_FSR /* can skip one block forwards */
-#define TP_HAVE_BSR /* can skip one block backwards */
-#define TP_HAVE_EOD /* can seek to end of recorded data */
-#define TP_HAVE_SEEK /* seek to block address */
-#define TP_HAVE_TELL /* tell current block address */
-#define TP_HAVE_RAS1 /* can run selftest 1 */
-#define TP_HAVE_RAS2 /* can run selftest 2 */
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVE_5945L2
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS 1 /* can do set density?? (QIC-24??) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif QIC02_TAPE_DRIVE == MT_ISTEAC_MT2ST
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS 0 /* cannot do set density?? (QIC-150?) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif QIC02_TAPE_DRIVE == MT_ISQIC02_ALL_FEATURES
+#define TP_HAVE_DENS 1 /* can do set density */
+#define TP_HAVE_BSF 1 /* can search filemark backwards */
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 1 /* seek to block address */
+#define TP_HAVE_TELL 1 /* tell current block address */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
/* These last two selftests shouldn't be used yet! */
*/
#endif
+#endif /* !CONFIG_QIC02_DYNCONF */
-/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
- * kernel space, you could decrease it to 1, or if you got a very
- * slow machine, you could increase it up to 128 blocks. Less kernel
- * buffer blocks result in more context-switching.
- */
-#define NR_BLK_BUF 20 /* max 128 blocks */
-#define TAPE_BLKSIZE 512 /* streamer tape block size (fixed) */
-#define TPQBUF_SIZE (TAPE_BLKSIZE*NR_BLK_BUF) /* buffer size */
+/* WANGTEK interface card specifics */
+#define WT_QIC02_STAT_PORT (QIC02_TAPE_PORT)
+#define WT_QIC02_CTL_PORT (QIC02_TAPE_PORT)
+#define WT_QIC02_CMD_PORT (QIC02_TAPE_PORT+1)
+#define WT_QIC02_DATA_PORT (QIC02_TAPE_PORT+1)
-#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */
+/* status register bits (Active LOW!) */
+#define WT_QIC02_STAT_READY 0x01
+#define WT_QIC02_STAT_EXCEPTION 0x02
+#define WT_QIC02_STAT_MASK (WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION)
-#if TAPE_QIC02_IFC == WANGTEK
- /* Wangtek interface card port locations */
-# define QIC_STAT_PORT TAPE_QIC02_PORT
-# define QIC_CTL_PORT TAPE_QIC02_PORT
-# define QIC_CMD_PORT (TAPE_QIC02_PORT+1)
-# define QIC_DATA_PORT (TAPE_QIC02_PORT+1)
+#define WT_QIC02_STAT_RESETMASK 0x07
+#define WT_QIC02_STAT_RESETVAL (WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION)
-/* status register bits (Active LOW!) */
-# define QIC_STAT_READY 0x01
-# define QIC_STAT_EXCEPTION 0x02
-# define QIC_STAT_MASK (QIC_STAT_READY|QIC_STAT_EXCEPTION)
-
-# define QIC_STAT_RESETMASK 0x07
-# define QIC_STAT_RESETVAL (QIC_STAT_RESETMASK & ~QIC_STAT_EXCEPTION)
-
-/* controller register (QIC_CTL_PORT) bits */
-# define WT_CTL_ONLINE 0x01
-# define QIC_CTL_RESET 0x02
-# define QIC_CTL_REQUEST 0x04
-# define WT_CTL_CMDOFF 0xC0
-# if TAPE_QIC02_DMA == 3 /* dip-switches alone don't seem to cut it */
-# define WT_CTL_DMA 0x10 /* enable dma chan3 */
-# elif TAPE_QIC02_DMA == 1
-# define WT_CTL_DMA 0x08 /* enable dma chan1 or chan2 */
-# else
-# error Unsupported or incorrect DMA configuration.
-# endif
+/* controller register (QIC02_CTL_PORT) bits */
+#define WT_QIC02_CTL_RESET 0x02
+#define WT_QIC02_CTL_REQUEST 0x04
+#define WT_CTL_ONLINE 0x01
+#define WT_CTL_CMDOFF 0xC0
-#elif TAPE_QIC02_IFC == ARCHIVE
- /* Archive interface card port locations */
-# define QIC_STAT_PORT (TAPE_QIC02_PORT+1)
-# define QIC_CTL_PORT (TAPE_QIC02_PORT+1)
-# define QIC_CMD_PORT (TAPE_QIC02_PORT)
-# define QIC_DATA_PORT (TAPE_QIC02_PORT)
-# define AR_START_DMA_PORT (TAPE_QIC02_PORT+2)
-# define AR_RESET_DMA_PORT (TAPE_QIC02_PORT+3)
-
- /* STAT port bits */
-# define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */
-# define QIC_STAT_READY 0x40 /* active low */
-# define QIC_STAT_EXCEPTION 0x20 /* active low */
-# define QIC_STAT_MASK (QIC_STAT_READY|QIC_STAT_EXCEPTION)
-# define AR_STAT_DMADONE 0x10 /* active high, DMA done */
-# define AR_STAT_DIRC 0x08 /* active high, direction */
-
-# define QIC_STAT_RESETMASK 0x70 /* check RDY,EXC,DMADONE */
-# define QIC_STAT_RESETVAL ((QIC_STAT_RESETMASK & ~AR_STAT_IRQF & ~QIC_STAT_EXCEPTION) | AR_STAT_DMADONE)
+#define WT_CTL_DMA3 0x10 /* enable dma chan3 */
+#define WT_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */
- /* CTL port bits */
-# define QIC_CTL_RESET 0x80 /* drive reset */
-# define QIC_CTL_REQUEST 0x40 /* notify of new command */
-# define AR_CTL_IEN 0x20 /* interrupt enable */
-# define AR_CTL_DNIEN 0x10 /* done-interrupt enable */
+
+
+
+/* ARCHIVE interface card specifics */
+#define AR_QIC02_STAT_PORT (QIC02_TAPE_PORT+1)
+#define AR_QIC02_CTL_PORT (QIC02_TAPE_PORT+1)
+#define AR_QIC02_CMD_PORT (QIC02_TAPE_PORT)
+#define AR_QIC02_DATA_PORT (QIC02_TAPE_PORT)
+
+#define AR_START_DMA_PORT (QIC02_TAPE_PORT+2)
+#define AR_RESET_DMA_PORT (QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */
+#define AR_QIC02_STAT_READY 0x40 /* active low */
+#define AR_QIC02_STAT_EXCEPTION 0x20 /* active low */
+#define AR_QIC02_STAT_MASK (AR_QIC02_STAT_READY|AR_QIC02_STAT_EXCEPTION)
+#define AR_STAT_DMADONE 0x10 /* active high, DMA done */
+#define AR_STAT_DIRC 0x08 /* active high, direction */
+
+#define AR_QIC02_STAT_RESETMASK 0x70 /* check RDY,EXC,DMADONE */
+#define AR_QIC02_STAT_RESETVAL ((AR_QIC02_STAT_RESETMASK & ~AR_STAT_IRQF & ~AR_QIC02_STAT_EXCEPTION) | AR_STAT_DMADONE)
+
+/* CTL port bits */
+#define AR_QIC02_CTL_RESET 0x80 /* drive reset */
+#define AR_QIC02_CTL_REQUEST 0x40 /* notify of new command */
+#define AR_CTL_IEN 0x20 /* interrupt enable */
+#define AR_CTL_DNIEN 0x10 /* done-interrupt enable */
/* Note: All of these bits are cleared automatically when writing to
* AR_RESET_DMA_PORT. So AR_CTL_IEN and AR_CTL_DNIEN must be
* reprogrammed before the write to AR_START_DMA_PORT.
*/
-# if TAPE_QIC02_DMA > 3 /* channel 2 is used by the floppy driver */
-# error DMA channels other than 1 and 3 are not supported.
+
+/* MOUNTAIN interface specifics */
+#define MTN_QIC02_STAT_PORT (QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CTL_PORT (QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CMD_PORT (QIC02_TAPE_PORT)
+#define MTN_QIC02_DATA_PORT (QIC02_TAPE_PORT)
+
+#define MTN_W_SELECT_DMA_PORT (QIC02_TAPE_PORT+2)
+#define MTN_R_DESELECT_DMA_PORT (QIC02_TAPE_PORT+2)
+#define MTN_W_DMA_WRITE_PORT (QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define MTN_QIC02_STAT_READY 0x02 /* active low */
+#define MTN_QIC02_STAT_EXCEPTION 0x04 /* active low */
+#define MTN_QIC02_STAT_MASK (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION)
+#define MTN_STAT_DMADONE 0x01 /* active high, DMA done */
+
+#define MTN_QIC02_STAT_RESETMASK 0x07 /* check RDY,EXC,DMADONE */
+#define MTN_QIC02_STAT_RESETVAL ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE)
+
+ /* CTL port bits */
+#define MTN_QIC02_CTL_RESET_NOT 0x80 /* drive reset, active low */
+#define MTN_QIC02_CTL_RESET 0x80 /* Fodder #definition to keep gcc happy */
+
+#define MTN_QIC02_CTL_ONLINE 0x40 /* Put drive on line */
+#define MTN_QIC02_CTL_REQUEST 0x20 /* notify of new command */
+#define MTN_QIC02_CTL_IRQ_DRIVER 0x10 /* Enable IRQ tristate driver */
+#define MTN_QIC02_CTL_DMA_DRIVER 0x08 /* Enable DMA tristate driver */
+#define MTN_CTL_EXC_IEN 0x04 /* Exception interrupt enable */
+#define MTN_CTL_RDY_IEN 0x02 /* Ready interrupt enable */
+#define MTN_CTL_DNIEN 0x01 /* done-interrupt enable */
+
+#define MTN_CTL_ONLINE (MTN_QIC02_CTL_RESET_NOT | MTN_QIC02_CTL_IRQ_DRIVER | MTN_QIC02_CTL_DMA_DRIVER)
+
+
+#ifndef CONFIG_QIC02_DYNCONF
+
+# define QIC02_TAPE_DEBUG (qic02_tape_debug)
+
+# if QIC02_TAPE_IFC == WANGTEK
+# define QIC02_STAT_PORT WT_QIC02_STAT_PORT
+# define QIC02_CTL_PORT WT_QIC02_CTL_PORT
+# define QIC02_CMD_PORT WT_QIC02_CMD_PORT
+# define QIC02_DATA_PORT WT_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY WT_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION WT_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK WT_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK WT_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL WT_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET WT_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST WT_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA == 3
+# ifdef QIC02_TAPE_DMA3_FIX
+# define WT_CTL_DMA WT_CTL_DMA1
+# else
+# define WT_CTL_DMA WT_CTL_DMA3
+# endif
+# elif QIC02_TAPE_DMA == 1
+# define WT_CTL_DMA WT_CTL_DMA1
+# else
+# error Unsupported or incorrect DMA configuration.
+# endif
+
+# elif QIC02_TAPE_IFC == ARCHIVE
+# define QIC02_STAT_PORT AR_QIC02_STAT_PORT
+# define QIC02_CTL_PORT AR_QIC02_CTL_PORT
+# define QIC02_CMD_PORT AR_QIC02_CMD_PORT
+# define QIC02_DATA_PORT AR_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY AR_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION AR_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK AR_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK AR_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL AR_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET AR_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST AR_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */
+# error DMA channels other than 1 and 3 are not supported.
+# endif
+
+# elif QIC02_TAPE_IFC == MOUNTAIN
+# define QIC02_STAT_PORT MTN_QIC02_STAT_PORT
+# define QIC02_CTL_PORT MTN_QIC02_CTL_PORT
+# define QIC02_CMD_PORT MTN_QIC02_CMD_PORT
+# define QIC02_DATA_PORT MTN_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY MTN_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION MTN_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK MTN_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK MTN_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL MTN_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET MTN_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST MTN_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */
+# error DMA channels other than 1 and 3 are not supported.
+# endif
+
+# else
+# error No valid interface card specified!
+# endif /* QIC02_TAPE_IFC */
+
+
+ /* An ugly hack to make sure WT_CTL_DMA is defined even for the
+ * static, non-Wangtek case. The alternative was even worse.
+ */
+# ifndef WT_CTL_DMA
+# define WT_CTL_DMA WT_CTL_DMA1
# endif
-#else
-# error No valid interface card specified!
-#endif /* TAPE_QIC02_IFC */
+/*******************/
+
+#else /* !CONFIG_QIC02_DYNCONF */
+
+/* Now the runtime config version, using variables instead of constants.
+ *
+ * qic02_tape_dynconf is R/O for the kernel, set from userspace.
+ * qic02_tape_ccb is private to the driver, R/W.
+ */
+
+# define QIC02_TAPE_DRIVE (qic02_tape_dynconf.mt_type)
+# define QIC02_TAPE_IFC (qic02_tape_ccb.ifc_type)
+# define QIC02_TAPE_IRQ (qic02_tape_dynconf.irqnr)
+# define QIC02_TAPE_DMA (qic02_tape_dynconf.dmanr)
+# define QIC02_TAPE_PORT (qic02_tape_dynconf.port)
+# define WT_CTL_DMA (qic02_tape_ccb.dma_enable_value)
+# define QIC02_TAPE_DEBUG (qic02_tape_dynconf.debug)
+
+# define QIC02_STAT_PORT (qic02_tape_ccb.port_stat)
+# define QIC02_CTL_PORT (qic02_tape_ccb.port_ctl)
+# define QIC02_CMD_PORT (qic02_tape_ccb.port_cmd)
+# define QIC02_DATA_PORT (qic02_tape_ccb.port_data)
+
+# define QIC02_STAT_READY (qic02_tape_ccb.stat_ready)
+# define QIC02_STAT_EXCEPTION (qic02_tape_ccb.stat_exception)
+# define QIC02_STAT_MASK (qic02_tape_ccb.stat_mask)
+
+# define QIC02_STAT_RESETMASK (qic02_tape_ccb.stat_resetmask)
+# define QIC02_STAT_RESETVAL (qic02_tape_ccb.stat_resetval)
+
+# define QIC02_CTL_RESET (qic02_tape_ccb.ctl_reset)
+# define QIC02_CTL_REQUEST (qic02_tape_ccb.ctl_request)
+
+# define TP_HAVE_DENS (qic02_tape_dynconf.have_dens)
+# define TP_HAVE_BSF (qic02_tape_dynconf.have_bsf)
+# define TP_HAVE_FSR (qic02_tape_dynconf.have_fsr)
+# define TP_HAVE_BSR (qic02_tape_dynconf.have_bsr)
+# define TP_HAVE_EOD (qic02_tape_dynconf.have_eod)
+# define TP_HAVE_SEEK (qic02_tape_dynconf.have_seek)
+# define TP_HAVE_TELL (qic02_tape_dynconf.have_tell)
+# define TP_HAVE_RAS1 (qic02_tape_dynconf.have_ras1)
+# define TP_HAVE_RAS2 (qic02_tape_dynconf.have_ras2)
+
+#endif /* CONFIG_QIC02_DYNCONF */
+
+
+/* "Vendor Unique" codes */
+/* Archive seek & tell stuff */
+#define AR_QCMDV_TELL_BLK 0xAE /* read current block address */
+#define AR_QCMDV_SEEK_BLK 0xAD /* seek to specific block */
+#define AR_SEEK_BUF_SIZE 3 /* address is 3 bytes */
+
+
+
+/*
+ * Misc common stuff
+ */
/* Standard QIC-02 commands -- rev F. All QIC-02 drives must support these */
#define QCMD_SEL_1 0x01 /* select drive 1 */
#define QCMD_SEL_2 0x02 /* select drive 2 */
#define QCMD_SEL_3 0x04 /* select drive 3 */
#define QCMD_SEL_4 0x08 /* select drive 4 */
-#define QCMD_REWIND 0x21 /* rewind tape*/
+#define QCMD_REWIND 0x21 /* rewind tape */
#define QCMD_ERASE 0x22 /* erase tape */
#define QCMD_RETEN 0x24 /* retension tape */
#define QCMD_WRT_DATA 0x40 /* write data */
-/* "Vendor Unique" codes */
-#if defined(MT_ISARCHIVESC499) || defined(MT_ISARCHIVE_2150L)
-# define QCMDV_TELL_BLK 0xAE /* read current block address */
-# define QCMDV_SEEK_BLK 0xAD /* seek to specific block */
-# define SEEK_BUF_SIZE 3 /* address is 3 bytes */
-#endif
-
-
/* Optional, QFA (Quick File Access) commands.
* Not all drives support this, but those that do could use these commands
* to implement semi-non-sequential access. `mt fsf` would benefit from this.
* causing some incompatibility problems wrt std QIC-02 data exchange.
* It would be useful to cache the directory info, but that might be tricky
* to do in kernel-space. [Size constraints.]
- * Refer to QIC-02, appendix A for more information.
+ * Refer to the QIC-02 specs, appendix A for more information.
* I have no idea how other *nix variants implement QFA.
* I have no idea which drives support QFA and which don't.
*/
+
+/*
+ * Debugging flags
+ */
+#define TPQD_SENSE_TEXT 0x0001
+#define TPQD_SENSE_CNTS 0x0002
+#define TPQD_REWIND 0x0004
+#define TPQD_TERM_CYCLE 0x0008
+#define TPQD_IOCTLS 0x0010
+#define TPQD_DMAX 0x0020
+#define TPQD_BLKSZ 0x0040
+#define TPQD_MISC 0x0080
+
+#define TPQD_DEBUG 0x0100
+
+#define TPQD_DIAGS 0x1000
+
+#define TPQD_ALWAYS 0x8000
+
+#define TPQD_DEFAULT_FLAGS 0x01fc
+
+#define TPQDBG(f) ((QIC02_TAPE_DEBUG) & (TPQD_##f))
+
+
/* Minor device codes for tapes:
* |7|6|5|4|3|2|1|0|
* | \ | / \ | / |_____ 1=rewind on close, 0=no rewind on close
/* rewind is only done if data has been transfered */
#define TP_DENS(dev) ((MINOR(dev) >> 1) & 0x07) /* tape density */
#define TP_UNIT(dev) ((MINOR(dev) >> 4) & 0x07) /* unit number */
-#define TP_DIAGS(dev) (MINOR(dev) & 0x80) /* print excessive diagnostics */
+/* print excessive diagnostics */
+#define TP_DIAGS(dev) (QIC02_TAPE_DEBUG & TPQD_DIAGS)
/* status codes returned by a WTS_RDSTAT call */
struct tpstatus { /* sizeof(short)==2), LSB first */
#define REPORT_ERR1 (TP_ILL|TP_NDT|TP_MBD|TP_PAR)
+/* exception numbers */
#define EXC_UNKNOWN 0 /* (extra) Unknown exception code */
-#define EXC_NCART 1 /* No cartridge */
-#define EXC_NDRV 2 /* No drive */
+#define EXC_NDRV 1 /* No drive */
+#define EXC_NCART 2 /* No cartridge */
#define EXC_WP 3 /* Write protected */
#define EXC_EOM 4 /* EOM */
#define EXC_RWA 5 /* read/write abort */
#define TIM_R (8*60*HZ) /* 8 minutes (retensioning) */
#define TIM_F (2*3600*HZ) /* est. 1.2hr for full tape read/write+2 retens */
-#define TIMERON(t) timer_table[TAPE_QIC02_TIMER].expires = jiffies + (t); \
- timer_active |= (1<<TAPE_QIC02_TIMER)
-#define TIMEROFF timer_active &= ~(1<<TAPE_QIC02_TIMER)
-#define TIMERCONT timer_active |= (1<<TAPE_QIC02_TIMER)
+#define TIMERON(t) timer_table[QIC02_TAPE_TIMER].expires = jiffies + (t); \
+ timer_active |= (1<<QIC02_TAPE_TIMER)
+#define TIMEROFF timer_active &= ~(1<<QIC02_TAPE_TIMER)
+#define TIMERCONT timer_active |= (1<<QIC02_TAPE_TIMER)
typedef char flag;
#endif
+/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
+ * kernel space, you could decrease it to 1, or if you got a very
+ * slow machine, you could increase it up to 128 blocks. Less kernel
+ * buffer blocks result in more context-switching.
+ */
+#define NR_BLK_BUF 20 /* max 128 blocks */
+#define TAPE_BLKSIZE 512 /* streamer tape block size (fixed) */
+#define TPQBUF_SIZE (TAPE_BLKSIZE*NR_BLK_BUF) /* buffer size */
-extern long tape_qic02_init(long); /* for kernel/mem.c */
+
+#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */
+#define BOGUS_IRQ 32009
+
+/* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
+ * as an interim solution for QIC-02 until DDI is fully implemented.
+ */
+struct mtconfiginfo {
+ long mt_type; /* drive type */
+ long ifc_type; /* interface card type */
+ unsigned short irqnr; /* IRQ number to use */
+ unsigned short dmanr; /* DMA channel to use */
+ unsigned short port; /* IO port base address */
+
+ unsigned long debug; /* debugging flags */
+
+ unsigned have_dens:1;
+ unsigned have_bsf:1;
+ unsigned have_fsr:1;
+ unsigned have_bsr:1;
+ unsigned have_eod:1;
+ unsigned have_seek:1;
+ unsigned have_tell:1;
+ unsigned have_ras1:1;
+ unsigned have_ras2:1;
+ unsigned have_ras3:1;
+ unsigned have_qfa:1;
+
+ unsigned pad1:5;
+ char reserved[10];
+};
+
+
+/* This is internal data, filled in based on the ifc_type field given
+ * by the user. Everex is mapped to Wangtek with a different
+ * `dma_enable_value', if dmanr==3.
+ */
+struct qic02_ccb {
+ long ifc_type;
+
+ unsigned short port_stat; /* Status port address */
+ unsigned short port_ctl; /* Control port address */
+ unsigned short port_cmd; /* Command port address */
+ unsigned short port_data; /* Data port address */
+
+ /* status register bits */
+ unsigned short stat_ready; /* drive ready */
+ unsigned short stat_exception; /* drive signals exception */
+ unsigned short stat_mask;
+ unsigned short stat_resetmask;
+ unsigned short stat_resetval;
+
+ /* control register bits */
+ unsigned short ctl_reset; /* reset drive */
+ unsigned short ctl_request; /* latch command */
+
+ /* This is used to change the DMA3 behaviour */
+ unsigned short dma_enable_value;
+};
-#endif /* CONFIG_TAPE_QIC02 */
+extern long qic02_tape_init(long); /* for mem.c */
+
+
+#endif /* CONFIG_QIC02_TAPE */
#endif /* _LINUX_TPQIC02_H */
+
* 'tty.h' defines some structures used by tty_io.c and some defines.
*/
+#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/termios.h>
#include <linux/tqueue.h>
#include <asm/system.h>
+
/*
* Note: don't mess with NR_PTYS until you understand the tty minor
* number allocation game...
extern int vt_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
+#endif /* __KERNEL__ */
#endif
--- /dev/null
+#ifndef LINUX_UMSDOS_FS_H
+#define LINUX_UMSDOS_FS_H
+
+#define UMSDOS_VERSION 0
+#define UMSDOS_RELEASE 3
+
+#ifndef LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
+/* This is the file acting as a directory extension */
+#define UMSDOS_EMD_FILE "--linux-.---"
+#define UMSDOS_EMD_NAMELEN 12
+#define UMSDOS_PSDROOT_NAME "linux"
+#define UMSDOS_PSDROOT_LEN 5
+
+struct umsdos_fake_info {
+ char fname[13];
+ int len;
+};
+
+#define UMSDOS_MAXNAME 220
+/* This structure is 256 bytes large, depending on the name, only part */
+/* of it is written to disk */
+struct umsdos_dirent {
+ unsigned char name_len; /* if == 0, then this entry is not used */
+ unsigned char flags; /* UMSDOS_xxxx */
+ unsigned short nlink; /* How many hard links point to this entry */
+ uid_t uid; /* Owner user id */
+ gid_t gid; /* Group id */
+ time_t atime; /* Access time */
+ time_t mtime; /* Last modification time */
+ time_t ctime; /* Creation time */
+ dev_t rdev; /* major and minor number of a device */
+ /* special file */
+ umode_t mode; /* Standard UNIX permissions bits + type of */
+ char spare[12]; /* unused bytes for future extensions */
+ /* file, see linux/stat.h */
+ char name[UMSDOS_MAXNAME]; /* Not '\0' terminated */
+ /* but '\0' padded, so it will allow */
+ /* for adding news fields in this record */
+ /* by reducing the size of name[] */
+};
+#define UMSDOS_HIDDEN 1 /* Never show this entry in directory search */
+#define UMSDOS_HLINK 2 /* It is a (pseudo) hard link */
+
+/* #Specification: EMD file / record size
+ Entry are 64 bytes wide in the EMD file. It allows for a 30 characters
+ name. If a name is longer, contiguous entries are allocated. So a
+ umsdos_dirent may span multiple records.
+*/
+#define UMSDOS_REC_SIZE 64
+
+/* Translation between MSDOS name and UMSDOS name */
+struct umsdos_info{
+ int msdos_reject; /* Tell if the file name is invalid for MSDOS */
+ /* See umsdos_parse */
+ struct umsdos_fake_info fake;
+ struct umsdos_dirent entry;
+ off_t f_pos; /* offset of the entry in the EMD file */
+ /* or offset where the entry may be store */
+ /* if it is a new entry */
+ int recsize; /* Record size needed to store entry */
+};
+
+/* Definitions for ioctl (number randomly chosen) */
+/* The next ioctl commands operate only on the DOS directory */
+/* The file umsdos_progs/umsdosio.c contain a string table */
+/* based on the order of those definition. Keep it in sync */
+#define UMSDOS_READDIR_DOS 1234 /* Do a readdir of the DOS directory */
+#define UMSDOS_UNLINK_DOS 1235 /* Erase in the DOS directory only */
+#define UMSDOS_RMDIR_DOS 1236 /* rmdir in the DOS directory only */
+#define UMSDOS_STAT_DOS 1237 /* Get info about a file */
+/* The next ioctl commands operate only on the EMD file */
+#define UMSDOS_CREAT_EMD 1238 /* Create a file */
+#define UMSDOS_UNLINK_EMD 1239 /* unlink (rmdir) a file */
+#define UMSDOS_READDIR_EMD 1240 /* read the EMD file only. */
+#define UMSDOS_GETVERSION 1241 /* Get the release number of UMSDOS */
+#define UMSDOS_INIT_EMD 1242 /* Create the EMD file if not there */
+#define UMSDOS_DOS_SETUP 1243 /* Set the defaults of the MsDOS driver */
+
+#ifndef _SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+struct umsdos_ioctl{
+ struct dirent dos_dirent;
+ struct umsdos_dirent umsdos_dirent;
+ struct stat stat;
+ char version,release;
+};
+
+/* Different macros to access struct umsdos_dirent */
+#define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0)
+
+#ifdef __KERNEL__
+
+extern struct inode_operations umsdos_dir_inode_operations;
+extern struct file_operations umsdos_file_operations;
+extern struct inode_operations umsdos_file_inode_operations;
+extern struct inode_operations umsdos_file_inode_operations_no_bmap;
+extern struct inode_operations umsdos_symlink_inode_operations;
+
+#include <linux/umsdos_fs.p>
+
+#endif /* __KERNEL__ */
+
+#endif
--- /dev/null
+/* check.c 20/07/94 10.08.36 */
+void check_page_tables (void);
+/* dir.c 22/07/94 01.06.58 */
+int UMSDOS_dir_read (struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count);
+void umsdos_lookup_patch (struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry,
+ off_t emd_pos);
+int umsdos_inode2entry (struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry);
+int umsdos_locate_path (struct inode *inode, char *path);
+int umsdos_is_pseudodos (struct inode *dir, const char *name, int len);
+int UMSDOS_lookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
+/* emd.c 22/07/94 01.06.38 */
+int umsdos_readdir_kmem (struct inode *inode,
+ struct file *filp,
+ struct dirent *dirent,
+ int count);
+int umsdos_file_read_kmem (struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count);
+int umsdos_file_write_kmem (struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count);
+int umsdos_emd_dir_write (struct inode *emd_dir,
+ struct file *filp,
+ char *buf,
+ int count);
+int umsdos_emd_dir_read (struct inode *emd_dir,
+ struct file *filp,
+ char *buf,
+ int count);
+struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
+int umsdos_emd_dir_readentry (struct inode *emd_dir,
+ struct file *filp,
+ struct umsdos_dirent *entry);
+int umsdos_writeentry (struct inode *dir,
+ struct inode *emd_dir,
+ struct umsdos_info *info,
+ int free_entry);
+int umsdos_newentry (struct inode *dir, struct umsdos_info *info);
+int umsdos_newhidden (struct inode *dir, struct umsdos_info *info);
+int umsdos_delentry (struct inode *dir,
+ struct umsdos_info *info,
+ int isdir);
+int umsdos_isempty (struct inode *dir);
+int umsdos_findentry (struct inode *dir,
+ struct umsdos_info *info,
+ int expect);
+/* file.c 20/07/94 10.08.36 */
+/* inode.c 20/07/94 10.08.36 */
+void UMSDOS_put_inode (struct inode *inode);
+void UMSDOS_put_super (struct super_block *sb);
+void UMSDOS_statfs (struct super_block *sb, struct statfs *buf);
+int umsdos_real_lookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+void umsdos_setup_dir_inode (struct inode *inode);
+void umsdos_set_dirinfo (struct inode *inode,
+ struct inode *dir,
+ off_t f_pos);
+int umsdos_isinit (struct inode *inode);
+void umsdos_patch_inode (struct inode *inode,
+ struct inode *dir,
+ off_t f_pos);
+int umsdos_get_dirowner (struct inode *inode, struct inode **result);
+void UMSDOS_read_inode (struct inode *inode);
+void UMSDOS_write_inode (struct inode *inode);
+int UMSDOS_notify_change (int flags, struct inode *inode);
+struct super_block *UMSDOS_read_super (struct super_block *s,
+ void *data,
+ int silent);
+/* ioctl.c 20/07/94 10.08.36 */
+int UMSDOS_ioctl_dir (struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data);
+/* mangle.c 20/07/94 10.08.36 */
+void umsdos_manglename (struct umsdos_info *info);
+int umsdos_evalrecsize (int len);
+int umsdos_parse (const char *fname, int len, struct umsdos_info *info);
+/* namei.c 22/07/94 00.59.28 */
+void umsdos_lockcreate (struct inode *dir);
+void umsdos_startlookup (struct inode *dir);
+void umsdos_unlockcreate (struct inode *dir);
+void umsdos_endlookup (struct inode *dir);
+int UMSDOS_symlink (struct inode *dir,
+ const char *name,
+ int len,
+ const char *symname);
+int UMSDOS_link (struct inode *oldinode,
+ struct inode *dir,
+ const char *name,
+ int len);
+int UMSDOS_create (struct inode *dir,
+ const char *name,
+ int len,
+ int mode,
+ struct inode **result);
+int UMSDOS_mkdir (struct inode *dir,
+ const char *name,
+ int len,
+ int mode);
+int UMSDOS_mknod (struct inode *dir,
+ const char *name,
+ int len,
+ int mode,
+ int rdev);
+int UMSDOS_rmdir (struct inode *dir, const char *name, int len);
+int UMSDOS_unlink (struct inode *dir, const char *name, int len);
+int UMSDOS_rename (struct inode *old_dir,
+ const char *old_name,
+ int old_len,
+ struct inode *new_dir,
+ const char *new_name,
+ int new_len);
+/* rdir.c 20/07/94 10.08.38 */
+int UMSDOS_rlookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+/* symlink.c 22/07/94 00.59.10 */
--- /dev/null
+#ifndef UMSDOS_FS_I_H
+#define UMSDOS_FS_I_H
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <linux/msdos_fs_i.h>
+#include <linux/pipe_fs_i.h>
+
+/* #Specification: strategy / in memory inode
+ Here is the information specific to the inode of the UMSDOS file
+ system. This information is added to the end of the standard struct
+ inode. Each file system has its own extension to struct inode,
+ so do the umsdos file system.
+
+ The strategy is to have the umsdos_inode_info as a superset of
+ the msdos_inode_info, since most of the time the job is done
+ by the msdos fs code.
+
+ So we duplicate the msdos_inode_info, and add our own info at the
+ end.
+
+ For all file type (and directory) the inode has a reference to:
+ the directory which hold this entry: i_dir_owner
+ The EMD file of i_dir_owner: i_emd_owner
+ The offset in this EMD file of the entry: pos
+
+ For directory, we also have a reference to the inode of its
+ own EMD file. Also, we have dir_locking_info to help synchronise
+ file creation and file lookup. This data is sharing space with
+ the pipe_inode_info not used by directory. See also msdos_fs_i.h
+ for more information about pipe_inode_info and msdos_inode_info.
+
+ Special file and fifo do have an inode which correspond to an
+ empty MSDOS file.
+
+ symlink are processed mostly like regular file. The content is the
+ link.
+
+ fifos add there own extension to the inode. I have reserved some
+ space for fifos side by side with msdos_inode_info. This is just
+ to for the show, because msdos_inode_info already include the
+ pipe_inode_info.
+
+ The UMSDOS specific extension is placed after the union.
+*/
+struct dir_locking_info {
+ struct wait_queue *p;
+ short int looking; /* How many process doing a lookup */
+ short int creating; /* Is there any creation going on here */
+ /* Only one at a time, although one */
+ /* may recursivly lock, so it is a counter */
+ long pid; /* pid of the process owning the creation */
+ /* lock */
+};
+struct umsdos_inode_info {
+ union {
+ struct msdos_inode_info msdos_info;
+ struct pipe_inode_info pipe_info;
+ struct dir_locking_info dir_info;
+ }u; /* Simply a filler, never referenced by fs/umsdos/... */
+ unsigned long i_dir_owner; /* Inode of the dir which hold this */
+ /* entry */
+ unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */
+ off_t pos; /* Entry offset in the emd_owner file */
+ /* The rest is used only if this inode describe a directory */
+ unsigned long i_emd_dir; /* Inode of the EMD file of this inode */
+};
+
+#endif
+#ifndef UMSDOS_FS_I_H
+#define UMSDOS_FS_I_H
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <linux/msdos_fs_i.h>
+#include <linux/pipe_fs_i.h>
+
+/* #Specification: strategy / in memory inode
+ Here is the information specific to the inode of the UMSDOS file
+ system. This information is added to the end of the standard struct
+ inode. Each file system has its own extension to struct inode,
+ so do the umsdos file system.
+
+ The strategy is to have the umsdos_inode_info as a superset of
+ the msdos_inode_info, since most of the time the job is done
+ by the msdos fs code.
+
+ So we duplicate the msdos_inode_info, and add our own info at the
+ end.
+
+ For all file type (and directory) the inode has a reference to:
+ the directory which hold this entry: i_dir_owner
+ The EMD file of i_dir_owner: i_emd_owner
+ The offset in this EMD file of the entry: pos
+
+ For directory, we also have a reference to the inode of its
+ own EMD file. Also, we have dir_locking_info to help synchronise
+ file creation and file lookup. This data is sharing space with
+ the pipe_inode_info not used by directory. See also msdos_fs_i.h
+ for more information about pipe_inode_info and msdos_inode_info.
+
+ Special file and fifo do have an inode which correspond to an
+ empty MSDOS file.
+
+ symlink are processed mostly like regular file. The content is the
+ link.
+
+ fifos add there own extension to the inode. I have reserved some
+ space for fifos side by side with msdos_inode_info. This is just
+ to for the show, because msdos_inode_info already include the
+ pipe_inode_info.
+
+ The UMSDOS specific extension is placed after the union.
+*/
+struct dir_locking_info {
+ struct wait_queue *p;
+ short int looking; /* How many process doing a lookup */
+ short int creating; /* Is there any creation going on here */
+ /* Only one at a time, although one */
+ /* may recursivly lock, so it is a counter */
+ long pid; /* pid of the process owning the creation */
+ /* lock */
+};
+struct umsdos_inode_info {
+ union {
+ struct msdos_inode_info msdos_info;
+ struct pipe_inode_info pipe_info;
+ struct dir_locking_info dir_info;
+ }u; /* Simply a filler, never referenced by fs/umsdos/... */
+ unsigned long i_dir_owner; /* Inode of the dir which hold this */
+ /* entry */
+ unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */
+ off_t pos; /* Entry offset in the emd_owner file */
+ /* The rest is used only if this inode describe a directory */
+ unsigned long i_emd_dir; /* Inode of the EMD file of this inode */
+};
+
+#endif
setup();
sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES);
+
+ #ifdef CONFIG_UMSDOS_FS
+ {
+ /*
+ When mounting a umsdos fs as root, we detect
+ the pseudo_root (/linux) and initialise it here.
+ pseudo_root is defined in fs/umsdos/inode.c
+ */
+ extern struct inode *pseudo_root;
+ if (pseudo_root != NULL){
+ current->fs->root = pseudo_root;
+ current->fs->pwd = pseudo_root;
+ }
+ }
+ #endif
+
(void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
(void) dup(0);
/* Channel n is busy iff dma_chan_busy[n] != 0.
- * DMA0 is reserved for DRAM refresh, I think.
- * DMA4 is reserved for cascading (?).
+ * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
+ * DMA4 is reserved for cascading.
*/
static volatile unsigned int dma_chan_busy[MAX_DMA_CHANNELS] = {
- 1, 0, 0, 0, 1, 0, 0, 0
+ 0, 0, 0, 0, 1, 0, 0, 0
};
}
if (pg & PAGE_COW)
pg &= ~PAGE_RW;
- if (in_swap_cache(pg)) {
- swap_cache_invalidate(pg);
+ if (delete_from_swap_cache(pg))
pg |= PAGE_DIRTY;
- }
*new_page_table = pg;
*old_page_table = pg;
mem_map[MAP_NR(pg)]++;
if (old_page & PAGE_RW)
goto end_wp_page;
vma->vm_task->mm->min_flt++;
- prot = (old_page & ~PAGE_MASK) | PAGE_RW;
+ prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY;
old_page &= PAGE_MASK;
if (mem_map[MAP_NR(old_page)] != 1) {
if (new_page) {
invalidate();
return;
}
- *(unsigned long *) pte |= PAGE_RW;
+ *(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY;
invalidate();
if (new_page)
free_page(new_page);
goto check_wp_fault_by_hand;
for (;;) {
struct vm_area_struct * next;
+ if (!(vma->vm_page_prot & PAGE_USER))
+ goto bad_area;
if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
goto bad_area;
if (vma->vm_end - start >= size)
return 0;
from |= PAGE_DIRTY;
*(unsigned long *) from_page = from;
- swap_cache_invalidate(from);
+ delete_from_swap_cache(from);
invalidate();
}
mem_map[MAP_NR(from)]++;
if (bit < 32)
current->screen_bitmap |= 1 << bit;
}
+ if (!(vma->vm_page_prot & PAGE_USER))
+ goto bad_area;
if (error_code & PAGE_PRESENT) {
- if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT)
+ if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW)))
goto bad_area;
#ifdef CONFIG_TEST_VERIFY_AREA
if (regs->cs == KERNEL_CS)
do_wp_page(vma, address, error_code);
return;
}
- if (!(vma->vm_page_prot & PAGE_PRESENT))
- goto bad_area;
do_no_page(vma, address, error_code);
return;
unsigned long);
/*
* description of effects of mapping type and prot in current implementation.
- * this is due to the current handling of page faults in memory.c. the expected
+ * this is due to the limited x86 page protection hardware. The expected
* behavior is in parens:
*
* map_type prot
* PROT_NONE PROT_READ PROT_WRITE PROT_EXEC
- * MAP_SHARED r: (no) yes r: (yes) yes r: (no) yes r: (no) no
- * w: (no) yes w: (no) copy w: (yes) yes w: (no) no
- * x: (no) no x: (no) no x: (no) no x: (yes) no
+ * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes
+ * w: (no) no w: (no) no w: (yes) yes w: (no) no
+ * x: (no) no x: (no) yes x: (no) yes x: (yes) yes
*
- * MAP_PRIVATE r: (no) yes r: (yes) yes r: (no) yes r: (no) no
- * w: (no) copy w: (no) copy w: (copy) copy w: (no) no
- * x: (no) no x: (no) no x: (no) no x: (yes) no
+ * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes
+ * w: (no) no w: (no) no w: (copy) copy w: (no) no
+ * x: (no) no x: (no) yes x: (no) yes x: (yes) yes
*
*/
-#define CODE_SPACE(addr) \
- (PAGE_ALIGN(addr) < current->start_code + current->end_code)
-
int do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off)
{
mask |= PAGE_COPY;
else
mask |= PAGE_SHARED;
- if (!mask)
- return -EINVAL;
+ if (!mask) /* PROT_NONE */
+ mask = PAGE_PRESENT; /* none of PAGE_USER, PAGE_RW, PAGE_COW */
do_munmap(addr, len); /* Clear old maps */
* This file should contain most things doing the swapping from/to disk.
* Started 18.12.91
*/
-#define SWAP_CACHE_INFO
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
+#include <linux/fs.h>
#include <asm/system.h> /* for cli()/sti() */
#include <asm/bitops.h>
extern int shm_swap (int);
unsigned long *swap_cache;
-static unsigned long swap_cache_size;
#ifdef SWAP_CACHE_INFO
-static unsigned long add_calls_total = 0;
-static unsigned long add_calls_success = 0;
-static unsigned long del_calls_total = 0;
-static unsigned long del_calls_success = 0;
-static unsigned long find_calls_total = 0;
-static unsigned long find_calls_success = 0;
-
-extern inline void show_swap_cache_info (void)
+unsigned long swap_cache_add_total = 0;
+unsigned long swap_cache_add_success = 0;
+unsigned long swap_cache_del_total = 0;
+unsigned long swap_cache_del_success = 0;
+unsigned long swap_cache_find_total = 0;
+unsigned long swap_cache_find_success = 0;
+
+extern inline void show_swap_cache_info(void)
{
printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
- add_calls_total, add_calls_success,
- del_calls_total, del_calls_success,
- find_calls_total, find_calls_success);
+ swap_cache_add_total, swap_cache_add_success,
+ swap_cache_del_total, swap_cache_del_success,
+ swap_cache_find_total, swap_cache_find_success);
}
#endif
-extern inline unsigned long init_swap_cache (unsigned long mem_start,
- unsigned long mem_end)
-{
- mem_start = (mem_start + 15) & ~15;
- swap_cache = (unsigned long *) mem_start;
- swap_cache_size = mem_end >> PAGE_SHIFT;
- memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
-#ifdef SWAP_CACHE_INFO
- printk("%ld bytes for swap cache allocated\n",
- swap_cache_size * sizeof(unsigned long));
-#endif
-
- return (unsigned long) (swap_cache + swap_cache_size);
-}
-
-extern inline long find_in_swap_cache (unsigned long addr)
-{
- unsigned long entry;
-
-#ifdef SWAP_CACHE_INFO
- find_calls_total++;
-#endif
- __asm__ __volatile__ (
- "xchgl %0,%1\n"
- : "=m" (swap_cache[addr >> PAGE_SHIFT]),
- "=r" (entry)
- : "0" (swap_cache[addr >> PAGE_SHIFT]),
- "1" (0)
- );
-#ifdef SWAP_CACHE_INFO
- if (entry)
- find_calls_success++;
-#endif
-
- return entry;
-}
-
-extern inline int add_to_swap_cache (unsigned long addr, unsigned long entry)
+extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry)
{
struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
#ifdef SWAP_CACHE_INFO
- add_calls_total++;
+ swap_cache_add_total++;
#endif
if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
__asm__ __volatile__ (
printk("swap_cache: replacing non-NULL entry\n");
}
#ifdef SWAP_CACHE_INFO
- add_calls_success++;
+ swap_cache_add_success++;
#endif
return 1;
}
return 0;
}
-
-extern inline int delete_from_swap_cache(unsigned long addr)
+static unsigned long init_swap_cache(unsigned long mem_start,
+ unsigned long mem_end)
{
- unsigned long entry;
-
+ unsigned long swap_cache_size;
+
+ mem_start = (mem_start + 15) & ~15;
+ swap_cache = (unsigned long *) mem_start;
+ swap_cache_size = mem_end >> PAGE_SHIFT;
+ memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
#ifdef SWAP_CACHE_INFO
- del_calls_total++;
+ printk("%ld bytes for swap cache allocated\n",
+ swap_cache_size * sizeof(unsigned long));
#endif
- __asm__ __volatile__ (
- "xchgl %0,%1\n"
- : "=m" (swap_cache[addr >> PAGE_SHIFT]),
- "=r" (entry)
- : "0" (swap_cache[addr >> PAGE_SHIFT]),
- "1" (0)
- );
- if (entry) {
-#ifdef SWAP_CACHE_INFO
- del_calls_success++;
-#endif
- swap_free(entry);
- return 1;
- }
- return 0;
+
+ return (unsigned long) (swap_cache + swap_cache_size);
}
-
void rw_swap_page(int rw, unsigned long entry, char * buf)
{
unsigned long type, offset;
if (p->swap_device) {
ll_rw_page(rw,p->swap_device,offset,buf);
} else if (p->swap_file) {
+ struct inode *swapf = p->swap_file;
unsigned int zones[8];
- unsigned int block;
- int i, j;
-
- block = offset << (12 - p->swap_file->i_sb->s_blocksize_bits);
-
- for (i=0, j=0; j< PAGE_SIZE ; i++, j +=p->swap_file->i_sb->s_blocksize)
- if (!(zones[i] = bmap(p->swap_file,block++))) {
- printk("rw_swap_page: bad swap file\n");
- return;
+ int i;
+ if (swapf->i_op->bmap == NULL
+ && swapf->i_op->smap != NULL){
+ /*
+ With MsDOS, we use msdos_smap which return
+ a sector number (not a cluster or block number).
+ It is a patch to enable the UMSDOS project.
+ Other people are working on better solution.
+
+ It sounds like ll_rw_swap_file defined
+ it operation size (sector size) based on
+ PAGE_SIZE and the number of block to read.
+ So using bmap ou smap should work even if
+ smap will requiered more blocks.
+ */
+ int j;
+ unsigned int block = offset << 3;
+
+ for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
+ if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
+ printk("rw_swap_page: bad swap file\n");
+ return;
+ }
}
- ll_rw_swap_file(rw,p->swap_file->i_dev, zones, i,buf);
+ }else{
+ int j;
+ unsigned int block = offset
+ << (12 - swapf->i_sb->s_blocksize_bits);
+
+ for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
+ if (!(zones[i] = bmap(swapf,block++))) {
+ printk("rw_swap_page: bad swap file\n");
+ return;
+ }
+ }
+ ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
} else
printk("re_swap_page: no swap file or device\n");
if (offset && !clear_bit(offset,p->swap_lockmap))