VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 31
+SUBLEVEL = 32
all: Version zImage
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
-bool 'SK_G16 support' CONFIG_SK_G16 n
bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
fi
bool 'HP PCLAN support' CONFIG_HPLAN n
bool 'NE2000/NE1000 support' CONFIG_NE2000 y
+ bool 'SK_G16 support' CONFIG_SK_G16 n
fi
bool 'EISA and on board controllers' CONFIG_NET_EISA n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
--- /dev/null
+IDE Performance Enhancements Version 2.0
+============================ ===========
+
+This version of hd.c includes support for two optional features:
+
+(1) The disk I/O routines can now run with interrupts unmasked
+ most of the time, making them much friendlier to high
+ speed serial ports and other system activity.
+
+(2) Support is included for IDE "Multiple Sector Mode", the use
+ of which can reduce disk I/O kernel overhead by 10-30%
+ on many systems, with a corresponding 10-20% increase in
+ data throughput.
+
+By default, both features are DISABLED, for compatibility with
+ systems on which they may cause troubles.
+
+The IRQ unmasking has been known to CORRUPT FILESYSTEMS in the
+past on systems with strange hard drives. Backup before trying!
+
+It works on most systems, but use at your own risk!!
+
+Drives which support "Multiple Sector Mode" are identified by the
+kernel at boot time, and a message is displayed indicating the
+largest possible setting for "MaxMult". I recommend using settings
+of 8, 16, or 32. Many drives also support non-powers of two,
+but many other drives do not -- try strange values at your own risk!
+
+For more detailed boot-time information about your drive, change
+the definition of VERBOSE_DRIVE_INFO from 0 to 1 near the top
+of hd.c and rebuild/reinstall the kernel.
+
+Some drives (mostly older CONNER drives) do not implement multiple mode
+correctly, and data corruption may occur.. but if you wait long enough
+the error recovery logic *should* be able to recover eventually.
+
+To try this out more safely, mount the drive's partitions read-only
+before using hdparm (see below) for the first time. If it doesn't
+work, email me (mlord@bnr.ca) with the drive name as displayed at
+boot time, so I can warn others and possibly add a hook to the code.
+
+To enable the features, a small program is included: hdparm.c
+This one is *different* from previous versions -- be sure to recompile it!
+
+Compile this using cc -O -o /usr/bin/hdparm hdparm.c
+and then use it to enable/disable the new features, as follows:
+
+To turn on 16-sector multiple mode, with interrupt unmasking:
+
+ hdparm /dev/hda 16 1
+
+To view the current settings:
+
+ hdparm /dev/hda
+
+If you have more than one drive, a separate command would need to be issued for the second drive as well, using the same or different
+settings as desired:
+
+ hdparm /dev/hdb 16 1
+
+To turn off both features on the first drive, use:
+
+ hdparm /dev/hda 0 0
+
+To benchmark the performance difference, try:
+
+ hdparm /dev/hda 0 0
+ sync
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+ hdparm /dev/hda 16 1
+ sync
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+ time dd if=/dev/hda of=/dev/null bs=1024k count=30
+
+This gives before and after views. Compare the total elapsed times,
+as well as the percent of CPU used in each case. Run several trials
+to ensure/verify consistent results. Some drives are actually *slower*
+with multiple mode enabled, but those are very rare indeed. Most systems
+experience a 10-30% increase in throughput, with a corresponding 5-50%
+decrease in kernel/system CPU usage.
+
+If you are using linux kernel 1.1.4 or higher (with the "cluster" code),
+then you may not notice a big difference with the above tests. However,
+I have noticed that the iozone benchmark program does seem to work fairly
+reliably under kernels with the "cluster" code, so you could try that
+instead (under older kernels, iozone seems to give wildly varying results
+from trial to trial, at least on my system).
+
+To have your favorite settings installed automatically at boot time,
+place the hdparm command(s) into the /etc/rc.d/rc.local file.
+Alternatively, one could modify the DEFAULTs near the top of hd.c
+and rebuild and install the new kernel.
+
+Enjoy,
+mlord@bnr.ca
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
+ *
+ * IDE IRQ-unmask & drive-id & multiple-mode code added by Mark Lord.
*/
+#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
+#define DEFAULT_UNMASK_INTR 0 /* set to 0 to *NOT* unmask irq's more often */
+#define VERBOSE_DRIVE_INFO 0 /* set to 1 for more drive info at boot time */
+#include <asm/irq.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
outb_p(cmd,++port);
}
+static void hd_request (void);
+unsigned int identified [MAX_HD] = {0,}; /* 1 = drive ID already displayed */
+unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O */
+unsigned int max_mult [MAX_HD] = {0,}; /* max sectors for MultMode */
+unsigned int mult_req [MAX_HD] = {0,}; /* requested MultMode count */
+unsigned int mult_count [MAX_HD] = {0,}; /* currently enabled MultMode count */
+struct request WCURRENT;
+
+static void rawstring (char *prefix, char *s, int n)
+{
+ if (prefix)
+ printk(prefix);
+ if (s && *s) {
+ int i;
+ for (i=0; i < n && s[i^1] == ' '; ++i); /* skip blanks */
+ for (; i < n && s[i^1]; ++i) /* flip bytes */
+ if (s[i^1] != ' ' || ((i+1) < n && s[(i+1)^1] != ' '))
+ printk("%c",s[i^1]);
+ }
+}
+
+#if VERBOSE_DRIVE_INFO
+
+char *cfg_str[] =
+{ "", " HardSect", " SoftSect", " NotMFM", " HdSw>15uSec", " SpinMotCtl",
+ " Fixed", " Removeable", " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs",
+ " RotSpdTol>.5%", " dStbOff", " TrkOff", " FmtGapReq", "",
+};
+
+char *ioready[] = {"no", "?", "yes", "on/off"};
+char *SlowMedFast[] = {"slow", "medium", "fast"};
+char *BuffType[] = {"?", "1Sect", "DualPort", "DualPortCache"};
+
+#define YN(b) (((b)==0)?"no":"yes")
+
+static void dmpstr (char *prefix, unsigned int i, char *s[], unsigned int maxi)
+{
+ printk(prefix);
+ printk( (i > maxi) ? "?" : s[i] );
+}
+
+static void dump_identity (unsigned int dev, unsigned short ib[])
+{
+ int i;
+ char dashes[] = "\n+-------------------------------------------------------------------+\n";
+ printk (dashes);
+ printk ("hd%c: Drive Identification Info:\n", dev+'a');
+ rawstring (" Model=",(char *)&ib[27],40);
+ rawstring (", FwRev=",(char *)&ib[23],8);
+ rawstring (", SerialNo=",(char *)&ib[10],20);
+ printk ("\n Config={");
+ for (i=0; i<=15; i++) if (ib[0] & (1<<i)) printk (cfg_str[i]);
+ printk (" }\n");
+ printk (" Default c/h/s=%d/%d/%d, TrkSize=%d, SectSize=%d, ECCbytes=%d\n",
+ ib[1],ib[3],ib[6],ib[4],ib[5], ib[22]);
+ dmpstr (" BuffType=",ib[20],BuffType,3);
+ ib[47] &= 0xFF;
+ printk (", BuffSize=%dKB, MaxMultSect=%d\n", ib[21]/2, ib[47]);
+ printk (" Features: DblWordIO=%s, IORDY=%s, LBA=%s, DMA=%s",
+ YN(ib[48]&1),ioready[(ib[49]&0xC00)>>10],YN(ib[49]&0x200),YN(ib[49]&0x100));
+ dmpstr (", tPIO=",ib[51]>>8,SlowMedFast,2);
+ if (ib[49]&0x100 && (ib[53]&1))
+ dmpstr (", tDMA=",ib[52]>>8,SlowMedFast,2);
+ printk ("\n (%s): Current c/h/s=%d/%d/%d, TotSect=%d",
+ (((ib[53]&1)==0)?"maybe":"valid"),
+ ib[54],ib[55],ib[56],*(int *)&ib[57]);
+ if (ib[49]&0x200)
+ printk (", MaxLBAsect=%d", *(int *)&ib[60]);
+ printk("\n CurMultSect=%d%s",ib[59]&0xFF,(ib[59]&0x100)?"":"?");
+ if (ib[49]&0x100)
+ printk (", DMA-1w=%04X, DMA-mw=%04X", ib[62], ib[63]);
+ printk ("%s\n",dashes);
+}
+#endif /* VERBOSE_DRIVE_INFO */
+
+static void identify_intr(void)
+{
+ unsigned int dev = DEVICE_NR(CURRENT->dev);
+ unsigned short ib[64], stat = inb_p(HD_STATUS);
+
+ if (unmask_intr[dev])
+ sti();
+ if (stat & (BUSY_STAT|ERR_STAT))
+ printk (" hd%c: multiple mode not supported\n", dev+'a');
+ else {
+ insw(HD_DATA,(char *)ib,64); /* get first 128 ID bytes */
+#if VERBOSE_DRIVE_INFO
+ dump_identity(dev, ib);
+#endif
+ printk (" hd%c: ", dev+'a');
+ rawstring(NULL, (char *)&ib[27], 40);
+ max_mult[dev] = ib[47] & 0xff;
+ printk (" (%dMB IDE w/%dKB Cache, MaxMult=%d)\n",
+ ib[1]*ib[3]*ib[6] / 2048, ib[21]>>1, max_mult[dev]);
+ insw(HD_DATA,(char *)ib,64); /* flush remaining 384 ID bytes */
+ insw(HD_DATA,(char *)ib,64);
+ insw(HD_DATA,(char *)ib,64);
+ }
+ hd_request();
+ return;
+}
+
+static void set_multmode_intr(void)
+{
+ unsigned int dev = DEVICE_NR(CURRENT->dev), stat = inb_p(HD_STATUS);
+
+ if (unmask_intr[dev])
+ sti();
+ if (stat & (BUSY_STAT|ERR_STAT)) {
+ mult_req[dev] = mult_count[dev] = 0;
+ printk (" hd%c: set multiple mode failed\n", dev+'a');
+ } else {
+ if ((mult_count[dev] = mult_req[dev]))
+ printk (" hd%c: enabled %d-sector multiple mode\n",
+ dev+'a', mult_count[dev]);
+ else
+ printk (" hd%c: disabled multiple mode\n", dev+'a');
+ }
+ hd_request();
+ return;
+}
+
static int drive_busy(void)
{
unsigned int i;
if (reset)
goto repeat;
}
- i++;
- if (i < NR_HD) {
+ if (++i < NR_HD) {
+ if (unmask_intr[i]) {
+ printk("hd%c: disabled irq-unmasking\n",i+'a');
+ unmask_intr[i] = 0;
+ }
+ if (mult_req[i] || mult_count[i]) {
+ printk("hd%c: disabled multiple mode\n",i+'a');
+ mult_req[i] = mult_count[i] = 0;
+ }
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
if (reset)
goto repeat;
} else
- do_hd_request();
+ hd_request();
}
/*
static void read_intr(void)
{
- int i;
- int retries = 100000;
+ unsigned int dev = DEVICE_NR(CURRENT->dev);
+ int i, retries = 100000, msect, nsect;
+ if (unmask_intr[dev])
+ sti(); /* permit other IRQs during xfer */
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
goto ok_to_read;
} while (--retries > 0);
sti();
- printk("HD: read_intr: status = 0x%02x\n",i);
+ printk("hd%c: read_intr: status = 0x%02x\n",dev+'a',i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
- printk("HD: read_intr: error = 0x%02x\n",hd_error);
+ printk("hd%c: read_intr: error = 0x%02x\n",dev+'a',hd_error);
}
bad_rw_intr();
cli();
- do_hd_request();
+ hd_request();
return;
ok_to_read:
- insw(HD_DATA,CURRENT->buffer,256);
+ msect = mult_count[dev];
+read_next:
+ if (msect) {
+ if ((nsect = CURRENT->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ insw(HD_DATA,CURRENT->buffer,nsect<<8);
+ CURRENT->sector += nsect;
+ CURRENT->buffer += nsect<<9;
CURRENT->errors = 0;
- CURRENT->buffer += 512;
- CURRENT->sector++;
- i = --CURRENT->nr_sectors;
- --CURRENT->current_nr_sectors;
+ i = (CURRENT->nr_sectors -= nsect);
+
#ifdef DEBUG
- printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
- MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT->
- buffer);
+ printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=%08lx\n",
+ dev+'a', CURRENT->sector, CURRENT->sector+nsect,
+ CURRENT->nr_sectors, (long) CURRENT->buffer+(nsect<<9));
#endif
- if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+ if ((CURRENT->current_nr_sectors -= nsect) <= 0)
end_request(1);
if (i > 0) {
+ if (msect)
+ goto read_next;
SET_INTR(&read_intr);
- sti();
return;
}
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
- do_hd_request();
+ if (CURRENT)
+ hd_request();
return;
}
+static inline void multwrite (unsigned int dev)
+{
+ unsigned int mcount = mult_count[dev];
+
+ while (mcount--) {
+ outsw(HD_DATA,WCURRENT.buffer,256);
+ if (!--WCURRENT.nr_sectors)
+ return;
+ WCURRENT.buffer += 512;
+ if (!--WCURRENT.current_nr_sectors) {
+ WCURRENT.bh = WCURRENT.bh->b_reqnext;
+ if (WCURRENT.bh == NULL)
+ panic("buffer list corrupted\n");
+ WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
+ WCURRENT.buffer = WCURRENT.bh->b_data;
+ }
+ }
+}
+
+static void multwrite_intr(void)
+{
+ int i;
+ unsigned int dev = DEVICE_NR(WCURRENT.dev);
+
+ if (unmask_intr[dev])
+ sti();
+ if (((i = inb_p(HD_STATUS)) & STAT_MASK) == STAT_OK) {
+ if (i & DRQ_STAT) {
+ if (WCURRENT.nr_sectors) {
+ multwrite(dev);
+ SET_INTR(&multwrite_intr);
+ return;
+ }
+ } else {
+ if (!WCURRENT.nr_sectors) { /* all done? */
+ for (i = CURRENT->nr_sectors; i > 0;){
+ i -= CURRENT->current_nr_sectors;
+ end_request(1);
+ }
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ if (CURRENT)
+ hd_request();
+ return;
+ }
+ }
+ }
+ sti();
+ printk("hd%c: multwrite_intr: status = 0x%02x\n",dev+'a',i);
+ if (i & ERR_STAT) {
+ hd_error = (unsigned) inb(HD_ERROR);
+ printk("hd:%c multwrite_intr: error = 0x%02x\n",dev+'a',hd_error);
+ }
+ bad_rw_intr();
+ cli();
+ hd_request();
+}
+
static void write_intr(void)
{
int i;
}
bad_rw_intr();
cli();
- do_hd_request();
+ hd_request();
return;
ok_to_write:
CURRENT->sector++;
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
- do_hd_request();
+ hd_request();
}
return;
}
{
if (win_result())
bad_rw_intr();
- do_hd_request();
+ hd_request();
}
/*
end_request(0);
}
- do_hd_request();
+ hd_request();
}
/*
* worst that can happen is that an unexpected HD-interrupt comes in and
* sets the "reset" variable and starts the timer)
*/
-static void do_hd_request(void)
+static void hd_request(void)
{
unsigned int block,dev;
unsigned int sec,head,cyl,track;
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
#ifdef DEBUG
- printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
+ printk("hd : attempted read for sector %d past end of device at sector %d.\n",
block, hd[dev].nr_sects);
#endif
end_request(0);
head = track % hd_info[dev].head;
cyl = track / hd_info[dev].head;
#ifdef DEBUG
- printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
- dev, cyl, head, sec, CURRENT->buffer);
+ printk("hd%c : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
+ dev+'a', cyl, head, sec, CURRENT->buffer);
#endif
- cli();
+ if (!unmask_intr[dev])
+ cli();
if (reset) {
int i;
for (i=0; i < NR_HD; i++)
recalibrate[i] = 1;
+ cli(); /* better play it safe, as resets are the last resort */
reset_hd();
- sti();
return;
}
if (recalibrate[dev]) {
hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
if (reset)
goto repeat;
- sti();
return;
}
- if (CURRENT->cmd == WRITE) {
- hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+ if (!identified[dev]) {
+ identified[dev] = 1;
+ unmask_intr[dev] = DEFAULT_UNMASK_INTR;
+ mult_req[dev] = DEFAULT_MULT_COUNT;
+ hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
if (reset)
goto repeat;
- if (wait_DRQ()) {
- printk("HD: do_hd_request: no DRQ\n");
- bad_rw_intr();
+ return;
+ }
+ if (mult_req[dev] != mult_count[dev]) {
+ hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
+ if (reset)
goto repeat;
- }
- outsw(HD_DATA,CURRENT->buffer,256);
- sti();
return;
}
if (CURRENT->cmd == READ) {
- hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
+ unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
+ hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
if (reset)
goto repeat;
- sti();
+#ifdef DEBUG
+ printk("hd%c: reading %d sectors(%ld-%ld), buffer=%08lx\n",
+ dev+'a', nsect, CURRENT->sector,
+ CURRENT->sector+nsect-1, (long) CURRENT->buffer);
+#endif
+ return;
+ }
+ if (CURRENT->cmd == WRITE) {
+ if (mult_count[dev])
+ hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
+ else
+ hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+ if (reset)
+ goto repeat;
+#ifdef DEBUG
+ printk("hd%c: writing %d sectors(%ld-%ld), buffer=%08lx\n",
+ dev+'a', nsect, CURRENT->sector,
+ CURRENT->sector+nsect-1, (long) CURRENT->buffer);
+#endif
+ if (wait_DRQ()) {
+ printk("hd%c: hd_request: no DRQ\n", dev+'a');
+ bad_rw_intr();
+ goto repeat;
+ }
+ if (mult_count[dev]) {
+ WCURRENT = *CURRENT;
+ multwrite(dev);
+ } else {
+ outsw(HD_DATA,CURRENT->buffer,256);
+ }
return;
}
panic("unknown hd-command");
}
+static void do_hd_request (void)
+{
+ disable_irq(HD_IRQ);
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
+
+ case HDIO_SETUNMASKINTR:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ unmask_intr[dev] = get_fs_long((long *) arg);
+ return 0;
+
+ case HDIO_GETUNMASKINTR:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ put_fs_long(unmask_intr[dev], (long *) arg);
+ return 0;
+
+ case HDIO_GETMULTCOUNT:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ put_fs_long(mult_count[dev], (long *) arg);
+ return 0;
+
+ case HDIO_SETMULTCOUNT:
+ {
+ unsigned long flags;
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ arg = get_fs_long((long *) arg);
+ save_flags(flags);
+ cli(); /* a prior request might still be in progress */
+ if (arg > max_mult[dev])
+ err = -EINVAL; /* out of range for device */
+ else if (mult_req[dev] != mult_count[dev])
+ err = -EBUSY; /* busy, try again */
+ else {
+ mult_req[dev] = arg;
+ err = 0;
+ }
+ restore_flags(flags);
+ return err;
+ }
+
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
int set_selection(const int arg);
int paste_selection(struct tty_struct *tty);
static void clear_selection(void);
+static void highlight_pointer(const int currcons, const int where);
/* Variables for selection control. */
#define SEL_BUFFER_SIZE 4096
return;
lock = 1;
kbdsave(new_console);
+#ifdef CONFIG_SELECTION
+ highlight_pointer(fg_console,-1);
+#endif /* CONFIG_SELECTION */
get_scrmem(fg_console);
fg_console = new_console;
set_scrmem(fg_console);
*p = (*p & 0x88) | ((*p << 4) & 0x70) | ((*p >> 4) & 0x07);
}
-/* is c in range [a-zA-Z0-9_]? */
-static inline int inword(const char c) { return (isalnum(c) || c == '_'); }
+/* use complementary color to show the pointer */
+static void highlight_pointer(const int currcons, const int where)
+{
+ unsigned char *p;
+ static char *prev=NULL;
+
+ if (where==-1) /* remove the pointer */
+ {
+ if (prev)
+ {
+ *prev ^= 0x77;
+ prev=NULL;
+ }
+ }
+ else
+ {
+ p = (unsigned char *)origin - hwscroll_offset + where + 1;
+ *p ^= 0x77;
+ if (prev) *prev ^= 0x77; /* remove the previous one */
+ prev=p;
+ }
+}
+
+
+/*
+ * This function uses a 128-bit look up table
+ */
+static unsigned long inwordLut[4]={
+ 0x00000000, /* control chars */
+ 0x03FF0000, /* digits */
+ 0x87FFFFFE, /* uppercase and '_' */
+ 0x07FFFFFE /* lowercase */
+};
+static inline int inword(const char c) {
+ return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
+}
+
+/* set inwordLut conntents. Invoked by ioctl(). */
+int sel_loadlut(const int arg)
+{
+ memcpy_fromfs(inwordLut,(unsigned long *)(arg+4),16);
+ return 0;
+}
/* does screen address p correspond to character at LH/RH edge of screen? */
static inline int atedge(const int p)
switch (sel_mode)
{
case 0: /* character-by-character selection */
- default:
new_sel_start = ps;
new_sel_end = pe;
break;
new_sel_end = pe + video_size_row
- pe % video_size_row - 2;
break;
+ case 3: /* pointer highlight */
+ if (sel_cons != currcons)
+ {
+ highlight_pointer(sel_cons,-1);
+ clear_selection();
+ sel_cons = currcons;
+ }
+ highlight_pointer(sel_cons,pe);
+ return 0; /* nothing more */
+ default:
+ return -EINVAL;
}
/* select to end of line if on trailing space */
if (new_sel_end > new_sel_start &&
the selection. */
static void clear_selection()
{
+ highlight_pointer(sel_cons, -1); /* hide the pointer */
if (sel_start != -1)
{
highlight(sel_cons, sel_start, sel_end);
static int want_console = -1;
static int last_console = 0; /* last used VC */
static int dead_key_next = 0;
-static int shift_state = 0;
+/*
+ * In order to retrieve the shift_state (for the mouse server), either
+ * the variable must be global, or a new procedure must be create to
+ * return the value. I chose the former way.
+ */
+/*static*/ int shift_state = 0;
static int npadch = -1; /* -1 or number assembled on pad */
static unsigned char diacr = 0;
static char rep = 0; /* flag telling character repeat */
#ifdef CONFIG_FTAPE
/* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
- printk( "ftape: allocated %d buffers alligned at: %p\n",
+ printk( "ftape: allocated %d buffers aligned at: %p\n",
NR_FTAPE_BUFFERS, ftape_big_buffer);
mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
#endif
#ifdef CONFIG_SELECTION
extern int set_selection(const int arg);
extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const int arg);
+extern int shift_state;
#endif /* CONFIG_SELECTION */
extern int do_screendump(int arg);
case 4:
unblank_screen();
return 0;
+ case 5:
+ return sel_loadlut(arg);
+ case 6:
+ put_fs_byte(shift_state,arg);
+ return 0;
#endif /* CONFIG_SELECTION */
default:
return -EINVAL;
extern int elplus_probe(struct device *);
extern int ac3200_probe(struct device *);
extern int e2100_probe(struct device *);
-extern int SK_init(struct device *dev);
+extern int ni52_probe(struct device *);
+extern int ni65_probe(struct device *);
+extern int SK_init(struct device *);
/* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
extern int atp_init(struct device *);
#endif
#if defined(CONFIG_SK_G16)
&& SK_init(dev)
+#endif
+#ifdef CONFIG_NI52
+ && ni52_probe(dev)
+#endif
+#ifdef CONFIG_NI65
+ && ni65_probe(dev)
#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
dev->mtu = PPP_MTU;
dev->hard_start_xmit = ppp_xmit;
dev->open = ppp_dev_open;
- dev->do_ioctl = ppp_dev_ioctl;
dev->stop = ppp_dev_close;
dev->get_stats = ppp_get_stats;
dev->hard_header = ppp_header;
#ifdef NET02D
dev->add_arp = ppp_add_arp;
dev->queue_xmit = dev_queue_xmit;
+#else
+ dev->do_ioctl = ppp_dev_ioctl;
#endif
for (i = 0; i < DEV_NUMBUFFS; i++)
return 0;
}
+#ifndef NET02D
static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr)
{
struct ppp *ppp = &ppp_ctrl[dev->base_addr];
return error;
}
+#endif
/*************************************************************
* TTY OUTPUT
/* If the device is still up then enable the transmitter of the
next frame. */
if (((struct ppp *) ppp)->dev->flags & IFF_UP)
+#ifndef NET02D
+ mark_bh (NET_BH);
+#else
dev_tint (((struct ppp *) ppp)->dev);
+#endif
/* enable any blocked process pending transmission */
wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
}
/* receive the frame through the network software */
- while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0)
- ;
-
+ (void) dev_rint (c, count, 0, ppp->dev);
return 1;
}
#
#
-VERSION = 2.90
+VERSION = 2.90-2
TARGET_OS = linux
.c.s:
there are some new features required by a popular
application. In addition there is also support
for the GUS MAX and the 16 bit sampling option of GUS.
- Also the Windows Sound System stuff is there but may not
- work yet (may work with some WSS compatible cards).
+
+ The MSS/WSS support works now. At least with SG NX Pro 16.
********* IMPORTANT *****************************************
Linux 1.0 or later is required to by this driver version.
Markus Aroharju and
Risto Kankkunen Major contributions to the mixer support
of GUS v3.7.
+ Hunyue Yau Mixer support for SG NX Pro.
Marc Hoffman PSS support.
Regards,
int
probe_ms_sound (struct address_info *hw_config)
{
- int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
-
if ((INB (hw_config->io_base + 3) & 0x04) == 0)
return 0; /* WSS ID test failed */
if (hw_config->irq > 11)
- return;
+ return 0;
- if (hw_config->dma > 3 || hw_config->dma == 2)
- return;
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ return 0;
return ad1848_detect (hw_config->io_base + 4);
}
long
attach_ms_sound (long mem_start, struct address_info *hw_config)
{
- static unsigned char interrupt_bits[11] =
+ static unsigned char interrupt_bits[12] =
{-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
char bits;
+ static unsigned char dma_bits[4] = {1, 2, 0, 3};
+
int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
if (!ad1848_detect (hw_config->io_base + 4))
if ((INB (version_port) & 0x40) == 0)
printk ("[IRQ?]");
- OUTB (bits | hw_config->dma, config_port); /* Write IRQ+DMA setup */
+ OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
ad1848_init ("MS Sound System", hw_config->io_base + 4,
hw_config->irq,
}
}
+ if (selected_options & B (OPT_SBPRO))
+ {
+ fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
+ if (think_positively (0))
+ printf("#define __SGNXPRO__\n");
+ }
+
if (selected_options & B (OPT_SB16))
selected_options |= B (OPT_SBPRO);
"if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
"then you must include the LD in the kernel.\n"
"(do you wish to include a LD) ? ");
- if (think_positively (1))
+ if (think_positively (0))
{
char path[512];
#endif
#ifndef EXCLUDE_MSS
{SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE},
+# ifdef MSS2_BASE
+ {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE},
+# endif
#endif
#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
if (!(dmap->flags & DMA_ALLOC_DONE))
reorganize_buffers (dev);
- if (dmap->dma_mode)
+ if (!dmap->dma_mode)
{
int err;
/*
* sound/sb_dsp.c
*
- * The low level driver for the SoundBlaster DSP chip.
+ * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
*
- * Copyright by Hannu Savolainen 1993
+ * Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support Sound Galaxy NX Pro
+ *
*/
#include "sound_config.h"
sb_dsp_init (long mem_start, struct address_info *hw_config)
{
int i;
+ int mixer_type = 0;
sbc_major = sbc_minor = 0;
sb_dsp_command (0xe1); /*
#ifndef EXCLUDE_SBPRO
if (sbc_major >= 3)
- sb_mixer_init (sbc_major);
+ mixer_type = sb_mixer_init (sbc_major);
#endif
#ifndef EXCLUDE_YM8312
if (sbc_major >= 3)
{
#ifndef SCO
- sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+# ifdef __SGNXPRO__
+ if (mixer_type == 2)
+ {
+ sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+ }
+ else
+# endif
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+ }
#endif
}
else
*
* The low level mixer driver for the SoundBlaster Pro and SB16 cards.
*
- * Copyright by Hannu Savolainen 1993
+ * Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support the Sound Galaxy NX Pro mixer.
+ *
*/
#include "sound_config.h"
| (mode ? STEREO_DAC : MONO_DAC)));
}
+/*
+ * Returns:
+ * 0 No mixer detected.
+ * 1 Only a plain Sound Blaster Pro style mixer detected.
+ * 2 The Sound Galaxy NX Pro mixer detected.
+ */
static int
detect_mixer (void)
{
+#ifdef __SGNXPRO__
+ int oldbass, oldtreble;
+
+#endif
+ int retcode = 1;
+
/*
* Detect the mixer by changing parameters of two volume channels. If the
* values read back match with the values written, the mixer is there (is
if (sb_getmixer (VOC_VOL) != 0x33)
return 0;
- return 1;
+#ifdef __SGNXPRO__
+ /* Attempt to detect the SG NX Pro by check for valid bass/treble
+ * registers.
+ */
+ oldbass = sb_getmixer (BASS_LVL);
+ oldtreble = sb_getmixer (TREBLE_LVL);
+
+ sb_setmixer (BASS_LVL, 0xaa);
+ sb_setmixer (TREBLE_LVL, 0x55);
+
+ if ((sb_getmixer (BASS_LVL) != 0xaa) ||
+ (sb_getmixer (TREBLE_LVL) != 0x55))
+ {
+ retcode = 1; /* 1 == Only SB Pro detected */
+ }
+ else
+ retcode = 2; /* 2 == SG NX Pro detected */
+ /* Restore register in either case since SG NX Pro has EEPROM with
+ * 'preferred' values stored.
+ */
+ sb_setmixer (BASS_LVL, oldbass);
+ sb_setmixer (TREBLE_LVL, oldtreble);
+#endif
+ return retcode;
}
static void
set_recmask (SOUND_MASK_MIC);
}
-void
+/*
+ * Returns a code depending on whether a SG NX Pro was detected.
+ * 1 == Plain SB Pro
+ * 2 == SG NX Pro detected.
+ * 3 == SB16
+ *
+ * Used to update message.
+ */
+int
sb_mixer_init (int major_model)
{
- sb_setmixer (0x00, 0); /*
- * Reset mixer
- */
+ int mixer_type = 0;
- if (!detect_mixer ())
- return; /*
- * No mixer. Why?
- */
+ sb_setmixer (0x00, 0); /* Reset mixer */
+
+ if (!(mixer_type = detect_mixer ()))
+ return 0; /* No mixer. Why? */
mixer_initialized = 1;
mixer_model = major_model;
{
case 3:
mixer_caps = SOUND_CAP_EXCL_INPUT;
- supported_devices = SBPRO_MIXER_DEVICES;
- supported_rec_devices = SBPRO_RECORDING_DEVICES;
- iomap = &sbpro_mix;
+#ifdef __SGNXPRO__
+ if (mixer_type == 2) /* A SGNXPRO was detected */
+ {
+ supported_devices = SGNXPRO_MIXER_DEVICES;
+ supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+ iomap = &sgnxpro_mix;
+ }
+ else
+#endif
+ {
+ supported_devices = SBPRO_MIXER_DEVICES;
+ supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ iomap = &sbpro_mix;
+ mixer_type = 1;
+ }
break;
case 4:
supported_devices = SB16_MIXER_DEVICES;
supported_rec_devices = SB16_RECORDING_DEVICES;
iomap = &sb16_mix;
+ mixer_type = 3;
break;
default:
printk ("SB Warning: Unsupported mixer type\n");
- return;
+ return 0;
}
if (num_mixers < MAX_MIXER_DEV)
mixer_devs[num_mixers++] = &sb_mixer_operations;
sb_mixer_reset ();
+ return mixer_type;
}
#endif
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added defines for the Sound Galaxy NX Pro mixer.
*
*/
+
#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+ SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD)
#define IRQ_STAT 0x82
#define OPSW 0x3c
+/*
+ * Additional registers on the SG NX Pro
+ */
+#define COVOX_VOL 0x42
+#define TREBLE_LVL 0x44
+#define BASS_LVL 0x46
+
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
+#ifdef __SGNXPRO__
+mixer_tab sgnxpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+
mixer_tab sb16_mix = {
MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
void sb_setmixer (unsigned int port, unsigned int value);
int sb_getmixer (unsigned int port);
void sb_mixer_set_stereo(int mode);
-void sb_mixer_init(int major_model);
+int sb_mixer_init(int major_model);
/* From opl3.c */
int opl3_detect (int ioaddr);
* In v3.0 it's /dev/sndproc but this could be a temporary solution.
*/
-#define SND_NDEVS 64 /* Number of supported devices */
+#define SND_NDEVS 256 /* Number of supported devices */
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
synthesizer and MIDI output) */
#define ON 1
#define OFF 0
-#define MAX_AUDIO_DEV 4
+#define MAX_AUDIO_DEV 5
#define MAX_MIXER_DEV 2
#define MAX_SYNTH_DEV 3
#define MAX_MIDI_DEV 6
#define WIN_DIAGNOSE 0x90
#define WIN_SPECIFY 0x91
+#define WIN_MULTREAD 0xC4 /* read multiple sectors */
+#define WIN_MULTWRITE 0xC5 /* write multiple sectors */
+#define WIN_SETMULT 0xC6 /* enable read multiple */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+
/* Bits for HD_ERROR */
#define MARK_ERR 0x01 /* Bad address mark */
#define TRK0_ERR 0x02 /* couldn't find track 0 */
unsigned short cylinders;
unsigned long start;
};
+#define HDIO_GETUNMASKINTR 0x302
+#define HDIO_SETUNMASKINTR 0x303
+#define HDIO_GETMULTCOUNT 0x304
+#define HDIO_SETMULTCOUNT 0x305
+#define HDIO_SETFEATURE 0x306
#endif
extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask);
extern int zeromap_page_range(unsigned long from, unsigned long size, int mask);
-extern void do_wp_page(unsigned long error_code, unsigned long address,
- struct task_struct *tsk);
-extern void do_no_page(unsigned long error_code, unsigned long address,
- struct task_struct *tsk);
+extern void do_wp_page(struct vm_area_struct * vma, unsigned long address,
+ unsigned long error_code);
+extern void do_no_page(struct vm_area_struct * vma, unsigned long address,
+ unsigned long error_code);
extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
extern void mem_init(unsigned long low_start_mem,
#define __WCLONE 0x80000000
+#ifdef __KERNEL__
+
struct wait_queue {
struct task_struct * task;
struct wait_queue * next;
#define __MAX_SELECT_TABLE_ENTRIES (4096 / sizeof (struct select_table_entry))
+#endif /* __KERNEL__ */
+
#endif
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
- *
- * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
- * zero. This routine shouldn't have to change when we make a better mm.
*/
-static unsigned long get_long(struct task_struct * tsk,
- unsigned long addr)
+static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
{
unsigned long page;
repeat:
- page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
+ page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
if (page & PAGE_PRESENT) {
page &= PAGE_MASK;
page += PAGE_PTR(addr);
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
- do_no_page(0,addr,tsk);
+ do_no_page(vma, addr, 0);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
-static void put_long(struct task_struct * tsk, unsigned long addr,
+static void put_long(struct vm_area_struct * vma, unsigned long addr,
unsigned long data)
{
unsigned long page, pte = 0;
int readonly = 0;
repeat:
- page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
+ page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
if (page & PAGE_PRESENT) {
page &= PAGE_MASK;
page += PAGE_PTR(addr);
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
- do_no_page(0 /* PAGE_RW */ ,addr,tsk);
+ do_no_page(vma, addr, 0 /* PAGE_RW */);
goto repeat;
}
if (!(page & PAGE_RW)) {
- if(!(page & PAGE_COW))
+ if (!(page & PAGE_COW))
readonly = 1;
- do_wp_page(PAGE_RW | PAGE_PRESENT,addr,tsk);
+ do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
page &= PAGE_MASK;
page += addr & ~PAGE_MASK;
*(unsigned long *) page = data;
- if(readonly) {
+ if (readonly) {
*(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
invalidate();
}
}
+static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr)
+{
+ struct vm_area_struct * vma;
+
+ addr &= PAGE_MASK;
+ for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
+ if (!vma)
+ return NULL;
+ if (vma->vm_end > addr)
+ break;
+ }
+ if (vma->vm_start <= addr)
+ return vma;
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ return NULL;
+ if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
+ return NULL;
+ vma->vm_offset -= vma->vm_start - addr;
+ vma->vm_start = addr;
+ return vma;
+}
+
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
static int read_long(struct task_struct * tsk, unsigned long addr,
unsigned long * result)
{
- unsigned long low,high;
+ struct vm_area_struct * vma = find_vma(tsk, addr);
- if (addr > TASK_SIZE-sizeof(long))
+ if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
- low = get_long(tsk,addr & ~(sizeof(long)-1));
- high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+ unsigned long low,high;
+ struct vm_area_struct * vma_high = vma;
+
+ if (addr + sizeof(long) >= vma->vm_end) {
+ vma_high = vma->vm_next;
+ if (!vma_high || vma_high->vm_start != vma->vm_end)
+ return -EIO;
+ }
+ low = get_long(vma, addr & ~(sizeof(long)-1));
+ high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 1:
low >>= 8;
}
*result = low;
} else
- *result = get_long(tsk,addr);
+ *result = get_long(vma, addr);
return 0;
}
static int write_long(struct task_struct * tsk, unsigned long addr,
unsigned long data)
{
- unsigned long low,high;
+ struct vm_area_struct * vma = find_vma(tsk, addr);
- if (addr > TASK_SIZE-sizeof(long))
+ if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
- low = get_long(tsk,addr & ~(sizeof(long)-1));
- high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+ unsigned long low,high;
+ struct vm_area_struct * vma_high = vma;
+
+ if (addr + sizeof(long) >= vma->vm_end) {
+ vma_high = vma->vm_next;
+ if (!vma_high || vma_high->vm_start != vma->vm_end)
+ return -EIO;
+ }
+ low = get_long(vma, addr & ~(sizeof(long)-1));
+ high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
low = data;
high |= data >> 8;
break;
}
- put_long(tsk,addr & ~(sizeof(long)-1),low);
- put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high);
+ put_long(vma, addr & ~(sizeof(long)-1),low);
+ put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
- put_long(tsk,addr,data);
+ put_long(vma, addr, data);
return 0;
}
unsigned long itimer_ticks = 0;
unsigned long itimer_next = ~0;
-static unsigned long lost_ticks = 0;
/*
* 'schedule()' is the scheduler function. It's a very simple and nice
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
-static struct timer_list * next_timer = NULL;
+/*
+ * The head for the timer-list has a "expires" field of MAX_UINT,
+ * and the sorting routine counts on this..
+ */
+static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
+#define SLOW_BUT_DEBUGGING_TIMERS 1
void add_timer(struct timer_list * timer)
{
unsigned long flags;
- struct timer_list ** p;
+ struct timer_list *p;
- if (!timer)
+#if SLOW_BUT_DEBUGGING_TIMERS
+ if (timer->next || timer->prev) {
+ printk("add_timer() called with non-zero list from %08lx\n",
+ ((unsigned long *) &timer)[-1]);
return;
- timer->next = NULL;
- p = &next_timer;
+ }
+#endif
+ p = &timer_head;
+ timer->expires += jiffies;
save_flags(flags);
cli();
- while (*p) {
- if ((*p)->expires > timer->expires) {
- (*p)->expires -= timer->expires;
- timer->next = *p;
- break;
- }
- timer->expires -= (*p)->expires;
- p = &(*p)->next;
- }
- *p = timer;
+ do {
+ p = p->next;
+ } while (timer->expires > p->expires);
+ timer->next = p;
+ timer->prev = p->prev;
+ p->prev = timer;
+ timer->prev->next = timer;
restore_flags(flags);
}
int del_timer(struct timer_list * timer)
{
unsigned long flags;
- unsigned long expires = 0;
- struct timer_list **p;
+#if SLOW_BUT_DEBUGGING_TIMERS
+ struct timer_list * p;
- p = &next_timer;
+ p = &timer_head;
save_flags(flags);
cli();
- while (*p) {
- if (*p == timer) {
- if ((*p = timer->next) != NULL)
- (*p)->expires += timer->expires;
- timer->expires += expires;
+ while ((p = p->next) != &timer_head) {
+ if (p == timer) {
+ timer->next->prev = timer->prev;
+ timer->prev->next = timer->next;
+ timer->next = timer->prev = NULL;
restore_flags(flags);
+ timer->expires -= jiffies;
return 1;
}
- expires += (*p)->expires;
- p = &(*p)->next;
}
+ if (p->next || p->prev)
+ printk("del_timer() called with timer not initialized\n");
restore_flags(flags);
return 0;
+#else
+ save_flags(flags);
+ cli();
+ if (timer->next) {
+ timer->next->prev = timer->prev;
+ timer->prev->next = timer->next;
+ timer->next = timer->prev = NULL;
+ restore_flags(flags);
+ timer->expires -= jiffies;
+ return 1;
+ }
+ restore_flags(flags);
+ return 0;
+#endif
}
unsigned long timer_active = 0;
{
unsigned long mask;
struct timer_struct *tp;
+ struct timer_list * timer;
cli();
- while (next_timer && next_timer->expires == 0) {
- void (*fn)(unsigned long) = next_timer->function;
- unsigned long data = next_timer->data;
- next_timer = next_timer->next;
+ while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) {
+ void (*fn)(unsigned long) = timer->function;
+ unsigned long data = timer->data;
+ timer->next->prev = timer->prev;
+ timer->prev->next = timer->next;
+ timer->next = timer->prev = NULL;
sti();
fn(data);
cli();
itimer_ticks++;
if (itimer_ticks > itimer_next)
need_resched = 1;
- if (next_timer) {
- if (next_timer->expires) {
- next_timer->expires--;
- if (!next_timer->expires)
- mark_bh(TIMER_BH);
- } else {
- lost_ticks++;
- mark_bh(TIMER_BH);
- }
- }
+ if (timer_head.next->expires < jiffies)
+ mark_bh(TIMER_BH);
if (tq_timer != &tq_last)
mark_bh(TQUEUE_BH);
sti();
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
*
- * Note that we do many checks twice (look at do_wp_page()), as
- * we have to be careful about race-conditions.
- *
* Goto-purists beware: the only reason for goto's here is that it results
* in better assembly code.. The "default" path will see no jumps at all.
*/
-static void __do_wp_page(unsigned long error_code, unsigned long address,
- struct task_struct * tsk)
+void do_wp_page(struct vm_area_struct * vma, unsigned long address,
+ unsigned long error_code)
{
unsigned long *pde, pte, old_page, prot;
unsigned long new_page;
new_page = __get_free_page(GFP_KERNEL);
- pde = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
+ pde = PAGE_DIR_OFFSET(vma->vm_task->tss.cr3,address);
pte = *pde;
if (!(pte & PAGE_PRESENT))
goto end_wp_page;
goto bad_wp_page;
if (old_page & PAGE_RW)
goto end_wp_page;
- tsk->mm->min_flt++;
+ vma->vm_task->mm->min_flt++;
prot = (old_page & ~PAGE_MASK) | PAGE_RW;
old_page &= PAGE_MASK;
if (mem_map[MAP_NR(old_page)] != 1) {
if (new_page) {
if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
- ++tsk->mm->rss;
+ ++vma->vm_task->mm->rss;
copy_page(old_page,new_page);
*(unsigned long *) pte = new_page | prot;
free_page(old_page);
return;
}
free_page(old_page);
- oom(tsk);
+ oom(vma->vm_task);
*(unsigned long *) pte = BAD_PAGE | prot;
invalidate();
return;
bad_wp_page:
printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
*(unsigned long *) pte = BAD_PAGE | PAGE_SHARED;
- send_sig(SIGKILL, tsk, 1);
+ send_sig(SIGKILL, vma->vm_task, 1);
goto end_wp_page;
bad_wp_pagetable:
printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte);
*pde = BAD_PAGETABLE | PAGE_TABLE;
- send_sig(SIGKILL, tsk, 1);
+ send_sig(SIGKILL, vma->vm_task, 1);
end_wp_page:
if (new_page)
free_page(new_page);
}
/*
- * check that a page table change is actually needed, and call
- * the low-level function only in that case..
+ * Ugly, ugly, but the goto's result in better assembly..
*/
-void do_wp_page(unsigned long error_code, unsigned long address,
- struct task_struct * tsk)
-{
- unsigned long page;
- unsigned long * pg_table;
-
- pg_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
- page = *pg_table;
- if (!page)
- return;
- if ((page & PAGE_PRESENT) && page < high_memory) {
- pg_table = (unsigned long *) ((page & PAGE_MASK) + PAGE_PTR(address));
- page = *pg_table;
- if (!(page & PAGE_PRESENT))
- return;
- if (page & PAGE_RW)
- return;
- if (!(page & PAGE_COW)) {
- if ((error_code & PAGE_USER) && tsk == current) {
- current->tss.cr2 = address;
- current->tss.error_code = error_code;
- current->tss.trap_no = 14;
- send_sig(SIGSEGV, tsk, 1);
- return;
- }
- }
- if (mem_map[MAP_NR(page)] == 1) {
- *pg_table |= PAGE_RW | PAGE_DIRTY;
- invalidate();
- return;
- }
- __do_wp_page(error_code, address, tsk);
- return;
- }
- printk("bad page directory entry %08lx\n",page);
- *pg_table = 0;
-}
-
-static int __verify_write(unsigned long start, unsigned long size)
-{
- size--;
- size += start & ~PAGE_MASK;
- size >>= PAGE_SHIFT;
- start &= PAGE_MASK;
- do {
- do_wp_page(1,start,current);
- start += PAGE_SIZE;
- } while (size--);
- return 0;
-}
-
int verify_area(int type, const void * addr, unsigned long size)
{
struct vm_area_struct * vma;
+ unsigned long start = (unsigned long) addr;
/* If the current user space is mapped to kernel space (for the
* case where we use a fake user buffer with get_fs/set_fs()) we
for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
goto bad_area;
- if (vma->vm_end > (unsigned long) addr)
+ if (vma->vm_end > start)
break;
}
- if (vma->vm_start <= (unsigned long) addr)
+ if (vma->vm_start <= start)
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (vma->vm_end - (unsigned long) addr > current->rlim[RLIMIT_STACK].rlim_cur)
+ if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur)
goto bad_area;
+
good_area:
- while (vma->vm_end - (unsigned long) addr < size) {
- struct vm_area_struct * next = vma->vm_next;
- if (!next)
+ if (!wp_works_ok && type == VERIFY_WRITE)
+ goto check_wp_fault_by_hand;
+ for (;;) {
+ struct vm_area_struct * next;
+ if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
goto bad_area;
- if (vma->vm_end != next->vm_start)
+ if (vma->vm_end - start >= size)
+ return 0;
+ next = vma->vm_next;
+ if (!next || vma->vm_end != next->vm_start)
goto bad_area;
vma = next;
}
- if (wp_works_ok || type == VERIFY_READ || !size)
- return 0;
- return __verify_write((unsigned long) addr,size);
+
+check_wp_fault_by_hand:
+ size--;
+ size += start & ~PAGE_MASK;
+ size >>= PAGE_SHIFT;
+ start &= PAGE_MASK;
+
+ for (;;) {
+ if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
+ goto bad_area;
+ do_wp_page(vma, start, PAGE_PRESENT);
+ if (!size)
+ return 0;
+ size--;
+ start += PAGE_SIZE;
+ if (start < vma->vm_end)
+ continue;
+ vma = vma->vm_next;
+ if (!vma || vma->vm_start != start)
+ break;
+ }
+
bad_area:
return -EFAULT;
}
return 0;
}
-static void handle_no_page(struct vm_area_struct * vma,
- unsigned long address, unsigned long error_code)
+void do_no_page(struct vm_area_struct * vma, unsigned long address,
+ unsigned long error_code)
{
- unsigned long page;
- int prot;
+ unsigned long page, entry, prot;
+
+ page = get_empty_pgtable(vma->vm_task,address);
+ if (!page)
+ return;
+ page &= PAGE_MASK;
+ page += PAGE_PTR(address);
+ entry = *(unsigned long *) page;
+ if (entry & PAGE_PRESENT)
+ return;
+ if (entry) {
+ ++vma->vm_task->mm->rss;
+ ++vma->vm_task->mm->maj_flt;
+ swap_in((unsigned long *) page);
+ return;
+ }
+ address &= PAGE_MASK;
+ if (!vma->vm_ops || !vma->vm_ops->nopage) {
+ ++vma->vm_task->mm->rss;
+ ++vma->vm_task->mm->min_flt;
+ get_empty_page(vma->vm_task,address);
+ return;
+ }
page = get_free_page(GFP_KERNEL);
if (share_page(vma, address, error_code, page)) {
++vma->vm_task->mm->min_flt;
++vma->vm_task->mm->maj_flt;
++vma->vm_task->mm->rss;
page = vma->vm_ops->nopage(vma, address, page, error_code);
- if (share_page(vma, address, error_code, 0))
+ if (share_page(vma, address, error_code, 0)) {
+ free_page(page);
return;
+ }
prot = vma->vm_page_prot;
if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1)
prot &= ~PAGE_RW;
oom(current);
}
-void do_no_page(unsigned long error_code, unsigned long address,
- struct task_struct *tsk)
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
- unsigned long page, tmp;
struct vm_area_struct * vma;
+ unsigned long address;
+ unsigned long page;
- page = get_empty_pgtable(tsk,address);
- if (!page)
- return;
- page &= PAGE_MASK;
- page += PAGE_PTR(address);
- tmp = *(unsigned long *) page;
- if (tmp & PAGE_PRESENT)
- return;
- if (tmp) {
- ++tsk->mm->rss;
- ++tsk->mm->maj_flt;
- swap_in((unsigned long *) page);
- return;
- }
- address &= PAGE_MASK;
- for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
+ /* get the address */
+ __asm__("movl %%cr2,%0":"=r" (address));
+ for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
goto bad_area;
if (vma->vm_end > address)
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (vma->vm_end - address > tsk->rlim[RLIMIT_STACK].rlim_cur)
+ if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur)
goto bad_area;
- vma->vm_offset -= vma->vm_start - address;
- vma->vm_start = address;
-
+ vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+ vma->vm_start = (address & PAGE_MASK);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
good_area:
- if (!vma->vm_ops || !vma->vm_ops->nopage) {
- ++tsk->mm->rss;
- ++tsk->mm->min_flt;
- get_empty_page(tsk,address);
+ if (regs->eflags & VM_MASK) {
+ unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
+ if (bit < 32)
+ current->screen_bitmap |= 1 << bit;
+ }
+ if (error_code & PAGE_PRESENT) {
+ if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT)
+ goto bad_area;
+#ifdef CONFIG_TEST_VERIFY_AREA
+ if (regs->cs == KERNEL_CS)
+ printk("WP fault at %08x\n", regs->eip);
+#endif
+ do_wp_page(vma, address, error_code);
return;
}
- handle_no_page(vma, address, error_code);
+ if (!(vma->vm_page_prot & PAGE_PRESENT))
+ goto bad_area;
+ do_no_page(vma, address, error_code);
return;
-bad_area:
- if (tsk != current)
- goto kernel_needs_bad_page;
- tsk->tss.cr2 = address;
- tsk->tss.error_code = error_code;
- tsk->tss.trap_no = 14;
- send_sig(SIGSEGV,tsk,1);
- if (error_code & 4) /* user level access? */
- return;
-
-kernel_needs_bad_page:
- ++tsk->mm->rss;
- ++tsk->mm->min_flt;
- get_empty_page(tsk,address);
-}
-
/*
- * This routine handles page faults. It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
*/
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
-{
- unsigned long address;
- unsigned long page;
- unsigned int bit;
-
- /* get the address */
- __asm__("movl %%cr2,%0":"=r" (address));
- if (address < TASK_SIZE) {
- if (regs->eflags & VM_MASK) {
- bit = (address - 0xA0000) >> PAGE_SHIFT;
- if (bit < 32)
- current->screen_bitmap |= 1 << bit;
- }
- if (error_code & PAGE_PRESENT) {
-#ifdef CONFIG_TEST_VERIFY_AREA
- if (regs->cs == KERNEL_CS)
- printk("WP fault at %08x\n", regs->eip);
-#endif
- do_wp_page(error_code, address, current);
- } else {
- do_no_page(error_code, address, current);
- }
+bad_area:
+ if (error_code & PAGE_USER) {
+ current->tss.cr2 = address;
+ current->tss.error_code = error_code;
+ current->tss.trap_no = 14;
+ send_sig(SIGSEGV, current, 1);
return;
}
- address -= TASK_SIZE;
- if (wp_works_ok < 0 && address == 0 && (error_code & PAGE_PRESENT)) {
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+ if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_PRESENT)) {
wp_works_ok = 1;
pg0[0] = PAGE_SHARED;
printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
return;
}
- if (address < PAGE_SIZE) {
+ if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) {
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
pg0[0] = PAGE_SHARED;
} else
printk(KERN_ALERT "Unable to handle kernel paging request");
- printk(" at kernel address %08lx\n",address);
- address += TASK_SIZE;
+ printk(" at virtual address %08lx\n",address);
__asm__("movl %%cr3,%0" : "=r" (page));
printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n",
current->tss.cr3, page);
sk->timeout = 0;
sk->broadcast = 0;
sk->localroute = 0;
+ sk->timer.next = sk->timer.prev = NULL;
sk->timer.data = (unsigned long)sk;
sk->timer.function = &net_timer;
skb_queue_head_init(&sk->back_log);
skb_queue_head_init(&entry->skb);
entry->next = arp_tables[hash];
arp_tables[hash] = entry;
-
+ entry->timer.next = entry->timer.prev = NULL;
sti();
}
entry->next = arp_tables[hash];
entry->dev = dev;
arp_tables[hash] = entry;
+ entry->timer.next = entry->timer.prev = NULL;
entry->timer.function = arp_expire_request;
entry->timer.data = (unsigned long)entry;
entry->timer.expires = ARP_RES_TIME;
entry->hlen = hlen;
entry->htype = htype;
entry->next = arp_tables[hash];
+ entry->timer.next = entry->timer.prev = NULL;
arp_tables[hash] = entry;
skb_queue_head_init(&entry->skb);
}
void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
unsigned long flags;
+ int nitcount;
+ struct packet_type *ptype;
int where = 0; /* used to say if the packet should go */
/* at the front or the back of the */
/* queue - front is a retranmsit try */
}
restore_flags(flags);
+ /* copy outgoing packets to any sniffer packet handlers */
+ for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) {
+ if (ptype->type == htons(ETH_P_ALL)) {
+ struct sk_buff *skb2;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+ ptype->func(skb2, skb->dev, ptype);
+ nitcount--;
+ }
+ }
+
if (dev->hard_start_xmit(skb, dev) == 0) {
/*
* Packet is now solely the responsibility of the driver
save_flags(flags);
cli();
skb = sk->partial;
- if (skb)
- {
+ if (skb) {
sk->partial = NULL;
del_timer(&sk->partial_timer);
}
if (tmp)
del_timer(&sk->partial_timer);
sk->partial = skb;
+ sk->partial_timer.next = sk->partial_timer.prev = NULL;
sk->partial_timer.expires = HZ;
sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
sk->partial_timer.data = (unsigned long) sk;
newsk->urg_data = 0;
newsk->retransmits = 0;
newsk->destroy = 0;
+ newsk->timer.next = newsk->timer.prev = NULL;
newsk->timer.data = (unsigned long)newsk;
newsk->timer.function = &net_timer;
newsk->dummy_th.source = skb->h.th->dest;
* XXX if retransmit count reaches limit, is tcp_close()
* called with timeout == 1 ? if not, we need to fix that.
*/
+ if (!timeout) {
+ int timer_active;
+
+ timer_active = del_timer(&sk->timer);
+ if (timer_active)
+ add_timer(&sk->timer);
+ else
+ reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
+ }
#ifdef NOTDEF
/*
* Start a timer.