diff -u --recursive --new-file v2.1.73/linux/CREDITS linux/CREDITS --- v2.1.73/linux/CREDITS Wed Dec 10 11:12:42 1997 +++ linux/CREDITS Fri Dec 19 15:24:20 1997 @@ -958,9 +958,10 @@ S: University of Stuttgart, Germany and S: Ecole Nationale Superieure des Telecommunications, Paris -N: Martin von Loewis +N: Martin von Löwis E: loewis@informatik.hu-berlin.de D: script binary format +D: NTFS driver N: Mark Lord E: mlord@pobox.com diff -u --recursive --new-file v2.1.73/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.73/linux/Documentation/Configure.help Fri Dec 19 15:52:47 1997 +++ linux/Documentation/Configure.help Fri Dec 19 15:24:20 1997 @@ -4749,6 +4749,20 @@ want). The module is called hpfs.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say N. + +Windows NT NTFS support (read only) +CONFIG_NTFS_FS + NTFS is the file system of Microsoft Windows NT. Say Y if you want + to access partitions using this file system. The Linux NTFS driver + supports most of the mount options of the VFAT driver, see + Documentation/filesystems/ntfs.txt. There is an experimental + write support available; use at your own risk. + +NTFS read-write support (experimental) +CONFIG_NTFS_RW + The read-write support in NTFS is far from being complete and well + tested. If you enable this, be prepared to recover the NTFS volume + from tape. System V and Coherent filesystem support CONFIG_SYSV_FS diff -u --recursive --new-file v2.1.73/linux/Documentation/filesystems/ntfs.txt linux/Documentation/filesystems/ntfs.txt --- v2.1.73/linux/Documentation/filesystems/ntfs.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/filesystems/ntfs.txt Fri Dec 19 15:24:20 1997 @@ -0,0 +1,30 @@ +NTFS Overview +============= + +To mount an NTFS volume, use the filesystem type 'ntfs'. The driver +currently works only in read-only mode, with no fault-tolerance +supported. If you enable the experimental write support, make sure +you can recover from a complete loss of data. For ftdisk support, +limit success was reported with volume sets on top of the md driver, +although mirror and stripe sets should work as well - if the md +driver can be talked into using the same lay-out as Windows NT. + +The ntfs driver supports the following mount options: +iocharset=name Character set to use when returning file names. + Unlike VFAT, NTFS suppresses names that contain + unconvertible characters +utf8= Use UTF-8 for converting file names +uni_xlate=,2 Use the VFAT-style encoding for file names outside + the current character set. A boolean value will + enable the feature, a value of 2 will enable the + encoding as documented in vfat.txt: + ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12), +uid= +gid= +umask= These options work as documented in mount(8). + By default, the files are owned by root and + not readable by somebody else. +posix= If enabled, the file system distinguishes between + upper and lower case. The 8.3 alias names are presented + as hard links instead of being suppressed. + diff -u --recursive --new-file v2.1.73/linux/Documentation/stallion.txt linux/Documentation/stallion.txt --- v2.1.73/linux/Documentation/stallion.txt Tue Sep 23 16:48:46 1997 +++ linux/Documentation/stallion.txt Fri Dec 19 12:30:53 1997 @@ -2,10 +2,10 @@ Stallion Multiport Serial Driver Readme --------------------------------------- -Copyright (C) 1994-1997, Stallion Technologies (support@stallion.oz.au). +Copyright (C) 1994-1997, Stallion Technologies (support@stallion.com). -Version: 5.3.4 -Date: 15SEP97 +Version: 5.4.1 +Date: 19DEC97 @@ -13,32 +13,36 @@ There are two drivers that work with the different families of Stallion multiport serial boards. One is for the Stallion smart boards - that is -EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent -multiport boards - EasyConnection 8/64, ONboard and Brumby. +EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for +the true Stallion intelligent multiport boards - EasyConnection 8/64 +(ISA and EISA), ONboard and Brumby. If you are using any of the Stallion intelligent multiport boards (Brumby, -ONboard, EasyConnection 8/64) with Linux you will need to get the driver -utility package. This package is available at most of the Linux archive -sites (and on CD's that contain these archives). The file will be called -stallion-X.X.X.tar.gz where X.X.X will be the version number. In particular -this package contains the board embedded executable images that are -required for these boards. It also contains the downloader program. -These boards cannot be used without this. - -The following ftp sites (and their mirrors) definitely have the stallion -driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu. - -ftp.stallion.com:/drivers/ata5/Linux/stallion-5.3.1.tar.gz -tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.3.1.tar.gz -sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.3.1.tar.gz +ONboard, EasyConnection 8/64 (ISA or EISA)) with Linux you will need to +get the driver utility package. This package is available at most of the +Linux archive sites (and on CD's that contain these archives). The file +will be called stallion-X.X.X.tar.gz where X.X.X will be the version +number. In particular this package contains the board embedded executable +images that are required for these boards. It also contains the downloader +program. These boards cannot be used without this. + +The Stallion Technologies ftp site, ftp.stallion.com, will always have +the latest version of the driver utility package. Other sites that usually +have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their +mirrors. + +ftp.stallion.com:/drivers/ata5/Linux/stallion-5.4.0.tar.gz +tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.4.0.tar.gz +sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.0.tar.gz As of the printing of this document the latest version of the driver -utility package is 5.3.1. If a later version is now available then you +utility package is 5.4.0. If a later version is now available then you should use the latest version. -If you are using the EasyIO or EasyConnection 8/32 boards then you don't -need this package. Although it does have a handy script to create the -/dev device nodes for these boards, and a serial stats display program. +If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI +boards then you don't need this package. Although it does have a handy +script to create the /dev device nodes for these boards, and a serial stats +display program. If you require DIP switch settings, EISA/MCA configuration files, or any other information related to Stallion boards then have a look at Stallion's @@ -63,7 +67,7 @@ card driver (the stallion.c driver) supports any combination of EasyIO and EasyConnection 8/32 boards (up to a total of 4). The intelligent driver supports any combination of ONboards, Brumbys, Stallions and EasyConnection -8/64 boards (up to a total of 4). +8/64 (ISA and EISA) boards (up to a total of 4). To set up the driver(s) for the boards that you want to use you need to edit the appropriate driver file and add configuration entries. @@ -76,7 +80,8 @@ (the comments before this structure should help) - save and exit -If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do: +If using ONboard, Brumby, Stallion or EasyConnection 8/64 (ISA or EISA) +boards then do: vi /usr/src/linux/drivers/char/istallion.c - find the definition of the stli_brdconf array (of structures) near the top of the file @@ -114,11 +119,11 @@ configuration files for *all* the EasyIO and EasyConnection 8/32 boards that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 EISA configuration files required are supplied by Stallion Technologies - on the EASY Utilities floppy (usually supplied in the box with the board - when purchased. If not, you can pick it up from Stallion's FTP site, - ftp.stallion.com). You will need to edit the board resources to choose - level triggered interrupts, and make sure to set each board's interrupt - to the same IRQ number. + on the EASY Utilities floppy diskette (usually supplied in the box with + the board when purchased. If not, you can pick it up from Stallion's FTP + site, ftp.stallion.com). You will need to edit the board resources to + choose level triggered interrupts, and make sure to set each board's + interrupt to the same IRQ number. You must complete both the above steps for this to work. When you reboot or load the driver your EasyIO and EasyConnection 8/32 boards will be @@ -149,8 +154,8 @@ the IRQ is software programmable, so if there is a conflict you may need to change the IRQ used for a board in the stallion.c configuration structure. There are no interrupts to worry about for ONboard, Brumby or EasyConnection -8/64 boards. The memory region on EasyConnection 8/64 and ONboard boards is -software programmable, but not on the Brumby boards. +8/64 (ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and +ONboard boards is software programmable, but not on the Brumby boards. diff -u --recursive --new-file v2.1.73/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.73/linux/MAINTAINERS Fri Dec 19 15:52:48 1997 +++ linux/MAINTAINERS Fri Dec 19 12:30:53 1997 @@ -433,6 +433,13 @@ L: linux-kernel@vger.rutgers.edu ? S: Supported +STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS +P: Greg Ungerer +M: support@stallion.oz.au +M: gerg@stallion.com +W: http://www.stallion.com +S: Supported + MOUSE AND MISC DEVICES [GENERAL] P: Alessandro Rubini M: rubini@ipvvis.unipv.it diff -u --recursive --new-file v2.1.73/linux/Makefile linux/Makefile --- v2.1.73/linux/Makefile Fri Dec 19 15:52:48 1997 +++ linux/Makefile Fri Dec 19 09:49:10 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 73 +SUBLEVEL = 74 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.73/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.1.73/linux/drivers/block/ide-cd.c Fri Dec 19 15:52:56 1997 +++ linux/drivers/block/ide-cd.c Fri Dec 19 09:35:05 1997 @@ -180,6 +180,8 @@ * * 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml * 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open" + * 4.08 Dec 18, 1997 -- spew less noise when tray is empty + * -- fix speed display for ACER 24X, 18X * *************************************************************************/ @@ -243,6 +245,15 @@ failed_command->c[0] == SCMD_READ_SUBCHANNEL) return; } + if (reqbuf->error_code == 0x70 && reqbuf->sense_key == 0x02 + && reqbuf->asc == 0x3a && reqbuf->ascq == 0x00) + { + /* + * No disc in drive ("Medium not present"), + * so keep the noise level down to a dull roar. + */ + return; + } #if VERBOSE_IDE_CD_ERRORS { @@ -466,7 +477,7 @@ /* Check for tray open. */ if (sense_key == NOT_READY) { cdrom_saw_media_change (drive); - +#if 0 /* let the upper layers do the complaining */ /* Print an error message to the syslog. Exception: don't print anything if this is a read subchannel command. This is @@ -474,12 +485,13 @@ with this command, and we don't want to uselessly fill up the syslog. */ if (pc->c[0] != SCMD_READ_SUBCHANNEL) - printk ("%s: tray open or drive not ready\n", - drive->name); + printk ("%s: tray open or drive not ready\n", drive->name); +#endif } else if (sense_key == UNIT_ATTENTION) { /* Check for media change. */ cdrom_saw_media_change (drive); printk ("%s: media changed\n", drive->name); + return 0; } else { /* Otherwise, print an error. */ ide_dump_status (drive, "packet command error", @@ -2734,12 +2746,13 @@ } } - if (drive->id && drive->id->model[0]) { - CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176; - CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176; - } else { /* no-name ACERs (AOpen) have it backwards */ + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) { CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176; CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176; } printk ("%s: ATAPI %dX CDROM", diff -u --recursive --new-file v2.1.73/linux/drivers/char/istallion.c linux/drivers/char/istallion.c --- v2.1.73/linux/drivers/char/istallion.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/char/istallion.c Fri Dec 19 12:30:54 1997 @@ -77,6 +77,8 @@ #define BRD_ECPE 24 #define BRD_ECPMC 25 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 #define BRD_BRUMBY BRD_BRUMBY4 @@ -166,8 +168,9 @@ * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stli_drvname = "Stallion Intelligent Multiport Serial Driver"; -static char *stli_drvversion = "5.3.4"; +static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; +static char *stli_drvname = "istallion"; +static char *stli_drvversion = "5.4.1"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -291,6 +294,8 @@ "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /* @@ -484,9 +489,9 @@ /* * Define the maximal baud rate, and the default baud base for ports. */ -#define STL_MAXBAUD 921600 +#define STL_MAXBAUD 460800 #define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CLOSEDELAY (5 * HZ / 10) /*****************************************************************************/ @@ -539,18 +544,18 @@ static void stli_stop(struct tty_struct *tty); static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); +static void stli_breakctl(struct tty_struct *tty, int state); +static void stli_waituntilsent(struct tty_struct *tty, int timeout); +static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); +static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos); -static inline int stli_initbrds(void); -static inline int stli_initecp(stlibrd_t *brdp); -static inline int stli_initonb(stlibrd_t *brdp); -static inline int stli_findeisabrds(void); -static inline int stli_initports(stlibrd_t *brdp); -static int stli_eisamemprobe(stlibrd_t *brdp); static int stli_brdinit(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); -static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count); -static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count); +static int stli_memopen(struct inode *ip, struct file *fp); +static int stli_memclose(struct inode *ip, struct file *fp); +static ssize_t stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp); +static ssize_t stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp); static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp); static void stli_poll(unsigned long arg); @@ -573,6 +578,7 @@ static int stli_setserial(stliport_t *portp, struct serial_struct *sp); static int stli_getbrdstats(combrd_t *bp); static int stli_getportstats(stliport_t *portp, comstats_t *cp); +static int stli_portcmdstats(stliport_t *portp); static int stli_clrportstats(stliport_t *portp, comstats_t *cp); static int stli_getportstruct(unsigned long arg); static int stli_getbrdstruct(unsigned long arg); @@ -613,6 +619,13 @@ static stliport_t *stli_getport(int brdnr, int panelnr, int portnr); +static inline int stli_initbrds(void); +static inline int stli_initecp(stlibrd_t *brdp); +static inline int stli_initonb(stlibrd_t *brdp); +static inline int stli_findeisabrds(void); +static inline int stli_eisamemprobe(stlibrd_t *brdp); +static inline int stli_initports(stlibrd_t *brdp); + /*****************************************************************************/ /* @@ -629,8 +642,8 @@ NULL, stli_memioctl, NULL, - NULL, - NULL, + stli_memopen, + stli_memclose, NULL }; @@ -690,7 +703,8 @@ printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle, + stli_drvversion); save_flags(flags); cli(); @@ -707,12 +721,14 @@ i = tty_unregister_driver(&stli_serial); j = tty_unregister_driver(&stli_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stli_tmpwritebuf != (char *) NULL) kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE); @@ -733,7 +749,9 @@ } iounmap(brdp->membase); - if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC)) + if ((brdp->brdtype == BRD_ECP) || + (brdp->brdtype == BRD_ECPE) || + (brdp->brdtype == BRD_ECPMC)) release_region(brdp->iobase, ECP_IOSIZE); else release_region(brdp->iobase, ONB_IOSIZE); @@ -767,7 +785,8 @@ int brdnr, portnr, rc; #if DEBUG - printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -789,6 +808,8 @@ if (portp->devnr < 1) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * Check if this port is in the middle of closing. If so then wait * until it is closed then return error status based on flag settings. @@ -853,10 +874,10 @@ return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -903,10 +924,14 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -927,10 +952,8 @@ if (tty == stli_txcooktty) stli_flushchars(tty); tty->closing = 1; - if (test_bit(ST_TXBUSY, &portp->state)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); portp->flags &= ~ASYNC_INITIALIZED; brdp = stli_brds[portp->brdnr]; @@ -940,7 +963,8 @@ if (test_bit(ST_CMDING, &portp->state)) set_bit(ST_DOSIGS, &portp->state); else - stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); } clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); @@ -951,7 +975,6 @@ stli_flushbuffer(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -960,8 +983,10 @@ wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -992,23 +1017,27 @@ memset(&nt, 0, sizeof(asynotify_t)); nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK); nt.signal = SG_DCD; - if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, + sizeof(asynotify_t), 0)) < 0) return(rc); tty = portp->tty; if (tty == (struct tty_struct *) NULL) return(-ENODEV); stli_mkasyport(portp, &aport, tty->termios); - if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, + sizeof(asyport_t), 0)) < 0) return(rc); set_bit(ST_GETSIGS, &portp->state); - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, + sizeof(asysigs_t), 1)) < 0) return(rc); if (test_and_clear_bit(ST_GETSIGS, &portp->state)) portp->sigs = stli_mktiocm(portp->asig.sigvalue); stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0)) < 0) return(rc); return(0); @@ -1032,7 +1061,8 @@ int rc; #if DEBUG - printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif /* @@ -1065,7 +1095,8 @@ cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1111,7 +1142,8 @@ int rc; #if DEBUG - printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); + printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", + (int) brdp, (int) portp, (int) arg, wait); #endif save_flags(flags); @@ -1139,7 +1171,8 @@ cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1182,7 +1215,9 @@ unsigned long flags; #if DEBUG - printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -1252,12 +1287,13 @@ static void stli_delay(int len) { #if DEBUG - printk("stl_delay(len=%d)\n", len); + printk("stli_delay(len=%d)\n", len); #endif if (len > 0) { current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + len; schedule(); + current->state = TASK_RUNNING; } } @@ -1274,7 +1310,8 @@ int rc, doclocal; #if DEBUG - printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp); + printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", + (int) brdp, (int) portp, (int) filp); #endif rc = 0; @@ -1291,16 +1328,18 @@ save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) { stli_mkasysigs(&portp->asig, 1, 1); - if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0)) < 0) break; } - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -1308,8 +1347,8 @@ break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (signal_pending(current)) { @@ -1347,10 +1386,12 @@ unsigned long flags; #if DEBUG - printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stli_tmpwritebuf == (char *) NULL)) return(0); if (tty == stli_txcooktty) stli_flushchars(tty); @@ -1384,7 +1425,8 @@ tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; - len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1); + len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : + (tail - head - 1); count = MIN(len, count); EBRDDISABLE(brdp); @@ -1439,7 +1481,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1570,7 +1613,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); @@ -1736,12 +1780,14 @@ copy_from_user(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1763,7 +1809,8 @@ int rc; #if DEBUG - printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1778,7 +1825,7 @@ return(0); if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { if (tty->flags & (1 << TTY_IO_ERROR)) return(-EIO); } @@ -1786,85 +1833,92 @@ rc = 0; switch (cmd) { - case TCSBRK: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - if (! arg) { - lval = 250; - rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0); - } - } - break; - case TCSBRKP: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - lval = (arg ? (arg * 100) : 250); - rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0); - } - break; case TIOCGSOFTCAR: rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int *) arg); + (unsigned int *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { + if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, + &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); lval = stli_mktiocm(portp->asig.sigvalue); put_user(lval, (unsigned int *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & TIOCM_RTS) ? 1 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & TIOCM_RTS) ? 0 : -1)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & TIOCM_RTS) ? 1 : 0)); + rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, + &portp->asig, sizeof(asysigs_t), 0); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stli_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) - rc = stli_setserial(portp, (struct serial_struct *) arg); + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) + rc = stli_setserial(portp, (struct serial_struct *)arg); break; case STL_GETPFLAG: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned long))) == 0) put_user(portp->pflag, (unsigned int *) arg); break; case STL_SETPFLAG: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned long))) == 0) { get_user(portp->pflag, (unsigned int *) arg); stli_setport(portp); } break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stli_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1912,13 +1966,15 @@ return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stli_mkasyport(portp, &aport, tiosp); stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1); - stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, + sizeof(asysigs_t), 0); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) @@ -2010,7 +2066,7 @@ memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STOPFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2044,7 +2100,7 @@ memset(&actrl, 0, sizeof(asyctrl_t)); actrl.txctrl = CT_STARTFLOW; #if 0 - stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t)); + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); #endif } @@ -2118,7 +2174,8 @@ set_bit(ST_DOFLUSHTX, &portp->state); set_bit(ST_DOFLUSHRX, &portp->state); } else { - stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0); + stli_sendcmd(brdp, portp, A_SETSIGNALSF, + &portp->asig, sizeof(asysigs_t), 0); } } restore_flags(flags); @@ -2126,7 +2183,6 @@ clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -2178,17 +2234,258 @@ ftype |= FLUSHRX; clear_bit(ST_DOFLUSHRX, &portp->state); } - stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0); + stli_sendcmd(brdp, portp, A_FLUSH, &ftype, + sizeof(unsigned long), 0); } restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } /*****************************************************************************/ +static void stli_breakctl(struct tty_struct *tty, int state) +{ + stlibrd_t *brdp; + stliport_t *portp; + long arg, savestate, savetime; + +#if DEBUG + printk("stli_breakctl(tty=%x,state=%d)\n", (int) tty, state); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return; + +/* + * Due to a bug in the tty send_break() code we need to preserve + * the current process state and timeout... + */ + savetime = current->timeout; + savestate = current->state; + + arg = (state == -1) ? BREAKON : BREAKOFF; + stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + + current->timeout = savetime; + current->state = savestate; +} + +/*****************************************************************************/ + +static void stli_waituntilsent(struct tty_struct *tty, int timeout) +{ + stliport_t *portp; + unsigned long tend; + +#if DEBUG + printk("stli_waituntilsent(tty=%x,timeout=%x)\n", (int) tty, timeout); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (test_bit(ST_TXBUSY, &portp->state)) { + if (signal_pending(current)) + break; + stli_delay(2); + if (jiffies >= tend) + break; + } +} + +/*****************************************************************************/ + +static void stli_sendxchar(struct tty_struct *tty, char ch) +{ + stlibrd_t *brdp; + stliport_t *portp; + asyctrl_t actrl; + +#if DEBUG + printk("stli_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return; + + memset(&actrl, 0, sizeof(asyctrl_t)); + if (ch == STOP_CHAR(tty)) { + actrl.rxctrl = CT_STOPFLOW; + } else if (ch == START_CHAR(tty)) { + actrl.rxctrl = CT_STARTFLOW; + } else { + actrl.txctrl = CT_SENDCHR; + actrl.tximdch = ch; + } + + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); +} + +/*****************************************************************************/ + +#define MAXLINE 80 + +/* + * Format info for a specified port. The line is deliberately limited + * to 80 characters. (If it is too long it will be truncated, if too + * short then padded with spaces). + */ + +static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos) +{ + char *sp, *uart; + int rc, cnt; + + rc = stli_portcmdstats(portp); + + uart = "UNKNOWN"; + if (brdp->state & BST_STARTED) { + switch (stli_comstats.hwid) { + case 0: uart = "2681"; break; + case 1: uart = "SC26198"; break; + default: uart = "CD1400"; break; + } + } + + sp = pos; + sp += sprintf(sp, "%d: uart:%s ", portnr, uart); + + if ((brdp->state & BST_STARTED) && (rc >= 0)) { + sp += sprintf(sp, "tx:%d rx:%d", (int) stli_comstats.txtotal, + (int) stli_comstats.rxtotal); + + if (stli_comstats.rxframing) + sp += sprintf(sp, " fe:%d", + (int) stli_comstats.rxframing); + if (stli_comstats.rxparity) + sp += sprintf(sp, " pe:%d", + (int) stli_comstats.rxparity); + if (stli_comstats.rxbreaks) + sp += sprintf(sp, " brk:%d", + (int) stli_comstats.rxbreaks); + if (stli_comstats.rxoverrun) + sp += sprintf(sp, " oe:%d", + (int) stli_comstats.rxoverrun); + + cnt = sprintf(sp, "%s%s%s%s%s ", + (stli_comstats.signals & TIOCM_RTS) ? "|RTS" : "", + (stli_comstats.signals & TIOCM_CTS) ? "|CTS" : "", + (stli_comstats.signals & TIOCM_DTR) ? "|DTR" : "", + (stli_comstats.signals & TIOCM_CD) ? "|DCD" : "", + (stli_comstats.signals & TIOCM_DSR) ? "|DSR" : ""); + *sp = ' '; + sp += cnt; + } + + for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++) + *sp++ = ' '; + if (cnt >= MAXLINE) + pos[(MAXLINE - 2)] = '+'; + pos[(MAXLINE - 1)] = '\n'; + + return(MAXLINE); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stli_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + stlibrd_t *brdp; + stliport_t *portp; + int brdnr, portnr, totalport; + int curoff, maxoff; + char *pos; + +#if DEBUG + printk("stli_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x," + "data=%x\n", (int) page, (int) start, (int) off, count, + (int) eof, (int) data); +#endif + + pos = page; + totalport = 0; + curoff = 0; + + if (off == 0) { + pos += sprintf(pos, "%s: version %s", stli_drvtitle, + stli_drvversion); + while (pos < (page + MAXLINE - 1)) + *pos++ = ' '; + *pos++ = '\n'; + } + curoff = MAXLINE; + +/* + * We scan through for each board, panel and port. The offset is + * calculated on the fly, and irrelevant ports are skipped. + */ + for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { + brdp = stli_brds[brdnr]; + if (brdp == (stlibrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + + maxoff = curoff + (brdp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + continue; + } + + totalport = brdnr * STL_MAXPORTS; + for (portnr = 0; (portnr < brdp->nrports); portnr++, + totalport++) { + portp = brdp->ports[portnr]; + if (portp == (stliport_t *) NULL) + continue; + if (off >= (curoff += MAXLINE)) + continue; + if ((pos - page + MAXLINE) > count) + goto stli_readdone; + pos += stli_portinfo(brdp, portp, totalport, pos); + } + } + + *eof = 1; + +stli_readdone: + *start = page; + return(pos - page); +} + +/*****************************************************************************/ + /* * Generic send command routine. This will send a message to the slave, * of the specified type with the specified argument. Must be very @@ -2207,7 +2504,9 @@ unsigned long flags; #if DEBUG - printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); + printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," + "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, + (int) arg, size, copyback); #endif save_flags(flags); @@ -2231,7 +2530,8 @@ cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; set_bit(ST_CMDING, &portp->state); EBRDDISABLE(brdp); @@ -2320,7 +2620,8 @@ int cmd; if (test_bit(ST_DOSIGS, &portp->state)) { - if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state)) + if (test_bit(ST_DOFLUSHTX, &portp->state) && + test_bit(ST_DOFLUSHRX, &portp->state)) cmd = A_SETSIGNALSF; else if (test_bit(ST_DOFLUSHTX, &portp->state)) cmd = A_SETSIGNALSFTX; @@ -2331,11 +2632,13 @@ clear_bit(ST_DOFLUSHTX, &portp->state); clear_bit(ST_DOFLUSHRX, &portp->state); clear_bit(ST_DOSIGS, &portp->state); - memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t)); + memcpy((void *) &(cp->args[0]), (void *) &portp->asig, + sizeof(asysigs_t)); cp->status = 0; cp->cmd = cmd; set_bit(ST_CMDING, &portp->state); - } else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) { + } else if (test_bit(ST_DOFLUSHTX, &portp->state) || + test_bit(ST_DOFLUSHRX, &portp->state)) { cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0); cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0); clear_bit(ST_DOFLUSHTX, &portp->state); @@ -2416,7 +2719,8 @@ if (rc > 0) rc--; if (portp->argp != (void *) NULL) { - memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize); + memcpy(portp->argp, (void *) &(cp->args[0]), + portp->argsize); portp->argp = (void *) NULL; } cp->status = 0; @@ -2443,12 +2747,14 @@ oldsigs = portp->sigs; portp->sigs = stli_mktiocm(nt.sigvalue); clear_bit(ST_GETSIGS, &portp->state); - if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) + if ((portp->sigs & TIOCM_CD) && + ((oldsigs & TIOCM_CD) == 0)) wake_up_interruptible(&portp->open_wait); - if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { + if ((oldsigs & TIOCM_CD) && + ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { if (tty != (struct tty_struct *) NULL) queue_task(&portp->tqhangup, &tq_scheduler); } @@ -2460,7 +2766,8 @@ clear_bit(ST_TXBUSY, &portp->state); if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { if (tty != (struct tty_struct *) NULL) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { (tty->ldisc.write_wakeup)(tty); EBRDENABLE(brdp); } @@ -2474,12 +2781,10 @@ tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = 0; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); EBRDENABLE(brdp); } -#endif tty_schedule_flip(tty); } } @@ -2538,7 +2843,8 @@ * 8 service bits at a time in the inner loop, so we can bypass * the lot if none of them want service. */ - memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize); + memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), + bitsize); memset(&slavebits[0], 0, bitsize); slavebitchange = 0; @@ -2621,7 +2927,8 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp) { #if DEBUG - printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp); + printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", + (int) portp, (int) pp, (int) tiosp); #endif memset(pp, 0, sizeof(asyport_t)); @@ -2632,7 +2939,7 @@ pp->baudout = tiosp->c_cflag & CBAUD; if (pp->baudout & CBAUDEX) { pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 5)) + if ((pp->baudout < 1) || (pp->baudout > 4)) tiosp->c_cflag &= ~CBAUDEX; else pp->baudout += 15; @@ -2643,6 +2950,10 @@ pp->baudout = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) pp->baudout = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + pp->baudout = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + pp->baudout = 460800; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) pp->baudout = (portp->baud_base / portp->custom_divisor); } @@ -2723,7 +3034,10 @@ /* * Transfer any persistent flags into the asyport structure. */ - pp->pflag = portp->pflag; + pp->pflag = (portp->pflag & 0xffff); + pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0; + pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0; + pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0; } /*****************************************************************************/ @@ -2872,11 +3186,14 @@ unsigned char val; #if DEBUG - printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -2959,11 +3276,14 @@ unsigned char val; #if DEBUG - printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3013,7 +3333,9 @@ unsigned char val; if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3088,11 +3410,14 @@ void *ptr; #if DEBUG - printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % ONB_ATPAGESIZE); @@ -3175,11 +3500,14 @@ unsigned char val; #if DEBUG - printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); + printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3241,11 +3569,14 @@ unsigned char val; #if DEBUG - printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { @@ -3299,11 +3630,14 @@ void *ptr; #if DEBUG - printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); + printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, + (int) offset); #endif if (offset > brdp->memsize) { - printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), brd=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); ptr = 0; } else { ptr = brdp->membase + (offset % STAL_PAGESIZE); @@ -3341,6 +3675,7 @@ cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; + char *name; int panelnr, nrports; #if DEBUG @@ -3353,10 +3688,9 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); - if (check_region(brdp->iobase, ECP_IOSIZE)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->iobase); - } + if (check_region(brdp->iobase, ECP_IOSIZE)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); /* * Based on the specific board type setup the common vars to access @@ -3375,6 +3709,7 @@ brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; + name = "serial(EC8/64)"; break; case BRD_ECPE: @@ -3388,6 +3723,7 @@ brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; + name = "serial(EC8/64-EI)"; break; case BRD_ECPMC: @@ -3401,6 +3737,7 @@ brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; + name = "serial(EC8/64-MCA)"; break; default: @@ -3430,10 +3767,11 @@ EBRDDISABLE(brdp); #if 0 - printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", + printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0], - (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3], - (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6], + (int) sig.panelid[1], (int) sig.panelid[2], + (int) sig.panelid[3], (int) sig.panelid[4], + (int) sig.panelid[5], (int) sig.panelid[6], (int) sig.panelid[7]); #endif @@ -3448,6 +3786,7 @@ status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; + brdp->panelids[panelnr] = status; nrports = (status & ECH_PNL16PORT) ? 16 : 8; if ((nrports == 16) && ((status & ECH_PNLXPID) == 0)) @@ -3458,7 +3797,7 @@ brdp->nrpanels++; } - request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)"); + request_region(brdp->iobase, ECP_IOSIZE, name); brdp->state |= BST_FOUND; return(0); } @@ -3474,6 +3813,7 @@ { cdkonbsig_t sig; cdkonbsig_t *sigsp; + char *name; int i; #if DEBUG @@ -3486,10 +3826,9 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); - if (check_region(brdp->iobase, ONB_IOSIZE)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->iobase); - } + if (check_region(brdp->iobase, ONB_IOSIZE)) + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->iobase); /* * Based on the specific board type setup the common vars to access @@ -3516,6 +3855,7 @@ brdp->enabval = ONB_MEMENABHI; else brdp->enabval = ONB_MEMENABLO; + name = "serial(ONBoard)"; break; case BRD_ONBOARDE: @@ -3529,6 +3869,7 @@ brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; + name = "serial(ONBoard/E)"; break; case BRD_BRUMBY4: @@ -3544,6 +3885,7 @@ brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; + name = "serial(Brumby)"; break; case BRD_STALLION: @@ -3557,6 +3899,7 @@ brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; + name = "serial(Stallion)"; break; default: @@ -3592,7 +3935,7 @@ #endif if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) || - (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) + (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) return(-ENODEV); /* @@ -3611,7 +3954,7 @@ } brdp->panels[0] = brdp->nrports; - request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)"); + request_region(brdp->iobase, ONB_IOSIZE, name); brdp->state |= BST_FOUND; return(0); } @@ -3646,14 +3989,16 @@ nrdevs = hdrp->nrdevs; #if 0 - printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n", + printk("%s(%d): CDK version %d.%d.%d --> " + "nrdevs=%d memp=%x hostp=%x slavep=%x\n", __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification, hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep); #endif if (nrdevs < (brdp->nrports + 1)) { - printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs); + printk("STALLION: slave failed to allocate memory for all " + "devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } brdp->nrdevs = nrdevs; @@ -3765,20 +4110,27 @@ case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: - printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]); + printk("STALLION: %s board type not supported in this driver\n", + stli_brdnames[brdp->brdtype]); return(ENODEV); default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); return(ENODEV); } if ((brdp->state & BST_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr); + printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", + stli_brdnames[brdp->brdtype], brdp->brdnr, + brdp->iobase, (int) brdp->memaddr); return(ENODEV); } stli_initports(brdp); - printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports); + printk("STALLION: %s found, unit=%d io=%x mem=%x " + "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], + brdp->brdnr, brdp->iobase, (int) brdp->memaddr, + brdp->nrpanels, brdp->nrports); return(0); } @@ -3789,7 +4141,7 @@ * might be. This is a bit if hack, but it is the best we can do. */ -__initfunc(static int stli_eisamemprobe(stlibrd_t *brdp)) +static inline int stli_eisamemprobe(stlibrd_t *brdp) { cdkecpsig_t ecpsig, *ecpsigp; cdkonbsig_t onbsig, *onbsigp; @@ -3841,15 +4193,19 @@ continue; if (brdp->brdtype == BRD_ECPE) { - ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__); + ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t)); if (ecpsig.magic == ECP_MAGIC) foundit = 1; } else { - onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__); + onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, + CDK_SIGADDR, __LINE__); memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t)); - if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) && - (onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3)) + if ((onbsig.magic0 == ONB_MAGIC0) && + (onbsig.magic1 == ONB_MAGIC1) && + (onbsig.magic2 == ONB_MAGIC2) && + (onbsig.magic3 == ONB_MAGIC3)) foundit = 1; } @@ -3870,7 +4226,9 @@ if (! foundit) { brdp->memaddr = 0; brdp->membase = 0; - printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12)); + printk("STALLION: failed to probe shared memory region for " + "%s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], + (brdp->iobase >> 12)); return(-ENODEV); } return(0); @@ -3936,7 +4294,8 @@ * info table. */ if (stli_nrbrds >= STL_MAXBRDS) { - printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS); + printk("STALLION: no room for more probed boards, " + "maximum supported %d\n", STL_MAXBRDS); break; } @@ -3946,7 +4305,8 @@ */ brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -3988,7 +4348,8 @@ #endif if (stli_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); stli_nrbrds = STL_MAXBRDS; } @@ -4000,7 +4361,8 @@ confp = &stli_brdconf[i]; brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlibrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlibrd_t)); @@ -4034,7 +4396,9 @@ nxtbrdp = stli_brds[j]; if (nxtbrdp == (stlibrd_t *) NULL) continue; - if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) { + if ((brdp->membase >= nxtbrdp->membase) && + (brdp->membase <= (nxtbrdp->membase + + nxtbrdp->memsize - 1))) { stli_shared++; break; } @@ -4066,7 +4430,7 @@ * the slave image (and debugging :-) */ -static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count) +static ssize_t stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp) { unsigned long flags; void *memptr; @@ -4074,10 +4438,11 @@ int brdnr, size, n; #if DEBUG - printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memread(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp, + (int) buf, count, (int) offp); #endif - brdnr = MINOR(ip->i_rdev); + brdnr = MINOR(fp->f_dentry->d_inode->i_rdev); if (brdnr >= stli_nrbrds) return(-ENODEV); brdp = stli_brds[brdnr]; @@ -4115,7 +4480,7 @@ * the slave image (and debugging :-) */ -static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count) +static ssize_t stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp) { unsigned long flags; void *memptr; @@ -4124,10 +4489,11 @@ int brdnr, size, n; #if DEBUG - printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp, + (int) buf, count, (int) offp); #endif - brdnr = MINOR(ip->i_rdev); + brdnr = MINOR(fp->f_dentry->d_inode->i_rdev); if (brdnr >= stli_nrbrds) return(-ENODEV); brdp = stli_brds[brdnr]; @@ -4226,31 +4592,28 @@ * what port to get stats for (used through board control device). */ -static int stli_getportstats(stliport_t *portp, comstats_t *cp) +static int stli_portcmdstats(stliport_t *portp) { unsigned long flags; stlibrd_t *brdp; int rc; - if (portp == (stliport_t *) NULL) { - copy_from_user(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); - if (portp == (stliport_t *) NULL) - return(-ENODEV); - } + memset(&stli_comstats, 0, sizeof(comstats_t)); + if (portp == (stliport_t *) NULL) + return(-ENODEV); brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if (brdp->state & BST_STARTED) { - if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0) + if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, + &stli_cdkstats, sizeof(asystats_t), 1)) < 0) return(rc); } else { memset(&stli_cdkstats, 0, sizeof(asystats_t)); } - memset(&stli_comstats, 0, sizeof(comstats_t)); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; @@ -4293,6 +4656,37 @@ stli_comstats.hwid = stli_cdkstats.hwid; stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals); + return(0); +} + +/*****************************************************************************/ + +/* + * Return the port stats structure to user app. A NULL port struct + * pointer passed in means that we need to find out from the app + * what port to get stats for (used through board control device). + */ + +static int stli_getportstats(stliport_t *portp, comstats_t *cp) +{ + stlibrd_t *brdp; + int rc; + + if (portp == (stliport_t *) NULL) { + copy_from_user(&stli_comstats, cp, sizeof(comstats_t)); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); + if (portp == (stliport_t *) NULL) + return(-ENODEV); + } + + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return(-ENODEV); + + if ((rc = stli_portcmdstats(portp)) < 0) + return(rc); + copy_to_user(cp, &stli_comstats, sizeof(comstats_t)); return(0); } @@ -4310,7 +4704,8 @@ if (portp == (stliport_t *) NULL) { copy_from_user(&stli_comstats, cp, sizeof(comstats_t)); - portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); + portp = stli_getport(stli_comstats.brd, stli_comstats.panel, + stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } @@ -4375,6 +4770,27 @@ /*****************************************************************************/ /* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stli_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static int stli_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +/* * The "staliomem" device is also required to do some special operations on * the board. We need to be able to send an interrupt to the board, * reset it, and start/stop it. @@ -4386,7 +4802,8 @@ int brdnr, rc, done; #if DEBUG - printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); #endif /* @@ -4397,27 +4814,34 @@ switch (cmd) { case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_getportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_getportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stli_clrportstats((stliport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stli_clrportstats((stliport_t *) NULL, + (comstats_t *) arg); done++; break; case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) rc = stli_getbrdstats((combrd_t *) arg); done++; break; case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stliport_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stliport_t))) == 0) rc = stli_getportstruct(arg); done++; break; case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlibrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlibrd_t))) == 0) rc = stli_getbrdstruct(arg); done++; break; @@ -4469,9 +4893,9 @@ /*****************************************************************************/ -__initfunc(int stli_init()) +__initfunc(int stli_init(void)) { - printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); stli_initbrds(); @@ -4480,10 +4904,12 @@ */ stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE); if (stli_txcookbuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STLI_TXBUFSIZE); /* * Set up a character driver for the shared memory region. We need this @@ -4498,6 +4924,7 @@ */ memset(&stli_serial, 0, sizeof(struct tty_driver)); stli_serial.magic = TTY_DRIVER_MAGIC; + stli_serial.driver_name = stli_drvname; stli_serial.name = stli_serialname; stli_serial.major = STL_SERIALMAJOR; stli_serial.minor_start = 0; @@ -4526,11 +4953,16 @@ stli_serial.start = stli_start; stli_serial.hangup = stli_hangup; stli_serial.flush_buffer = stli_flushbuffer; + stli_serial.break_ctl = stli_breakctl; + stli_serial.wait_until_sent = stli_waituntilsent; + stli_serial.send_xchar = stli_sendxchar; + stli_serial.read_proc = stli_readproc; stli_callout = stli_serial; stli_callout.name = stli_calloutname; stli_callout.major = STL_CALLOUTMAJOR; stli_callout.subtype = STL_DRVTYPCALLOUT; + stli_callout.read_proc = 0; if (tty_register_driver(&stli_serial)) printk("STALLION: failed to register serial driver\n"); diff -u --recursive --new-file v2.1.73/linux/drivers/char/stallion.c linux/drivers/char/stallion.c --- v2.1.73/linux/drivers/char/stallion.c Wed Dec 10 11:12:43 1997 +++ linux/drivers/char/stallion.c Fri Dec 19 12:30:54 1997 @@ -66,6 +66,8 @@ #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECHPCI 26 +#define BRD_ECH64PCI 27 +#define BRD_EASYIOPCI 28 /* * Define a configuration structure to hold the board configuration. @@ -101,34 +103,11 @@ int irqtype; } stlconf_t; -/*static stlconf_t stl_brdconf[] = { - { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }, -};*/ - -#ifdef MODULE -static char *brdtype[STL_MAXBRDS] = {"\0", }; -static int io[STL_MAXBRDS] = { 0, }; -static int secio[STL_MAXBRDS] = { 0, }; -static int irq[STL_MAXBRDS] = { 0, }; - -MODULE_PARM(brdtype, "1-" __MODULE_STRING(STL_MAXBRDS) "s"); -MODULE_PARM(io, "1-" __MODULE_STRING(STL_MAXBRDS) "i"); -MODULE_PARM(secio, "1-" __MODULE_STRING(STL_MAXBRDS) "i"); -MODULE_PARM(irq, "1-" __MODULE_STRING(STL_MAXBRDS) "i"); - -static stlconf_t stl_brdconf[STL_MAXBRDS]; -static int stl_nrbrds = 0; - -#else static stlconf_t stl_brdconf[] = { { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }, - { BRD_EASYIO, 0x2a8, 0, 0, 10, 0 }, - { BRD_EASYIO, 0x2b0, 0, 0, 10, 0 }, - { BRD_ECH, 0x2a0, 0x240, 0, 10, 0 }, }; static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); -#endif /*****************************************************************************/ @@ -162,8 +141,9 @@ * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ -static char *stl_drvname = "Stallion Multiport Serial Driver"; -static char *stl_drvversion = "5.3.4"; +static char *stl_drvtitle = "Stallion Multiport Serial Driver"; +static char *stl_drvname = "stallion"; +static char *stl_drvversion = "5.4.1"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -272,6 +252,8 @@ (char *) NULL, (char *) NULL, "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /*****************************************************************************/ @@ -320,12 +302,6 @@ #define ECH_ADDR2MASK 0x1e0 /* - * Define real Stallion PCI vemdor and device ID. - */ -#define PCI_VENDOR_ID_STALLION 0x124d -#define PCI_DEVICE_ID_ECHPCI832 0x0000 - -/* * Define the vector mapping bits for the programmable interrupt board * hardware. These bits encode the interrupt for the board to use - it * is software selectable (except the EIO-8M). @@ -354,9 +330,47 @@ outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ stl_brds[(brdnr)]->ioctrl); -#define STL_MAXBAUD 921600 -#define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CD1400MAXBAUD 230400 +#define STL_SC26198MAXBAUD 460800 + +#define STL_BAUDBASE 115200 +#define STL_CLOSEDELAY (5 * HZ / 10) + +/*****************************************************************************/ + +/* + * Define the Stallion PCI vendor and device IDs. + */ +#ifndef PCI_VENDOR_ID_STALLION +#define PCI_VENDOR_ID_STALLION 0x124d +#endif +#ifndef PCI_DEVICE_ID_ECHPCI832 +#define PCI_DEVICE_ID_ECHPCI832 0x0000 +#endif +#ifndef PCI_DEVICE_ID_ECHPCI864 +#define PCI_DEVICE_ID_ECHPCI864 0x0002 +#endif +#ifndef PCI_DEVICE_ID_EIOPCI +#define PCI_DEVICE_ID_EIOPCI 0x0003 +#endif + +/* + * Define structure to hold all Stallion PCI boards. + */ +typedef struct stlpcibrd { + unsigned short vendid; + unsigned short devid; + int brdtype; +} stlpcibrd_t; + +static stlpcibrd_t stl_pcibrds[] = { + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864, BRD_ECH64PCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI, BRD_EASYIOPCI }, + { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, BRD_ECHPCI }, + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, BRD_ECHPCI }, +}; + +static int stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t); /*****************************************************************************/ @@ -409,15 +423,19 @@ static void stl_stop(struct tty_struct *tty); static void stl_start(struct tty_struct *tty); static void stl_flushbuffer(struct tty_struct *tty); +static void stl_breakctl(struct tty_struct *tty, int state); +static void stl_waituntilsent(struct tty_struct *tty, int timeout); +static void stl_sendxchar(struct tty_struct *tty, char ch); static void stl_hangup(struct tty_struct *tty); +static int stl_memopen(struct inode *ip, struct file *fp); +static int stl_memclose(struct inode *ip, struct file *fp); static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); +static int stl_portinfo(stlport_t *portp, int portnr, char *pos); +static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data); -static inline int stl_initbrds(void); -static inline int stl_initeio(stlbrd_t *brdp); -static inline int stl_initech(stlbrd_t *brdp); static int stl_brdinit(stlbrd_t *brdp); static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp); -static int stl_mapirq(int irq); +static int stl_mapirq(int irq, char *name); static void stl_getserial(stlport_t *portp, struct serial_struct *sp); static int stl_setserial(stlport_t *portp, struct serial_struct *sp); static int stl_getbrdstats(combrd_t *bp); @@ -432,12 +450,18 @@ static void stl_echatintr(stlbrd_t *brdp); static void stl_echmcaintr(stlbrd_t *brdp); static void stl_echpciintr(stlbrd_t *brdp); +static void stl_echpci64intr(stlbrd_t *brdp); static void stl_offintr(void *private); static void *stl_memalloc(int len); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); +static inline int stl_initbrds(void); +static inline int stl_initeio(stlbrd_t *brdp); +static inline int stl_initech(stlbrd_t *brdp); + #ifdef CONFIG_PCI -static inline int stl_findpcibrds(void); +static inline int stl_findpcibrds(void); +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr); #endif /* @@ -455,15 +479,19 @@ static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx); static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx); static void stl_cd1400disableintrs(stlport_t *portp); -static void stl_cd1400sendbreak(stlport_t *portp, long len); +static void stl_cd1400sendbreak(stlport_t *portp, int len); static void stl_cd1400flowctrl(stlport_t *portp, int state); +static void stl_cd1400sendflow(stlport_t *portp, int state); static void stl_cd1400flush(stlport_t *portp); +static int stl_cd1400datastate(stlport_t *portp); static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase); static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase); static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr); static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr); static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr); +static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr); + /* * SC26198 uart specific handling functions. */ @@ -479,9 +507,12 @@ static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx); static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx); static void stl_sc26198disableintrs(stlport_t *portp); -static void stl_sc26198sendbreak(stlport_t *portp, long len); +static void stl_sc26198sendbreak(stlport_t *portp, int len); static void stl_sc26198flowctrl(stlport_t *portp, int state); +static void stl_sc26198sendflow(stlport_t *portp, int state); static void stl_sc26198flush(stlport_t *portp); +static int stl_sc26198datastate(stlport_t *portp); +static void stl_sc26198wait(stlport_t *portp); static void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty); static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase); static void stl_sc26198txisr(stlport_t *port); @@ -504,9 +535,11 @@ void (*enablerxtx)(stlport_t *portp, int rx, int tx); void (*startrxtx)(stlport_t *portp, int rx, int tx); void (*disableintrs)(stlport_t *portp); - void (*sendbreak)(stlport_t *portp, long len); + void (*sendbreak)(stlport_t *portp, int len); void (*flowctrl)(stlport_t *portp, int state); + void (*sendflow)(stlport_t *portp, int state); void (*flush)(stlport_t *portp); + int (*datastate)(stlport_t *portp); void (*intr)(stlpanel_t *panelp, unsigned int iobase); } uart_t; @@ -523,7 +556,9 @@ #define stl_disableintrs (* ((uart_t *) portp->uartp)->disableintrs) #define stl_sendbreak (* ((uart_t *) portp->uartp)->sendbreak) #define stl_flowctrl (* ((uart_t *) portp->uartp)->flowctrl) +#define stl_sendflow (* ((uart_t *) portp->uartp)->sendflow) #define stl_flush (* ((uart_t *) portp->uartp)->flush) +#define stl_datastate (* ((uart_t *) portp->uartp)->datastate) /*****************************************************************************/ @@ -541,7 +576,9 @@ stl_cd1400disableintrs, stl_cd1400sendbreak, stl_cd1400flowctrl, + stl_cd1400sendflow, stl_cd1400flush, + stl_cd1400datastate, stl_cd1400eiointr }; @@ -585,7 +622,9 @@ stl_sc26198disableintrs, stl_sc26198sendbreak, stl_sc26198flowctrl, + stl_sc26198sendflow, stl_sc26198flush, + stl_sc26198datastate, stl_sc26198intr }; @@ -626,8 +665,8 @@ NULL, stl_memioctl, NULL, - NULL, - NULL, + stl_memopen, + stl_memclose, NULL }; @@ -639,22 +678,9 @@ * Loadable module initialization stuff. */ -struct board_type_elem -{ - int type_id; - char *name; -}; - -struct board_type_elem board_types[] = { - { BRD_EASYIO, "easyio" }, - { BRD_ECH, "ech" }, - { BRD_ECHMC, "echmc" }, - { BRD_ECHPCI, "echpci" } }; - int init_module() { unsigned long flags; - int i, j, num_board_types; #if DEBUG printk("init_module()\n"); @@ -662,25 +688,6 @@ save_flags(flags); cli(); - - num_board_types = sizeof(board_types) / sizeof(struct board_type_elem); - for (i = 0; (i < STL_MAXBRDS && io[i]); i++) - { - stl_brdconf[stl_nrbrds].brdtype = 0; - for (j = 0; j < num_board_types; j++) - if(strcmp(board_types[j].name, brdtype[i]) == 0) - stl_brdconf[stl_nrbrds].brdtype = board_types[j].type_id; - if(stl_brdconf[stl_nrbrds].brdtype != 0) - { - stl_brdconf[stl_nrbrds].ioaddr1 = io[i]; - stl_brdconf[stl_nrbrds].ioaddr2 = secio[i]; - stl_brdconf[stl_nrbrds].memaddr = 0; - stl_brdconf[stl_nrbrds].irq = irq[i]; - stl_brdconf[stl_nrbrds].irqtype = 0; - stl_nrbrds++; - } - } - stl_init(); restore_flags(flags); @@ -701,7 +708,8 @@ printk("cleanup_module()\n"); #endif - printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion); + printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle, + stl_drvversion); save_flags(flags); cli(); @@ -715,12 +723,14 @@ i = tty_unregister_driver(&stl_serial); j = tty_unregister_driver(&stl_callout); if (i || j) { - printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j); + printk("STALLION: failed to un-register tty driver, " + "errno=%d,%d\n", -i, -j); restore_flags(flags); return; } if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem"))) - printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i); + printk("STALLION: failed to un-register serial memory device, " + "errno=%d\n", -i); if (stl_tmpwritebuf != (char *) NULL) kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE); @@ -729,33 +739,24 @@ brdp = stl_brds[i]; for (j = 0; (j < STL_MAXPANELS); j++) { panelp = brdp->panels[j]; - if (panelp != (stlpanel_t *) NULL) { - for (k = 0; (k < STL_PORTSPERPANEL); k++) { - portp = panelp->ports[k]; - if (portp != (stlport_t *) NULL) { - if (portp->tty != (struct tty_struct *) NULL) - stl_hangup(portp->tty); - if (portp->tx.buf != (char *) NULL) - kfree_s(portp->tx.buf, STL_TXBUFSIZE); - kfree_s(portp, sizeof(stlport_t)); - } - } - kfree_s(panelp, sizeof(stlpanel_t)); + if (panelp == (stlpanel_t *) NULL) + continue; + for (k = 0; (k < STL_PORTSPERPANEL); k++) { + portp = panelp->ports[k]; + if (portp == (stlport_t *) NULL) + continue; + if (portp->tty != (struct tty_struct *) NULL) + stl_hangup(portp->tty); + if (portp->tx.buf != (char *) NULL) + kfree_s(portp->tx.buf, STL_TXBUFSIZE); + kfree_s(portp, sizeof(stlport_t)); } - + kfree_s(panelp, sizeof(stlpanel_t)); } - if (brdp->brdtype == BRD_ECH) { - release_region(brdp->ioaddr1, 2); - release_region(brdp->ioaddr2, 32); - } else if (brdp->brdtype == BRD_ECHPCI) { - release_region(brdp->ioaddr1, 4); - release_region(brdp->ioaddr2, 8); - } else if (brdp->brdtype == BRD_ECHMC) { - release_region(brdp->ioaddr1, 64); - } else if (brdp->brdtype == BRD_EASYIO) { - release_region(brdp->ioaddr1, 8); - } + release_region(brdp->ioaddr1, brdp->iosize1); + if (brdp->iosize2 > 0) + release_region(brdp->ioaddr2, brdp->iosize2); kfree_s(brdp, sizeof(stlbrd_t)); stl_brds[i] = (stlbrd_t *) NULL; @@ -790,7 +791,8 @@ int brdnr, panelnr, portnr, rc; #if DEBUG - printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device); + printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, + (int) filp, tty->device); #endif minordev = MINOR(tty->device); @@ -817,6 +819,8 @@ if (portp == (stlport_t *) NULL) return(-ENODEV); + MOD_INC_USE_COUNT; + /* * On the first open of the device setup the port hardware, and * initialize the per port data structure. @@ -865,10 +869,10 @@ return(-EBUSY); if (portp->flags & ASYNC_CALLOUT_ACTIVE) { if ((portp->flags & ASYNC_SESSION_LOCKOUT) && - (portp->session != current->session)) + (portp->session != current->session)) return(-EBUSY); if ((portp->flags & ASYNC_PGRP_LOCKOUT) && - (portp->pgrp != current->pgrp)) + (portp->pgrp != current->pgrp)) return(-EBUSY); } portp->flags |= ASYNC_CALLOUT_ACTIVE; @@ -878,10 +882,7 @@ return(-EBUSY); } else { if ((rc = stl_waitcarrier(portp, filp)) != 0) - { - MOD_INC_USE_COUNT; return(rc); - } } portp->flags |= ASYNC_NORMAL_ACTIVE; } @@ -896,7 +897,6 @@ portp->session = current->session; portp->pgrp = current->pgrp; - MOD_INC_USE_COUNT; return(0); } @@ -930,13 +930,14 @@ save_flags(flags); cli(); portp->openwaitcnt++; - if (portp->refcount > 0) + if (! tty_hung_up_p(filp)) portp->refcount--; for (;;) { if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) stl_setsignals(portp, 1, 1); - if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) { + if (tty_hung_up_p(filp) || + ((portp->flags & ASYNC_INITIALIZED) == 0)) { if (portp->flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else @@ -944,8 +945,8 @@ break; } if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) && - ((portp->flags & ASYNC_CLOSING) == 0) && - (doclocal || (portp->sigs & TIOCM_CD))) { + ((portp->flags & ASYNC_CLOSING) == 0) && + (doclocal || (portp->sigs & TIOCM_CD))) { break; } if (signal_pending(current)) { @@ -981,11 +982,14 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } - MOD_DEC_USE_COUNT; + if ((tty->count == 1) && (portp->refcount != 1)) + portp->refcount = 1; if (portp->refcount-- > 1) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1000,14 +1004,14 @@ /* * May want to wait for any data to drain before closing. The BUSY - * flag keeps track of whether we are still sending or not - it allows - * for the FIFO in the cd1400. + * flag keeps track of whether we are still sending or not - it is + * very accurate for the cd1400, not quite so for the sc26198. + * (The sc26198 has no "end-of-data" interrupt only empty FIFO) */ tty->closing = 1; - if (test_bit(ASYI_TXBUSY, &portp->istate)) { - if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, portp->closing_wait); - } + if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, portp->closing_wait); + stl_waituntilsent(tty, (HZ / 2)); portp->flags &= ~ASYNC_INITIALIZED; stl_disableintrs(portp); @@ -1027,7 +1031,6 @@ (tty->ldisc.flush_buffer)(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -1036,8 +1039,10 @@ wake_up_interruptible(&portp->open_wait); } - portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | + ASYNC_CLOSING); wake_up_interruptible(&portp->close_wait); + MOD_DEC_USE_COUNT; restore_flags(flags); } @@ -1058,6 +1063,7 @@ current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + len; schedule(); + current->state = TASK_RUNNING; } } @@ -1077,10 +1083,12 @@ char *head, *tail; #if DEBUG - printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count); + printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", + (int) tty, from_user, (int) buf, count); #endif - if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL)) + if ((tty == (struct tty_struct *) NULL) || + (stl_tmpwritebuf == (char *) NULL)) return(0); portp = tty->driver_data; if (portp == (stlport_t *) NULL) @@ -1202,7 +1210,8 @@ return; #if 0 - if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail)) + if (tty->stopped || tty->hw_stopped || + (portp->tx.head == portp->tx.tail)) return; #endif stl_startrxtx(portp, -1, 1); @@ -1327,12 +1336,14 @@ copy_from_user(&sio, sp, sizeof(struct serial_struct)); if (!suser()) { if ((sio.baud_base != portp->baud_base) || - (sio.close_delay != portp->close_delay) || - ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK))) + (sio.close_delay != portp->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (portp->flags & ~ASYNC_USR_MASK))) return(-EPERM); } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); + portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; @@ -1350,7 +1361,8 @@ int rc; #if DEBUG - printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg); + printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", + (int) tty, (int) file, cmd, (int) arg); #endif if (tty == (struct tty_struct *) NULL) @@ -1360,7 +1372,7 @@ return(-ENODEV); if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { if (tty->flags & (1 << TTY_IO_ERROR)) return(-EIO); } @@ -1368,67 +1380,68 @@ rc = 0; switch (cmd) { - case TCSBRK: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - if (! arg) - stl_sendbreak(portp, 250); - } - break; - case TCSBRKP: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - stl_sendbreak(portp, (arg ? (arg * 100) : 250)); - } - break; case TIOCGSOFTCAR: rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int *) arg); + (unsigned int *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(int))) == 0) { get_user(ival, (unsigned int *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? CLOCAL : 0); } break; case TIOCMGET: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { ival = stl_getsignals(portp); put_user(ival, (unsigned int *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & TIOCM_RTS) ? 1 : -1)); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & TIOCM_RTS) ? 0 : -1)); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & TIOCM_RTS) ? 1 : 0)); } break; case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) stl_getserial(portp, (struct serial_struct *) arg); break; case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) rc = stl_setserial(portp, (struct serial_struct *) arg); break; case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_getportstats(portp, (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) rc = stl_clrportstats(portp, (comstats_t *) arg); break; case TIOCSERCONFIG: @@ -1464,11 +1477,13 @@ return; tiosp = tty->termios; - if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag)) + if ((tiosp->c_cflag == old->c_cflag) && + (tiosp->c_iflag == old->c_iflag)) return; stl_setport(portp, tiosp); - stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1); + stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), + -1); if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) { tty->hw_stopped = 0; stl_start(tty); @@ -1603,7 +1618,6 @@ portp->tx.head = (char *) NULL; portp->tx.tail = (char *) NULL; } - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); portp->refcount = 0; @@ -1628,12 +1642,219 @@ stl_flush(portp); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); } /*****************************************************************************/ +static void stl_breakctl(struct tty_struct *tty, int state) +{ + stlport_t *portp; + +#if DEBUG + printk("stl_breakctl(tty=%x,state=%d)\n", (int) tty, state); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + stl_sendbreak(portp, ((state == -1) ? 1 : 2)); +} + +/*****************************************************************************/ + +static void stl_waituntilsent(struct tty_struct *tty, int timeout) +{ + stlport_t *portp; + unsigned long tend; + +#if DEBUG + printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (stl_datastate(portp)) { + if (signal_pending(current)) + break; + stl_delay(2); + if (jiffies >= tend) + break; + } +} + +/*****************************************************************************/ + +static void stl_sendxchar(struct tty_struct *tty, char ch) +{ + stlport_t *portp; + +#if DEBUG + printk("stl_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + if (ch == STOP_CHAR(tty)) + stl_sendflow(portp, 0); + else if (ch == START_CHAR(tty)) + stl_sendflow(portp, 1); + else + stl_putchar(tty, ch); +} + +/*****************************************************************************/ + +#define MAXLINE 80 + +/* + * Format info for a specified port. The line is deliberately limited + * to 80 characters. (If it is too long it will be truncated, if too + * short then padded with spaces). + */ + +static int stl_portinfo(stlport_t *portp, int portnr, char *pos) +{ + char *sp; + int sigs, cnt; + + sp = pos; + sp += sprintf(sp, "%d: uart:%s tx:%d rx:%d", + portnr, (portp->hwid == 1) ? "SC26198" : "CD1400", + (int) portp->stats.txtotal, (int) portp->stats.rxtotal); + + if (portp->stats.rxframing) + sp += sprintf(sp, " fe:%d", (int) portp->stats.rxframing); + if (portp->stats.rxparity) + sp += sprintf(sp, " pe:%d", (int) portp->stats.rxparity); + if (portp->stats.rxbreaks) + sp += sprintf(sp, " brk:%d", (int) portp->stats.rxbreaks); + if (portp->stats.rxoverrun) + sp += sprintf(sp, " oe:%d", (int) portp->stats.rxoverrun); + + sigs = stl_getsignals(portp); + cnt = sprintf(sp, "%s%s%s%s%s ", + (sigs & TIOCM_RTS) ? "|RTS" : "", + (sigs & TIOCM_CTS) ? "|CTS" : "", + (sigs & TIOCM_DTR) ? "|DTR" : "", + (sigs & TIOCM_CD) ? "|DCD" : "", + (sigs & TIOCM_DSR) ? "|DSR" : ""); + *sp = ' '; + sp += cnt; + + for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++) + *sp++ = ' '; + if (cnt >= MAXLINE) + pos[(MAXLINE - 2)] = '+'; + pos[(MAXLINE - 1)] = '\n'; + + return(MAXLINE); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + stlport_t *portp; + int brdnr, panelnr, portnr, totalport; + int curoff, maxoff; + char *pos; + +#if DEBUG + printk("stl_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x," + "data=%x\n", (int) page, (int) start, (int) off, count, + (int) eof, (int) data); +#endif + + pos = page; + totalport = 0; + curoff = 0; + + if (off == 0) { + pos += sprintf(pos, "%s: version %s", stl_drvtitle, + stl_drvversion); + while (pos < (page + MAXLINE - 1)) + *pos++ = ' '; + *pos++ = '\n'; + } + curoff = MAXLINE; + +/* + * We scan through for each board, panel and port. The offset is + * calculated on the fly, and irrelevant ports are skipped. + */ + for (brdnr = 0; (brdnr < stl_nrbrds); brdnr++) { + brdp = stl_brds[brdnr]; + if (brdp == (stlbrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + + maxoff = curoff + (brdp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + continue; + } + + totalport = brdnr * STL_MAXPORTS; + for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { + panelp = brdp->panels[panelnr]; + if (panelp == (stlpanel_t *) NULL) + continue; + + maxoff = curoff + (panelp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + totalport += panelp->nrports; + continue; + } + + for (portnr = 0; (portnr < panelp->nrports); portnr++, + totalport++) { + portp = panelp->ports[portnr]; + if (portp == (stlport_t *) NULL) + continue; + if (off >= (curoff += MAXLINE)) + continue; + if ((pos - page + MAXLINE) > count) + goto stl_readdone; + pos += stl_portinfo(portp, totalport, pos); + } + } + } + + *eof = 1; + +stl_readdone: + *start = page; + return(pos - page); +} + +/*****************************************************************************/ + /* * All board interrupts are vectored through here first. This code then * calls off to the approrpriate board interrupt handlers. @@ -1755,6 +1976,29 @@ /*****************************************************************************/ /* + * Interrupt service routine for ECH-8/64-PCI board types. + */ + +static void stl_echpci64intr(stlbrd_t *brdp) +{ + stlpanel_t *panelp; + unsigned int ioaddr; + int bnknr; + + while (inb(brdp->ioctrl) & 0x1) { + for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) { + ioaddr = brdp->bnkstataddr[bnknr]; + if (inb(ioaddr) & ECH_PNLINTRPEND) { + panelp = brdp->bnk2panel[bnknr]; + (* panelp->isr)(panelp, (ioaddr & 0xfffc)); + } + } + } +} + +/*****************************************************************************/ + +/* * Service an off-level request for some channel. */ @@ -1777,7 +2021,8 @@ return; if (test_bit(ASYI_TXLOW, &portp->istate)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); } @@ -1790,7 +2035,7 @@ if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) && - (portp->flags & ASYNC_CALLOUT_NOHUP))) { + (portp->flags & ASYNC_CALLOUT_NOHUP))) { tty_hangup(tty); } } @@ -1806,12 +2051,12 @@ * interrupt across multiple boards. */ -__initfunc(static int stl_mapirq(int irq)) +__initfunc(static int stl_mapirq(int irq, char *name)) { int rc, i; #if DEBUG - printk("stl_mapirq(irq=%d)\n", irq); + printk("stl_mapirq(irq=%d,name=%s)\n", irq, name); #endif rc = 0; @@ -1820,8 +2065,9 @@ break; } if (i >= stl_numintrs) { - if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) { - printk("STALLION: failed to register interrupt routine for irq=%d\n", irq); + if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) { + printk("STALLION: failed to register interrupt " + "routine for %s irq=%d\n", name, irq); rc = -ENODEV; } else { stl_gotintrs[stl_numintrs++] = irq; @@ -1854,7 +2100,8 @@ for (i = 0; (i < panelp->nrports); i++) { portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); if (portp == (stlport_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlport_t)); break; } memset(portp, 0, sizeof(stlport_t)); @@ -1892,19 +2139,54 @@ { stlpanel_t *panelp; unsigned int status; + char *name; int rc; #if DEBUG printk("stl_initeio(brdp=%x)\n", (int) brdp); #endif - if (check_region(brdp->ioaddr1, 8)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->ioaddr1); - } - brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 2; + +/* + * Handle board specific stuff now. The real difference is PCI + * or not PCI. + */ + if (brdp->brdtype == BRD_EASYIOPCI) { + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EIO-PCI)"; + outb(0x41, (brdp->ioaddr2 + 0x4c)); + } else { + brdp->iosize1 = 8; + name = "serial(EIO)"; + if ((brdp->irq < 0) || (brdp->irq > 15) || + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); + return(-EINVAL); + } + outb((stl_vecmap[brdp->irq] | + ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), + brdp->ioctrl); + } + + if (check_region(brdp->ioaddr1, brdp->iosize1)) { + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, brdp->ioaddr1); + } + if (brdp->iosize2 > 0) { + if (check_region(brdp->ioaddr2, brdp->iosize2)) { + printk("STALLION: Warning, unit %d I/O address %x " + "conflicts with another device\n", + brdp->brdnr, brdp->ioaddr2); + } + } + +/* + * Everything looks OK, so lets go ahead and probe for the hardware. + */ brdp->clk = CD1400_CLK; brdp->isr = stl_eiointr; @@ -1940,23 +2222,18 @@ return(-ENODEV); } - request_region(brdp->ioaddr1, 8, "serial(EIO)"); - /* - * Check that the supplied IRQ is good and then use it to setup the - * programmable interrupt bits on EIO board. Also set the edge/level - * triggered interrupt bit. + * We have verfied that the board is actually present, so now we + * can complete the setup. */ - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); - return(-EINVAL); - } - outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlpanel_t)); return(-ENOMEM); } memset(panelp, 0, sizeof(stlpanel_t)); @@ -1979,7 +2256,7 @@ brdp->nrpanels = 1; brdp->state |= BRD_FOUND; brdp->hwid = status; - rc = stl_mapirq(brdp->irq); + rc = stl_mapirq(brdp->irq, name); return(rc); } @@ -1990,11 +2267,12 @@ * dealing with all types of ECH board. */ -static inline int stl_initech(stlbrd_t *brdp) +static int inline stl_initech(stlbrd_t *brdp) { stlpanel_t *panelp; unsigned int status, nxtid, ioaddr, conflict; int panelnr, banknr, i; + char *name; #if DEBUG printk("stl_initech(brdp=%x)\n", (int) brdp); @@ -2008,69 +2286,101 @@ * bit between the different board types. So we need to handle each * separately. Also do a check that the supplied IRQ is good. */ - if (brdp->brdtype == BRD_ECH) { + switch (brdp->brdtype) { + + case BRD_ECH: brdp->isr = stl_echatintr; brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 1; status = inb(brdp->iostatus); if ((status & ECH_IDBITMASK) != ECH_ID) return(-ENODEV); - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); return(-EINVAL); } status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); status |= (stl_vecmap[brdp->irq] << 1); outb((status | ECH_BRDRESET), brdp->ioaddr1); - brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); - outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); - conflict = check_region(brdp->ioaddr1, 2) ? brdp->ioaddr1 : 0; - if (conflict == 0) - conflict = check_region(brdp->ioaddr2, 32) ? brdp->ioaddr2 : 0; - request_region(brdp->ioaddr1, 2, "serial(EC8/32)"); - request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)"); + brdp->ioctrlval = ECH_INTENABLE | + ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); + for (i = 0; (i < 10); i++) + outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); + brdp->iosize1 = 2; + brdp->iosize2 = 32; + name = "serial(EC8/32)"; outb(status, brdp->ioaddr1); - } else if (brdp->brdtype == BRD_ECHMC) { + break; + + case BRD_ECHMC: brdp->isr = stl_echmcaintr; brdp->ioctrl = brdp->ioaddr1 + 0x20; brdp->iostatus = brdp->ioctrl; status = inb(brdp->iostatus); if ((status & ECH_IDBITMASK) != ECH_ID) return(-ENODEV); - if ((brdp->irq < 0) || (brdp->irq > 15) || - (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { - printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); + (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { + printk("STALLION: invalid irq=%d for brd=%d\n", + brdp->irq, brdp->brdnr); return(-EINVAL); } outb(ECHMC_BRDRESET, brdp->ioctrl); outb(ECHMC_INTENABLE, brdp->ioctrl); - conflict = check_region(brdp->ioaddr1, 64) ? brdp->ioaddr1 : 0; - request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)"); - } else if (brdp->brdtype == BRD_ECHPCI) { + brdp->iosize1 = 64; + name = "serial(EC8/32-MC)"; + break; + + case BRD_ECHPCI: brdp->isr = stl_echpciintr; brdp->ioctrl = brdp->ioaddr1 + 2; - conflict = check_region(brdp->ioaddr1, 4) ? brdp->ioaddr1 : 0; - if (conflict == 0) - conflict = check_region(brdp->ioaddr2, 8) ? brdp->ioaddr2 : 0; - request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)"); - request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)"); + brdp->iosize1 = 4; + brdp->iosize2 = 8; + name = "serial(EC8/32-PCI)"; + break; + + case BRD_ECH64PCI: + brdp->isr = stl_echpci64intr; + brdp->ioctrl = brdp->ioaddr2 + 0x40; + outb(0x43, (brdp->ioaddr1 + 0x4c)); + brdp->iosize1 = 0x80; + brdp->iosize2 = 0x80; + name = "serial(EC8/64-PCI)"; + break; + + default: + printk("STALLION: unknown board type=%d\n", brdp->brdtype); + return(-EINVAL); + break; } +/* + * Check boards for possible IO address conflicts. We won't actually + * do anything about it here, just issue a warning... + */ + conflict = check_region(brdp->ioaddr1, brdp->iosize1) ? + brdp->ioaddr1 : 0; + if ((conflict == 0) && (brdp->iosize2 > 0)) + conflict = check_region(brdp->ioaddr2, brdp->iosize2) ? + brdp->ioaddr2 : 0; if (conflict) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, conflict); + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, conflict); } - brdp->clk = CD1400_CLK; - brdp->hwid = status; + request_region(brdp->ioaddr1, brdp->iosize1, name); + if (brdp->iosize2 > 0) + request_region(brdp->ioaddr2, brdp->iosize2, name); /* * Scan through the secondary io address space looking for panels. * As we find'em allocate and initialize panel structures for each. */ + brdp->clk = CD1400_CLK; + brdp->hwid = status; + ioaddr = brdp->ioaddr2; banknr = 0; panelnr = 0; @@ -2086,7 +2396,8 @@ break; panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); if (panelp == (stlpanel_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlpanel_t)); break; } memset(panelp, 0, sizeof(stlpanel_t)); @@ -2107,7 +2418,8 @@ panelp->nrports = 16; brdp->bnk2panel[banknr] = panelp; brdp->bnkpageaddr[banknr] = nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + 4 + ECH_PNLSTATUS; + brdp->bnkstataddr[banknr++] = ioaddr + 4 + + ECH_PNLSTATUS; } else { panelp->nrports = 8; } @@ -2121,7 +2433,8 @@ ioaddr += EREG_BANKSIZE; brdp->bnk2panel[banknr] = panelp; brdp->bnkpageaddr[banknr] = ++nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; + brdp->bnkstataddr[banknr++] = ioaddr + + ECH_PNLSTATUS; } else { panelp->nrports = 8; panelp->ackmask = 0xc0; @@ -2132,7 +2445,8 @@ ioaddr += EREG_BANKSIZE; brdp->nrports += panelp->nrports; brdp->panels[panelnr++] = panelp; - if (ioaddr >= (brdp->ioaddr2 + 0x20)) + if ((brdp->brdtype != BRD_ECHPCI) && + (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) break; } @@ -2142,7 +2456,7 @@ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); brdp->state |= BRD_FOUND; - i = stl_mapirq(brdp->irq); + i = stl_mapirq(brdp->irq, name); return(i); } @@ -2165,21 +2479,26 @@ switch (brdp->brdtype) { case BRD_EASYIO: + case BRD_EASYIOPCI: stl_initeio(brdp); break; case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: + case BRD_ECH64PCI: stl_initech(brdp); break; default: - printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); + printk("STALLION: unit=%d is unknown board type=%d\n", + brdp->brdnr, brdp->brdtype); return(ENODEV); } stl_brds[brdp->brdnr] = brdp; if ((brdp->state & BRD_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq); + printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", + stl_brdnames[brdp->brdtype], brdp->brdnr, + brdp->ioaddr1, brdp->irq); return(ENODEV); } @@ -2187,90 +2506,160 @@ if (brdp->panels[i] != (stlpanel_t *) NULL) stl_initports(brdp, brdp->panels[i]); - printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports); + printk("STALLION: %s found, unit=%d io=%x irq=%d " + "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], + brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, + brdp->nrports); return(0); } /*****************************************************************************/ +#ifdef CONFIG_PCI + /* - * Find any ECH-PCI boards that might be installed. Initialize each + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and IRQ resources from PCI + * configuration space. + */ + +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr) +{ + unsigned int bar[4]; + stlbrd_t *brdp; + int i, rc; + unsigned char irq; + +#if DEBUG + printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", + brdtype, busnr, devnr); +#endif + + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlbrd_t)); + return(-ENOMEM); + } + + memset(brdp, 0, sizeof(stlbrd_t)); + brdp->magic = STL_BOARDMAGIC; + brdp->brdnr = stl_nrbrds++; + brdp->brdtype = brdtype; + +/* + * Read in all the BAR registers from this board. Different Stallion + * boards use these in different ways, so we just read in the whole + * lot and then figure out what is what later. + */ + for (i = 0; (i < 4); i++) { + rc = pcibios_read_config_dword(busnr, devnr, + (PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]); + if (rc) { + printk("STALLION: failed to read BAR register %d " + "from PCI board, errno=%x\n", i, rc); + return(0); + } + } + + rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq); + if (rc) { + printk("STALLION: failed to read INTERRUPT register " + "from PCI board, errno=%x\n", rc); + return(0); + } + +#if DEBUG + printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__, + bar[0], bar[1], bar[2], bar[3], irq); +#endif + +/* + * We have all resources from the board, so lets setup the actual + * board structure now. + */ + switch (brdtype) { + case BRD_ECHPCI: + brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_ECH64PCI: + brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + case BRD_EASYIOPCI: + brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + break; + default: + printk("STALLION: unknown PCI board type=%d\n", brdtype); + break; + } + + brdp->irq = irq; + stl_brdinit(brdp); + + return(0); +} + + +/*****************************************************************************/ + +/* + * Find all Stallion PCI boards that might be installed. Initialize each * one as it is found. */ -#ifdef CONFIG_PCI static inline int stl_findpcibrds() { - stlbrd_t *brdp; - unsigned char busnr, devnr, irq; + unsigned char busnr, devnr; unsigned short class; - unsigned int ioaddr; - int i, rc; + int i, rc, brdtypnr; #if DEBUG printk("stl_findpcibrds()\n"); #endif - if (pcibios_present()) { - for (i = 0; (i < STL_MAXBRDS); i++) { - if (pcibios_find_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, i, &busnr, &devnr)) - if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr)) - break; + if (! pcibios_present()) + return(0); + + for (i = 0; (i < stl_nrpcibrds); i++) { + for (brdtypnr = 0; ; brdtypnr++) { + + rc = pcibios_find_device(stl_pcibrds[i].vendid, + stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr); + if (rc) + break; /* - * Found a device on the PCI bus that has our vendor and - * device ID. Need to check now that it is really us. + * Check that we can handle more boards... */ - if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) { - printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc); - continue; - } - if (class == PCI_CLASS_STORAGE_IDE) - continue; - if (stl_nrbrds >= STL_MAXBRDS) { - printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS); + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + i = stl_nrpcibrds; break; } /* - * We have a Stallion board. Allocate a board structure - * and initialize it. Read its IO and IRQ resources - * from conf space. - */ - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlbrd_t)); - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = stl_nrbrds++; - brdp->brdtype = BRD_ECHPCI; - - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); - continue; - } - brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); - - if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); + * Found a device on the PCI bus that has our vendor and + * device ID. Need to check now that it is really us. + */ + rc = pcibios_read_config_word(busnr, devnr, + PCI_CLASS_DEVICE, &class); + if (rc) { + printk("STALLION: failed to read class type " + "from PCI board, errno=%x\n", rc); continue; } - brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); -#if DEBUG - printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1); -#endif - - if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) { - printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc); + if (class == PCI_CLASS_STORAGE_IDE) continue; - } - brdp->irq = irq; - stl_brdinit(brdp); + rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr, + devnr); + if (rc) + return(rc); } } @@ -2297,12 +2686,11 @@ printk("stl_initbrds()\n"); #endif -#ifndef MODULE if (stl_nrbrds > STL_MAXBRDS) { - printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS); + printk("STALLION: too many boards in configuration table, " + "truncating to %d\n", STL_MAXBRDS); stl_nrbrds = STL_MAXBRDS; } -#endif /* * Firstly scan the list of static boards configured. Allocate @@ -2312,7 +2700,8 @@ confp = &stl_brdconf[i]; brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlbrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlbrd_t)); @@ -2419,7 +2808,8 @@ if (portp == (stlport_t *) NULL) { copy_from_user(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } @@ -2453,7 +2843,8 @@ head = portp->tx.head; tail = portp->tx.tail; - portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head))); + portp->stats.txbuffered = ((head >= tail) ? (head - tail) : + (STL_TXBUFSIZE - (tail - head))); portp->stats.signals = (unsigned long) stl_getsignals(portp); @@ -2471,7 +2862,8 @@ { if (portp == (stlport_t *) NULL) { copy_from_user(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } @@ -2526,6 +2918,27 @@ /*****************************************************************************/ /* + * Memory device open code. Need to keep track of opens and close + * for module handling. + */ + +static int stl_memopen(struct inode *ip, struct file *fp) +{ + MOD_INC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +static int stl_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +/* * The "staliomem" device is also required to do some special operations * on the board and/or ports. In this driver it is mostly used for stats * collection. @@ -2536,7 +2949,8 @@ int brdnr, rc; #if DEBUG - printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, + (int) fp, cmd, (int) arg); #endif brdnr = MINOR(ip->i_rdev); @@ -2546,23 +2960,30 @@ switch (cmd) { case COM_GETPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_getportstats((stlport_t *) NULL, + (comstats_t *) arg); break; case COM_CLRPORTSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0) - rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg); + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(comstats_t))) == 0) + rc = stl_clrportstats((stlport_t *) NULL, + (comstats_t *) arg); break; case COM_GETBRDSTATS: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(combrd_t))) == 0) rc = stl_getbrdstats((combrd_t *) arg); break; case COM_READPORT: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlport_t))) == 0) rc = stl_getportstruct(arg); break; case COM_READBOARD: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlbrd_t))) == 0) rc = stl_getbrdstruct(arg); break; default: @@ -2577,7 +2998,7 @@ __initfunc(int stl_init(void)) { - printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion); + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); stl_initbrds(); @@ -2586,7 +3007,8 @@ */ stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); if (stl_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STL_TXBUFSIZE); /* * Set up a character driver for per board stuff. This is mainly used @@ -2601,6 +3023,7 @@ */ memset(&stl_serial, 0, sizeof(struct tty_driver)); stl_serial.magic = TTY_DRIVER_MAGIC; + stl_serial.driver_name = stl_drvname; stl_serial.name = stl_serialname; stl_serial.major = STL_SERIALMAJOR; stl_serial.minor_start = 0; @@ -2629,11 +3052,16 @@ stl_serial.start = stl_start; stl_serial.hangup = stl_hangup; stl_serial.flush_buffer = stl_flushbuffer; + stl_serial.break_ctl = stl_breakctl; + stl_serial.wait_until_sent = stl_waituntilsent; + stl_serial.send_xchar = stl_sendxchar; + stl_serial.read_proc = stl_readproc; stl_callout = stl_serial; stl_callout.name = stl_calloutname; stl_callout.major = STL_CALLOUTMAJOR; stl_callout.subtype = STL_DRVTYPCALLOUT; + stl_callout.read_proc = 0; if (tty_register_driver(&stl_serial)) printk("STALLION: failed to register serial driver\n"); @@ -2719,7 +3147,9 @@ break; } if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) { - printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i); + printk("STALLION: cd1400 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); continue; } chipmask |= (0x1 << i); @@ -2744,7 +3174,8 @@ (int) brdp, (int) panelp, (int) portp); #endif - if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL)) + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) return; portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || @@ -2776,7 +3207,8 @@ } } - printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); } /*****************************************************************************/ @@ -2887,7 +3319,7 @@ baudrate = tiosp->c_cflag & CBAUD; if (baudrate & CBAUDEX) { baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 2)) + if ((baudrate < 1) || (baudrate > 4)) tiosp->c_cflag &= ~CBAUDEX; else baudrate += 15; @@ -2898,11 +3330,15 @@ baudrate = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } - if (baudrate > STL_MAXBAUD) - baudrate = STL_MAXBAUD; + if (baudrate > STL_CD1400MAXBAUD) + baudrate = STL_CD1400MAXBAUD; if (baudrate > 0) { for (clk = 0; (clk < CD1400_NUMCLKS); clk++) { @@ -2949,11 +3385,16 @@ */ #if DEBUG - printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); - printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5); - printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff); + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); + printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", + cor1, cor2, cor3, cor4, cor5); + printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", + mcor1, mcor2, rtpr, sreron, sreroff); printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); - printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); #endif save_flags(flags); @@ -3009,7 +3450,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif msvr1 = 0; @@ -3059,10 +3501,14 @@ sigs = 0; sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; - sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; - sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; +#if 0 + sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; + sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; +#else + sigs |= TIOCM_DSR; +#endif return(sigs); } @@ -3078,7 +3524,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif ccr = 0; @@ -3114,8 +3561,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, - rx, tx); + printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif sreron = 0; @@ -3135,7 +3582,8 @@ cli(); BRDENABLE(portp->brdnr, portp->pagenr); stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); if (tx > 0) set_bit(ASYI_TXBUSY, &portp->istate); @@ -3166,24 +3614,25 @@ /*****************************************************************************/ -static void stl_cd1400sendbreak(stlport_t *portp, long len) +static void stl_cd1400sendbreak(stlport_t *portp, int len) { unsigned long flags; #if DEBUG - printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); + printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, len); #endif save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, COR2, (stl_cd1400getreg(portp, COR2) | COR2_ETC)); - stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | + SRER_TXEMPTY)); BRDDISABLE(portp->brdnr); - len = len / 5; - portp->brklen = (len > 255) ? 255 : len; - portp->stats.txbreaks++; + portp->brklen = len; + if (len == 1) + portp->stats.txbreaks++; restore_flags(flags); } @@ -3227,7 +3676,9 @@ * set the RTS line by hand. */ if (tty->termios->c_cflag & CRTSCTS) { - stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD)); + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) | + FIFO_RTSTHRESHOLD)); stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); portp->stats.rxrtson++; } @@ -3239,7 +3690,8 @@ stl_cd1400ccrwait(portp); } if (tty->termios->c_cflag & CRTSCTS) { - stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) & 0xf0)); + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) & 0xf0)); stl_cd1400setreg(portp, MSVR2, 0); portp->stats.rxrtsoff++; } @@ -3251,6 +3703,46 @@ /*****************************************************************************/ +/* + * Send a flow control character... + */ + +static void stl_cd1400sendflow(stlport_t *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + +#if DEBUG + printk("stl_cd1400sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif + + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); + if (state) { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1); + portp->stats.rxxon++; + stl_cd1400ccrwait(portp); + } else { + stl_cd1400ccrwait(portp); + stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2); + portp->stats.rxxoff++; + stl_cd1400ccrwait(portp); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + static void stl_cd1400flush(stlport_t *portp) { unsigned long flags; @@ -3277,6 +3769,27 @@ /*****************************************************************************/ /* + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... This is easy for the cd1400, it accurately + * maintains the busy port flag. + */ + +static int stl_cd1400datastate(stlport_t *portp) +{ +#if DEBUG + printk("stl_cd1400datastate(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return(0); + + return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0); +} + +/*****************************************************************************/ + +/* * Interrupt service routine for cd1400 EasyIO boards. */ @@ -3285,7 +3798,8 @@ unsigned char svrtype; #if DEBUG - printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", (int) panelp, iobase); + printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", + (int) panelp, iobase); #endif outb(SVRR, iobase); @@ -3297,13 +3811,12 @@ if (svrtype & SVRR_RX) stl_cd1400rxisr(panelp, iobase); - if (svrtype & SVRR_TX) + else if (svrtype & SVRR_TX) stl_cd1400txisr(panelp, iobase); - if (svrtype & SVRR_MDM) + else if (svrtype & SVRR_MDM) stl_cd1400mdmisr(panelp, iobase); } - /*****************************************************************************/ /* @@ -3315,7 +3828,8 @@ unsigned char svrtype; #if DEBUG - printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, iobase); + printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, + iobase); #endif outb(SVRR, iobase); @@ -3324,12 +3838,48 @@ svrtype |= inb(iobase + EREG_DATA); if (svrtype & SVRR_RX) stl_cd1400rxisr(panelp, iobase); - if (svrtype & SVRR_TX) + else if (svrtype & SVRR_TX) stl_cd1400txisr(panelp, iobase); - if (svrtype & SVRR_MDM) + else if (svrtype & SVRR_MDM) stl_cd1400mdmisr(panelp, iobase); } + +/*****************************************************************************/ + +/* + * Unfortunately we need to handle breaks in the TX data stream, since + * this is the only way to generate them on the cd1400. + */ + +static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr) +{ + if (portp->brklen == 1) { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) | COR2_ETC), + (ioaddr + EREG_DATA)); + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); + outb((SRER + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)), + (ioaddr + EREG_DATA)); + return(1); + } else if (portp->brklen > 1) { + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); + portp->brklen = -1; + return(1); + } else { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), + (ioaddr + EREG_DATA)); + portp->brklen = 0; + } + return(0); +} + /*****************************************************************************/ /* @@ -3356,7 +3906,8 @@ #endif ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { printk("STALLION: bad TX interrupt ack value=%x\n", ioack); return; } @@ -3367,29 +3918,15 @@ * this is the only way to generate them on the cd1400. Do it now if * a break is to be sent. */ - if (portp->brklen != 0) { - if (portp->brklen > 0) { - outb((TDR + portp->uartaddr), ioaddr); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_DELAY, (ioaddr + EREG_DATA)); - outb(portp->brklen, (ioaddr + EREG_DATA)); - outb(ETC_CMD, (ioaddr + EREG_DATA)); - outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); - portp->brklen = -1; + if (portp->brklen != 0) + if (stl_cd1400breakisr(portp, ioaddr)) goto stl_txalldone; - } else { - outb((COR2 + portp->uartaddr), ioaddr); - outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA)); - portp->brklen = 0; - } - } head = portp->tx.head; tail = portp->tx.tail; len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); queue_task(&portp->tqueue, &tq_scheduler); } @@ -3461,8 +3998,9 @@ if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb((RDCR + portp->uartaddr), ioaddr); len = inb(ioaddr + EREG_DATA); - if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || - ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + if ((tty == (struct tty_struct *) NULL) || + (tty->flip.char_buf_ptr == (char *) NULL) || + ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { outb((RDSR + portp->uartaddr), ioaddr); insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; @@ -3499,16 +4037,15 @@ portp->stats.txxoff++; goto stl_rxalldone; } - if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) { + if ((tty != (struct tty_struct *) NULL) && + ((portp->rxignoremsk & status) == 0)) { if (portp->rxmarkmsk & status) { if (status & ST_BREAK) { status = TTY_BREAK; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } -#endif } else if (status & ST_PARITY) { status = TTY_PARITY; } else if (status & ST_FRAMING) { @@ -3559,7 +4096,8 @@ #endif ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); return; } @@ -3643,7 +4181,8 @@ int nrchips, ioaddr; #if DEBUG - printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); + printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", + (int) brdp, (int) panelp); #endif BRDENABLE(panelp->brdnr, panelp->pagenr); @@ -3662,7 +4201,9 @@ outb(CR_RESETALL, (ioaddr + XP_DATA)); outb(TSTR, (ioaddr + XP_ADDR)); if (inb(ioaddr + XP_DATA) != 0) { - printk("STALLION: sc26198 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i); + printk("STALLION: sc26198 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); continue; } chipmask |= (0x1 << i); @@ -3689,7 +4230,8 @@ (int) brdp, (int) panelp, (int) portp); #endif - if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL)) + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) return; portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); @@ -3735,7 +4277,8 @@ */ portp->rxignoremsk = 0; if (tiosp->c_iflag & IGNPAR) - portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN); + portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | + SR_RXOVERRUN); if (tiosp->c_iflag & IGNBRK) portp->rxignoremsk |= SR_RXBREAK; @@ -3795,7 +4338,7 @@ baudrate = tiosp->c_cflag & CBAUD; if (baudrate & CBAUDEX) { baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 5)) + if ((baudrate < 1) || (baudrate > 4)) tiosp->c_cflag &= ~CBAUDEX; else baudrate += 15; @@ -3806,11 +4349,15 @@ baudrate = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } - if (baudrate > STL_MAXBAUD) - baudrate = STL_MAXBAUD; + if (baudrate > STL_SC26198MAXBAUD) + baudrate = STL_SC26198MAXBAUD; if (baudrate > 0) { for (clk = 0; (clk < SC26198_NRBAUDS); clk++) { @@ -3856,10 +4403,13 @@ */ #if DEBUG - printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); printk(" mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk); printk(" iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff); - printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); + printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], + tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); #endif save_flags(flags); @@ -3905,7 +4455,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif iopioron = 0; @@ -3956,6 +4507,7 @@ sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS; sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR; sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS; + sigs |= TIOCM_DSR; return(sigs); } @@ -3971,7 +4523,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif ccr = portp->crenable; @@ -4005,8 +4558,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, - rx, tx); + printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif imr = portp->imr; @@ -4055,28 +4608,23 @@ /*****************************************************************************/ -static void stl_sc26198sendbreak(stlport_t *portp, long len) +static void stl_sc26198sendbreak(stlport_t *portp, int len) { unsigned long flags; #if DEBUG - printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); + printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, len); #endif - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (len / (1000 / HZ)); - save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); - BRDDISABLE(portp->brdnr); - portp->stats.txbreaks++; - - schedule(); - - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + if (len == 1) { + stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); + portp->stats.txbreaks++; + } else { + stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + } BRDDISABLE(portp->brdnr); restore_flags(flags); } @@ -4114,6 +4662,7 @@ stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); mr0 |= MR0_SWFRX; portp->stats.rxxon++; + stl_sc26198wait(portp); stl_sc26198setreg(portp, MR0, mr0); } /* @@ -4136,6 +4685,7 @@ stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); mr0 &= ~MR0_SWFRX; portp->stats.rxxoff++; + stl_sc26198wait(portp); stl_sc26198setreg(portp, MR0, mr0); } if (tty->termios->c_cflag & CRTSCTS) { @@ -4153,6 +4703,52 @@ /*****************************************************************************/ +/* + * Send a flow control character. + */ + +static void stl_sc26198sendflow(stlport_t *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; + +#if DEBUG + printk("stl_sc26198sendflow(portp=%x,state=%x)\n", (int) portp, state); +#endif + + if (portp == (stlport_t *) NULL) + return; + tty = portp->tty; + if (tty == (struct tty_struct *) NULL) + return; + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + if (state) { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); + mr0 |= MR0_SWFRX; + portp->stats.rxxon++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } else { + mr0 = stl_sc26198getreg(portp, MR0); + stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); + stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF); + mr0 &= ~MR0_SWFRX; + portp->stats.rxxoff++; + stl_sc26198wait(portp); + stl_sc26198setreg(portp, MR0, mr0); + } + BRDDISABLE(portp->brdnr); + restore_flags(flags); +} + +/*****************************************************************************/ + static void stl_sc26198flush(stlport_t *portp) { unsigned long flags; @@ -4177,10 +4773,67 @@ /*****************************************************************************/ /* + * Return the current state of data flow on this port. This is only + * really interresting when determining if data has fully completed + * transmission or not... The sc26198 interrupt scheme cannot + * determine when all data has actually drained, so we need to + * check the port statusy register to be sure. + */ + +static int stl_sc26198datastate(stlport_t *portp) +{ + unsigned long flags; + unsigned char sr; + +#if DEBUG + printk("stl_sc26198datastate(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return(0); + if (test_bit(ASYI_TXBUSY, &portp->istate)) + return(1); + + save_flags(flags); + cli(); + BRDENABLE(portp->brdnr, portp->pagenr); + sr = stl_sc26198getreg(portp, SR); + BRDDISABLE(portp->brdnr); + restore_flags(flags); + + return((sr & SR_TXEMPTY) ? 0 : 1); +} + +/*****************************************************************************/ + +/* + * Delay for a small amount of time, to give the sc26198 a chance + * to process a command... + */ + +static void stl_sc26198wait(stlport_t *portp) +{ + int i; + +#if DEBUG + printk("stl_sc26198wait(portp=%x)\n", (int) portp); +#endif + + if (portp == (stlport_t *) NULL) + return; + + for (i = 0; (i < 20); i++) + stl_sc26198getglobreg(portp, TSTR); +} + +/*****************************************************************************/ + +/* * If we are TX flow controlled and in IXANY mode then we may * need to unflow control here. We gotta do this because of the * automatic flow control modes of the sc26198. */ + static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty) { unsigned char mr0; @@ -4188,6 +4841,7 @@ mr0 = stl_sc26198getreg(portp, MR0); stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX)); stl_sc26198setreg(portp, SCCR, CR_HOSTXON); + stl_sc26198wait(portp); stl_sc26198setreg(portp, MR0, mr0); clear_bit(ASYI_TXFLOWED, &portp->istate); } @@ -4247,7 +4901,8 @@ head = portp->tx.head; tail = portp->tx.tail; len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); - if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); queue_task(&portp->tqueue, &tq_scheduler); } @@ -4340,8 +4995,8 @@ */ if (test_bit(ASYI_TXFLOWED, &portp->istate)) { if ((tty != (struct tty_struct *) NULL) && - (tty->termios != (struct termios *) NULL) && - (tty->termios->c_iflag & IXANY)) { + (tty->termios != (struct termios *) NULL) && + (tty->termios->c_iflag & IXANY)) { stl_sc26198txunflow(portp, tty); } } @@ -4371,16 +5026,14 @@ portp->stats.rxbreaks++; if ((tty != (struct tty_struct *) NULL) && - ((portp->rxignoremsk & status) == 0)) { + ((portp->rxignoremsk & status) == 0)) { if (portp->rxmarkmsk & status) { if (status & SR_RXBREAK) { status = TTY_BREAK; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } -#endif } else if (status & SR_RXPARITY) { status = TTY_PARITY; } else if (status & SR_RXFRAMING) { @@ -4487,7 +5140,6 @@ stl_sc26198rxbadchars(portp); break; default: - /*printk("%s(%d): unknown other intr cir=%x, iack=%x!\n", __FILE__, __LINE__, cir, iack);*/ break; } } diff -u --recursive --new-file v2.1.73/linux/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c --- v2.1.73/linux/drivers/net/hamradio/scc.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/net/hamradio/scc.c Fri Dec 19 10:56:30 1997 @@ -166,10 +166,7 @@ #include #include -#include -#include -#include -#include +#include #include #include diff -u --recursive --new-file v2.1.73/linux/fs/Config.in linux/fs/Config.in --- v2.1.73/linux/fs/Config.in Wed Dec 10 11:12:44 1997 +++ linux/fs/Config.in Fri Dec 19 15:24:21 1997 @@ -53,6 +53,12 @@ tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS fi tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS + +tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS +if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' NTFS read-write support (experimental)' CONFIG_NTFS_RW +fi + tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS tristate 'ROM filesystem support' CONFIG_ROMFS_FS diff -u --recursive --new-file v2.1.73/linux/fs/Makefile linux/fs/Makefile --- v2.1.73/linux/fs/Makefile Tue Dec 2 16:45:19 1997 +++ linux/fs/Makefile Fri Dec 19 15:24:21 1997 @@ -16,7 +16,7 @@ inode.o dcache.o attr.o bad_inode.o $(BINFMTS) MOD_LIST_NAME := FS_MODULES -ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos \ +ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs affs romfs autofs lockd nfsd nls ifeq ($(CONFIG_QUOTA),y) @@ -167,6 +167,14 @@ else ifeq ($(CONFIG_HPFS_FS),m) MOD_SUB_DIRS += hpfs + endif +endif + +ifeq ($(CONFIG_NTFS_FS),y) +SUB_DIRS += ntfs +else + ifeq ($(CONFIG_NTFS_FS),m) + MOD_SUB_DIRS += ntfs endif endif diff -u --recursive --new-file v2.1.73/linux/fs/filesystems.c linux/fs/filesystems.c --- v2.1.73/linux/fs/filesystems.c Wed Nov 12 13:34:27 1997 +++ linux/fs/filesystems.c Fri Dec 19 15:24:21 1997 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,10 @@ #ifdef CONFIG_HPFS_FS init_hpfs_fs(); +#endif + +#ifdef CONFIG_NTFS_FS + init_ntfs_fs(); #endif #ifdef CONFIG_AFFS_FS diff -u --recursive --new-file v2.1.73/linux/fs/lockd/mon.c linux/fs/lockd/mon.c --- v2.1.73/linux/fs/lockd/mon.c Mon Apr 7 11:35:30 1997 +++ linux/fs/lockd/mon.c Fri Dec 19 10:54:10 1997 @@ -33,29 +33,38 @@ nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc) { struct rpc_clnt *clnt; + int status; struct nsm_args args; struct nsm_res res; - int status; dprintk("lockd: nsm_%s(%s)\n", what, host->h_name); - if (!(clnt = nsm_create())) - return -EACCES; + status = -EACCES; + clnt = nsm_create(); + if (!clnt) + goto out; args.addr = host->h_addr.sin_addr.s_addr; args.prog = NLM_PROGRAM; args.vers = 1; args.proc = NLMPROC_NSM_NOTIFY; - if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0) - return status; + status = rpc_call(clnt, proc, &args, &res, 0); + if (status < 0) { + printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n", + status); + goto out; + } + status = -EACCES; if (res.status != 0) { printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name); - return -EACCES; + goto out; } nsm_local_state = res.state; - return 0; + status = 0; +out: + return status; } /* @@ -66,7 +75,8 @@ { int status; - if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0) + status = nsm_mon_unmon(host, "monitor", SM_MON); + if (status >= 0) host->h_monitored = 1; return status; } @@ -90,28 +100,32 @@ static struct rpc_clnt * nsm_create(void) { - struct sockaddr_in sin; struct rpc_xprt *xprt; - struct rpc_clnt *clnt; + struct rpc_clnt *clnt = NULL; + struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_port = 0; - if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL))) - return NULL; + xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL); + if (!xprt) + goto out; clnt = rpc_create_client(xprt, "localhost", &nsm_program, SM_VERSION, RPC_AUTH_NULL); - if (!clnt) { - xprt_destroy(xprt); - } else { - clnt->cl_softrtry = 1; - clnt->cl_chatty = 1; - clnt->cl_oneshot = 1; - } + if (!clnt) + goto out_destroy; + clnt->cl_softrtry = 1; + clnt->cl_chatty = 1; + clnt->cl_oneshot = 1; +out: return clnt; + +out_destroy: + xprt_destroy(xprt); + goto out; } /* diff -u --recursive --new-file v2.1.73/linux/fs/lockd/svc.c linux/fs/lockd/svc.c --- v2.1.73/linux/fs/lockd/svc.c Mon Dec 1 12:04:14 1997 +++ linux/fs/lockd/svc.c Fri Dec 19 10:54:10 1997 @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -33,11 +32,11 @@ #include #include #include - +#include #define NLMDBG_FACILITY NLMDBG_SVC #define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE) -#define BLOCKABLE_SIGS (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP)) extern struct svc_program nlmsvc_program; struct nlmsvc_binding * nlmsvc_ops = NULL; @@ -148,23 +147,22 @@ } dprintk("lockd: request from %08x\n", - (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr)); + (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr)); /* * Look up the NFS client handle. The handle is needed for * all but the GRANTED callback RPCs. */ + rqstp->rq_client = NULL; if (nlmsvc_ops) { nlmsvc_ops->exp_readlock(); rqstp->rq_client = nlmsvc_ops->exp_getclient(&rqstp->rq_addr); - } else { - rqstp->rq_client = NULL; } - /* Process request with all signals blocked. */ + /* Process request with signals blocked. */ spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ~BLOCKABLE_SIGS); + siginitsetinv(¤t->blocked, ALLOWED_SIGS); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); @@ -202,22 +200,6 @@ } /* - * Make a socket for lockd - * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd. - */ -static int -lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port) -{ - struct sockaddr_in sin; - - dprintk("lockd: creating socket proto = %d\n", protocol); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons(port); - return svc_create_socket(serv, protocol, &sin); -} - -/* * Bring up the lockd process if it's not already up. */ int @@ -252,8 +234,8 @@ goto out; } - if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0 - || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) { + if ((error = svc_makesock(serv, IPPROTO_UDP, 0)) < 0 + || (error = svc_makesock(serv, IPPROTO_TCP, 0)) < 0) { printk("lockd_up: makesock failed, error=%d\n", error); goto destroy_and_out; } @@ -302,13 +284,17 @@ * Wait for the lockd process to exit, but since we're holding * the lockd semaphore, we can't wait around forever ... */ - current->timeout = jiffies + 5 * HZ; + current->sigpending = 0; + current->timeout = jiffies + HZ; interruptible_sleep_on(&lockd_exit); current->timeout = 0; if (nlmsvc_pid) { printk("lockd_down: lockd failed to exit, clearing pid\n"); nlmsvc_pid = 0; } + spin_lock_irq(¤t->sigmask_lock); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); out: up(&nlmsvc_sema); } diff -u --recursive --new-file v2.1.73/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c --- v2.1.73/linux/fs/minix/bitmap.c Thu Sep 11 09:02:23 1997 +++ linux/fs/minix/bitmap.c Fri Dec 19 15:55:45 1997 @@ -21,17 +21,31 @@ static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 }; -static unsigned long count_free(struct buffer_head *map[], unsigned numblocks) +static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits) { unsigned i, j, sum = 0; struct buffer_head *bh; - for (i=0; ib_data[j] & 0xf] - + nibblemap[(bh->b_data[j]>>4)&0xf]; + sum += nibblemap[bh->b_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4) & 0xf]; + } + + if (numblocks==0 || !(bh=map[numblocks-1])) + return(0); + i = (numbits-(numblocks-1)*BLOCK_SIZE*8)/8; + for (j=0; jb_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4) & 0xf]; + } + + i = numbits%8; + if (i!=0) { + i = bh->b_data[j] | ~((1<>4) & 0xf]; } return(sum); } @@ -108,8 +122,9 @@ unsigned long minix_count_free_blocks(struct super_block *sb) { - return (count_free(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks) - << sb->u.minix_sb.s_log_zone_size); + return (count_free(sb->u.minix_sb.s_zmap, sb->u.minix_sb.s_zmap_blocks, + sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone + 1) + << sb->u.minix_sb.s_log_zone_size); } static struct buffer_head *V1_minix_clear_inode(struct inode *inode) @@ -266,5 +281,6 @@ unsigned long minix_count_free_inodes(struct super_block *sb) { - return count_free(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks); + return count_free(sb->u.minix_sb.s_imap, sb->u.minix_sb.s_imap_blocks, + sb->u.minix_sb.s_ninodes + 1); } diff -u --recursive --new-file v2.1.73/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c --- v2.1.73/linux/fs/nfsd/nfssvc.c Thu Dec 4 14:53:56 1997 +++ linux/fs/nfsd/nfssvc.c Fri Dec 19 10:54:10 1997 @@ -36,28 +36,13 @@ #define NFSDDBG_FACILITY NFSDDBG_SVC #define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) -#define BLOCKABLE_SIGS (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) +#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP)) +#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGTERM)) extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot = { 0, 0 }; -/* - * Make a socket for nfsd - */ -static int -nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port) -{ - struct sockaddr_in sin; - - dprintk("nfsd: creating socket proto = %d\n", protocol); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons(port); - return svc_create_socket(serv, protocol, &sin); -} - int nfsd_svc(unsigned short port, int nrservs) { @@ -65,17 +50,19 @@ int error; dprintk("nfsd: creating service\n"); + error = -EINVAL; if (nrservs < 0) - return -EINVAL; + goto out; if (nrservs > NFSD_MAXSERVS) nrservs = NFSD_MAXSERVS; + error = -ENOMEM; serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); if (serv == NULL) - return -ENOMEM; + goto out; - if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0 - || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0) + if ((error = svc_makesock(serv, IPPROTO_UDP, port)) < 0 + || (error = svc_makesock(serv, IPPROTO_TCP, port)) < 0) goto failure; while (nrservs--) { @@ -86,6 +73,7 @@ failure: svc_destroy(serv); /* Release server */ +out: return error; } @@ -98,16 +86,15 @@ struct svc_serv *serv = rqstp->rq_server; int oldumask, err; - lock_kernel(); /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; + lock_kernel(); exit_mm(current); current->session = 1; current->pgrp = 1; sprintf(current->comm, "nfsd"); oldumask = current->fs->umask; /* Set umask to 0. */ - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); current->fs->umask = 0; nfssvc_boot = xtime; /* record boot time */ lockd_up(); /* start lockd */ @@ -116,6 +103,12 @@ * The main request loop */ for (;;) { + /* Block all but the shutdown signals */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + /* * Find a socket with data available and call its * recvfrom routine. @@ -140,18 +133,13 @@ svc_drop(rqstp); serv->sv_stats->rpcbadclnt++; } else { - /* Process request with all signals blocked. */ + /* Process request with signals blocked. */ spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ~BLOCKABLE_SIGS); + siginitsetinv(¤t->blocked, ALLOWED_SIGS); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); svc_process(serv, rqstp); - - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); } /* Unlock export hash tables */ diff -u --recursive --new-file v2.1.73/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c --- v2.1.73/linux/fs/nfsd/vfs.c Fri Dec 19 15:53:01 1997 +++ linux/fs/nfsd/vfs.c Fri Dec 19 10:51:50 1997 @@ -568,7 +568,8 @@ dchild = lookup_dentry(fname, dget(dentry), 0); err = PTR_ERR(dchild); if(IS_ERR(dchild)) - return nfserrno(-err); + goto out_nfserr; + fh_compose(resfhp, fhp->fh_export, dchild); } else dchild = resfhp->fh_dentry; /* @@ -597,19 +598,14 @@ fh_unlock(fhp); if (err < 0) - return nfserrno(-err); + goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) write_inode_now(dirp); /* - * Assemble the file handle for the newly created file, - * or update the filehandle to get the new inode info. + * Update the filehandle to get the new inode info. */ - if (!resfhp->fh_dverified) { - fh_compose(resfhp, fhp->fh_export, dchild); - } else { - fh_update(resfhp); - } + fh_update(resfhp); /* Set file attributes. Mode has already been set and * setting uid/gid works only for root. Irix appears to @@ -621,6 +617,10 @@ err = nfsd_setattr(rqstp, resfhp, iap); out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -694,19 +694,21 @@ goto out; UPDATE_ATIME(inode); + /* N.B. Why does this call need a get_fs()?? */ oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(inode, buf, *lenp); set_fs(oldfs); if (err < 0) - err = nfserrno(-err); - else { - *lenp = err; - err = 0; - } - + goto out_nfserr; + *lenp = err; + err = 0; out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -755,12 +757,14 @@ } } fh_compose(resfhp, fhp->fh_export, dnew); - -out_nfserr: if (err) - err = nfserrno(-err); + goto out_nfserr; out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -821,11 +825,14 @@ } dput_and_out: dput(dnew); -out_nfserr: if (err) - err = nfserrno(-err); + goto out_nfserr; out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -917,11 +924,14 @@ out_dput_old: dput(odentry); -out_nfserr: if (err) - err = nfserrno(-err); + goto out_nfserr; out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -965,14 +975,16 @@ fh_unlock(fhp); dput(rdentry); - if (!err && EX_ISSYNC(fhp->fh_export)) - write_inode_now(dirp); - -out_nfserr: if (err) - err = nfserrno(-err); + goto out_nfserr; + if (EX_ISSYNC(fhp->fh_export)) + write_inode_now(dirp); out: return err; + +out_nfserr: + err = nfserrno(-err); + goto out; } /* @@ -982,22 +994,23 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, encode_dent_fn func, u32 *buffer, int *countp) { - struct readdir_cd cd; struct inode *inode; - struct file file; u32 *p; int oldlen, eof, err; + struct file file; + struct readdir_cd cd; + err = 0; if (offset > ~(u32) 0) - return 0; + goto out; - if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0) - return err; + err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file); + if (err) + goto out; - if (!file.f_op->readdir) { - nfsd_close(&file); - return nfserr_notdir; - } + err = nfserr_notdir; + if (!file.f_op->readdir) + goto out_close; file.f_pos = offset; /* Set up the readdir context */ @@ -1012,7 +1025,7 @@ * may choose to do less. */ inode = file.f_dentry->d_inode; - do { + while (1) { oldlen = cd.buflen; /* @@ -1020,16 +1033,16 @@ file.f_inode->i_dev, file.f_inode->i_ino, (int) file.f_pos, (int) oldlen, (int) cd.buflen); */ - err = file.f_op->readdir(&file, - &cd, (filldir_t) func); - - if (err < 0) { - nfsd_close(&file); - return nfserrno(-err); - } + down(&inode->i_sem); + err = file.f_op->readdir(&file, &cd, (filldir_t) func); + up(&inode->i_sem); + if (err < 0) + goto out_nfserr; if (oldlen == cd.buflen) break; - } while (oldlen != cd.buflen && !cd.eob); + if (cd.eob) + break; + } /* If we didn't fill the buffer completely, we're at EOF */ eof = !cd.eob; @@ -1040,9 +1053,6 @@ if (cd.offset && !eof) *cd.offset = htonl(file.f_pos); - /* Close the file */ - nfsd_close(&file); - p = cd.buffer; *p++ = 0; /* no more entries */ *p++ = htonl(eof); /* end of directory */ @@ -1051,7 +1061,15 @@ dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n", *countp, eof, cd.offset? ntohl(*cd.offset) : -1); - return 0; + err = 0; +out_close: + nfsd_close(&file); +out: + return err; + +out_nfserr: + err = nfserrno(-err); + goto out_close; } /* diff -u --recursive --new-file v2.1.73/linux/fs/nls/Config.in linux/fs/nls/Config.in --- v2.1.73/linux/fs/nls/Config.in Sat Nov 1 11:04:27 1997 +++ linux/fs/nls/Config.in Fri Dec 19 15:24:21 1997 @@ -6,7 +6,8 @@ comment 'Native Language Support' # msdos and Joliet want NLS -if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" ]; then +if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \ + -o "$CONFIG_NTFS_FS" != "n" ]; then define_bool CONFIG_NLS y else define_bool CONFIG_NLS n diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/Makefile linux/fs/ntfs/Makefile --- v2.1.73/linux/fs/ntfs/Makefile Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/Makefile Fri Dec 19 15:24:21 1997 @@ -0,0 +1,9 @@ +# Rules for making the NTFS driver + +O_TARGET := ntfs.o +O_OBJS := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o +M_OBJS := $(O_TARGET) +EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"971219\" + +include $(TOPDIR)/Rules.make + diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/attr.c linux/fs/ntfs/attr.c --- v2.1.73/linux/fs/ntfs/attr.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/attr.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,519 @@ +/* + * attr.c + * + * Copyright (C) 1996-1997 Martin von Löwis + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#include "types.h" +#include "struct.h" +#include "attr.h" + +#include +#include "macros.h" +#include "support.h" +#include "util.h" +#include "super.h" +#include "inode.h" + +/* Look if an attribute already exists in the inode, and if not, create it */ +static int +new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found) +{ + int do_insert=0; + int i; + + for(i=0;iattr_count;i++) + { + int n=min(namelen,ino->attrs[i].namelen); + int s=ntfs_uni_strncmp(ino->attrs[i].name,name,n); + /* + * We assume that each attribute can be uniquely + * identified by inode + * number, attribute type and attribute name. + */ + if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){ + *found=1; + *pos=i; + return 0; + } + /* attributes are ordered by type, then by name */ + if(ino->attrs[i].type>type || (ino->attrs[i].type==type && s==1)){ + do_insert=1; + break; + } + } + + /* re-allocate space */ + if(ino->attr_count % 8 ==0) + { + ntfs_attribute* old=ino->attrs; + ino->attrs = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)* + sizeof(ntfs_attribute)); + if(old){ + ntfs_memcpy(ino->attrs,old,ino->attr_count*sizeof(ntfs_attribute)); + ntfs_free(old); + } + } + if(do_insert) + ntfs_memcpy(ino->attrs+i+1,ino->attrs+i,(ino->attr_count-i)* + sizeof(ntfs_attribute)); + ino->attr_count++; + ino->attrs[i].type=type; + ino->attrs[i].namelen=namelen; + ino->attrs[i].name=name; + *pos=i; + *found=0; + return 0; +} + +int +ntfs_make_attr_resident(ntfs_inode *ino,ntfs_attribute *attr) +{ + int size=attr->size; + if(size>0){ + /* FIXME: read data, free clusters */ + return EOPNOTSUPP; + } + attr->resident=1; + return 0; +} + +/* Store in the inode readable information about a run */ +static void +ntfs_insert_run(ntfs_attribute *attr,int cnum,int cluster,int len) +{ + /* (re-)allocate space if necessary */ + if(attr->d.r.len % 8 == 0) { + ntfs_runlist* old; + old=attr->d.r.runlist; + attr->d.r.runlist=ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist)); + if(old) { + ntfs_memcpy(attr->d.r.runlist,old,attr->d.r.len + *sizeof(ntfs_runlist)); + ntfs_free(old); + } + } + if(attr->d.r.len>cnum) + ntfs_memcpy(attr->d.r.runlist+cnum+1,attr->d.r.runlist+cnum, + (attr->d.r.len-cnum)*sizeof(ntfs_runlist)); + attr->d.r.runlist[cnum].cluster=cluster; + attr->d.r.runlist[cnum].len=len; + attr->d.r.len++; +} + +int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len, + int flags) +{ + int error=0; + ntfs_runlist *rl; + int rlen,cluster; + int clen; + if(attr->compressed)return EOPNOTSUPP; + if(attr->resident)return EOPNOTSUPP; + if(ino->record_count>1)return EOPNOTSUPP; + rl=attr->d.r.runlist; + rlen=attr->d.r.len-1; + if(rlen>=0) + cluster=rl[rlen].cluster+rl[rlen].len; + else + /* no preference for allocation space */ + cluster=0; + /* round up to multiple of cluster size */ + clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize; + /* FIXME: try to allocate smaller pieces */ + error=ntfs_allocate_clusters(ino->vol,&cluster,&clen, + flags|ALLOC_REQUIRE_SIZE); + if(error)return error; + attr->allocated+=clen; + *len=clen*ino->vol->clustersize; + /* contiguous chunk */ + if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){ + rl[rlen].len+=clen; + return 0; + } + ntfs_insert_run(attr,rlen+1,cluster,*len); + return 0; +} + +int +ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr) +{ + void *data=attr->d.data; + int len=attr->size; + int error,alen; + ntfs_io io; + attr->d.r.len=0; + attr->d.r.runlist=0; + attr->resident=0; + attr->allocated=attr->initialized=0; + alen=len; + error=ntfs_extend_attr(ino,attr,&alen,ALLOC_REQUIRE_SIZE); + if(error)return error;/* FIXME: On error, restore old values */ + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=data; + io.size=len; + io.do_read=0; + return ntfs_readwrite_attr(ino,attr,0,&io); +} + +/* Resize the attribute to a newsize */ +int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize) +{ + int error=0; + int oldsize=attr->size; + int clustersize=ino->vol->clustersize; + int i,count,newlen,newcount; + ntfs_runlist *rl; + + if(newsize==oldsize) + return 0; + /* modifying compressed attributes not supported yet */ + if(attr->compressed) + /* extending is easy: just insert sparse runs */ + return EOPNOTSUPP; + if(attr->resident){ + void *v; + if(newsize>ino->vol->clustersize){ + error=ntfs_make_attr_nonresident(ino,attr); + if(error)return error; + return ntfs_resize_attr(ino,attr,newsize); + } + v=attr->d.data; + if(newsize){ + attr->d.data=ntfs_malloc(newsize); + if(!attr->d.data) + return ENOMEM; + ntfs_bzero(attr->d.data+oldsize,newsize); + ntfs_memcpy(attr->d.data,v,min(newsize,oldsize)); + }else + attr->d.data=0; + ntfs_free(v); + attr->size=newsize; + return 0; + } + /* non-resident attribute */ + rl=attr->d.r.runlist; + if(newsized.r.len;i++){ + if((count+rl[i].len)*clustersize>newsize) + break; + count+=rl[i].len; + } + newlen=i+1; + /* free unused clusters in current run, unless sparse */ + newcount=count; + if(rl[i].cluster!=-1){ + int rounded=newsize-count*clustersize; + rounded=(rounded+clustersize-1)/clustersize; + error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded, + rl[i].len-rounded); + if(error) + return error; /* FIXME: incomplete operation */ + rl[i].len=rounded; + newcount=count+rounded; + } + /* free all other runs */ + for(i++;id.r.len;i++) + if(rl[i].cluster!=-1){ + error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,rl[i].len); + if(error) + return error; /* FIXME: incomplete operation */ + } + /* FIXME? free space for extra runs in memory */ + attr->d.r.len=newlen; + }else{ + newlen=newsize; + error=ntfs_extend_attr(ino,attr,&newlen,ALLOC_REQUIRE_SIZE); + if(error)return error; /* FIXME: incomplete */ + newcount=newlen/clustersize; + } + /* fill in new sizes */ + attr->allocated = newcount*clustersize; + attr->size = newsize; + attr->initialized = newsize; + if(!newsize) + error=ntfs_make_attr_resident(ino,attr); + return error; +} + +int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data, + int dsize, ntfs_attribute **rattr) +{ + void *name; + int namelen; + int found,i; + int error; + ntfs_attribute *attr; + if(dsize>ino->vol->mft_recordsize) + /* FIXME: non-resident attributes */ + return EOPNOTSUPP; + if(aname){ + namelen=strlen(aname); + name=ntfs_malloc(2*namelen); + ntfs_ascii2uni(name,aname,namelen); + }else{ + name=0; + namelen=0; + } + new_attr(ino,anum,name,namelen,&i,&found); + if(found){ + ntfs_free(name); + return EEXIST; + } + *rattr=attr=ino->attrs+i; + /* allocate a new number. + FIXME: Should this happen on inode writeback? + FIXME: extensions records not supported */ + error=ntfs_allocate_attr_number(ino,&i); + if(error) + return error; + attr->attrno=i; + + attr->resident=1; + attr->compressed=attr->cengine=0; + attr->size=attr->allocated=attr->initialized=dsize; + + /* FIXME: INDEXED information should come from $AttrDef + Currently, only file names are indexed */ + if(anum==ino->vol->at_file_name){ + attr->indexed=1; + }else + attr->indexed=0; + attr->d.data=ntfs_malloc(dsize); + ntfs_memcpy(attr->d.data,data,dsize); + return 0; +} + +/* Non-resident attributes are stored in runs (intervals of clusters). + * + * This function stores in the inode readable information about a non-resident + * attribute. + */ +static int +ntfs_process_runs(ntfs_inode *ino,ntfs_attribute* attr,unsigned char *data) +{ + int startvcn,endvcn; + int vcn,cnum; + int cluster,len,ctype; + startvcn = NTFS_GETU64(data+0x10); + endvcn = NTFS_GETU64(data+0x18); + + /* check whether this chunk really belongs to the end */ + for(cnum=0,vcn=0;cnumd.r.len;cnum++) + vcn+=attr->d.r.runlist[cnum].len; + if(vcn!=startvcn) + { + ntfs_error("Problem with runlist in extended record\n"); + return -1; + } + if(!endvcn) + { + endvcn = NTFS_GETU64(data+0x28)-1; /* allocated length */ + endvcn /= ino->vol->clustersize; + } + data=data+NTFS_GETU16(data+0x20); + cnum=attr->d.r.len; + cluster=0; + for(vcn=startvcn; vcn<=endvcn; vcn+=len) + { + if(ntfs_decompress_run(&data,&len,&cluster,&ctype)) + return -1; + if(ctype) + ntfs_insert_run(attr,cnum,-1,len); + else + ntfs_insert_run(attr,cnum,cluster,len); + cnum++; + } + return 0; +} + +/* Insert the attribute starting at attr in the inode ino */ +int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata) +{ + int i,found; + int type; + short int *name; + int namelen; + void *data; + ntfs_attribute *attr; + + type = NTFS_GETU32(attrdata); + namelen = NTFS_GETU8(attrdata+9); + /* read the attribute's name if it has one */ + if(!namelen) + name=0; + else + { + /* 1 Unicode character fits in 2 bytes */ + name=ntfs_malloc(2*namelen); + ntfs_memcpy(name,attrdata+NTFS_GETU16(attrdata+10),2*namelen); + } + new_attr(ino,type,name,namelen,&i,&found); + /* We can have in one inode two attributes with type 0x00000030 (File Name) + and without name */ + if(found && /*FIXME*/type!=ino->vol->at_file_name) + { + ntfs_process_runs(ino,ino->attrs+i,attrdata); + return 0; + } + attr=ino->attrs+i; + attr->resident=NTFS_GETU8(attrdata+8)==0; + attr->compressed=NTFS_GETU16(attrdata+0xC); + attr->attrno=NTFS_GETU16(attrdata+0xE); + + if(attr->resident) { + attr->size=NTFS_GETU16(attrdata+0x10); + data=attrdata+NTFS_GETU16(attrdata+0x14); + attr->d.data = (void*)ntfs_malloc(attr->size); + ntfs_memcpy(attr->d.data,data,attr->size); + attr->indexed=NTFS_GETU16(attrdata+0x16); + }else{ + attr->allocated=NTFS_GETU32(attrdata+0x28); + attr->size=NTFS_GETU32(attrdata+0x30); + attr->initialized=NTFS_GETU32(attrdata+0x38); + attr->cengine=NTFS_GETU16(attrdata+0x22); + if(attr->compressed) + attr->compsize=NTFS_GETU32(attrdata+0x40); + ino->attrs[i].d.r.runlist=0; + ino->attrs[i].d.r.len=0; + ntfs_process_runs(ino,attr,attrdata); + } + return 0; +} + +/* process compressed attributes */ +int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest) +{ + int error=0; + int clustersize,l; + int s_vcn,rnum,vcn,cluster,len,chunk,got,cl1,l1,offs1,copied; + char *comp=0,*comp1; + char *decomp=0; + ntfs_io io; + ntfs_runlist *rl; + + l=dest->size; + clustersize=ino->vol->clustersize; + /* starting cluster of potential chunk + there are three situations: + a) in a large uncompressable or sparse chunk, + s_vcn is in the middle of a run + b) s_vcn is right on a run border + c) when several runs make a chunk, s_vcn is before the chunks + */ + s_vcn=offset/clustersize; + /* round down to multiple of 16 */ + s_vcn &= ~15; + rl=attr->d.r.runlist; + for(rnum=vcn=0;rnumd.r.len && vcn+rl->len<=s_vcn;rnum++,rl++) + vcn+=rl->len; + if(rnum==attr->d.r.len){ + /* beyond end of file */ + /* FIXME: check allocated/initialized */ + dest->size=0; + return 0; + } + io.do_read=1; + io.fn_put=ntfs_put; + io.fn_get=0; + cluster=rl->cluster; + len=rl->len; + copied=0; + while(l){ + chunk=0; + if(cluster==-1){ + /* sparse cluster */ + char *sparse=ntfs_calloc(512); + int l1; + if(!sparse)return ENOMEM; + if((len-(s_vcn-vcn)) & 15) + ntfs_error("unexpected sparse chunk size"); + l1=chunk = min((vcn+len)*clustersize-offset,l); + while(l1){ + int i=min(l1,512); + dest->fn_put(dest,sparse,i); + l1-=i; + } + ntfs_free(sparse); + }else if(dest->do_read){ + if(!comp){ + comp=ntfs_malloc(16*clustersize); + if(!comp){ + error=ENOMEM; + goto out; + } + } + got=0; + /* we might need to start in the middle of a run */ + cl1=cluster+s_vcn-vcn; + comp1=comp; + do{ + io.param=comp1; + l1=min(len-max(s_vcn-vcn,0),16-got); + io.size=l1*clustersize; + error=ntfs_getput_clusters(ino->vol,cl1,0,&io); + if(error)goto out; + if(l1+max(s_vcn-vcn,0)==len){ + rnum++;rl++; + vcn+=len; + cluster=cl1=rl->cluster; + len=rl->len; + } + got+=l1; + comp1+=l1*clustersize; + }while(cluster!=-1 && got<16); /* until empty run */ + chunk=16*clustersize; + if(cluster!=-1 || got==16) + /* uncompressible */ + comp1=comp; + else{ + if(!decomp){ + decomp=ntfs_malloc(16*clustersize); + if(!decomp){ + error=ENOMEM; + goto out; + } + } + /* make sure there are null bytes + after the last block */ + *(ntfs_u32*)comp1=0; + ntfs_decompress(decomp,comp,chunk); + comp1=decomp; + } + offs1=offset-s_vcn*clustersize; + chunk=min(16*clustersize-offs1,chunk); + chunk=min(l,chunk); + dest->fn_put(dest,comp1+offs1,chunk); + } + l-=chunk; + copied+=chunk; + offset+=chunk; + s_vcn=offset/clustersize & ~15; + if(l && offset>=((vcn+len)*clustersize)){ + rnum++;rl++; + vcn+=len; + cluster=rl->cluster; + len=rl->len; + } + } + out: + if(comp)ntfs_free(comp); + if(decomp)ntfs_free(decomp); + dest->size=copied; + return error; +} + +int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest) +{ + return EOPNOTSUPP; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/attr.h linux/fs/ntfs/attr.h --- v2.1.73/linux/fs/ntfs/attr.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/attr.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,17 @@ +/* + * attr.h + * Header file for attr.c + * + * Copyright (C) 1997 Régis Duchesne + */ + +int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len, + int flags); +int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize); +int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata); +int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest); +int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest); +int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data, + int dsize, ntfs_attribute **rattr); diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/dir.c linux/fs/ntfs/dir.c --- v2.1.73/linux/fs/ntfs/dir.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/dir.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,807 @@ +/* + * dir.c + * + * Copyright (C) 1995-1997 Martin von Löwis + */ + +#include "types.h" +#include "struct.h" +#include "dir.h" + +#include +#include "super.h" +#include "inode.h" +#include "attr.h" +#include "support.h" +#include "util.h" + +static char I30[]="$I30"; + +/* An index record should start with INDX, and the last word in each + block should contain the check value. If it passes, the original + values need to be restored */ +int ntfs_check_index_record(ntfs_inode *ino, char *record) +{ + return ntfs_fixup_record(ino->vol, record, "INDX", + ino->u.index.recordsize); +} + +static inline int ntfs_is_top(long long stack) +{ + return stack==14; +} + +static long long ntfs_pop(long long *stack) +{ + static int width[16]={1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1}; + int res=-1; + switch(width[*stack & 15]) + { + case 1:res=(*stack&15)>>1; + *stack>>=4; + break; + case 2:res=((*stack&63)>>2)+7; + *stack>>=6; + break; + case 3:res=((*stack & 255)>>3)+23; + *stack>>=8; + break; + case 4:res=((*stack & 1023)>>4)+55; + *stack>>=10; + break; + default:ntfs_error("Unknown encoding\n"); + } + return res; +} + +static inline unsigned int ntfs_top(void) +{ + return 14; +} + +static long long ntfs_push(long long stack,int i) +{ + if(i<7)return (stack<<4)|(i<<1); + if(i<23)return (stack<<6)|((i-7)<<2)|1; + if(i<55)return (stack<<8)|((i-23)<<3)|3; + if(i<120)return (stack<<10)|((i-55)<<4)|7; + ntfs_error("Too many entries\n"); + return -1; +} + +#if 0 +static void ntfs_display_stack(long long stack) +{ + while(!ntfs_is_top(stack)) + { + printf("%d ",ntfs_pop(&stack)); + } + printf("\n"); +} +#endif + +/* True if the entry points to another block of entries */ +static inline int ntfs_entry_has_subnodes(char* entry) +{ + return (int)NTFS_GETU8(entry+12)&1; +} + +/* True if it is not the 'end of dir' entry */ +static inline int ntfs_entry_is_used(char* entry) +{ + return (int)(NTFS_GETU8(entry+12)&2)==0; +} + +static int ntfs_allocate_index_block(ntfs_iterate_s *walk) +{ + ntfs_attribute *allocation=0,*bitmap=0; + int error,size,i,bit; + ntfs_u8 *bmap; + ntfs_io io; + ntfs_volume *vol=walk->dir->vol; + + /* check for allocation attribute */ + allocation=ntfs_find_attr(walk->dir,vol->at_index_allocation,I30); + if(!allocation){ + ntfs_u8 bmp[8]; + /* create index allocation attribute */ + error=ntfs_create_attr(walk->dir,vol->at_index_allocation,I30, + 0,0,&allocation); + if(error)return error; + ntfs_bzero(bmp,sizeof(bmp)); + error=ntfs_create_attr(walk->dir,vol->at_bitmap,I30, + bmp,sizeof(bmp),&bitmap); + if(error)return error; + }else + bitmap=ntfs_find_attr(walk->dir,vol->at_bitmap,I30); + if(!bitmap){ + ntfs_error("Directory w/o bitmap\n"); + return EINVAL; + } + size=bitmap->size; + bmap=ntfs_malloc(size); + if(!bmap)return ENOMEM; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=bmap; + io.size=size; + error=ntfs_read_attr(walk->dir,vol->at_bitmap,I30,0,&io); + if(error){ + ntfs_free(bmap); + return error; + } + if(io.size!=size){ + ntfs_free(bmap); + return EIO; + } + + /* allocate a bit */ + for(i=bit=0;i>bit) & 1) == 0) + break; + if(bit!=8)break; + } + if(i==size) + /* FIXME: extend bitmap */ + return EOPNOTSUPP; + walk->newblock=(i*8+bit)*walk->dir->u.index.clusters_per_record; + bmap[i]|= 1<dir,vol->at_bitmap,I30,0,&io); + if(error || io.size!=size){ + ntfs_free(bmap); + return error?error:EIO; + } + ntfs_free(bmap); + + /* check whether record is out of allocated range */ + size=allocation->size; + if(walk->newblock * vol->clustersize >= size){ + /* build index record */ + int s1=walk->dir->u.index.recordsize; + char *record=ntfs_malloc(s1); + int newlen; + ntfs_bzero(record,s1); + /* magic */ + ntfs_memcpy(record,"INDX",4); + /* offset to fixups */ + NTFS_PUTU16(record+4,0x28); + /* number of fixups */ + NTFS_PUTU16(record+6,s1/vol->blocksize+1); + /* FIXME: log file number */ + /* VCN of buffer */ + NTFS_PUTU64(record+0x10,walk->newblock); + /* header size. FIXME */ + NTFS_PUTU16(record+0x18,28); + /* total size of record */ + NTFS_PUTU32(record+0x20,s1-0x18); + io.param=record; + newlen=walk->dir->u.index.recordsize; + /* allocate contiguous index record */ + error=ntfs_extend_attr(walk->dir,allocation,&newlen, + ALLOC_REQUIRE_SIZE); + if(error){ + /* FIXME: try smaller allocations */ + ntfs_free(record); + return ENOSPC; + } + io.size=s1; + error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30, + size,&io); + if(error || io.size!=s1){ + ntfs_free(record); + return error?error:EIO; + } + ntfs_free(record); + } + + return 0; +} + +static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block, + int used) +{ + ntfs_io io; + int error; + io.fn_put=0; + io.fn_get=ntfs_get; + io.param=buf; + if(walk->block==-1){ + NTFS_PUTU16(buf+0x14,used-0x10); + /* 0x18 is a copy thereof */ + NTFS_PUTU16(buf+0x18,used-0x10); + io.size=used; + error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_root, + I30,0,&io); + if(error)return error; + if(io.size!=used)return EIO; + }else{ + NTFS_PUTU16(buf+0x1C,used-0x20); + ntfs_insert_fixups(buf,walk->dir->vol->blocksize); + io.size=walk->dir->u.index.recordsize; + error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_allocation,I30, + walk->block*walk->dir->vol->clustersize, + &io); + if(error)return error; + if(io.size!=walk->dir->u.index.recordsize) + return EIO; + } + return 0; +} + +static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize, + int usize) +{ + char *entry,*prev; + ntfs_u8 *newbuf=0,*middle=0; + int error,othersize,mlen; + ntfs_io io; + ntfs_volume *vol=walk->dir->vol; + error=ntfs_allocate_index_block(walk); + if(error) + return error; + for(entry=prev=start+NTFS_GETU16(start+0x18)+0x18; + entry-startindex_recordsize); + if(!newbuf) + return ENOMEM; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=newbuf; + io.size=vol->index_recordsize; + /* read in old header. FIXME: reading everything is overkill */ + error=ntfs_read_attr(walk->dir,vol->at_index_allocation,I30, + walk->newblock*vol->clustersize,&io); + if(error)goto out; + if(io.size!=vol->index_recordsize){ + error=EIO; + goto out; + } + /* FIXME: adjust header */ + /* copy everything from entry to new block */ + othersize=usize-(entry-start); + ntfs_memcpy(newbuf+NTFS_GETU16(newbuf+0x18)+0x18,entry,othersize); + error=ntfs_index_writeback(walk,newbuf,walk->newblock,othersize); + if(error)goto out; + + /* move prev to walk */ + mlen=NTFS_GETU16(prev+0x8); + /* allow for pointer to subnode */ + middle=ntfs_malloc(ntfs_entry_has_subnodes(prev)?mlen:mlen+8); + if(!middle){ + error=ENOMEM; + goto out; + } + ntfs_memcpy(middle,prev,mlen); + /* set has_subnodes flag */ + NTFS_PUTU8(middle+0xC, NTFS_GETU8(middle+0xC) | 1); + /* middle entry points to block, parent entry will point to newblock */ + NTFS_PUTU64(middle+mlen-8,walk->block); + if(walk->new_entry) + ntfs_error("entry not reset"); + walk->new_entry=middle; + walk->u.flags|=ITERATE_SPLIT_DONE; + /* write back original block */ + error=ntfs_index_writeback(walk,start,walk->block,usize-(prev-start)); + out: + if(newbuf)ntfs_free(newbuf); + if(middle)ntfs_free(middle); + return error; +} + +static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry) +{ + int blocksize,usedsize,error,offset; + int do_split=0; + offset=entry-start; + if(walk->block==-1){ /*index root */ + /* FIXME: adjust to maximum allowed index root value */ + blocksize=walk->dir->vol->mft_recordsize; + usedsize=NTFS_GETU16(start+0x14)+0x10; + }else{ + blocksize=walk->dir->u.index.recordsize; + usedsize=NTFS_GETU16(start+0x1C)+0x20; + } + if(usedsize+walk->new_entry_size > blocksize){ + char* s1=ntfs_malloc(blocksize+walk->new_entry_size); + if(!s1)return ENOMEM; + ntfs_memcpy(s1,start,usedsize); + do_split=1; + /* adjust entry to s1 */ + entry=s1+(entry-start); + start=s1; + } + ntfs_memmove(entry+walk->new_entry_size,entry,usedsize-offset); + ntfs_memcpy(entry,walk->new_entry,walk->new_entry_size); + usedsize+=walk->new_entry_size; + ntfs_free(walk->new_entry); + walk->new_entry=0; + /*FIXME: split root */ + if(do_split){ + error=ntfs_split_record(walk,start,blocksize,usedsize); + ntfs_free(start); + }else + ntfs_index_writeback(walk,start,walk->block,usedsize); + return 0; +} + +/* The entry has been found. Copy the result in the caller's buffer */ +static int ntfs_copyresult(char *dest,char *source) +{ + int length=NTFS_GETU16(source+8); + ntfs_memcpy(dest,source,length); + return 1; +} + +/* use $UpCase some day */ +static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x) +{ + /* we should read any pending rest of $UpCase here */ + if(x >= vol->upcase_length) + return x; + return vol->upcase[x]; +} + +/* everything passed in walk and entry */ +static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry) +{ + int lu=*(entry+0x50); + int i; + + ntfs_u16* name=(ntfs_u16*)(entry+0x52); + ntfs_volume *vol=walk->dir->vol; + for(i=0;inamelen;i++) + if(ntfs_my_toupper(vol,name[i])!=ntfs_my_toupper(vol,walk->name[i])) + break; + if(i==lu && i==walk->namelen)return 0; + if(i==lu)return 1; + if(i==walk->namelen)return -1; + if(ntfs_my_toupper(vol,name[i])name[i]))return 1; + return -1; +} + +/* Necessary forward declaration */ +static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry); + +/* Parse a block of entries. Load the block, fix it up, and iterate + over the entries. The block is given as virtual cluster number */ +static int ntfs_getdir_record(ntfs_iterate_s *walk, int block) +{ + int length=walk->dir->u.index.recordsize; + char *record=(char*)ntfs_malloc(length); + char *offset; + int retval,error; + int oldblock; + ntfs_io io; + + io.fn_put=ntfs_put; + io.param=record; + io.size=length; + /* Read the block from the index allocation attribute */ + error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_allocation,I30, + block*walk->dir->vol->clustersize,&io); + if(error || io.size!=length){ + ntfs_error("read failed\n"); + ntfs_free(record); + return 0; + } + if(!ntfs_check_index_record(walk->dir,record)){ + ntfs_error("%x is not an index record\n",block); + ntfs_free(record); + return 0; + } + offset=record+NTFS_GETU16(record+0x18)+0x18; + oldblock=walk->block; + walk->block=block; + retval=ntfs_getdir_iterate(walk,record,offset); + walk->block=oldblock; + ntfs_free(record); + return retval; +} + +/* go down to the next block of entries. These collate before + the current entry */ +static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry) +{ + int length=NTFS_GETU16(entry+8); + int nextblock=NTFS_GETU32(entry+length-8); + int error; + + if(!ntfs_entry_has_subnodes(entry)) { + ntfs_error("illegal ntfs_descend call\n"); + return 0; + } + error=ntfs_getdir_record(walk,nextblock); + if(!error && walk->type==DIR_INSERT && + (walk->u.flags & ITERATE_SPLIT_DONE)){ + /* split has occured. adjust entry, insert new_entry */ + NTFS_PUTU32(entry+length-8,walk->newblock); + /* reset flags, as the current block might be split again */ + walk->u.flags &= ~ITERATE_SPLIT_DONE; + error=ntfs_dir_insert(walk,start,entry); + } + return error; +} + +static int +ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk,char* start,char *entry) +{ + int retval=0; + int curpos=0,destpos=0; + int length; + if(walk->u.pos!=0){ + if(ntfs_is_top(walk->u.pos))return 0; + destpos=ntfs_pop(&walk->u.pos); + } + while(1){ + if(walk->u.pos==0) + { + if(ntfs_entry_has_subnodes(entry)) + ntfs_descend(walk,start,entry); + else + walk->u.pos=ntfs_top(); + if(ntfs_is_top(walk->u.pos) && !ntfs_entry_is_used(entry)) + { + return 1; + } + walk->u.pos=ntfs_push(walk->u.pos,curpos); + return 1; + } + if(curpos==destpos) + { + if(!ntfs_is_top(walk->u.pos) && ntfs_entry_has_subnodes(entry)) + { + retval=ntfs_descend(walk,start,entry); + if(retval){ + walk->u.pos=ntfs_push(walk->u.pos,curpos); + return retval; + }else{ + if(!ntfs_entry_is_used(entry)) + return 0; + walk->u.pos=0; + } + } + if(ntfs_entry_is_used(entry)) + { + retval=ntfs_copyresult(walk->result,entry); + walk->u.pos=0; + }else{ + walk->u.pos=ntfs_top(); + return 0; + } + } + curpos++; + if(!ntfs_entry_is_used(entry))break; + length=NTFS_GETU16(entry+8); + if(!length){ + ntfs_error("infinite loop\n"); + break; + } + entry+=length; + } + return -1; +} + +/* Iterate over a list of entries, either from an index block, or from + the index root. + If searching BY_POSITION, pop the top index from the position. If the + position stack is empty then, return the item at the index and set the + position to the next entry. If the position stack is not empty, + recursively proceed for subnodes. If the entry at the position is the + 'end of dir' entry, return 'not found' and the empty stack. + If searching BY_NAME, walk through the items until found or until + one item is collated after the requested item. In the former case, return + the result. In the latter case, recursively proceed to the subnodes. + If 'end of dir' is reached, the name is not in the directory */ +static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry) +{ + int length; + int retval=0; + int cmp; + + if(walk->type==BY_POSITION) + return ntfs_getdir_iterate_byposition(walk,start,entry); + do{ + /* if the current entry is a real one, compare with the + requested item. If the current entry is the last item, + it is always larger than the requested item */ + cmp = ntfs_entry_is_used(entry) ? ntfs_my_strcmp(walk,entry) : -1; + switch(walk->type){ + case BY_NAME: + switch(cmp) + { + case -1:return ntfs_entry_has_subnodes(entry)? + ntfs_descend(walk,start,entry):0; + case 0:return ntfs_copyresult(walk->result,entry); + case 1:break; + } + break; + case DIR_INSERT: + switch(cmp){ + case -1:return ntfs_entry_has_subnodes(entry)? + ntfs_descend(walk,start,entry): + ntfs_dir_insert(walk,start,entry); + case 0:return EEXIST; + case 1:break; + } + break; + default: + ntfs_error("TODO\n"); + } + if(!ntfs_entry_is_used(entry))break; + length=NTFS_GETU16(entry+8); + if(!length){ + ntfs_error("infinite loop\n"); + break; + } + entry+=length; + }while(1); + return retval; +} + +/* Tree walking is done using position numbers. The following numbers have + a special meaning: + 0 start (.) + -1 no more entries + -2 .. + All other numbers encode sequences of indices. The sequence a,b,c is + encoded as , where is the encoding of foo. The + first few integers are encoded as follows: + 0: 0000 1: 0010 2: 0100 3: 0110 + 4: 1000 5: 1010 6: 1100 stop: 1110 + 7: 000001 8: 000101 9: 001001 10: 001101 + The least significant bits give the width of this encoding, the + other bits encode the value, starting from the first value of the + interval. + tag width first value last value + 0 3 0 6 + 01 4 7 22 + 011 5 23 54 + 0111 6 55 119 + More values are hopefully not needed, as the file position has currently + 64 bits in total. +*/ + +/* Find an entry in the directory. Return 0 if not found, otherwise copy + the entry to the result buffer. */ +int ntfs_getdir(ntfs_iterate_s* walk) +{ + int length=walk->dir->vol->mft_recordsize; + int retval,error; + /* start at the index root.*/ + char *root=ntfs_malloc(length); + ntfs_io io; + + io.fn_put=ntfs_put; + io.param=root; + io.size=length; + error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_root, + I30,0,&io); + if(error) + { + ntfs_error("Not a directory\n"); + return 0; + } + walk->block=-1; + /* FIXME: move these to walk */ + walk->dir->u.index.recordsize = NTFS_GETU32(root+0x8); + walk->dir->u.index.clusters_per_record = NTFS_GETU32(root+0xC); + /* FIXME: consistency check */ + /* skip header */ + retval = ntfs_getdir_iterate(walk,root,root+0x20); + ntfs_free(root); + return retval; +} + +/* Find an entry in the directory by its position stack. Iteration starts + if the stack is 0, in which case the position is set to the first item + in the directory. If the position is nonzero, return the item at the + position and change the position to the next item. The position is -1 + if there are no more items */ +int ntfs_getdir_byposition(ntfs_iterate_s *walk) +{ + walk->type=BY_POSITION; + return ntfs_getdir(walk); +} + +/* Find an entry in the directory by its name. Return 0 if not found */ +int ntfs_getdir_byname(ntfs_iterate_s *walk) +{ + walk->type=BY_NAME; + return ntfs_getdir(walk); +} + +int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low, + int(*cb)(ntfs_u8*,void*),void *param) +{ + char *buf=0,*entry=0; + ntfs_io io; + int length; + int block; + int start; + ntfs_attribute *attr; + ntfs_volume *vol=ino->vol; + int byte,bit; + int error=0; + + if(!ino){ + ntfs_error("No inode passed to getdir_unsorted\n"); + return EINVAL; + } + if(!vol){ + ntfs_error("Inode %d has no volume\n",ino->i_number); + return EINVAL; + } + /* are we still in the index root */ + if(*p_high==0){ + buf=ntfs_malloc(length=vol->mft_recordsize); + io.fn_put=ntfs_put; + io.param=buf; + io.size=length; + error=ntfs_read_attr(ino,vol->at_index_root,I30,0,&io); + if(error){ + ntfs_free(buf); + return error; + } + ino->u.index.recordsize = NTFS_GETU32(buf+0x8); + ino->u.index.clusters_per_record = NTFS_GETU32(buf+0xC); + entry=buf+0x20; + }else{ /* we are in an index record */ + length=ino->u.index.recordsize; + buf=ntfs_malloc(length); + io.fn_put=ntfs_put; + io.param=buf; + io.size=length; + /* 0 is index root, index allocation starts with 4 */ + block = *p_high - ino->u.index.clusters_per_record; + error=ntfs_read_attr(ino,vol->at_index_allocation,I30, + block*vol->clustersize,&io); + if(!error && io.size!=length)error=EIO; + if(error){ + ntfs_error("read failed\n"); + ntfs_free(buf); + return error; + } + if(!ntfs_check_index_record(ino,buf)){ + ntfs_error("%x is not an index record\n",block); + ntfs_free(buf); + return ENOTDIR; + } + entry=buf+NTFS_GETU16(buf+0x18)+0x18; + } + + /* process the entries */ + start=*p_low; + while(ntfs_entry_is_used(entry)){ + if(start) + start--; /* skip entries that were already processed */ + else{ + if((error=cb(entry,param))) + /* the entry could not be processed */ + break; + (*p_low)++; + } + entry+=NTFS_GETU16(entry+8); + } + + /* caller did not process all entries */ + if(error){ + ntfs_free(buf); + return error; + } + + /* we have to locate the next record */ + ntfs_free(buf); + buf=0; + *p_low=0; + attr=ntfs_find_attr(ino,vol->at_bitmap,I30); + if(!attr){ + /* directory does not have index allocation */ + *p_high=0xFFFFFFFF; + *p_low=0; + return 0; + } + buf=ntfs_malloc(length=attr->size); + io.param=buf; + io.size=length; + error=ntfs_read_attr(ino,vol->at_bitmap,I30,0,&io); + if(!error && io.size!=length)error=EIO; + if(error){ + ntfs_free(buf); + return EIO; + } + attr=ntfs_find_attr(ino,vol->at_index_allocation,I30); + while(1){ + if(*p_high*vol->clustersize > attr->size){ + /* no more index records */ + *p_high=0xFFFFFFFF; + ntfs_free(buf); + return 0; + } + *p_high+=ino->u.index.clusters_per_record; + byte=*p_high/ino->u.index.clusters_per_record-1; + bit = 1 << (byte & 7); + byte = byte >> 3; + /* this record is allocated */ + if(buf[byte] & bit) + break; + } + return 0; +} + +int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name) +{ + ntfs_iterate_s walk; + int nsize,esize; + ntfs_u8* entry,*ndata; + int error; + + walk.type=DIR_INSERT; + walk.dir=dir; + walk.u.flags=0; + nsize = name->size; + ndata = name->d.data; + walk.name=(ntfs_u16*)(ndata+0x42); + walk.namelen=NTFS_GETU8(ndata+0x40); + walk.new_entry_size = esize = ((nsize+0x18)/8)*8; + walk.new_entry=entry=ntfs_malloc(esize); + if(!entry)return ENOMEM; + ntfs_bzero(entry,esize); + NTFS_PUTINUM(entry,new); + NTFS_PUTU16(entry+0x8,esize); /* size of entry */ + NTFS_PUTU16(entry+0xA,nsize); /* size of original name attribute */ + NTFS_PUTU32(entry+0xC,0); /* FIXME: D-F? */ + ntfs_memcpy(entry+0x10,ndata,nsize); + error=ntfs_getdir(&walk); + if(walk.new_entry) + ntfs_free(walk.new_entry); + return error; +} + +#if 0 +int ntfs_dir_add1(ntfs_inode *dir,const char* name,int namelen,ntfs_inode *ino) +{ + ntfs_iterate_s walk; + int error; + int nsize; + char *entry; + ntfs_attribute *name_attr; + error=ntfs_decodeuni(dir->vol,name,namelen,&walk.name,&walk.namelen); + if(error) + return error; + /* FIXME: set flags */ + walk.type=DIR_INSERT; + walk.dir=dir; + /*walk.new=ino;*/ + /* prepare new entry */ + /* round up to a multiple of 8 */ + walk.new_entry_size = nsize = ((0x52+2*walk.namelen+7)/8)*8; + walk.new_entry=entry=ntfs_malloc(nsize); + if(!entry) + return ENOMEM; + ntfs_bzero(entry,nsize); + NTFS_PUTINUM(entry,ino); + NTFS_PUTU16(entry+8,nsize); + NTFS_PUTU16(entry+0xA,0x42+2*namelen); /*FIXME: size of name attr*/ + NTFS_PUTU32(entry+0xC,0); /*FIXME: D-F? */ + name_attr=ntfs_find_attr(ino,vol->at_file_name,0); /* FIXME:multiple names */ + if(!name_attr || !name_attr->resident) + return EIDRM; + /* directory, file stamps, sizes, filename */ + ntfs_memcpy(entry+0x10,name_attr->d.data,0x42+2*namelen); + error=ntfs_getdir(&walk); + ntfs_free(walk.name); + return error; +} +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/dir.h linux/fs/ntfs/dir.h --- v2.1.73/linux/fs/ntfs/dir.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/dir.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,39 @@ +/* + * dir.h + * Header file for dir.c + * + * Copyright (C) 1997 Régis Duchesne + */ + +#define ITERATE_SPLIT_DONE 1 + +enum ntfs_iterate_e { + BY_POSITION, + BY_NAME, + DIR_INSERT +}; + +/* not all fields are used for all operations */ +typedef struct ntfs_iterate_s{ + enum ntfs_iterate_e type; + ntfs_inode *dir; + union{ + long long pos; + int flags; + }u; + char *result; /* pointer to entry if found */ + ntfs_u16* name; + int namelen; + int block; /* current index record */ + int newblock; /* index record created in a split */ + char *new_entry; + int new_entry_size; + /*ntfs_inode* new;*/ +} ntfs_iterate_s; + +int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low, + int(*cb)(ntfs_u8*,void*), void *param); +int ntfs_getdir_byname(ntfs_iterate_s *walk); +int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name); +int ntfs_check_index_record(ntfs_inode *ino, char *record); +int ntfs_getdir_byposition(ntfs_iterate_s *walk); diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c --- v2.1.73/linux/fs/ntfs/fs.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/fs.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,968 @@ +/* + * fs.c + * NTFS driver for Linux 2.1 + * + * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1996 Richard Russon + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef NTFS_IN_LINUX_KERNEL +#include +#endif + +#include "types.h" +#include "struct.h" +#include "util.h" +#include "inode.h" +#include "super.h" +#include "dir.h" +#include "support.h" +#include "macros.h" +#include "sysctl.h" +#include +#include +#include +#include +#include + +#define ITEM_SIZE 2040 + +/* io functions to user space */ +static void ntfs_putuser(ntfs_io* dest,void *src,ntfs_size_t len) +{ + copy_to_user(dest->param,src,len); + dest->param+=len; +} + +#ifdef CONFIG_NTFS_RW +static void ntfs_getuser(void *dest,ntfs_io *src,ntfs_size_t len) +{ + copy_from_user(dest,src->param,len); + src->param+=len; +} +#endif + +static ssize_t +ntfs_read(struct file * filp, char *buf, size_t count, loff_t *off) +{ + int error; + ntfs_io io; + ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode); + + /* inode is not properly initialized */ + if(!ino)return -EINVAL; + ntfs_debug(DEBUG_OTHER, "ntfs_read %x,%x,%x ->", + (unsigned)ino->i_number,(unsigned)*off,(unsigned)count); + /* inode has no unnamed data attribute */ + if(!ntfs_find_attr(ino,ino->vol->at_data,NULL)) + return -EINVAL; + + /* read the data */ + io.fn_put=ntfs_putuser; + io.fn_get=0; + io.param=buf; + io.size=count; + error=ntfs_read_attr(ino,ino->vol->at_data,NULL,*off,&io); + if(error)return -error; + + *off+=io.size; + return io.size; +} + +#ifdef CONFIG_NTFS_RW +static ssize_t +ntfs_write(struct file *filp,const char* buf,size_t count,loff_t *pos) +{ + struct super_block* sb; + int ret; + ntfs_io io; + ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode); + + if(!ino)return -EINVAL; + ntfs_debug(DEBUG_OTHER, "ntfs_write %x,%x,%x ->", + (unsigned)ino->i_number,(unsigned)*pos,(unsigned)count); + sb = filp->f_dentry->d_inode->i_sb; + /* Allows to lock fs ro at any time */ + if(sb->s_flags & MS_RDONLY) + return -ENOSPC; + if(!ntfs_find_attr(ino,ino->vol->at_data,NULL)) + return -EINVAL; + + io.fn_put=0; + io.fn_get=ntfs_getuser; + io.param=(void*)buf; /* to get rid of the const */ + io.size=count; + ret = ntfs_write_attr(ino,ino->vol->at_data,NULL,*pos,&io); + ntfs_debug(DEBUG_OTHER, "%x\n",ret); + if(ret<0)return -EINVAL; + + *pos+=ret; + return ret; +} +#endif + +struct ntfs_filldir{ + struct inode *dir; + filldir_t filldir; + unsigned int type; + ntfs_u32 ph,pl; + void *dirent; + char *name; + int namelen; +}; + +static int ntfs_printcb(ntfs_u8 *entry,void *param) +{ + struct ntfs_filldir* nf=param; + int flags=NTFS_GETU8(entry+0x51); + int show_hidden=0,to_lower=0; + int length=NTFS_GETU8(entry+0x50); + int inum=NTFS_GETU32(entry); + int i,error; + switch(nf->type){ + case ngt_dos: + /* Don't display long names */ + if((flags & 2)==0) + return 0; + break; + case ngt_nt: + /* Don't display short-only names */ + switch(flags&3){ + case 2: return 0; + case 3: to_lower=1; + } + break; + case ngt_posix: + break; + case ngt_full: + show_hidden=1; + break; + } + if(!show_hidden && ((NTFS_GETU8(entry+0x48) & 2)==2)){ + ntfs_debug(DEBUG_OTHER,"Skipping hidden file\n"); + return 0; + } + nf->name=0; + if(ntfs_encodeuni(NTFS_INO2VOL(nf->dir),(ntfs_u16*)(entry+0x52), + length,&nf->name,&nf->namelen)){ + ntfs_debug(DEBUG_OTHER,"Skipping unrepresentable file\n"); + if(nf->name)ntfs_free(nf->name); + return 0; + } + /* Do not return ".", as this is faked */ + if(length==1 && *nf->name=='.') + return 0; + if(to_lower) + for(i=0;inamelen;i++) + /* This supports ASCII only. Since only DOS-only + names get converted, and since those are restricted + to ASCII, this should be correct */ + if(nf->name[i]>='A' && nf->name[i]<='Z') + nf->name[i]+='a'-'A'; + nf->name[nf->namelen]=0; + ntfs_debug(DEBUG_OTHER, "readdir got %s,len %d\n",nf->name,nf->namelen); + /* filldir expects an off_t rather than an loff_t. + Hope we don't have more than 65535 index records */ + error=nf->filldir(nf->dirent,nf->name,nf->namelen, + (nf->ph<<16)|nf->pl,inum); + ntfs_free(nf->name); + /* Linux filldir errors are negative, other errors positive */ + return error; +} + +/* readdir returns '..', then '.', then the directory entries in sequence + As the root directory contains a entry for itself, '.' is not emulated + for the root directory */ +static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir) +{ + struct ntfs_filldir cb; + int error; + struct inode *dir=filp->f_dentry->d_inode; + + ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n", + (unsigned)dir->i_ino,(unsigned int)dir->i_mode); + if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF; + + ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n", + (unsigned)filp->f_pos,dir->i_count); + cb.pl=filp->f_pos & 0xFFFF; + cb.ph=filp->f_pos >> 16; + /* end of directory */ + if(cb.ph==0xFFFF){ + /* FIXME: Maybe we can return those with the previous call */ + switch(cb.pl){ + case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino); + filp->f_pos=0xFFFF0001; + return 0; + /* FIXME: parent directory */ + case 1: filldir(dirent,"..",2,filp->f_pos,0); + filp->f_pos=0xFFFF0002; + return 0; + } + ntfs_debug(DEBUG_OTHER, "readdir: EOD\n"); + return 0; + } + cb.dir=dir; + cb.filldir=filldir; + cb.dirent=dirent; + cb.type=NTFS_INO2VOL(dir)->ngt; + do{ + ntfs_debug(DEBUG_OTHER,"looking for next file\n"); + error=ntfs_getdir_unsorted(NTFS_LINO2NINO(dir),&cb.ph,&cb.pl, + ntfs_printcb,&cb); + }while(!error && cb.ph!=0xFFFFFFFF); + filp->f_pos=(cb.ph<<16)|cb.pl; + ntfs_debug(DEBUG_OTHER, "new position %x\n",(unsigned)filp->f_pos); + /* -EINVAL is on user buffer full. This is not considered + as an error by sys_getdents */ + if(error<0) + error=0; + /* Otherwise (device error, inconsistent data), switch the sign */ + return -error; +} + +/* Copied from vfat driver */ +static int simple_getbool(char *s, int *setval) +{ + if (s) { + if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) { + *setval = 1; + } else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) { + *setval = 0; + } else { + return 0; + } + } else { + *setval = 1; + } + return 1; +} + +/* Parse the (re)mount options */ +static int parse_options(ntfs_volume* vol,char *opt) +{ + char *value; + + vol->uid=vol->gid=0; + vol->umask=0077; + vol->ngt=ngt_nt; + vol->nls_map=0; + vol->nct=0; + if(!opt)goto done; + + for(opt = strtok(opt,",");opt;opt=strtok(NULL,",")) + { + if ((value = strchr(opt, '=')) != NULL) + *value++='\0'; + if(strcmp(opt,"uid")==0) + { + if(!value || !*value)goto needs_arg; + vol->uid=simple_strtoul(value,&value,0); + if(*value){ + printk(KERN_ERR "NTFS: uid invalid argument\n"); + return 0; + } + }else if(strcmp(opt, "gid") == 0) + { + if(!value || !*value)goto needs_arg; + vol->gid=simple_strtoul(value,&value,0); + if(*value){ + printk(KERN_ERR "gid invalid argument\n"); + return 0; + } + }else if(strcmp(opt, "umask") == 0) + { + if(!value || !*value)goto needs_arg; + vol->umask=simple_strtoul(value,&value,0); + if(*value){ + printk(KERN_ERR "umask invalid argument\n"); + return 0; + } + }else if(strcmp(opt, "iocharset") == 0){ + if(!value || !*value)goto needs_arg; + vol->nls_map=load_nls(value); + vol->nct |= nct_map; + if(!vol->nls_map){ + printk(KERN_ERR "NTFS: charset not found"); + return 0; + } + }else if(strcmp(opt, "posix") == 0){ + int val; + if(!value || !*value)goto needs_arg; + if(!simple_getbool(value,&val)) + goto needs_bool; + vol->ngt=val?ngt_posix:ngt_nt; + }else if(strcmp(opt,"utf8") == 0){ + int val=0; + if(!value || !*value) + val=1; + else if(!simple_getbool(value,&val)) + goto needs_bool; + if(val) + vol->nct|=nct_utf8; + }else if(strcmp(opt,"uni_xlate") == 0){ + int val=0; + /* no argument: uni_vfat. + boolean argument: uni_vfat. + "2": uni. + */ + if(!value || !*value) + val=1; + else if(strcmp(value,"2")==0) + vol->nct |= nct_uni_xlate; + else if(!simple_getbool(value,&val)) + goto needs_bool; + if(val) + vol->nct |= nct_uni_xlate_vfat | nct_uni_xlate; + }else{ + printk(KERN_ERR "NTFS: unkown option '%s'\n", opt); + return 0; + } + } + if(vol->nct & nct_utf8 & (nct_map | nct_uni_xlate)){ + printk(KERN_ERR "utf8 cannot be combined with iocharset or uni_xlate\n"); + return 0; + } + done: + if((vol->nct & (nct_uni_xlate | nct_map | nct_utf8))==0) + /* default to UTF-8 */ + vol->nct=nct_utf8; + if(!vol->nls_map) + vol->nls_map=load_nls_default(); + return 1; + + needs_arg: + printk(KERN_ERR "NTFS: %s needs an argument",opt); + return 0; + needs_bool: + printk(KERN_ERR "NTFS: %s needs boolean argument",opt); + return 0; +} + +static int ntfs_lookup(struct inode *dir, struct dentry *d) +{ + struct inode *res=0; + char *item=0; + ntfs_iterate_s walk; + int error; + ntfs_debug(DEBUG_OTHER, "Looking up %s in %x\n",d->d_name.name, + (unsigned)dir->i_ino); + /* convert to wide string */ + error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name, + d->d_name.len,&walk.name,&walk.namelen); + if(error) + return error; + item=ntfs_malloc(ITEM_SIZE); + /* ntfs_getdir will place the directory entry into item, + and the first long long is the MFT record number */ + walk.type=BY_NAME; + walk.dir=NTFS_LINO2NINO(dir); + walk.result=item; + if(ntfs_getdir_byname(&walk)) + { + res=iget(dir->i_sb,NTFS_GETU32(item)); + } + d_add(d,res); + ntfs_free(item); + ntfs_free(walk.name); + return res?0:-ENOENT; +} + +struct file_operations ntfs_file_operations_nommap = { + NULL, /* lseek */ + ntfs_read, +#ifdef CONFIG_NTFS_RW + ntfs_write, +#else + NULL, +#endif + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +struct inode_operations ntfs_inode_operations_nobmap = { + &ntfs_file_operations_nommap, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + +#ifdef CONFIG_NTFS_RW +static int +ntfs_create(struct inode* dir,struct dentry *d,int mode) +{ + struct inode *r=0; + ntfs_inode *ino=0; + ntfs_volume *vol; + int error=0; + ntfs_attribute *si; + + r=get_empty_inode(); + if(!r){ + error=ENOMEM; + goto fail; + } + + ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n",d->d_name.name); + vol=NTFS_INO2VOL(dir); +#ifdef NTFS_IN_LINUX_KERNEL + ino=NTFS_LINO2NINO(r); +#else + ino=ntfs_malloc(sizeof(ntfs_inode)); + if(!ino){ + error=ENOMEM; + goto fail; + } + r->u.generic_ip=ino; +#endif + error=ntfs_alloc_inode(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name, + d->d_name.len); + if(error)goto fail; + error=ntfs_update_inode(ino); + if(error)goto fail; + error=ntfs_update_inode(NTFS_LINO2NINO(dir)); + if(error)goto fail; + + r->i_uid=vol->uid; + r->i_gid=vol->gid; + r->i_nlink=1; + r->i_sb=dir->i_sb; + /* FIXME: dirty? dev? */ + /* get the file modification times from the standard information */ + si=ntfs_find_attr(ino,vol->at_standard_information,NULL); + if(si){ + char *attr=si->d.data; + r->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18)); + r->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr)); + r->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8)); + } + /* It's not a directory */ + r->i_op=&ntfs_inode_operations_nobmap; + r->i_mode=S_IFREG|S_IRUGO; + r->i_mode &= ~vol->umask; + + d_instantiate(d,r); + return 0; + fail: + #ifndef NTFS_IN_LINUX_KERNEL + if(ino)ntfs_free(ino); + #endif + if(r)iput(r); + return -error; +} +#endif + +static int +ntfs_bmap(struct inode *ino,int block) +{ + int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block); + ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n", + ino->i_ino,block,ret); + return (ret==-1) ? 0:ret; +} + +struct file_operations ntfs_file_operations = { + NULL, /* lseek */ + ntfs_read, +#ifdef CONFIG_NTFS_RW + ntfs_write, +#else + NULL, +#endif + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + generic_file_mmap, + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +struct inode_operations ntfs_inode_operations = { + &ntfs_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 */ + generic_readpage, + NULL, /* writepage */ + ntfs_bmap, + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + +struct file_operations ntfs_dir_operations = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + ntfs_readdir, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +struct inode_operations ntfs_dir_inode_operations = { + &ntfs_dir_operations, +#ifdef CONFIG_NTFS_RW + ntfs_create, /* create */ +#else + NULL, +#endif + ntfs_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + +/* ntfs_read_inode is called by the Virtual File System (the kernel layer that + * deals with filesystems) when iget is called requesting an inode not already + * present in the inode table. Typically filesystems have separate + * inode_operations for directories, files and symlinks. + */ +static void ntfs_read_inode(struct inode* inode) +{ + ntfs_volume *vol; + int can_mmap=0; + ntfs_inode *ino; + ntfs_attribute *data; + ntfs_attribute *si; + + vol=NTFS_INO2VOL(inode); + inode->i_op=NULL; + inode->i_mode=0; + ntfs_debug(DEBUG_OTHER, "ntfs_read_inode %x\n",(unsigned)inode->i_ino); + + switch(inode->i_ino) + { + /* those are loaded special files */ + case FILE_MFT: + ntfs_error("Trying to open MFT\n");return; + default: + #ifdef NTFS_IN_LINUX_KERNEL + ino=&inode->u.ntfs_i; + #else + ino=(ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode)); + inode->u.generic_ip=ino; + #endif + if(!ino || ntfs_init_inode(ino, + NTFS_INO2VOL(inode),inode->i_ino)) + { + ntfs_debug(DEBUG_OTHER, "NTFS:Error loading inode %x\n", + (unsigned int)inode->i_ino); + return; + } + } + /* Set uid/gid from mount options */ + inode->i_uid=vol->uid; + inode->i_gid=vol->gid; + inode->i_nlink=1; + /* Use the size of the data attribute as file size */ + data = ntfs_find_attr(ino,vol->at_data,NULL); + if(!data) + { + inode->i_size=0; + can_mmap=0; + } + else + { + inode->i_size=data->size; + can_mmap=!data->resident && !data->compressed; + } + /* get the file modification times from the standard information */ + si=ntfs_find_attr(ino,vol->at_standard_information,NULL); + if(si){ + char *attr=si->d.data; + inode->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18)); + inode->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr)); + inode->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8)); + } + /* if it has an index root, it's a directory */ + if(ntfs_find_attr(ino,vol->at_index_root,"$I30")) + { + ntfs_attribute *at; + at = ntfs_find_attr (ino, vol->at_index_allocation, "$I30"); + inode->i_size = at ? at->size : 0; + + inode->i_op=&ntfs_dir_inode_operations; + inode->i_mode=S_IFDIR|S_IRUGO|S_IXUGO; + } + else + { + inode->i_op=can_mmap ? &ntfs_inode_operations : + &ntfs_inode_operations_nobmap; + inode->i_mode=S_IFREG|S_IRUGO|S_IMMUTABLE; + inode->i_mode=S_IFREG|S_IRUGO; + } + inode->i_mode &= ~vol->umask; +} + +static void ntfs_put_inode(struct inode *ino) +{ + ntfs_debug(DEBUG_OTHER, "ntfs_put_inode %lx\n",ino->i_ino); +#ifdef NTFS_IN_LINUX_KERNEL + if(ino->i_ino!=FILE_MFT) + ntfs_clear_inode(&ino->u.ntfs_i); +#else + if(ino->i_ino!=FILE_MFT && ino->u.generic_ip) + { + ntfs_clear_inode(ino->u.generic_ip); + ntfs_free(ino->u.generic_ip); + ino->u.generic_ip=0; + } +#endif + clear_inode(ino); +} + +/* Called when umounting a filesystem by do_umount() in fs/super.c */ +static void ntfs_put_super(struct super_block *sb) +{ + ntfs_volume *vol; + + ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n"); + /* Ensure that nobody uses the super block anymore */ + lock_super(sb); + vol=NTFS_SB2VOL(sb); + /* Tell the kernel that the super block is no more used */ + sb->s_dev = 0; + unlock_super(sb); + ntfs_release_volume(vol); + if(vol->nls_map) + unload_nls(vol->nls_map); +#ifndef NTFS_IN_LINUX_KERNEL + ntfs_free(vol); +#endif + ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n"); + MOD_DEC_USE_COUNT; +} + +/* Called by the kernel when asking for stats */ +static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize) +{ + struct statfs fs; + struct inode *mft; + ntfs_volume *vol; + + ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n"); + vol=NTFS_SB2VOL(sb); + memset(&fs,0,sizeof(fs)); + fs.f_type=NTFS_SUPER_MAGIC; + fs.f_bsize=vol->clustersize; + + fs.f_blocks=ntfs_get_volumesize(NTFS_SB2VOL(sb)); + fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap); + fs.f_bavail=fs.f_bfree; + + /* Number of files is limited by free space only, so we lie here */ + fs.f_ffree=0; + mft=iget(sb,FILE_MFT); + fs.f_files=mft->i_size/vol->mft_recordsize; + iput(mft); + + /* should be read from volume */ + fs.f_namelen=255; + copy_to_user(sf,&fs,bufsize); + return 0; +} + +/* Called when remounting a filesystem by do_remount_sb() in fs/super.c */ +static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options) +{ + if(!parse_options(NTFS_SB2VOL(sb), options)) + return -EINVAL; + return 0; +} + +/* Define the super block operation that are implemented */ +struct super_operations ntfs_super_operations = { + ntfs_read_inode, + NULL, /* write_inode */ + ntfs_put_inode, + NULL, /* delete_inode */ + NULL, /* notify_change */ + ntfs_put_super, + NULL, /* write_super */ + ntfs_statfs, + ntfs_remount_fs, /* remount */ +}; + +/* Called to mount a filesystem by read_super() in fs/super.c + * Return a super block, the main structure of a filesystem + * + * NOTE : Don't store a pointer to an option, as the page containing the + * options is freed after ntfs_read_super() returns. + * + * NOTE : A context switch can happen in kernel code only if the code blocks + * (= calls schedule() in kernel/sched.c). + */ +struct super_block * ntfs_read_super(struct super_block *sb, + void *options, int silent) +{ + ntfs_volume *vol; + struct buffer_head *bh; + int i; + + /* When the driver is compiled as a module, kerneld must know when it + * can safely remove it from memory. To do this, each module owns a + * reference counter. + */ + MOD_INC_USE_COUNT; + /* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block + * so this could lead to a race condition with kerneld. + */ + ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n"); + +#ifdef NTFS_IN_LINUX_KERNEL + vol = NTFS_SB2VOL(sb); +#else + if(!(vol = ntfs_malloc(sizeof(ntfs_volume)))) + goto ntfs_read_super_dec; + NTFS_SB2VOL(sb)=vol; +#endif + + if(!parse_options(vol,(char*)options)) + goto ntfs_read_super_vol; + + /* Ensure that the super block won't be used until it is completed */ + lock_super(sb); + ntfs_debug(DEBUG_OTHER, "lock_super\n"); +#if 0 + /* Set to read only, user option might reset it */ + sb->s_flags |= MS_RDONLY; +#endif + + /* Assume a 512 bytes block device for now */ + set_blocksize(sb->s_dev, 512); + /* Read the super block (boot block) */ + if(!(bh=bread(sb->s_dev,0,512))) { + ntfs_error("Reading super block failed\n"); + goto ntfs_read_super_unl; + } + ntfs_debug(DEBUG_OTHER, "Done reading boot block\n"); + + /* Check for 'NTFS' magic number */ + if(!IS_NTFS_VOLUME(bh->b_data)){ + ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n"); + brelse(bh); + goto ntfs_read_super_unl; + } + + ntfs_debug(DEBUG_OTHER, "Going to init volume\n"); + ntfs_init_volume(vol,bh->b_data); + ntfs_debug(DEBUG_OTHER, "MFT record at cluster 0x%X\n",vol->mft_cluster); + brelse(bh); + NTFS_SB(vol)=sb; + ntfs_debug(DEBUG_OTHER, "Done to init volume\n"); + + /* Inform the kernel that a device block is a NTFS cluster */ + sb->s_blocksize=vol->clustersize; + for(i=sb->s_blocksize,sb->s_blocksize_bits=0;i;i>>=1) + sb->s_blocksize_bits++; + set_blocksize(sb->s_dev,sb->s_blocksize); + ntfs_debug(DEBUG_OTHER, "set_blocksize\n"); + + /* Allocate a MFT record (MFT record can be smaller than a cluster) */ + if(!(vol->mft=ntfs_malloc(max(vol->mft_recordsize,vol->clustersize)))) + goto ntfs_read_super_unl; + + /* Read at least the MFT record for $MFT */ + for(i=0;imft_clusters_per_record,1);i++){ + if(!(bh=bread(sb->s_dev,vol->mft_cluster+i,vol->clustersize))) { + ntfs_error("Could not read MFT record 0\n"); + goto ntfs_read_super_mft; + } + ntfs_memcpy(vol->mft+i*vol->clustersize,bh->b_data,vol->clustersize); + brelse(bh); + ntfs_debug(DEBUG_OTHER, "Read cluster %x\n",vol->mft_cluster+i); + } + + /* Check and fixup this MFT record */ + if(!ntfs_check_mft_record(vol,vol->mft)){ + ntfs_error("Invalid MFT record 0\n"); + goto ntfs_read_super_mft; + } + + /* Inform the kernel about which super operations are available */ + sb->s_op = &ntfs_super_operations; + sb->s_magic = NTFS_SUPER_MAGIC; + + ntfs_debug(DEBUG_OTHER, "Reading special files\n"); + if(ntfs_load_special_files(vol)){ + ntfs_error("Error loading special files\n"); + goto ntfs_read_super_mft; + } + + ntfs_debug(DEBUG_OTHER, "Getting RootDir\n"); + /* Get the root directory */ + if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){ + ntfs_error("Could not get root dir inode\n"); + goto ntfs_read_super_mft; + } + unlock_super(sb); + ntfs_debug(DEBUG_OTHER, "unlock_super\n"); + ntfs_debug(DEBUG_OTHER, "read_super: done\n"); + return sb; + +ntfs_read_super_mft: + ntfs_free(vol->mft); +ntfs_read_super_unl: + sb->s_dev = 0; + unlock_super(sb); + ntfs_debug(DEBUG_OTHER, "unlock_super\n"); +ntfs_read_super_vol: + ntfs_free(vol); +ntfs_read_super_dec: + ntfs_debug(DEBUG_OTHER, "read_super: done\n"); + MOD_DEC_USE_COUNT; + return NULL; +} + +/* Define the filesystem + * + * Define SECOND if you cannot unload ntfs, and want to avoid rebooting + * for just one more test + */ +struct file_system_type ntfs_fs_type = { +/* Filesystem name, as used after mount -t */ +#ifndef SECOND + "ntfs", +#else + "ntfs2", +#endif +/* This filesystem requires a device (a hard disk) + * May want to add FS_IBASKET when it works + */ + FS_REQUIRES_DEV, +/* Entry point of the filesystem */ + ntfs_read_super, +/* Will point to the next filesystem in the kernel table */ + NULL +}; + +/* When this code is not compiled as a module, this is the main entry point, + * called by do_sys_setup() in fs/filesystems.c + * + * NOTE : __initfunc() is a macro used to remove this function from memory + * once initialization is done + */ +__initfunc(int init_ntfs_fs(void)) +{ + /* Comment this if you trust klogd. There are reasons not to trust it + */ +#if defined(DEBUG) && !defined(MODULE) + extern int console_loglevel; + console_loglevel=15; +#endif + printk(KERN_NOTICE "NTFS version " NTFS_VERSION "\n"); + SYSCTL(1); + ntfs_debug(DEBUG_OTHER, "registering %s\n",ntfs_fs_type.name); + /* add this filesystem to the kernel table of filesystems */ + return register_filesystem(&ntfs_fs_type); +} + +#ifdef MODULE +/* A module is a piece of code which can be inserted in and removed + * from the running kernel whenever you want using lsmod, or on demand using + * kerneld + */ + +/* No function of this module is needed by another module */ +EXPORT_NO_SYMBOLS; +/* Only used for documentation purposes at the moment, + * see include/linux/module.h + */ +MODULE_AUTHOR("Martin von Löwis"); +MODULE_DESCRIPTION("NTFS driver"); +/* no MODULE_SUPPORTED_DEVICE() */ +/* Load-time parameter */ +MODULE_PARM(ntdebug, "i"); +MODULE_PARM_DESC(ntdebug, "Debug level"); + +/* When this code is compiled as a module, if you use mount -t ntfs when no + * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in + * fs/super.c asks kerneld to load the module named ntfs in memory. + * + * Therefore, this function is the main entry point in this case + */ +int init_module(void) +{ + return init_ntfs_fs(); +} + +/* Called by kerneld just before the kernel removes the module from memory */ +void cleanup_module(void) +{ + SYSCTL(0); + ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name); + unregister_filesystem(&ntfs_fs_type); +} +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c --- v2.1.73/linux/fs/ntfs/inode.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/inode.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,1196 @@ +/* + * inode.c + * + * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1996 Albert D. Cahalan + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#include "types.h" +#include "struct.h" +#include "inode.h" + +#include +#include "macros.h" +#include "attr.h" +#include "super.h" +#include "dir.h" +#include "support.h" +#include "util.h" + +typedef struct { + int recno; + unsigned char* record; +} ntfs_mft_record; + +typedef struct { + int size; + int count; + ntfs_mft_record* records; +} ntfs_disk_inode; + +static void +fill_mft_header(ntfs_u8*mft,int record_size,int blocksize, + int sequence_number) +{ + int fixup_count = record_size / blocksize + 1; + int attr_offset = (0x2a + (2 * fixup_count) + 7) & ~7; + int fixup_offset = 0x2a; + + NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */ + NTFS_PUTU16(mft + 0x04, 0x2a); /* offset to fixup */ + NTFS_PUTU16(mft + 0x06, fixup_count); /* Number of fixups */ + NTFS_PUTU16(mft + 0x10, sequence_number); + NTFS_PUTU16(mft + 0x12, 1); /* hard link count */ + NTFS_PUTU16(mft + 0x14, attr_offset); /* Offset to attributes */ + NTFS_PUTU16(mft + 0x16, 1); /*FIXME: flags ?? */ + NTFS_PUTU32(mft + 0x18, attr_offset + 0x08); /* In use */ + NTFS_PUTU32(mft + 0x1c, record_size); /* Total size */ + + NTFS_PUTU16(mft + fixup_offset, 1); /* Fixup word */ + NTFS_PUTU32(mft + attr_offset, 0xffffffff); /* End marker */ +} + +/* Search in an inode an attribute by type and name */ +ntfs_attribute* +ntfs_find_attr(ntfs_inode *ino,int type,char *name) +{ + int i; + if(!ino){ + ntfs_error("ntfs_find_attr: NO INODE!\n"); + return 0; + } + for(i=0;iattr_count;i++) + { + if(type==ino->attrs[i].type) + { + if(!name && !ino->attrs[i].name) + return ino->attrs+i; + if(name && !ino->attrs[i].name) + return 0; + if(!name && ino->attrs[i].name) + return 0; + if(ntfs_ua_strncmp(ino->attrs[i].name,name,strlen(name))==0) + return ino->attrs+i; + } + if(typeattrs[i].type) + return 0; + } + return 0; +} + +/* FIXME: need better strategy to extend the MFT */ +static int +ntfs_extend_mft(ntfs_volume *vol) +{ + /* Try to allocate at least 0.1% of the remaining disk space + for inodes. If the disk is almost full, make sure at least one + inode is requested. + */ + int size,rcount,error,block; + ntfs_attribute* mdata,*bmp; + ntfs_u8 *buf; + ntfs_io io; + + mdata=ntfs_find_attr(vol->mft_ino,vol->at_data,0); + /* first check whether there is uninitialized space */ + if(mdata->allocatedsize+vol->mft_recordsize){ + size=ntfs_get_free_cluster_count(vol->bitmap)*vol->clustersize; + block=vol->mft_recordsize; + size=max(size/1000,block); + size=((size+block-1)/block)*block; + /* require this to be a single chunk */ + error=ntfs_extend_attr(vol->mft_ino,mdata,&size, + ALLOC_REQUIRE_SIZE); + /* Try again, now we have the largest available fragment */ + if(error==ENOSPC){ + /* round down to multiple of mft record size */ + size=(size/vol->mft_recordsize)*vol->mft_recordsize; + if(!size)return ENOSPC; + error=ntfs_extend_attr(vol->mft_ino,mdata,&size, + ALLOC_REQUIRE_SIZE); + } + if(error) + return error; + } + /* even though we might have allocated more than needed, + we initialize only one record */ + mdata->size+=vol->mft_recordsize; + + /* now extend the bitmap if necessary*/ + rcount=mdata->size/vol->mft_recordsize; + bmp=ntfs_find_attr(vol->mft_ino,vol->at_bitmap,0); + if(bmp->size*8mft_ino,bmp,bmp->size+1); + if(error)return error; + /* write the single byte */ + buf[0]=0; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=buf; + io.size=1; + error=ntfs_write_attr(vol->mft_ino,vol->at_bitmap,0, + bmp->size-1,&io); + if(error)return error; + if(io.size!=1)return EIO; + } + + /* now fill in the MFT header for the new block */ + buf=ntfs_calloc(vol->mft_recordsize); + if(!buf)return ENOMEM; + fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0); + ntfs_insert_fixups(buf,vol->blocksize); + io.param=buf; + io.size=vol->mft_recordsize; + error=ntfs_write_attr(vol->mft_ino,vol->at_data,0, + (rcount-1)*vol->mft_recordsize,&io); + if(error)return error; + if(io.size!=vol->mft_recordsize)return EIO; + return 0; +} + +/* Insert all attributes from the record mftno of the MFT in the inode ino */ +void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno) +{ + int i; + char *it; + int type,len; + /* check for duplicate */ + for(i=0;irecord_count;i++) + if(ino->records[i]==mftno) + return; + /* (re-)allocate space if necessary */ + if(ino->record_count % 8==0) + { + int *old=ino->records; + ino->records=ntfs_malloc((ino->record_count+8)*sizeof(int)); + if(old) { + for(i=0;irecord_count;i++) + ino->records[i]=old[i]; + ntfs_free(old); + } + } + ino->records[ino->record_count]=mftno; + ino->record_count++; + it = mft + NTFS_GETU16(mft + 0x14); + do{ + type=NTFS_GETU32(it); + len=NTFS_GETU32(it+4); + if(type!=-1) + ntfs_insert_attribute(ino,it); + it+=len; + }while(type!=-1); /* attribute list ends with type -1 */ +} + +/* Read and insert all the attributes of an 'attribute list' attribute + Return the number of remaining bytes in *plen +*/ +static int parse_attributes(ntfs_inode *ino, void *alist, int *plen) +{ + char *mft; + int mftno,l,error; + int last_mft=-1; + int len=*plen; + mft=ntfs_malloc(ino->vol->mft_recordsize); + while(len>8) + { + l=NTFS_GETU16(alist+4); + if(l>len)break; + /* process an attribute description */ + mftno=NTFS_GETU32(alist+0x10); /* BUG: this is u64 */ + if(mftno!=last_mft){ + last_mft=mftno; + /* FIXME: avoid loading record if it's + already processed */ + error=ntfs_read_mft_record(ino->vol,mftno,mft); + if(error)return error; + ntfs_insert_mft_attributes(ino,mft,mftno); + } + len-=l; + alist+=l; + } + ntfs_free(mft); + *plen=len; + return 0; +} + +static void ntfs_load_attributes(ntfs_inode* ino) +{ + ntfs_attribute *alist; + int datasize; + int offset,len,delta; + char *buf; + ntfs_volume *vol=ino->vol; + ntfs_debug(DEBUG_OTHER, "load_attributes %x 1\n",ino->i_number); + ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number); + ntfs_debug(DEBUG_OTHER, "load_attributes %x 2\n",ino->i_number); + alist=ntfs_find_attr(ino,vol->at_attribute_list,0); + ntfs_debug(DEBUG_OTHER, "load_attributes %x 3\n",ino->i_number); + if(!alist) + return; + ntfs_debug(DEBUG_OTHER, "load_attributes %x 4\n",ino->i_number); + datasize=alist->size; + if(alist->resident) + { + parse_attributes(ino,alist->d.data,&datasize); + return; + } + buf=ntfs_malloc(1024); + delta=0; + for(offset=0;datasize;datasize-=len) + { + ntfs_io io; + io.fn_put=ntfs_put; + io.fn_get=0; + io.param=buf+delta; + io.size=len=min(datasize,1024-delta); + if(ntfs_read_attr(ino,vol->at_attribute_list,0,offset,&io)){ + ntfs_error("error in load_attributes\n"); + } + delta=len; + parse_attributes(ino,buf,&delta); + if(delta) + /* move remaining bytes to buffer start */ + ntfs_memmove(buf,buf+len-delta,delta); + } + ntfs_debug(DEBUG_OTHER, "load_attributes %x 5\n",ino->i_number); + ntfs_free(buf); +} + +int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum) +{ + char *buf; + int error; + + ntfs_debug(DEBUG_OTHER, "Initializing inode %x\n",inum); + if(!vol) + ntfs_error("NO VOLUME!\n"); + ino->i_number=inum; + ino->vol=vol; + ino->attr=buf=ntfs_malloc(vol->mft_recordsize); + error=ntfs_read_mft_record(vol,inum,ino->attr); + if(error){ + ntfs_debug(DEBUG_OTHER, "init inode: %x failed\n",inum); + return error; + } + ntfs_debug(DEBUG_OTHER, "Init: got mft %x\n",inum); + ino->sequence_number=NTFS_GETU16(buf+0x10); + ino->attr_count=0; + ino->record_count=0; + ino->records=0; + ino->attrs=0; + ntfs_load_attributes(ino); + ntfs_debug(DEBUG_OTHER, "Init: done %x\n",inum); + return 0; +} + +void ntfs_clear_inode(ntfs_inode *ino) +{ + int i; + ntfs_free(ino->attr); + ntfs_free(ino->records); + for(i=0;iattr_count;i++) + { + if(ino->attrs[i].name) + ntfs_free(ino->attrs[i].name); + if(ino->attrs[i].resident) + { + if(ino->attrs[i].d.data) + ntfs_free(ino->attrs[i].d.data); + }else{ + if(ino->attrs[i].d.r.runlist) + ntfs_free(ino->attrs[i].d.r.runlist); + } + } + ntfs_free(ino->attrs); +} + +/* Check and fixup a MFT record */ +int ntfs_check_mft_record(ntfs_volume *vol,char *record) +{ + return ntfs_fixup_record(vol, record, "FILE", vol->mft_recordsize); +} + +/* Return (in result) the value indicating the next available attribute + chunk number. Works for inodes w/o extension records only */ +int ntfs_allocate_attr_number(ntfs_inode *ino, int *result) +{ + if(ino->record_count!=1) + return EOPNOTSUPP; + *result=NTFS_GETU16(ino->attr+0x28); + NTFS_PUTU16(ino->attr+0x28, (*result)+1); + return 0; +} + +/* find the location of an attribute in the inode. A name of NULL indicates + unnamed attributes. Return pointer to attribute or NULL if not found */ +char * +ntfs_get_attr(ntfs_inode *ino,int attr,char *name) +{ + /* location of first attribute */ + char *it= ino->attr + NTFS_GETU16(ino->attr + 0x14); + int type; + int len; + /* Only check for magic DWORD here, fixup should have happened before */ + if(!IS_MFT_RECORD(ino->attr))return 0; + do{ + type=NTFS_GETU32(it); + len=NTFS_GETU16(it+4); + /* We found the attribute type. Is the name correct, too? */ + if(type==attr) + { + int namelen=NTFS_GETU8(it+9); + char *name_it; + /* match given name and attribute name if present, + make sure attribute name is Unicode */ + for(name_it=it+NTFS_GETU16(it+10);namelen; + name++,name_it+=2,namelen--) + if(*name_it!=*name || name_it[1])break; + if(!namelen)break; + } + it+=len; + }while(type!=-1); /* attribute list end with type -1 */ + if(type==-1)return 0; + return it; +} + +int +ntfs_get_attr_size(ntfs_inode*ino,int type,char*name) +{ + ntfs_attribute *attr=ntfs_find_attr(ino,type,name); + if(!attr)return 0; + return attr->size; +} + +int +ntfs_attr_is_resident(ntfs_inode*ino,int type,char*name) +{ + ntfs_attribute *attr=ntfs_find_attr(ino,type,name); + if(!attr)return 0; + return attr->resident; +} + +/* + * A run is coded as a type indicator, an unsigned length, and a signed cluster + * offset. + * . To save space, length and offset are fields of variable length. The low + * nibble of the type indicates the width of the length :), the high nibble + * the width of the offset. + * . The first offset is relative to cluster 0, later offsets are relative to + * the previous cluster. + * + * This function decodes a run. Length is an output parameter, data and cluster + * are in/out parameters. + */ +int ntfs_decompress_run(unsigned char **data, int *length, int *cluster, + int *ctype) +{ + unsigned char type=*(*data)++; + *ctype=0; + switch(type & 0xF) + { + case 1: *length=NTFS_GETU8(*data);(*data)++;break; + case 2: *length=NTFS_GETU16(*data); + *data+=2; + break; + case 3: *length = NTFS_GETU24(*data); + *data+=3; + break; + /* TODO: case 4-8 */ + default: + ntfs_error("Can't decode run type field %x\n",type); + return -1; + } + switch(type & 0xF0) + { + case 0: *ctype=2;break; + case 0x10: *cluster+=NTFS_GETS8(*data);(*data)++;break; + case 0x20: *cluster+=NTFS_GETS16(*data); + *data+=2; + break; + case 0x30: *cluster+=NTFS_GETS24(*data); + *data+=3; + break; + /* TODO: case 0x40-0x80 */ + default: + ntfs_error("Can't decode run type field %x\n",type); + return -1; + } + return 0; +} + +/* Reads l bytes of the attribute (attr,name) of ino starting at offset + on vol into buf. Returns the number of bytes read in the ntfs_io struct. + Returns 0 on success, errno on failure */ +int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest) +{ + int datasize,rnum; + int cluster,s_cluster,vcn,len,l,chunk,copied; + int s_vcn; + int clustersize; + int error; + + clustersize=ino->vol->clustersize; + datasize=attr->size; + l=dest->size; + if(dest->do_read) + { + if(offset>=datasize){ + dest->size=0; + return 0; + } + if(offset+l>=datasize) + l=dest->size=datasize-offset; + }else { /* fixed by CSA: if writing beyond end, extend attribute */ + if (offset+l>datasize) { + error=ntfs_resize_attr(ino,attr,offset+l); + if(error) + return error; + } + } + if(attr->resident) + { + if(dest->do_read) + dest->fn_put(dest,attr->d.data+offset,l); + else + { + dest->fn_get(attr->d.data+offset,dest,l); + ntfs_update_inode(ino); + } + dest->size=l; + return 0; + } + if(attr->compressed) + if(dest->do_read) + return ntfs_read_compressed(ino,attr,offset,dest); + else + return ntfs_write_compressed(ino,attr,offset,dest); + vcn=0; + s_vcn = offset/clustersize; + for(rnum=0;rnumd.r.len && + vcn+attr->d.r.runlist[rnum].len<=s_vcn;rnum++) + vcn+=attr->d.r.runlist[rnum].len; + if(rnum==attr->d.r.len) + /*FIXME: should extend runlist */ + return EOPNOTSUPP; + + copied=0; + while(l) + { + s_vcn = offset/clustersize; + cluster=attr->d.r.runlist[rnum].cluster; + len=attr->d.r.runlist[rnum].len; + + s_cluster = cluster+s_vcn-vcn; + + chunk=min((vcn+len)*clustersize-offset,l); + dest->size=chunk; + error=ntfs_getput_clusters(ino->vol,s_cluster, + offset-s_vcn*clustersize,dest); + if(error)/* FIXME: maybe return failure */ + { + ntfs_error("Read error\n"); + dest->size=copied; + return 0; + } + l-=chunk; + copied+=chunk; + offset+=chunk; + if(l && offset>=((vcn+len)*clustersize)) + { + rnum++; + vcn+=len; + cluster = attr->d.r.runlist[rnum].cluster; + len = attr->d.r.runlist[rnum].len; + } + } + dest->size=copied; + return 0; +} + +int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset, + ntfs_io *buf) +{ + ntfs_attribute *attr; + buf->do_read=1; + attr=ntfs_find_attr(ino,type,name); + if(!attr) + return EINVAL; + return ntfs_readwrite_attr(ino,attr,offset,buf); +} + +int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset, + ntfs_io *buf) +{ + ntfs_attribute *attr; + buf->do_read=0; + attr=ntfs_find_attr(ino,type,name); + if(!attr) + return EINVAL; + return ntfs_readwrite_attr(ino,attr,offset,buf); +} + +int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn) +{ + int rnum; + ntfs_attribute *data=ntfs_find_attr(ino,ino->vol->at_data,0); + /* It's hard to give an error code */ + if(!data)return -1; + if(data->resident)return -1; + if(data->compressed)return -1; + if(data->sizevol->clustersize)return -1; + + for(rnum=0;rnumd.r.len && + vcn>data->d.r.runlist[rnum].len;rnum++) + vcn-=data->d.r.runlist[rnum].len; + + return data->d.r.runlist[rnum].cluster+vcn; +} + +static int +allocate_store(ntfs_volume *vol,ntfs_disk_inode *store,int count) +{ + int i; + if(store->count>count) + return 0; + if(store->sizesize){ + for(i=0;isize;i++) + n[i]=store->records[i]; + ntfs_free(store->records); + } + store->size=count+4; + store->records=n; + } + for(i=store->count;irecords[i].record=ntfs_malloc(vol->mft_recordsize); + if(!store->records[i].record) + return ENOMEM; + store->count++; + } + return 0; +} + +static void +deallocate_store(ntfs_disk_inode* store) +{ + int i; + for(i=0;icount;i++) + ntfs_free(store->records[i].record); + ntfs_free(store->records); + store->count=store->size=0; + store->records=0; +} + +int +layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size) +{ + int i,cluster,rclus,len,offset,coffs; + ntfs_runlist *rl=attr->d.r.runlist; + cluster=0; + offset=*offs; + for(i=0;id.r.len;i++){ + rclus=rl[i].cluster-cluster; + len=rl[i].len; + rec[offset]=0; + if(offset+8>size) + return E2BIG; /* it might still fit, but this simplifies testing */ + if(len<0x100){ + *(rec+offset)|=1; + NTFS_PUTU8(rec+offset+1,len); + coffs=2; + }else if(len<0x10000){ + *(rec+offset)|=2; + NTFS_PUTU16(rec+offset+1,len); + coffs=3; + }else if(len<0x1000000){ + *(rec+offset)|=3; + NTFS_PUTU24(rec+offset+1,len); + coffs=4; + }else{ + *(rec+offset)|=4; + NTFS_PUTU32(rec+offset+1,len); + coffs=5; + } + + if(rl[i].cluster==0) /*compressed run*/ + /*nothing*/; + else if(rclus>-0x80 && rclus<0x7F){ + *(rec+offset)|=0x10; + NTFS_PUTS8(rec+offset+coffs,rclus); + coffs+=1; + }else if(rclus>-0x8000 && rclus<0x7FFF){ + *(rec+offset)|=0x20; + NTFS_PUTS16(rec+offset+coffs,rclus); + coffs+=2; + }else if(rclus>-0x800000 && rclus<0x7FFFFF){ + *(rec+offset)|=0x30; + NTFS_PUTS24(rec+offset+coffs,rclus); + coffs+=3; + }else{ + *(rec+offset)|=0x40; + NTFS_PUTS32(rec+offset+coffs,rclus); + coffs+=4; + } + offset+=coffs; + if(rl[i].cluster) + cluster=rl[i].cluster; + } + *offs=offset; + return 0; +} + +static void +count_runs(ntfs_attribute *attr,char *buf) +{ + int first,count,last,i; + first=0; + for(i=0,count=0;id.r.len;i++) + count+=attr->d.r.runlist[i].len; + last=first+count-1; + + NTFS_PUTU32(buf+0x10,first); + NTFS_PUTU32(buf+0x18,last); +} + +static int +layout_attr(ntfs_attribute* attr,char*buf, int size,int *psize) +{ + int asize,error; + if(size<10)return E2BIG; + NTFS_PUTU32(buf,attr->type); + /* fill in length later */ + NTFS_PUTU8(buf+8,attr->resident ? 0:1); + NTFS_PUTU8(buf+9,attr->namelen); + /* fill in offset to name later */ + NTFS_PUTU16(buf+0xA,0); + NTFS_PUTU16(buf+0xC,attr->compressed); + /* FIXME: assign attribute ID??? */ + NTFS_PUTU16(buf+0xE,attr->attrno); + if(attr->resident){ + if(sizesize+0x18+attr->namelen)return E2BIG; + asize=0x18; + NTFS_PUTU32(buf+0x10,attr->size); + NTFS_PUTU16(buf+0x16,attr->indexed); + if(attr->name){ + ntfs_memcpy(buf+asize,attr->name,2*attr->namelen); + NTFS_PUTU16(buf+0xA,asize); + asize+=2*attr->namelen; + asize=(asize+7) & ~7; + } + NTFS_PUTU16(buf+0x14,asize); + ntfs_memcpy(buf+asize,attr->d.data,attr->size); + asize+=attr->size; + }else{ + /* FIXME: fragments */ + count_runs(attr,buf); + /* offset to data is added later */ + NTFS_PUTU16(buf+0x22,attr->cengine); + NTFS_PUTU32(buf+0x24,0); + NTFS_PUTU64(buf+0x28,attr->allocated); + NTFS_PUTU64(buf+0x30,attr->size); + NTFS_PUTU64(buf+0x38,attr->initialized); + if(attr->compressed){ + NTFS_PUTU64(buf+0x40,attr->compsize); + asize=0x48; + }else + asize=0x40; + if(attr->name){ + NTFS_PUTU16(buf+0xA,asize); + ntfs_memcpy(buf+asize,attr->name,2*attr->namelen); + asize+=2*attr->namelen; + asize=(asize+7) & ~7; + } + /* asize points at the beginning of the data */ + NTFS_PUTU16(buf+0x20,asize); + error=layout_runs(attr,buf,&asize,size); + /* now asize pointes at the end of the data */ + if(error) + return error; + } + asize=(asize+7) & ~7; + NTFS_PUTU32(buf+4,asize); + *psize=asize; + return 0; +} + + + +/* Try to layout ino into store. Return 0 on success, + E2BIG if it does not fit, + ENOMEM if memory allocation problem, + EOPNOTSUP if beyound our capabilities +*/ +int +layout_inode(ntfs_inode *ino,ntfs_disk_inode *store) +{ + int offset,i; + ntfs_attribute *attr; + unsigned char *rec; + int size,psize; + int error; + + if(ino->record_count>1) + { + ntfs_error("layout_inode: attribute lists not supported\n"); + return EOPNOTSUPP; + } + error=allocate_store(ino->vol,store,1); + if(error) + return error; + rec=store->records[0].record; + size=ino->vol->mft_recordsize; + store->records[0].recno=ino->records[0]; + /* copy header */ + offset=NTFS_GETU16(ino->attr+0x14); + ntfs_memcpy(rec,ino->attr,offset); + for(i=0;iattr_count;i++){ + attr=ino->attrs+i; + error=layout_attr(attr,rec+offset,size-offset,&psize); + if(error)return error; + offset+=psize; +#if 0 + /* copy attribute header */ + ntfs_memcpy(rec+offset,attr->header, + min(sizeof(attr->header),size-offset)); /* consider overrun */ + if(attr->namelen) + /* named attributes are added later */ + return EOPNOTSUPP; + /* FIXME: assign attribute ID??? */ + if(attr->resident){ + asize=attr->size; + aoffset=NTFS_GETU16(rec+offset+0x14); + if(offset+aoffset+asize>size) + return E2BIG; + ntfs_memcpy(rec+offset+aoffset,attr->d.data,asize); + next=offset+aoffset+asize; + }else{ + count_runs(attr,rec+offset); + aoffset=NTFS_GETU16(rec+offset+0x20); + next=offset+aoffset; + error=layout_runs(attr,rec,&next,size); + if(error) + return error; + } + next=(next+7) & ~7; /* align to DWORD */ + NTFS_PUTU16(rec+offset+4,next-offset); + offset=next; +#endif + } + /* terminating attribute */ + if(offset+8i_number); + deallocate_store(&store); + return EOPNOTSUPP; + } + if(error){ + deallocate_store(&store); + return error; + } + io.fn_get=ntfs_get; + io.fn_put=0; + for(i=0;ivol->blocksize); + io.param=store.records[i].record; + io.size=ino->vol->mft_recordsize; + /* FIXME: is this the right way? */ + error=ntfs_write_attr( + ino->vol->mft_ino,ino->vol->at_data,0, + store.records[i].recno*ino->vol->mft_recordsize,&io); + if(error || io.size!=ino->vol->mft_recordsize){ + /* big trouble, partially written file */ + ntfs_error("Please unmount: write error in inode %x\n",ino->i_number); + deallocate_store(&store); + return error?error:EIO; + } + } + return 0; +} + +void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l) +{ + int head,comp; + int copied=0; + unsigned char *stop; + int bits; + int tag=0; + int clear_pos; + while(1) + { + head = NTFS_GETU16(src) & 0xFFF; + /* high bit indicates that compression was performed */ + comp = NTFS_GETU8(src) & 0x80; + comp = (head == 0xFFF); + src += 2; + stop = src+head; + bits = 0; + clear_pos=0; + if(head==0) + /* block is not used */ + return;/* FIXME: copied */ + if(!comp) /* uncompressible */ + { + ntfs_memcpy(dest,src,0x1000); + dest+=0x1000; + copied+=0x1000; + src+=0x1000; + if(l==copied) + return; + continue; + } + while(src<=stop) + { + if(clear_pos>4096) + { + ntfs_error("Error 1 in decompress\n"); + return; + } + if(!bits){ + tag=NTFS_GETU8(src); + bits=8; + src++; + if(src>stop) + break; + } + if(tag & 1){ + int i,len,delta,code,lmask,dshift; + code = NTFS_GETU16(src); + src+=2; + if(!clear_pos) + { + ntfs_error("Error 2 in decompress\n"); + return; + } + for(i=clear_pos-1,lmask=0xFFF,dshift=12;i>=0x10;i>>=1) + { + lmask >>= 1; + dshift--; + } + delta = code >> dshift; + len = (code & lmask) + 3; + for(i=0; i>=1; + bits--; + } + dest+=clear_pos; + } +} + +/* Caveat: No range checking in either ntfs_set_bit or ntfs_clear_bit */ +void +ntfs_set_bit (unsigned char *byte, int bit) +{ + byte += (bit >> 3); + bit &= 7; + *byte |= (1 << bit); +} + +void +ntfs_clear_bit (unsigned char *byte, int bit) +{ + byte += (bit >> 3); + bit &= 7; + *byte &= ~(1 << bit); +} + +/* We have to skip the 16 metafiles and the 8 reserved entries */ +static int +new_inode (ntfs_volume* vol,int* result) +{ + int byte,error; + int bit; + int size,length; + unsigned char value; + ntfs_u8 *buffer; + ntfs_io io; + ntfs_attribute *data; + + buffer=ntfs_malloc(2048); + if(!buffer)return ENOMEM; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=buffer; + /* FIXME: bitmaps larger than 2048 bytes */ + io.size=2048; + error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,0,&io); + if(error){ + ntfs_free(buffer); + return error; + } + size=io.size; + data=ntfs_find_attr(vol->mft_ino,vol->at_data,0); + length=data->size/vol->mft_recordsize; + + for (byte = 3; 8*byte < length; byte++) + { + value = buffer[byte]; + if(value==0xFF) + continue; + for (bit = 0; (bit < 8) && (8*byte+bit>= 1) + { + if (!(value & 1)){ + *result=byte*8+bit; + return 0; + } + } + } + /* There is no free space. We must first extend the MFT. */ + return ENOSPC; +} + +static int +add_mft_header (ntfs_inode *ino) +{ + unsigned char* mft; + ntfs_volume *vol=ino->vol; + mft=ino->attr; + + ntfs_bzero(mft, vol->mft_recordsize); + fill_mft_header(mft,vol->mft_recordsize,vol->blocksize, + ino->sequence_number); + return 0; +} + +/* We need 0x48 bytes in total */ +static int +add_standard_information (ntfs_inode *ino) +{ + ntfs_time64_t now; + char data[0x30]; + char *position=data; + int error; + ntfs_attribute *si; + + now = ntfs_now(); + NTFS_PUTU64(position + 0x00, now); /* File creation */ + NTFS_PUTU64(position + 0x08, now); /* Last modification */ + NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */ + NTFS_PUTU64(position + 0x18, now); /* Last access */ + + NTFS_PUTU64(position + 0x20, 0x00); /* MSDOS file perms */ + NTFS_PUTU64(position + 0x28, 0); /* unknown */ + error=ntfs_create_attr(ino,ino->vol->at_standard_information,0, + data,sizeof(data),&si); + + return error; +} + +static int +add_filename (ntfs_inode* ino, ntfs_inode* dir, + const unsigned char *filename, int length) +{ + unsigned char *position; + unsigned int size; + ntfs_time64_t now; + int count; + int error; + unsigned char* data; + ntfs_attribute *fn; + + /* work out the size */ + size = 0x42 + 2 * length; + data = ntfs_malloc(size); + ntfs_bzero(data,size); + + /* search for a position */ + position = data; + + NTFS_PUTINUM(position, dir); /* Inode num of dir */ + + now = ntfs_now(); + NTFS_PUTU64(position + 0x08, now); /* File creation */ + NTFS_PUTU64(position + 0x10, now); /* Last modification */ + NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */ + NTFS_PUTU64(position + 0x20, now); /* Last access */ + + /* Don't know */ + NTFS_PUTU8(position+0x38, 0x0); /*should match standard attributes*/ + + NTFS_PUTU8(position + 0x40, length); /* Filename length */ + NTFS_PUTU8(position + 0x41, 0x0); /* only long name */ + + position += 0x42; + for (count = 0; count < length; count++) + { + NTFS_PUTU16(position + 2 * count, filename[count]); + } + + error=ntfs_create_attr(ino,ino->vol->at_file_name,0,data,size,&fn); + if(!error) + error=ntfs_dir_add(dir,ino,fn); + ntfs_free(data); + return error; +} + +int +add_security (ntfs_inode* ino, ntfs_inode* dir) +{ + int error; + char *buf; + int size; + ntfs_attribute* attr; + ntfs_io io; + ntfs_attribute *se; + + attr=ntfs_find_attr(dir,ino->vol->at_security_descriptor,0); + if(!attr) + return EOPNOTSUPP; /* need security in directory */ + size = attr->size; + if(size>512) + return EOPNOTSUPP; + buf=ntfs_malloc(size); + if(!buf) + return ENOMEM; + io.fn_get=ntfs_get; + io.fn_put=ntfs_put; + io.param=buf; + io.size=size; + error=ntfs_read_attr(dir,ino->vol->at_security_descriptor,0,0,&io); + if(!error && io.size!=size)ntfs_error("wrong size in add_security"); + if(error){ + ntfs_free(buf); + return error; + } + /* FIXME: consider ACL inheritance */ + error=ntfs_create_attr(ino,ino->vol->at_security_descriptor, + 0,buf,size,&se); + ntfs_free(buf); + return error; +} + +static int +add_data (ntfs_inode* ino, unsigned char *data, int length) +{ + int error; + ntfs_attribute *da; + error=ntfs_create_attr(ino,ino->vol->at_data,0,data,length,&da); + return error; +} + + +/* We _could_ use 'dir' to help optimise inode allocation */ +int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename, + int namelen) +{ + ntfs_io io; + int error; + ntfs_u8 buffer[1]; + ntfs_volume* vol=dir->vol; + int byte,bit; + + error=new_inode (vol,&(result->i_number)); + if(error==ENOSPC){ + error=ntfs_extend_mft(vol); + if(error)return error; + error=new_inode(vol,&(result->i_number)); + } + if(error){ + ntfs_error ("ntfs_get_empty_inode: no free inodes\n"); + return error; + } + byte=result->i_number/8; + bit=result->i_number & 7; + + io.fn_put = ntfs_put; + io.fn_get = ntfs_get; + io.param = buffer; + io.size=1; + /* set a single bit */ + error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,byte,&io); + if(error)return error; + if(io.size!=1) + return EIO; + ntfs_set_bit (buffer, bit); + io.param = buffer; + io.size = 1; + error = ntfs_write_attr (vol->mft_ino, vol->at_bitmap, 0, byte, &io); + if(error)return error; + if (io.size != 1) + return EIO; + /*FIXME: Should change MFT on disk + error=ntfs_update_inode(vol->mft_ino); + if(error)return error; + */ + /* get the sequence number */ + io.param = buffer; + io.size = 0x10; + error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, + result->i_number*vol->mft_recordsize+0x10,&io); + if(error) + return error; + result->sequence_number=NTFS_GETU16(buffer)+1; + result->vol=vol; + result->attr=ntfs_malloc(vol->mft_recordsize); + result->attr_count=0; + result->attrs=0; + result->record_count=1; + result->records=ntfs_malloc(8*sizeof(int)); + result->records[0]=result->i_number; + error=add_mft_header(result); + if(error) + return error; + error=add_standard_information(result); + if(error) + return error; + error=add_filename(result,dir,filename,namelen); + if(error) + return error; + error=add_security(result,dir); + /*FIXME: check error */ + error=add_data(result,0,0); + if(error) + return error; + return 0; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/inode.h linux/fs/ntfs/inode.h --- v2.1.73/linux/fs/ntfs/inode.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/inode.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,25 @@ +/* + * inode.h + * Header file for inode.c + * + * Copyright (C) 1997 Régis Duchesne + */ + +ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name); +int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset, + ntfs_io *buf); +int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset, + ntfs_io *buf); +int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum); +void ntfs_clear_inode(ntfs_inode *ino); +int ntfs_check_mft_record(ntfs_volume *vol,char *record); +int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename, + int namelen); +int ntfs_update_inode(ntfs_inode *ino); +int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn); +int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset, + ntfs_io *dest); +int ntfs_allocate_attr_number(ntfs_inode *ino, int *result); +int ntfs_decompress_run(unsigned char **data, int *length, int *cluster, + int *ctype); +void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l); diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/macros.h linux/fs/ntfs/macros.h --- v2.1.73/linux/fs/ntfs/macros.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/macros.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,44 @@ +/* + * macros.h + * + * Copyright (C) 1995 Martin von Löwis + * Copyright (C) 1996 Régis Duchesne + */ + +#define NTFS_FD(vol) ((vol)->u.fd) + +/* Linux */ +#ifdef NTFS_IN_LINUX_KERNEL +#define NTFS_SB(vol) ((struct super_block*)(vol)->sb) +#define NTFS_SB2VOL(sb) (&(sb)->u.ntfs_sb) +#define NTFS_INO2VOL(ino) (&((ino)->i_sb->u.ntfs_sb)) +#define NTFS_LINO2NINO(ino) (&((ino)->u.ntfs_i)) +#else +#define NTFS_SB(vol) ((struct super_block*)(vol)->u.sb) +#define NTFS_SB2VOL(sb) ((ntfs_volume*)(sb)->u.generic_sbp) +#define NTFS_INO2VOL(ino) ((ntfs_volume*)((ino)->i_sb->u.generic_sbp)) +#define NTFS_LINO2NINO(ino) ((ntfs_inode*)((ino)->u.generic_ip)) +#endif + +/* BSD */ +#define NTFS_MNT(vol) ((struct mount*)(vol)->u.sb) +#define NTFS_MNT2VOL(sb) ((ntfs_volume*)(sb)->mnt_data) +#define NTFS_V2INO(ino) ((ntfs_inode*)((ino)->v_data)) + +/* Classical min and max macros still missing in standard headers... */ +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) + +#define IS_MAGIC(a,b) (*(int*)(a)==*(int*)(b)) +#define IS_MFT_RECORD(a) IS_MAGIC((a),"FILE") +#define IS_NTFS_VOLUME(a) IS_MAGIC((a)+3,"NTFS") +#define IS_INDEX_RECORD(a) IS_MAGIC((a),"INDX") + +/* 'NTFS' in little endian */ +#define NTFS_SUPER_MAGIC 0x5346544E + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/struct.h linux/fs/ntfs/struct.h --- v2.1.73/linux/fs/ntfs/struct.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/struct.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,155 @@ +/* + * struct.h + * Structure definitions + * + * Copyright (C) 1997 Régis Duchesne + */ + +/* Necessary forward definition */ +struct ntfs_inode; + +#ifdef __FreeBSD__ +#include +/* Define the struct ntfs_head type */ +LIST_HEAD(ntfs_head,ntfs_inode); +#endif + +/* which files should be returned from a director listing */ +/* only short names, no hidden files */ +#define ngt_dos 1 +/* only long names, all-uppercase becomes all-lowercase, no hidden files */ +#define ngt_nt 2 +/* all names except hidden files */ +#define ngt_posix 3 +/* all entries */ +#define ngt_full 4 + +#ifdef NTFS_IN_LINUX_KERNEL +typedef struct ntfs_sb_info ntfs_volume; +#else +typedef struct _ntfs_volume{ + /* NTFS_SB_INFO_START */ + /* Configuration provided by user at mount time */ + ntfs_uid_t uid; + ntfs_gid_t gid; + ntmode_t umask; + unsigned int nct; + void *nls_map; + unsigned int ngt; + /* Configuration provided by user with ntfstools */ + ntfs_size_t partition_bias; /* for access to underlying device */ + /* Attribute definitions */ + ntfs_u32 at_standard_information; + ntfs_u32 at_attribute_list; + ntfs_u32 at_file_name; + ntfs_u32 at_security_descriptor; + ntfs_u32 at_data; + ntfs_u32 at_index_root; + ntfs_u32 at_index_allocation; + ntfs_u32 at_bitmap; + ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */ + /* Data read from the boot file */ + int blocksize; + int clusterfactor; + int clustersize; + int mft_recordsize; + int mft_clusters_per_record; + int index_recordsize; + int index_clusters_per_record; + int mft_cluster; + /* data read from special files */ + unsigned char *mft; + unsigned short *upcase; + unsigned int upcase_length; + /* inodes we always hold onto */ + struct ntfs_inode *mft_ino; + struct ntfs_inode *mftmirr; + struct ntfs_inode *bitmap; + /* NTFS_SB_INFO_END */ + union{ + int fd; /* file descriptor for the tools */ + void *sb; /* pointer to super block for the kernel */ + }u; +#ifdef __FreeBSD__ + dev_t rdev; + struct vnode *devvp; + struct ntfs_head *inode_hash; /* not really a hash */ +#endif +}ntfs_volume; +#endif + +typedef struct { + ntfs_cluster_t cluster; + ntfs_cluster_t len; +}ntfs_runlist; + +typedef struct ntfs_attribute{ + int type; + ntfs_u16 *name; + int namelen; + int attrno; + int size,allocated,initialized,compsize; + int compressed,resident,indexed; + int cengine; + union{ + void *data; /* if resident */ + struct { + ntfs_runlist *runlist; + int len; + }r; + }d; +}ntfs_attribute; + +/* Structure to define IO to user buffer. do_read means that + the destination has to be written using fn_put, do_write means + that the destination has to read using fn_get. So, do_read is + from a user's point of view, while put and get are from the driver's + point of view. The first argument is always the destination of the IO +*/ +#ifdef NTFS_IN_LINUX_KERNEL +typedef struct ntfs_inode_info ntfs_inode; +#else +typedef struct ntfs_inode{ + ntfs_volume *vol; + /* NTFS_INODE_INFO_START */ + int i_number; /* should be really 48 bits */ + unsigned sequence_number; + unsigned char* attr; /* array of the attributes */ + int attr_count; /* size of attrs[] */ + struct ntfs_attribute *attrs; + int record_count; /* size of records[] */ + /* array of the record numbers of the MFT + whose attributes have been inserted in the inode */ + int *records; + union{ + struct{ + int recordsize; + int clusters_per_record; + }index; + } u; + /* NTFS_INODE_INFO_END */ +#ifdef __FreeBSD__ + struct vnode *vp; + LIST_ENTRY(ntfs_inode) h_next; +#endif +}ntfs_inode; +#endif + +typedef struct ntfs_io{ + int do_read; + void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t); + void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len); + void *param; + int size; +}ntfs_io; + +#if 0 +typedef struct { + ntfs_volume *vol; + ntfs_inode *ino; + int type; + char *name; + int mftno; + int start_vcn; +} ntfs_attrlist_item; +#endif diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/super.c linux/fs/ntfs/super.c --- v2.1.73/linux/fs/ntfs/super.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/super.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,547 @@ +/* + * super.c + * + * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#include "types.h" +#include "struct.h" +#include "super.h" + +#include +#include "macros.h" +#include "inode.h" +#include "support.h" +#include "util.h" + +/* + * All important structures in NTFS use 2 consistency checks : + * . a magic structure identifier (FILE, INDX, RSTR, RCRD...) + * . a fixup technique : the last word of each sector (called a fixup) of a + * structure's record should end with the word at offset of the first + * sector, and if it is the case, must be replaced with the words following + * . The value of and the number of fixups is taken from the fields + * at the offsets 4 and 6. + * + * This function perform these 2 checks, and _fails_ if : + * . the magic identifier is wrong + * . the size is given and does not match the number of sectors + * . a fixup is invalid + */ +int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size) +{ + int start, count, offset; + short fixup; + + if(!IS_MAGIC(record,magic)) + return 0; + start=NTFS_GETU16(record+4); + count=NTFS_GETU16(record+6); + count--; + if(size && vol->blocksize*count != size) + return 0; + fixup = NTFS_GETU16(record+start); + start+=2; + offset=vol->blocksize-2; + while(count--){ + if(NTFS_GETU16(record+offset)!=fixup) + return 0; + NTFS_PUTU16(record+offset, NTFS_GETU16(record+start)); + start+=2; + offset+=vol->blocksize; + } + return 1; +} + +/* Get vital informations about the ntfs partition from the boot sector */ +int ntfs_init_volume(ntfs_volume *vol,char *boot) +{ + /* Historical default values, in case we don't load $AttrDef */ + vol->at_standard_information=0x10; + vol->at_attribute_list=0x20; + vol->at_file_name=0x30; + vol->at_security_descriptor=0x50; + vol->at_data=0x80; + vol->at_index_root=0x90; + vol->at_index_allocation=0xA0; + vol->at_bitmap=0xB0; + vol->at_symlink=0xC0; + + /* Sector size */ + vol->blocksize=NTFS_GETU16(boot+0xB); + vol->clusterfactor=NTFS_GETU8(boot+0xD); + vol->mft_clusters_per_record=NTFS_GETS8(boot+0x40); + vol->index_clusters_per_record=NTFS_GETS8(boot+0x44); + + /* Just some consistency checks */ + if(NTFS_GETU32(boot+0x40)>256) + ntfs_error("Unexpected data #1 in boot block\n"); + if(NTFS_GETU32(boot+0x44)>256) + ntfs_error("Unexpected data #2 in boot block\n"); + if(vol->index_clusters_per_record<0){ + ntfs_error("Unexpected data #3 in boot block\n"); + /* If this really means a fraction, setting it to 1 + should be safe. */ + vol->index_clusters_per_record=1; + } + /* in some cases, 0xF6 meant 1024 bytes. Other strange values have not + been observed */ + if(vol->mft_clusters_per_record<0 && vol->mft_clusters_per_record!=-10) + ntfs_error("Unexpected data #4 in boot block\n"); + + vol->clustersize=vol->blocksize*vol->clusterfactor; + if(vol->mft_clusters_per_record>0) + vol->mft_recordsize= + vol->clustersize*vol->mft_clusters_per_record; + else + vol->mft_recordsize=1<<(-vol->mft_clusters_per_record); + vol->index_recordsize=vol->clustersize*vol->index_clusters_per_record; + /* FIXME: long long value */ + vol->mft_cluster=NTFS_GETU64(boot+0x30); + + /* This will be initialized later */ + vol->upcase=0; + vol->upcase_length=0; + vol->mft_ino=0; + return 0; +} + +static void +ntfs_init_upcase(ntfs_inode *upcase) +{ + ntfs_io io; +#define UPCASE_LENGTH 256 + upcase->vol->upcase = ntfs_malloc(2*UPCASE_LENGTH); + upcase->vol->upcase_length = UPCASE_LENGTH; + io.fn_put=ntfs_put; + io.fn_get=0; + io.param=upcase->vol->upcase; + io.size=2*UPCASE_LENGTH; + ntfs_read_attr(upcase,upcase->vol->at_data,0,0,&io); +} + +static int +process_attrdef(ntfs_inode* attrdef,ntfs_u8* def) +{ + int type = NTFS_GETU32(def+0x80); + int check_type = 0; + ntfs_volume *vol=attrdef->vol; + ntfs_u16* name = (ntfs_u16*)def; + + if(ntfs_ua_strncmp(name,"$STANDARD_INFORMATION",64)==0){ + vol->at_standard_information=type; + check_type=0x10; + }else if(ntfs_ua_strncmp(name,"$ATTRIBUTE_LIST",64)==0){ + vol->at_attribute_list=type; + check_type=0x20; + }else if(ntfs_ua_strncmp(name,"$FILE_NAME",64)==0){ + vol->at_file_name=type; + check_type=0x30; + }else if(ntfs_ua_strncmp(name,"$SECURITY_DESCRIPTOR",64)==0){ + vol->at_file_name=type; + }else if(ntfs_ua_strncmp(name,"$DATA",64)==0){ + vol->at_data=type; + check_type=0x80; + }else if(ntfs_ua_strncmp(name,"$INDEX_ROOT",64)==0){ + vol->at_index_root=type; + check_type=0x90; + }else if(ntfs_ua_strncmp(name,"$INDEX_ALLOCATION",64)==0){ + vol->at_index_allocation=type; + check_type=0xA0; + }else if(ntfs_ua_strncmp(name,"$BITMAP",64)==0){ + vol->at_bitmap=type; + check_type=0xB0; + }else if(ntfs_ua_strncmp(name,"$SYMBOLIC_LINK",64) || + ntfs_ua_strncmp(name,"$REPARSE_POINT",64)){ + vol->at_symlink=type; + } + if(check_type && check_type!=type){ + ntfs_error("Unexpected type %x for %x\n",type,check_type); + return EINVAL; + } + return 0; +} + +int +ntfs_init_attrdef(ntfs_inode* attrdef) +{ + ntfs_u8 *buf; + ntfs_io io; + int offset,error,i; + ntfs_attribute *data; + buf=ntfs_malloc(4096); + if(!buf)return ENOMEM; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.do_read=1; + offset=0; + data=ntfs_find_attr(attrdef,attrdef->vol->at_data,0); + if(!data){ + ntfs_free(buf); + return EINVAL; + } + do{ + io.param=buf; + io.size=4096; + error=ntfs_readwrite_attr(attrdef,data,offset,&io); + for(i=0;!error && imft_ino=(ntfs_inode*)ntfs_calloc(3*sizeof(ntfs_inode)); + error=ENOMEM; + if(!vol->mft_ino || (error=ntfs_init_inode(vol->mft_ino,vol,FILE_MFT))) + { + ntfs_error("Problem loading MFT\n"); + return error; + } + vol->mftmirr=vol->mft_ino+1; + if((error=ntfs_init_inode(vol->mftmirr,vol,FILE_MFTMIRR))){ + ntfs_error("Problem %d loading MFTMirr\n",error); + return error; + } + vol->bitmap=vol->mft_ino+2; + if((error=ntfs_init_inode(vol->bitmap,vol,FILE_BITMAP))){ + ntfs_error("Problem loading Bitmap\n"); + return error; + } + error=ntfs_init_inode(&upcase,vol,FILE_UPCASE); + if(error)return error; + ntfs_init_upcase(&upcase); + ntfs_clear_inode(&upcase); + error=ntfs_init_inode(&attrdef,vol,FILE_ATTRDEF); + if(error)return error; + error=ntfs_init_attrdef(&attrdef); + ntfs_clear_inode(&attrdef); + if(error)return error; + return 0; +} + +int ntfs_release_volume(ntfs_volume *vol) +{ + if(vol->mft_ino){ + ntfs_clear_inode(vol->mft_ino); + ntfs_clear_inode(vol->mftmirr); + ntfs_clear_inode(vol->bitmap); + ntfs_free(vol->mft_ino); + vol->mft_ino=0; + } + ntfs_free(vol->mft); + ntfs_free(vol->upcase); + return 0; +} + +int ntfs_get_volumesize(ntfs_volume *vol) +{ + ntfs_io io; + char *cluster0=ntfs_malloc(vol->clustersize); + int size; + + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=cluster0; + io.do_read=1; + io.size=vol->clustersize; + ntfs_getput_clusters(vol,0,0,&io); + size=NTFS_GETU64(cluster0+0x28); + ntfs_free(cluster0); + size/=vol->clusterfactor; + return size; +} + +static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0}; + +int +ntfs_get_free_cluster_count(ntfs_inode *bitmap) +{ + unsigned char bits[2048]; + int offset,error; + int clusters=0; + ntfs_io io; + + offset=0; + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + while(1) + { + register int i; + io.param=bits; + io.size=2048; + error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0, + offset,&io); + if(error || io.size==0)break; + /* I never thought I would do loop unrolling some day */ + for(i=0;i>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF]; + } + for(;i>4];clusters+=nc[bits[i++] & 0xF]; + } + offset+=io.size; + } + return clusters; +} + +/* Insert the fixups for the record. The number and location of the fixes + is obtained from the record header */ +void ntfs_insert_fixups(unsigned char *rec, int secsize) +{ + int first=NTFS_GETU16(rec+4); + int count=NTFS_GETU16(rec+6); + int offset=-2; + ntfs_u16 fix=NTFS_GETU16(rec+first); + fix=fix+1; + NTFS_PUTU16(rec+first,fix); + count--; + while(count--){ + first+=2; + offset+=secsize; + NTFS_PUTU16(rec+first,NTFS_GETU16(rec+offset)); + NTFS_PUTU16(rec+offset,fix); + }; +} + +/* search the bitmap bits of l bytes for *cnt zero bits. Return the bit + number in *loc, which is initially set to the number of the first bit. + Return the largest block found in *cnt. Return 0 on success, ENOSPC if + all bits are used */ +static int +search_bits(unsigned char* bits,int *loc,int *cnt,int l) +{ + unsigned char c=0; + int bc=0; + int bstart=0,bstop=0,found=0; + int start,stop=0,in=0; + /* special case searching for a single block */ + if(*cnt==1){ + while(l && *cnt==0xFF){ + bits++; + *loc+=8; + l--; + } + if(!l)return ENOSPC; + for(c=*bits;c & 1;c>>=1) + (*loc)++; + return 0; + } + start=*loc; + while(l || bc){ + if(bc==0){ + c=*bits; + if(l){ + l--;bits++; + } + bc=8; + } + if(in){ + if((c&1)==0) + stop++; + else{ /* end of sequence of zeroes */ + in=0; + if(!found || bstop-bstart*cnt) + break; + } + start=stop+1; + } + }else{ + if(c&1) + start++; + else{ /*start of sequence*/ + in=1; + stop=start+1; + } + } + bc--; + c>>=1; + } + if(in && (!found || bstop-bstartbstop-bstart) + *cnt=bstop-bstart; + return 0; +} + +int +ntfs_set_bitrange(ntfs_inode* bitmap,int loc,int cnt,int bit) +{ + int bsize,locit,error; + unsigned char *bits,*it; + ntfs_io io; + + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + bsize=(cnt+loc%8+7)/8; /* round up */ + bits=ntfs_malloc(bsize); + io.param=bits; + io.size=bsize; + if(!bits) + return ENOMEM; + error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io); + if(error || io.size!=bsize){ + ntfs_free(bits); + return error?error:EIO; + } + /* now set the bits */ + it=bits; + locit=loc; + while(locit%8 && cnt){ /* process first byte */ + if(bit) + *it |= 1<<(locit%8); + else + *it &= ~(1<<(locit%8)); + cnt--;locit++; + if(locit%8==7) + it++; + } + while(cnt>8){ /*process full bytes */ + *it= bit ? 0xFF : 0; + cnt-=8; + locit+=8; + it++; + } + while(cnt){ /*process last byte */ + if(bit) + *it |= 1<<(locit%8); + else + *it &= ~(1<<(locit%8)); + cnt--;locit++; + } + /* reset to start */ + io.param=bits; + io.size=bsize; + error=ntfs_write_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io); + ntfs_free(bits); + if(error)return error; + if(io.size!=bsize) + return EIO; + return 0; +} + + + +/* allocate count clusters around location. If location is -1, + it does not matter where the clusters are. Result is 0 if + success, in which case location and count says what they really got */ +int +ntfs_search_bits(ntfs_inode* bitmap, int *location, int *count, int flags) +{ + unsigned char *bits; + ntfs_io io; + int error=0,found=0; + int loc,cnt,bloc=-1,bcnt=0; + int start; + + bits=ntfs_malloc(2048); + io.fn_put=ntfs_put; + io.fn_get=ntfs_get; + io.param=bits; + + /* first search within +/- 8192 clusters */ + start=*location/8; + start= start>1024 ? start-1024 : 0; + io.size=2048; + error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,start,&io); + if(error)goto fail; + loc=start*8; + cnt=*count; + error=search_bits(bits,&loc,&cnt,io.size); + if(error) + goto fail; + if(*count==cnt){ + bloc=loc; + bcnt=cnt; + goto success; + } + + /* now search from the beginning */ + for(start=0;1;start+=2048) + { + io.param=bits; + io.size=2048; + error=ntfs_read_attr(bitmap,bitmap->vol->at_data, + 0,start,&io); + if(error)goto fail; + if(io.size==0) + if(found) + goto success; + else{ + error=ENOSPC; + goto fail; + } + loc=start*8; + cnt=*count; + error=search_bits(bits,&loc,&cnt,io.size); + if(error) + goto fail; + if(*count==cnt) + goto success; + if(bcntbitmap,location,count,flags); + return error; +} + +int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count) +{ + int error; + error=ntfs_set_bitrange(vol->bitmap,location,count,0); + return error; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ + + diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/super.h linux/fs/ntfs/super.h --- v2.1.73/linux/fs/ntfs/super.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/super.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,21 @@ +/* + * super.h + * Header file for super.c + * + * Copyright (C) 1995-1997 Martin von Löwis + * Copyright (C) 1996-1997 Régis Duchesne + */ + +#define ALLOC_REQUIRE_LOCATION 1 +#define ALLOC_REQUIRE_SIZE 2 + +int ntfs_get_free_cluster_count(ntfs_inode *bitmap); +int ntfs_get_volumesize(ntfs_volume *vol); +int ntfs_init_volume(ntfs_volume *vol,char *boot); +int ntfs_load_special_files(ntfs_volume *vol); +int ntfs_release_volume(ntfs_volume *vol); +void ntfs_insert_fixups(unsigned char *rec, int secsize); +int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size); +int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count, + int flags); +int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count); diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/support.c linux/fs/ntfs/support.c --- v2.1.73/linux/fs/ntfs/support.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/support.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,313 @@ +/* + * support.c + * Specific support functions + * + * Copyright (C) 1997 Martin von Löwis + * Copyright (C) 1997 Régis Duchesne + * + */ + +#ifdef NTFS_IN_LINUX_KERNEL +#include +#endif +#include "types.h" +#include "struct.h" +#include "support.h" + +#include +#include +#include +#include +#include "util.h" +#include "inode.h" +#include "macros.h" + +static char print_buf[1024]; + +#ifdef DEBUG +#include "sysctl.h" +#include + +/* Debugging output */ +void ntfs_debug(int mask, const char *fmt, ...) +{ + va_list ap; + + /* Filter it with the debugging level required */ + if(ntdebug & mask){ + va_start(ap,fmt); + strcpy(print_buf, KERN_DEBUG); + vsprintf(print_buf + 3, fmt, ap); + printk(print_buf); + va_end(ap); + } +} + +/* Verbose kmalloc */ +void *ntfs_malloc(int size) +{ + void *ret; + + ret = kmalloc(size, GFP_KERNEL); + ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret); + + return ret; +} + +/* Verbose kfree() */ +void ntfs_free(void *block) +{ + ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block); + kfree(block); +} +#else +void ntfs_debug(int mask, const char *fmt, ...) +{ +} + +void *ntfs_malloc(int size) +{ + return kmalloc(size, GFP_KERNEL); +} + +void ntfs_free(void *block) +{ + kfree(block); +} +#endif /* DEBUG */ + +void ntfs_bzero(void *s, int n) +{ + memset(s, 0, n); +} + +void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n) +{ + return memcpy(dest, src, n); +} + +void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n) +{ + return memmove(dest, src, n); +} + +/* Warn that an error occured */ +void ntfs_error(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + strcpy(print_buf, KERN_ERR); + vsprintf(print_buf + 3, fmt, ap); + printk(print_buf); + va_end(ap); +} + +int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf) +{ + int error; + ntfs_io io; + + ntfs_debug(DEBUG_OTHER, "read_mft_record %x\n",mftno); + if(mftno==FILE_MFT) + { + ntfs_memcpy(buf,vol->mft,vol->mft_recordsize); + return 0; + } + if(!vol->mft_ino) + { + printk("ntfs:something is terribly wrong here\n"); + return ENODATA; + } + io.fn_put=ntfs_put; + io.fn_get=0; + io.param=buf; + io.size=vol->mft_recordsize; + error=ntfs_read_attr(vol->mft_ino,vol->at_data,NULL, + mftno*vol->mft_recordsize,&io); + if(error || (io.size!=vol->mft_recordsize)) + { + ntfs_debug(DEBUG_OTHER, "read_mft_record: read %x failed (%d,%d,%d)\n", + mftno,error,io.size,vol->mft_recordsize); + return error?error:ENODATA; + } + ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read %x\n",mftno); + if(!ntfs_check_mft_record(vol,buf)) + { + printk("Invalid MFT record for %x\n",mftno); + return EINVAL; + } + ntfs_debug(DEBUG_OTHER, "read_mft_record: Done %x\n",mftno); + return 0; +} + +int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs, + ntfs_io *buf) +{ + struct super_block *sb=NTFS_SB(vol); + struct buffer_head *bh; + ntfs_size_t to_copy; + int length=buf->size; + if(buf->do_read) + ntfs_debug(DEBUG_OTHER, "get_clusters %d %d %d\n",cluster,start_offs,length); + else + ntfs_debug(DEBUG_OTHER, "put_clusters %d %d %d\n",cluster,start_offs,length); + while(length) + { + if(!(bh=bread(sb->s_dev,cluster,vol->clustersize))) + { + ntfs_debug(DEBUG_OTHER, "%s failed\n", buf->do_read?"Reading":"Writing"); + return EIO; + } + to_copy=min(vol->clustersize-start_offs,length); + lock_buffer(bh); + if(buf->do_read) + buf->fn_put(buf,bh->b_data+start_offs,to_copy); + else + { + buf->fn_get(bh->b_data+start_offs,buf,to_copy); + mark_buffer_dirty(bh,1); + } + unlock_buffer(bh); + length-=to_copy; + start_offs=0; + cluster++; + brelse(bh); + } + return 0; +} + +ntfs_time64_t ntfs_now(void) +{ + return ntfs_unixutc2ntutc(CURRENT_TIME); +} + +/* when printing unicode characters base64, use this table. + It is not strictly base64, but the Linux vfat encoding. + base64 has the disadvantage of using the slash. +*/ +static char uni2esc[64]= + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-"; + +static unsigned char +esc2uni(char c) +{ + if(c<'0')return 255; + if(c<='9')return c-'0'; + if(c<'A')return 255; + if(c<='Z')return c-'A'+10; + if(c<'a')return 255; + if(c<='z')return c-'a'+36; + if(c=='+')return 62; + if(c=='-')return 63; + return 255; +} + +int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out, + int *out_len) +{ + int i,o,val; + char *result,*buf; + struct nls_table* nls=vol->nls_map; + + result=ntfs_malloc(in_len+1); + if(!result)return ENOMEM; + *out_len=in_len; + result[in_len]='\0'; + for(i=o=0;i> 8) & 0xFF; + if(!nls){ + if(!ch){ + result[o++]=cl; + continue; + } + }else{ + uni_page=nls->page_uni2charset[ch]; + if(uni_page && uni_page[cl]){ + result[o++]=uni_page[cl]; + continue; + } + } + if(!(vol->nct & nct_uni_xlate))goto inval; + /* realloc */ + buf=ntfs_malloc(*out_len+3); + memcpy(buf,result,o); + ntfs_free(result); + result=buf; + *out_len+=3; + result[o++]=':'; + if(vol->nct & nct_uni_xlate_vfat){ + val=(cl<<8)+ch; + result[o+2]=uni2esc[val & 0x3f]; + val>>=6; + result[o+1]=uni2esc[val & 0x3f]; + val>>=6; + result[o]=uni2esc[val & 0x3f]; + o+=3; + }else{ + val=(ch<<8)+cl; + result[o++]=uni2esc[val & 0x3f]; + val>>=6; + result[o++]=uni2esc[val & 0x3f]; + val>>=6; + result[o++]=uni2esc[val & 0x3f]; + } + } + *out=result; + return 0; + inval: + ntfs_free(result); + *out=0; + return EILSEQ; +} + +int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out, + int *out_len) +{ + int i,o; + ntfs_u16* result; + struct nls_table* nls=vol->nls_map; + + *out=result=ntfs_malloc(2*in_len); + if(!result)return ENOMEM; + *out_len=in_len; + for(i=o=0;inct & nct_uni_xlate)==0){ + cl=nls->charset2uni[(unsigned char)in[i]].uni1; + ch=nls->charset2uni[(unsigned char)in[i]].uni2; + }else{ + unsigned char c1,c2,c3; + *out_len-=3; + c1=esc2uni(in[++i]); + c2=esc2uni(in[++i]); + c3=esc2uni(in[++i]); + if(c1==255 || c2==255 || c3==255) + cl=ch=0; + else if(vol->nct & nct_uni_xlate_vfat){ + cl = (c1 << 4) + (c2 >> 2); + ch = ((c2 & 0x3) << 6) + c3; + }else{ + ch=(c3 << 4) + (c2 >> 2); + cl=((c2 & 0x3) << 6) + c1; + } + } + /* FIXME: byte order */ + result[o] = (ch<<8) | cl; + if(!result[o]){ + ntfs_free(result); + return EILSEQ; + } + } + return 0; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/support.h linux/fs/ntfs/support.h --- v2.1.73/linux/fs/ntfs/support.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/support.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,32 @@ +/* + * support.h + * Header file for specific support.c + * + * Copyright (C) 1997 Régis Duchesne + */ + +/* Debug levels */ +#define DEBUG_OTHER 1 +#define DEBUG_MALLOC 2 + +void ntfs_debug(int mask, const char *fmt, ...); +void *ntfs_malloc(int size); +void ntfs_free(void *block); +void ntfs_bzero(void *s, int n); +void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n); +void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n); +void ntfs_error(const char *fmt,...); +int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf); +int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs, + ntfs_io *buf); +ntfs_time64_t ntfs_now(void); +int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out, + int *out_len); +int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out, + int *out_len); + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/sysctl.c linux/fs/ntfs/sysctl.c --- v2.1.73/linux/fs/ntfs/sysctl.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/sysctl.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,62 @@ +/* + * sysctl.c + * System control stuff + * + * Copyright (C) 1997 Martin von Löwis + * Copyright (C) 1997 Régis Duchesne + * + */ + +#include "sysctl.h" + +#ifdef DEBUG +#include +#include + +int ntdebug = 0; + +/* Add or remove the debug sysctl + * Is this really the only file system with sysctls ? + */ +void ntfs_sysctl(int add) +{ +#define FS_NTFS 1 + /* Definition of the sysctl */ + static ctl_table ntfs_sysctls[]={ + {FS_NTFS, /* ID */ + "ntfs-debug", /* name in /proc */ + &ntdebug,sizeof(ntdebug), /* data ptr, data size */ + 0644, /* mode */ + 0, /* child */ + proc_dointvec, /* proc handler */ + 0, /* strategy */ + 0, /* proc control block */ + 0,0}, /* extra */ + {0} + }; + /* Define the parent file : /proc/sys/fs */ + static ctl_table sysctls_root[]={ + {CTL_FS, + "fs", + NULL,0, + 0555, + ntfs_sysctls}, + {0} + }; + static struct ctl_table_header *sysctls_root_header = NULL; + + if(add){ + if(!sysctls_root_header) + sysctls_root_header = register_sysctl_table(sysctls_root, 0); + } else if(sysctls_root_header) { + unregister_sysctl_table(sysctls_root_header); + sysctls_root_header = NULL; + } +} +#endif /* DEBUG */ + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/sysctl.h linux/fs/ntfs/sysctl.h --- v2.1.73/linux/fs/ntfs/sysctl.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/sysctl.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,24 @@ +/* + * sysctl.h + * Header file for sysctl.c + * + * Copyright (C) 1997 Martin von Löwis + * Copyright (C) 1997 Régis Duchesne + * + */ + +#ifdef DEBUG +extern int ntdebug; + +void ntfs_sysctl(int add); + +#define SYSCTL(x) ntfs_sysctl(x) +#else +#define SYSCTL(x) +#endif /* DEBUG */ + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/types.h linux/fs/ntfs/types.h --- v2.1.73/linux/fs/ntfs/types.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/types.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,128 @@ +/* + * types.h + * This file defines four things: + * - generic platform independent fixed-size types (e.g. ntfs_u32) + * - specific fixed-size types (e.g. ntfs_offset_t) + * - macros that read and write those types from and to byte arrays + * - types derived from OS specific ones + * + * Copyright (C) 1996 Martin von Löwis + */ + +#ifdef NTFS_IN_LINUX_KERNEL +/* get installed types if we compile the kernel*/ +#include +#endif + +#if defined(i386) || defined(__i386__) + +/* unsigned integral types */ +#ifndef NTFS_INTEGRAL_TYPES +#define NTFS_INTEGRAL_TYPES +typedef unsigned char ntfs_u8; +typedef unsigned short ntfs_u16; +typedef unsigned int ntfs_u32; +typedef unsigned long long ntfs_u64; +#endif + +/* unicode character type */ +#ifndef NTFS_WCHAR_T +#define NTFS_WCHAR_T +typedef unsigned short ntfs_wchar_t; +#endif +/* file offset */ +#ifndef NTFS_OFFSET_T +#define NTFS_OFFSET_T +typedef unsigned long long ntfs_offset_t; +#endif +/* UTC */ +#ifndef NTFS_TIME64_T +#define NTFS_TIME64_T +typedef unsigned long long ntfs_time64_t; +#endif +/* This is really unsigned long long. So we support only volumes up to 2 TB */ +#ifndef NTFS_CLUSTER_T +#define NTFS_CLUSTER_T +typedef unsigned int ntfs_cluster_t; +#endif + +/* Macros reading unsigned integers from a byte pointer */ +/* these should work for all little endian machines */ +#define NTFS_GETU8(p) (*(ntfs_u8*)(p)) +#define NTFS_GETU16(p) (*(ntfs_u16*)(p)) +#define NTFS_GETU24(p) (NTFS_GETU32(p) & 0xFFFFFF) +#define NTFS_GETU32(p) (*(ntfs_u32*)(p)) +#define NTFS_GETU64(p) (*(ntfs_u64*)(p)) + +/* Macros writing unsigned integers */ +#define NTFS_PUTU8(p,v) (*(ntfs_u8*)(p))=(v) +#define NTFS_PUTU16(p,v) (*(ntfs_u16*)(p))=(v) +#define NTFS_PUTU24(p,v) NTFS_PUTU16(p,(v) & 0xFFFF);\ + NTFS_PUTU8(((char*)p)+2,(v)>>16) +#define NTFS_PUTU32(p,v) (*(ntfs_u32*)(p))=(v) +#define NTFS_PUTU64(p,v) (*(ntfs_u64*)(p))=(v) + +/* Macros reading signed integers, returning int */ +#define NTFS_GETS8(p) ((int)(*(char*)(p))) +#define NTFS_GETS16(p) ((int)(*(short*)(p))) +#define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) | 0xFF000000)) + +#define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v) +#define NTFS_PUTS16(p,v) NTFS_PUTU16(p,v) +#define NTFS_PUTS24(p,v) NTFS_PUTU24(p,v) +#define NTFS_PUTS32(p,v) NTFS_PUTU32(p,v) + +#else +#error Put your machine description here +#endif + +/* architecture independent macros */ + +/* PUTU32 would not clear all bytes */ +#define NTFS_PUTINUM(p,i) NTFS_PUTU64(p,i->i_number);\ + NTFS_PUTU16(((char*)p)+6,i->sequence_number) + +/* system dependent types */ +#ifdef __linux__ +/* We always need kernel types, because glibc makes them of different size */ +#include +/* Avoid a type redefinition with future include of glibc */ +#undef __FD_ZERO +#undef __FD_SET +#undef __FD_CLR +#undef __FD_ISSET +#ifndef NTMODE_T +#define NTMODE_T +typedef __kernel_mode_t ntmode_t; +#endif +#ifndef NTFS_UID_T +#define NTFS_UID_T +typedef __kernel_uid_t ntfs_uid_t; +#endif +#ifndef NTFS_GID_T +#define NTFS_GID_T +typedef __kernel_gid_t ntfs_gid_t; +#endif +#ifndef NTFS_SIZE_T +#define NTFS_SIZE_T +typedef __kernel_size_t ntfs_size_t; +#endif +#ifndef NTFS_TIME_T +#define NTFS_TIME_T +typedef __kernel_time_t ntfs_time_t; +#endif +#else +#include +#include +typedef mode_t ntmode_t; +typedef uid_t ntfs_uid_t; +typedef gid_t ntfs_gid_t; +typedef size_t ntfs_size_t; +typedef time_t ntfs_time_t; +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/util.c linux/fs/ntfs/util.c --- v2.1.73/linux/fs/ntfs/util.c Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/util.c Fri Dec 19 15:24:21 1997 @@ -0,0 +1,343 @@ +/* + * util.c + * Miscellaneous support + * + * Copyright (C) 1997 Martin von Löwis + * Copyright (C) 1997 Régis Duchesne + * + * The utf8 routines are copied from Python wstrop module, + */ + +#include "types.h" +#include "struct.h" +#include "util.h" + +#include +/* FreeBSD doesn't seem to have EILSEQ in errno.h */ +#ifndef EILSEQ +# define EILSEQ EINVAL +#endif +#include "support.h" + +/* Converts a single wide character to a sequence of utf8 bytes. + * Returns the number of bytes, or 0 on error. + */ +static int +to_utf8(ntfs_u16 c,unsigned char* buf) +{ + if(c==0) + return 0; /* No support for embedded 0 runes */ + if(c<0x80){ + if(buf)buf[0]=c; + return 1; + } + if(c<0x800){ + if(buf){ + buf[0] = 0xc0 | (c>>6); + buf[1] = 0x80 | (c & 0x3f); + } + return 2; + } + if(c<0x10000){ + if(buf){ + buf[0] = 0xe0 | (c>>12); + buf[1] = 0x80 | ((c>>6) & 0x3f); + buf[2] = 0x80 | (c & 0x3f); + } + return 3; + } + /* We don't support characters above 0xFFFF in NTFS */ + return 0; +} + +/* Decodes a sequence of utf8 bytes into a single wide character. + * Returns the number of bytes consumed, or 0 on error + */ +static int +from_utf8(const unsigned char* str,ntfs_u16 *c) +{ + int l=0,i; + + if(*str<0x80){ + *c = *str; + return 1; + } + if(*str<0xc0) /* lead byte must not be 10xxxxxx */ + return 0; /* is c0 a possible lead byte? */ + if(*str<0xe0){ /* 110xxxxx */ + *c = *str & 0x1f; + l=2; + }else if(*str<0xf0){ /* 1110xxxx */ + *c = *str & 0xf; + l=3; + }else if(*str<0xf8){ /* 11110xxx */ + *c = *str & 7; + l=4; + }else /* We don't support characters above 0xFFFF in NTFS */ + return 0; + + + for(i=1;i=256) + return EILSEQ; + *out=result=ntfs_malloc(in_len+1); + if(!result) + return ENOMEM; + result[in_len]='\0'; + *out_len=in_len; + for(i=0;inct & nct_utf8) + return ntfs_dupuni2utf8(in,in_len,out,out_len); + else if(vol->nct & nct_iso8859_1) + return ntfs_dupuni288591(in,in_len,out,out_len); + else if(vol->nct & (nct_map|nct_uni_xlate)) + /* uni_xlate is handled inside map */ + return ntfs_dupuni2map(vol,in,in_len,out,out_len); + else + return EINVAL; /* unknown encoding */ +} + +int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len, + ntfs_u16 **out, int *out_len) +{ + if(vol->nct & nct_utf8) + return ntfs_duputf82uni(in,in_len,out,out_len); + else if(vol->nct & nct_iso8859_1) + return ntfs_dup885912uni(in,in_len,out,out_len); + else if(vol->nct & (nct_map | nct_uni_xlate)) + return ntfs_dupmap2uni(vol,in,in_len,out,out_len); + else + return EINVAL; +} + +/* Same address space copies */ +void ntfs_put(ntfs_io *dest,void *src,ntfs_size_t n) +{ + ntfs_memcpy(dest->param,src,n); + dest->param+=n; +} + +void ntfs_get(void* dest,ntfs_io *src,ntfs_size_t n) +{ + ntfs_memcpy(dest,src->param,n); + src->param+=n; +} + +void *ntfs_calloc(int size) +{ + void *result=ntfs_malloc(size); + + if(result) + ntfs_bzero(result,size); + return result; +} + +#if 0 +/* copy len unicode characters from from to to :) */ +void ntfs_uni2ascii(char *to,char *from,int len) +{ + int i; + + for(i=0;i> 32); + unsigned int L = (unsigned int)ntutc; + unsigned int numerator2; + unsigned int lowseconds; + unsigned int result; + + /* It is best to subtract 0x019db1ded53e8000 first. */ + /* Then the 1601-based date becomes a 1970-based date. */ + if(L < (unsigned)0xd53e8000) H--; + L -= (unsigned)0xd53e8000; + H -= (unsigned)0x019db1de; + + /* + * Now divide 64-bit numbers on a 32-bit machine :-) + * With the subtraction already done, the result fits in 32 bits. + * The numerator fits in 56 bits and the denominator fits + * in 24 bits, so we can shift by 8 bits to make this work. + */ + + numerator2 = (H<<8) | (L>>24); + result = (numerator2 / D); /* shifted 24 right!! */ + lowseconds = result << 24; + + numerator2 = ((numerator2-result*D)<<8) | ((L>>16)&0xff); + result = (numerator2 / D); /* shifted 16 right!! */ + lowseconds |= result << 16; + + numerator2 = ((numerator2-result*D)<<8) | ((L>>8)&0xff); + result = (numerator2 / D); /* shifted 8 right!! */ + lowseconds |= result << 8; + + numerator2 = ((numerator2-result*D)<<8) | (L&0xff); + result = (numerator2 / D); /* not shifted */ + lowseconds |= result; + + return lowseconds; +} + +/* Convert the Unix UTC into NT UTC */ +ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t) +{ + return ((t + (ntfs_time64_t)(369*365+89)*24*3600) * 10000000); +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/util.h linux/fs/ntfs/util.h --- v2.1.73/linux/fs/ntfs/util.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ntfs/util.h Fri Dec 19 15:24:21 1997 @@ -0,0 +1,63 @@ +/* + * util.h + * Header file for util.c + * + * Copyright (C) 1997 Régis Duchesne + */ + +/* Which character set is used for file names */ +/* Translate everything to UTF-8 */ +#define nct_utf8 1 +/* Translate to 8859-1 */ +#define nct_iso8859_1 2 +/* Quote unprintables with : */ +#define nct_uni_xlate 4 +/* Do that in the vfat way instead of the documented way */ +#define nct_uni_xlate_vfat 8 +/* Use a mapping table to determine printables */ +#define nct_map 16 + +/* The first 11 inodes correspond to special files */ +#define FILE_MFT 0 +#define FILE_MFTMIRR 1 +#define FILE_LOGFILE 2 +#define FILE_VOLUME 3 +#define FILE_ATTRDEF 4 +#define FILE_ROOT 5 +#define FILE_BITMAP 6 +#define FILE_BOOT 7 +#define FILE_BADCLUS 8 +#define FILE_QUOTA 9 +#define FILE_UPCASE 10 + +/* Memory management */ +void *ntfs_calloc(int size); + +/* String operations */ +/* Copy Unicode <-> ASCII */ +#if 0 +void ntfs_uni2ascii(char *to,char *from,int len); +#endif +void ntfs_ascii2uni(short int *to,char *from,int len); +/* Comparison */ +int ntfs_uni_strncmp(short int* a,short int *b,int n); +int ntfs_ua_strncmp(short int* a,char* b,int n); + +/* Same address space copies */ +void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n); +void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n); + +/* Charset conversion */ +int ntfs_encodeuni(ntfs_volume *vol,ntfs_u16 *in, int in_len,char **out, int *out_len); +int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len, ntfs_u16 **out, int *out_len); + +/* Time conversion */ +/* NT <-> Unix */ +ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc); +ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t); + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --recursive --new-file v2.1.73/linux/fs/select.c linux/fs/select.c --- v2.1.73/linux/fs/select.c Wed Dec 10 11:12:45 1997 +++ linux/fs/select.c Fri Dec 19 15:52:10 1997 @@ -197,7 +197,6 @@ free_page((unsigned long) wait_table.entry); } out_nowait: - current->timeout = 0; unlock_kernel(); return retval; } diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c --- v2.1.73/linux/fs/smbfs/dir.c Fri Dec 19 15:53:01 1997 +++ linux/fs/smbfs/dir.c Fri Dec 19 10:53:29 1997 @@ -74,40 +74,6 @@ return -EISDIR; } -/* - * Check whether a dentry already exists for the given name, - * and return the inode number if it has an inode. This is - * needed to keep getcwd() working. - */ -static ino_t -find_inode_number(struct dentry *dir, struct qstr *name) -{ - struct dentry * dentry; - ino_t ino = 0; - - /* - * Check for a fs-specific hash function. Note that we must - * calculate the standard hash first, as the d_op->d_hash() - * routine may choose to leave the hash value unchanged. - */ - name->hash = full_name_hash(name->name, name->len); - if (dir->d_op && dir->d_op->d_hash) - { - if (dir->d_op->d_hash(dir, name) != 0) - goto out; - } - - dentry = d_lookup(dir, name); - if (dentry) - { - if (dentry->d_inode) - ino = dentry->d_inode->i_ino; - dput(dentry); - } -out: - return ino; -} - static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { @@ -193,7 +159,7 @@ /* * Note: in order to allow the smbclient process to open the - * mount point, we don't revalidate for the connection pid. + * mount point, we don't revalidate if conn_pid is NULL. */ static int smb_dir_open(struct inode *dir, struct file *file) @@ -218,8 +184,6 @@ if (server->conn_pid) error = smb_revalidate_inode(dir); - else - printk("smb_dir_open: smbclient process\n"); return error; } diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c --- v2.1.73/linux/fs/smbfs/inode.c Fri Dec 19 15:53:01 1997 +++ linux/fs/smbfs/inode.c Fri Dec 19 10:53:29 1997 @@ -364,6 +364,7 @@ kill_proc(server->conn_pid, SIGTERM, 1); kfree(server->mnt); + kfree(sb->u.smbfs_sb.temp_buf); if (server->packet) smb_vfree(server->packet); sb->s_dev = 0; @@ -376,18 +377,16 @@ struct super_block * smb_read_super(struct super_block *sb, void *raw_data, int silent) { - struct smb_mount_data *mnt, *data = (struct smb_mount_data *) raw_data; - struct smb_fattr root; - kdev_t dev = sb->s_dev; + struct smb_mount_data *mnt; struct inode *root_inode; struct dentry *dentry; + struct smb_fattr root; MOD_INC_USE_COUNT; - if (!data) + if (!raw_data) goto out_no_data; - - if (data->version != SMB_MOUNT_VERSION) + if (((struct smb_mount_data *) raw_data)->version != SMB_MOUNT_VERSION) goto out_wrong_data; lock_super(sb); @@ -396,7 +395,6 @@ sb->s_blocksize_bits = 10; sb->s_magic = SMB_SUPER_MAGIC; sb->s_flags = 0; - sb->s_dev = dev; /* shouldn't need this ... */ sb->s_op = &smb_sops; sb->u.smbfs_sb.sock_file = NULL; @@ -410,10 +408,16 @@ if (!sb->u.smbfs_sb.packet) goto out_no_mem; + /* Allocate the global temp buffer */ + sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL); + if (!sb->u.smbfs_sb.temp_buf) + goto out_no_temp; + + /* Allocate the mount data structure */ mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL); if (!mnt) goto out_no_mount; - *mnt = *data; + *mnt = *((struct smb_mount_data *) raw_data); /* ** temp ** pass config flags in file mode */ mnt->version = (mnt->file_mode >> 9); #ifdef CONFIG_SMB_WIN95 @@ -450,15 +454,14 @@ return sb; out_no_root: - printk(KERN_ERR "smb_read_super: get root inode failed\n"); iput(root_inode); kfree(sb->u.smbfs_sb.mnt); out_no_mount: + kfree(sb->u.smbfs_sb.temp_buf); +out_no_temp: smb_vfree(sb->u.smbfs_sb.packet); - goto out_unlock; out_no_mem: - printk("smb_read_super: could not alloc packet\n"); -out_unlock: + printk(KERN_ERR "smb_read_super: allocation failure\n"); unlock_super(sb); goto out_fail; out_wrong_data: diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.1.73/linux/fs/smbfs/proc.c Fri Dec 19 15:53:01 1997 +++ linux/fs/smbfs/proc.c Fri Dec 19 10:53:29 1997 @@ -975,7 +975,7 @@ smb_close_fileid(struct dentry *dentry, __u16 fileid) { struct smb_sb_info *server = server_from_dentry(dentry); - int result = 0; + int result; smb_lock_server(server); result = smb_proc_close(server, fileid, CURRENT_TIME); @@ -1270,6 +1270,9 @@ /* * Note that we are now returning the name as a reference to avoid * an extra copy, and that the upper/lower casing is done in place. + * + * Bugs Noted: + * (1) Pathworks servers may pad the name with extra spaces. */ static __u8 * smb_decode_dirent(struct smb_sb_info *server, __u8 *p, @@ -1442,10 +1445,6 @@ * * Bugs Noted: * (1) Win NT 4.0 appends a null byte to names and counts it in the length! - * (2) When using Info Level 1 Win NT 4.0 truncates directory listings - * for certain patterns of names and/or lengths. The breakage pattern is - * completely reproducible and can be toggled by the addition of a single - * file to the directory. (E.g. echo hi >foo breaks, rm -f foo works.) */ static char * smb_decode_long_dirent(struct smb_sb_info *server, char *p, @@ -1509,24 +1508,29 @@ return result; } +/* + * Bugs Noted: + * (1) When using Info Level 1 Win NT 4.0 truncates directory listings + * for certain patterns of names and/or lengths. The breakage pattern + * is completely reproducible and can be toggled by the creation of a + * single file. (E.g. echo hi >foo breaks, rm -f foo works.) + */ static int smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, void *cachep) { + char *p, *mask, *lastname, *param = server->temp_buf; + __u16 command; + int first, entries, entries_seen; + /* Both NT and OS/2 accept info level 1 (but see note below). */ int info_level = 1; const int max_matches = 512; - char *p, *mask, *lastname; - int first, entries, entries_seen; - unsigned char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; - - __u16 command; - int ff_resume_key = 0; /* this isn't being used */ int ff_searchcount = 0; int ff_eos = 0; @@ -1534,8 +1538,6 @@ int ff_dir_handle = 0; int loop_count = 0; int mask_len, i, result; - - char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */ static struct qstr star = { "*", 1, 0 }; /* @@ -1553,7 +1555,7 @@ /* * Encode the initial path */ - mask = &(param[12]); + mask = param + 12; mask_len = smb_encode_path(server, mask, dir, &star) - mask; first = 1; #ifdef SMBFS_DEBUG_VERBOSE @@ -1643,11 +1645,6 @@ entries = -smb_errno(server); break; } -#ifdef SMBFS_PARANOIA -if (resp_data + resp_data_len > server->packet + server->packet_size) -printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n", -resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size); -#endif /* parse out some important return info */ if (first != 0) @@ -1799,15 +1796,14 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *attr) { - char *p; - int result; + char *p, *param = server->temp_buf; __u16 date, time; int off_date = 0, off_time = 2; unsigned char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; - char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */ + int result; retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ @@ -2023,14 +2019,12 @@ struct dentry *dir, struct smb_fattr *fattr) { __u16 date, time; - char *p; - int result; - + char *p, *param = server->temp_buf; unsigned char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; - char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */ + int result; char data[26]; retry: @@ -2082,7 +2076,8 @@ * Bugs Noted: * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message * with info level 1 (INFO_STANDARD). - * (2) Under the core protocol apparently the only way to set the + * (2) Win 95 seems not to support setting directory timestamps. + * (3) Under the core protocol apparently the only way to set the * timestamp is to open and close the file. */ int @@ -2092,7 +2087,7 @@ struct inode *inode = dentry->d_inode; int result; -#ifdef SMBFS_DEBUG_TIMESTAMP +#ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_settime: setting %s/%s, open=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode)); #endif @@ -2127,16 +2122,6 @@ } } -#if 1 /* temporary */ - if (result) - { -printk("smb_proc_settime: %s/%s failed, open=%d, res=%d, rcls=%d, err=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode), -result, server->rcls, server->err); - /* squash errors for now */ - result = 0; - } -#endif smb_unlock_server(server); return result; } diff -u --recursive --new-file v2.1.73/linux/include/linux/cdk.h linux/include/linux/cdk.h --- v2.1.73/linux/include/linux/cdk.h Mon Mar 17 14:54:34 1997 +++ linux/include/linux/cdk.h Fri Dec 19 12:30:54 1997 @@ -304,6 +304,10 @@ #define P_DTRFOLLOW 0x20 #define P_FAKEDCD 0x40 +#define P_RXIMIN 0x10000 +#define P_RXITIME 0x20000 +#define P_RXTHOLD 0x40000 + /* * Define a structure to communicate serial port signal and data state * information. diff -u --recursive --new-file v2.1.73/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.1.73/linux/include/linux/fs.h Fri Dec 19 15:53:04 1997 +++ linux/include/linux/fs.h Fri Dec 19 15:24:22 1997 @@ -256,6 +256,7 @@ #include #include #include +#include #include #include #include @@ -355,6 +356,7 @@ struct minix_inode_info minix_i; struct ext2_inode_info ext2_i; struct hpfs_inode_info hpfs_i; + struct ntfs_inode_info ntfs_i; struct msdos_inode_info msdos_i; struct umsdos_inode_info umsdos_i; struct iso_inode_info isofs_i; @@ -492,6 +494,7 @@ #include #include #include +#include #include #include #include @@ -526,6 +529,7 @@ struct minix_sb_info minix_sb; struct ext2_sb_info ext2_sb; struct hpfs_sb_info hpfs_sb; + struct ntfs_sb_info ntfs_sb; struct msdos_sb_info msdos_sb; struct isofs_sb_info isofs_sb; struct nfs_sb_info nfs_sb; diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs.h linux/include/linux/ntfs_fs.h --- v2.1.73/linux/include/linux/ntfs_fs.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/ntfs_fs.h Fri Dec 19 15:24:22 1997 @@ -0,0 +1,7 @@ +#ifndef _LINUX_NTFS_FS_H +#define _LINUX_NTFS_FS_H + +int init_ntfs_fs(); + +#endif + diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs_i.h linux/include/linux/ntfs_fs_i.h --- v2.1.73/linux/include/linux/ntfs_fs_i.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/ntfs_fs_i.h Fri Dec 19 15:24:22 1997 @@ -0,0 +1,79 @@ +#ifndef _LINUX_NTFS_FS_I_H +#define _LINUX_NTFS_FS_I_H + +/* Forward declarations, to keep number of mutual includes low */ +struct ntfs_attribute; +struct ntfs_sb_info; + +/* Duplicate definitions from ntfs/types.h */ +#ifndef NTFS_INTEGRAL_TYPES +#define NTFS_INTEGRAL_TYPES +typedef unsigned char ntfs_u8; +typedef unsigned short ntfs_u16; +typedef unsigned int ntfs_u32; +typedef unsigned long long ntfs_u64; +#endif + +#ifndef NTMODE_T +#define NTMODE_T +typedef __kernel_mode_t ntmode_t; +#endif +#ifndef NTFS_UID_T +#define NTFS_UID_T +typedef __kernel_uid_t ntfs_uid_t; +#endif +#ifndef NTFS_GID_T +#define NTFS_GID_T +typedef __kernel_gid_t ntfs_gid_t; +#endif +#ifndef NTFS_SIZE_T +#define NTFS_SIZE_T +typedef __kernel_size_t ntfs_size_t; +#endif +#ifndef NTFS_TIME_T +#define NTFS_TIME_T +typedef __kernel_time_t ntfs_time_t; +#endif + +/* unicode character type */ +#ifndef NTFS_WCHAR_T +#define NTFS_WCHAR_T +typedef unsigned short ntfs_wchar_t; +#endif +/* file offset */ +#ifndef NTFS_OFFSET_T +#define NTFS_OFFSET_T +typedef unsigned long long ntfs_offset_t; +#endif +/* UTC */ +#ifndef NTFS_TIME64_T +#define NTFS_TIME64_T +typedef unsigned long long ntfs_time64_t; +#endif +/* This is really unsigned long long. So we support only volumes up to 2 TB */ +#ifndef NTFS_CLUSTER_T +#define NTFS_CLUSTER_T +typedef unsigned int ntfs_cluster_t; +#endif + +/* Definition of NTFS in-memory inode structure */ +struct ntfs_inode_info{ + struct ntfs_sb_info *vol; + int i_number; /* should be really 48 bits */ + unsigned sequence_number; + unsigned char* attr; /* array of the attributes */ + int attr_count; /* size of attrs[] */ + struct ntfs_attribute *attrs; + int record_count; /* size of records[] */ + /* array of the record numbers of the MFT + whose attributes have been inserted in the inode */ + int *records; + union{ + struct{ + int recordsize; + int clusters_per_record; + }index; + } u; +}; + +#endif diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs_sb.h linux/include/linux/ntfs_fs_sb.h --- v2.1.73/linux/include/linux/ntfs_fs_sb.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/ntfs_fs_sb.h Fri Dec 19 15:24:22 1997 @@ -0,0 +1,44 @@ +#ifndef _LINUX_NTFS_FS_SB_H +#define _LINUX_NTFS_FS_SB_H + +struct ntfs_sb_info{ + /* Configuration provided by user at mount time */ + ntfs_uid_t uid; + ntfs_gid_t gid; + ntmode_t umask; + unsigned int nct; + void *nls_map; + unsigned int ngt; + /* Configuration provided by user with ntfstools */ + ntfs_size_t partition_bias; /* for access to underlying device */ + /* Attribute definitions */ + ntfs_u32 at_standard_information; + ntfs_u32 at_attribute_list; + ntfs_u32 at_file_name; + ntfs_u32 at_security_descriptor; + ntfs_u32 at_data; + ntfs_u32 at_index_root; + ntfs_u32 at_index_allocation; + ntfs_u32 at_bitmap; + ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */ + /* Data read from the boot file */ + int blocksize; + int clusterfactor; + int clustersize; + int mft_recordsize; + int mft_clusters_per_record; + int index_recordsize; + int index_clusters_per_record; + int mft_cluster; + /* data read from special files */ + unsigned char *mft; + unsigned short *upcase; + unsigned int upcase_length; + /* inodes we always hold onto */ + struct ntfs_inode_info *mft_ino; + struct ntfs_inode_info *mftmirr; + struct ntfs_inode_info *bitmap; + struct super_block *sb; +}; + +#endif diff -u --recursive --new-file v2.1.73/linux/include/linux/sc26198.h linux/include/linux/sc26198.h --- v2.1.73/linux/include/linux/sc26198.h Mon Mar 17 14:54:35 1997 +++ linux/include/linux/sc26198.h Fri Dec 19 12:30:54 1997 @@ -28,15 +28,15 @@ /* * Define the number of async ports per sc26198 uart device. */ -#define SC26198_PORTS 8 +#define SC26198_PORTS 8 /* * Baud rate timing clocks. All derived from a master 14.7456 MHz clock. */ -#define SC26198_MASTERCLOCK 14745600L -#define SC26198_DCLK (SC26198_MASTERCLOCK) -#define SC26198_CCLK (SC26198_MASTERCLOCK / 2) -#define SC26198_BCLK (SC26198_MASTERCLOCK / 4) +#define SC26198_MASTERCLOCK 14745600L +#define SC26198_DCLK (SC26198_MASTERCLOCK) +#define SC26198_CCLK (SC26198_MASTERCLOCK / 2) +#define SC26198_BCLK (SC26198_MASTERCLOCK / 4) /* * Define internal FIFO sizes for the 26198 ports. diff -u --recursive --new-file v2.1.73/linux/include/linux/smb_fs_sb.h linux/include/linux/smb_fs_sb.h --- v2.1.73/linux/include/linux/smb_fs_sb.h Sat Nov 1 11:04:27 1997 +++ linux/include/linux/smb_fs_sb.h Fri Dec 19 10:53:29 1997 @@ -24,6 +24,7 @@ struct file * sock_file; struct smb_mount_data *mnt; + unsigned char *temp_buf; /* Connections are counted. Each time a new socket arrives, * generation is incremented. diff -u --recursive --new-file v2.1.73/linux/include/linux/stallion.h linux/include/linux/stallion.h --- v2.1.73/linux/include/linux/stallion.h Mon Mar 17 14:54:35 1997 +++ linux/include/linux/stallion.h Fri Dec 19 12:30:54 1997 @@ -131,6 +131,8 @@ void (*isr)(struct stlbrd *brdp); unsigned int ioaddr1; unsigned int ioaddr2; + unsigned int iosize1; + unsigned int iosize2; unsigned int iostatus; unsigned int ioctrl; unsigned int ioctrlval; diff -u --recursive --new-file v2.1.73/linux/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h --- v2.1.73/linux/include/linux/sunrpc/svcsock.h Thu Jun 12 15:30:33 1997 +++ linux/include/linux/sunrpc/svcsock.h Fri Dec 19 10:54:10 1997 @@ -49,7 +49,7 @@ /* * Function prototypes. */ -int svc_create_socket(struct svc_serv *, int, struct sockaddr_in *); +int svc_makesock(struct svc_serv *, int, unsigned short); void svc_delete_socket(struct svc_sock *); int svc_recv(struct svc_serv *, struct svc_rqst *); int svc_send(struct svc_rqst *); diff -u --recursive --new-file v2.1.73/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c --- v2.1.73/linux/net/ipv4/ip_fw.c Mon Dec 1 12:04:16 1997 +++ linux/net/ipv4/ip_fw.c Fri Dec 19 11:03:31 1997 @@ -152,9 +152,12 @@ struct ip_fw *ip_fw_in_chain; struct ip_fw *ip_fw_out_chain; struct ip_fw *ip_acct_chain; +struct ip_fw *ip_masq_chain; static struct ip_fw **chains[] = - {&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain}; + {&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain, + &ip_masq_chain + }; #endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */ #ifdef CONFIG_IP_FIREWALL diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c --- v2.1.73/linux/net/sunrpc/clnt.c Mon Dec 1 12:04:18 1997 +++ linux/net/sunrpc/clnt.c Fri Dec 19 10:54:10 1997 @@ -753,8 +753,10 @@ rpc_exit(task, error); return NULL; } - if (!(p = rpcauth_checkverf(task, p))) + if (!(p = rpcauth_checkverf(task, p))) { + printk("call_verify: auth check failed\n"); goto garbage; /* bad verifier, retry */ + } switch ((n = ntohl(*p++))) { case RPC_SUCCESS: return p; @@ -768,7 +770,8 @@ garbage: dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid); task->tk_client->cl_stats->rpcgarbage++; - if (0 && task->tk_garb_retry--) { + if (task->tk_garb_retry--) { + printk("RPC: garbage, retrying %4d\n", task->tk_pid); task->tk_action = call_encode; return NULL; } diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c --- v2.1.73/linux/net/sunrpc/sched.c Tue Dec 2 16:45:21 1997 +++ linux/net/sunrpc/sched.c Fri Dec 19 10:54:10 1997 @@ -388,6 +388,8 @@ /* sync task: sleep here */ dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); + if (current->pid == rpciod_pid) + printk("RPC: rpciod waiting on sync task!\n"); current->timeout = 0; sleep_on(&task->tk_wait); @@ -792,29 +794,21 @@ rpciod_killall(void) { unsigned long flags; - sigset_t old_set; - - /* FIXME: What had been going on before was saving and restoring - current->signal. This as opposed to blocking signals? Do we - still need them to wake up out of schedule? In any case it - isn't playing nice and a better way should be found. */ - - spin_lock_irqsave(¤t->sigmask_lock, flags); - old_set = current->blocked; - sigfillset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irqrestore(¤t->sigmask_lock, flags); while (all_tasks) { + current->sigpending = 0; rpc_killall_tasks(NULL); __rpc_schedule(); - current->timeout = jiffies + HZ / 100; - need_resched = 1; - schedule(); + if (all_tasks) { +printk("rpciod_killall: waiting for tasks to exit\n"); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + current->timeout = 0; + } } spin_lock_irqsave(¤t->sigmask_lock, flags); - current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c --- v2.1.73/linux/net/sunrpc/sunrpc_syms.c Mon Dec 1 12:04:18 1997 +++ linux/net/sunrpc/sunrpc_syms.c Fri Dec 19 10:54:10 1997 @@ -62,7 +62,6 @@ /* RPC server stuff */ EXPORT_SYMBOL(svc_create); -EXPORT_SYMBOL(svc_create_socket); EXPORT_SYMBOL(svc_create_thread); EXPORT_SYMBOL(svc_exit_thread); EXPORT_SYMBOL(svc_destroy); @@ -70,6 +69,7 @@ EXPORT_SYMBOL(svc_process); EXPORT_SYMBOL(svc_recv); EXPORT_SYMBOL(svc_wake_up); +EXPORT_SYMBOL(svc_makesock); /* RPC statistics */ #ifdef CONFIG_PROC_FS diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c --- v2.1.73/linux/net/sunrpc/svc.c Fri Dec 19 15:53:06 1997 +++ linux/net/sunrpc/svc.c Fri Dec 19 10:54:10 1997 @@ -172,7 +172,6 @@ { struct svc_program *progp; unsigned long flags; - sigset_t old_set; int i, error = 0, dummy; progp = serv->sv_program; @@ -180,18 +179,8 @@ dprintk("RPC: svc_register(%s, %s, %d)\n", progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); - /* FIXME: What had been going on before was saving and restoring - current->signal. This as opposed to blocking signals? Do we - still need them to wake up out of schedule? In any case it - isn't playing nice and a better way should be found. */ - - if (!port) { - spin_lock_irqsave(¤t->sigmask_lock, flags); - old_set = current->blocked; - sigfillset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irqrestore(¤t->sigmask_lock, flags); - } + if (!port) + current->sigpending = 0; for (i = 0; i < progp->pg_nvers; i++) { if (progp->pg_vers[i] == NULL) @@ -207,7 +196,6 @@ if (!port) { spin_lock_irqsave(¤t->sigmask_lock, flags); - current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -235,7 +223,7 @@ bufp = argp->buf; if (argp->len < 5) - goto dropit; + goto err_short_len; dir = ntohl(*bufp++); vers = ntohl(*bufp++); @@ -244,10 +232,8 @@ svc_putlong(resp, xdr_one); /* REPLY */ svc_putlong(resp, xdr_zero); /* ACCEPT */ - if (dir != 0) { /* direction != CALL */ - serv->sv_stats->rpcbadfmt++; - goto dropit; /* drop request */ - } + if (dir != 0) /* direction != CALL */ + goto err_bad_dir; if (vers != 2) /* RPC version number */ goto err_bad_rpc; @@ -281,7 +267,7 @@ procp = versp->vs_proc + proc; if (proc >= versp->vs_nproc || !procp->pc_func) - goto err_unknown; + goto err_bad_proc; rqstp->rq_server = serv; rqstp->rq_procinfo = procp; @@ -329,21 +315,36 @@ if (procp->pc_release) procp->pc_release(rqstp, NULL, rqstp->rq_resp); - if (procp->pc_encode != NULL) - return svc_send(rqstp); + if (procp->pc_encode == NULL) + goto dropit; +sendit: + return svc_send(rqstp); dropit: dprintk("svc: svc_process dropit\n"); svc_drop(rqstp); return 0; +err_short_len: +#ifdef RPC_PARANOIA + printk("svc: short len %d, dropping request\n", argp->len); +#endif + goto dropit; /* drop request */ + +err_bad_dir: +#ifdef RPC_PARANOIA + printk("svc: bad direction %d, dropping request\n", dir); +#endif + serv->sv_stats->rpcbadfmt++; + goto dropit; /* drop request */ + err_bad_rpc: serv->sv_stats->rpcbadfmt++; resp->buf[-1] = xdr_one; /* REJECT */ svc_putlong(resp, xdr_zero); /* RPC_MISMATCH */ svc_putlong(resp, xdr_two); /* Only RPCv2 supported */ svc_putlong(resp, xdr_two); - goto error; + goto sendit; err_bad_auth: dprintk("svc: authentication failed (%ld)\n", ntohl(auth_stat)); @@ -351,7 +352,7 @@ resp->buf[-1] = xdr_one; /* REJECT */ svc_putlong(resp, xdr_one); /* AUTH_ERROR */ svc_putlong(resp, auth_stat); /* status */ - goto error; + goto sendit; err_bad_prog: #ifdef RPC_PARANOIA @@ -359,7 +360,7 @@ #endif serv->sv_stats->rpcbadfmt++; svc_putlong(resp, rpc_prog_unavail); - goto error; + goto sendit; err_bad_vers: #ifdef RPC_PARANOIA @@ -369,15 +370,15 @@ svc_putlong(resp, rpc_prog_mismatch); svc_putlong(resp, htonl(progp->pg_lovers)); svc_putlong(resp, htonl(progp->pg_hivers)); - goto error; + goto sendit; -err_unknown: +err_bad_proc: #ifdef RPC_PARANOIA printk("svc: unknown procedure (%d)\n", proc); #endif serv->sv_stats->rpcbadfmt++; svc_putlong(resp, rpc_proc_unavail); - goto error; + goto sendit; err_garbage: #ifdef RPC_PARANOIA @@ -385,7 +386,5 @@ #endif serv->sv_stats->rpcbadfmt++; svc_putlong(resp, rpc_garbage_args); - -error: - return svc_send(rqstp); + goto sendit; } diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c --- v2.1.73/linux/net/sunrpc/svcsock.c Tue Dec 2 09:49:41 1997 +++ linux/net/sunrpc/svcsock.c Fri Dec 19 10:54:10 1997 @@ -886,7 +886,7 @@ /* * Create socket for RPC service. */ -int +static int svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin) { struct svc_sock *svsk; @@ -969,3 +969,19 @@ /* svsk->sk_server = NULL; */ } } + +/* + * Make a socket for nfsd and lockd + */ +int +svc_makesock(struct svc_serv *serv, int protocol, unsigned short port) +{ + struct sockaddr_in sin; + + dprintk("svc: creating socket proto = %d\n", protocol); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + return svc_create_socket(serv, protocol, &sin); +} +