summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-03 01:22:27 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-03 01:22:27 +0000
commitf9bbe9da79dbc8557c74efeb158b431cd67ace52 (patch)
tree3220d014a35f9d88a48668a1468524e988daebff /drivers
parent3d697109c1ff85ef563aec3d5e113ef225ed2792 (diff)
Upgrade to 2.1.73.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/Config.in24
-rw-r--r--drivers/block/Makefile18
-rw-r--r--drivers/block/ali14xx.c1
-rw-r--r--drivers/block/cmd640.c1
-rw-r--r--drivers/block/dtc2278.c1
-rw-r--r--drivers/block/ht6560b.c2
-rw-r--r--drivers/block/ide-cd.c93
-rw-r--r--drivers/block/ide-cd.h10
-rw-r--r--drivers/block/ide-disk.c65
-rw-r--r--drivers/block/ide-dma.c473
-rw-r--r--drivers/block/ide-floppy.c113
-rw-r--r--drivers/block/ide-pci.c382
-rw-r--r--drivers/block/ide-probe.c107
-rw-r--r--drivers/block/ide-proc.c505
-rw-r--r--drivers/block/ide-tape.c141
-rw-r--r--drivers/block/ide.c252
-rw-r--r--drivers/block/ide.h93
-rw-r--r--drivers/block/ll_rw_blk.c26
-rw-r--r--drivers/block/ns87415.c228
-rw-r--r--drivers/block/opti621.c28
-rw-r--r--drivers/block/pdc4030.c8
-rw-r--r--drivers/block/qd6580.c1
-rw-r--r--drivers/block/rz1000.c43
-rw-r--r--drivers/block/trm290.c181
-rw-r--r--drivers/block/umc8672.c1
-rw-r--r--drivers/char/pc_keyb.c25
-rw-r--r--drivers/char/pc_keyb.h6
-rw-r--r--drivers/char/pcwd.c7
-rw-r--r--drivers/net/3c505.c8
-rw-r--r--drivers/net/3c59x.c1223
-rw-r--r--drivers/net/Config.in1
-rw-r--r--drivers/net/eth16i.c2
-rw-r--r--drivers/net/hamradio/Config.in1
-rw-r--r--drivers/net/hamradio/dmascc.c1260
-rw-r--r--drivers/net/ipddp.c3
-rw-r--r--drivers/pci/pci.c22
-rw-r--r--drivers/scsi/ide-scsi.c51
-rw-r--r--drivers/scsi/ide-scsi.h3
-rw-r--r--drivers/scsi/st.c1
-rw-r--r--drivers/sgi/Makefile22
-rw-r--r--drivers/sgi/char/Makefile18
-rw-r--r--drivers/sgi/char/cons_newport.c613
-rw-r--r--drivers/sgi/char/gconsole.h33
-rw-r--r--drivers/sgi/char/graphics.c327
-rw-r--r--drivers/sgi/char/graphics.h28
-rw-r--r--drivers/sgi/char/linux_logo.h909
-rw-r--r--drivers/sgi/char/newport.c217
-rw-r--r--drivers/sgi/char/newport.h587
-rw-r--r--drivers/sgi/char/rrm.c69
-rw-r--r--drivers/sgi/char/sgicons.c183
-rw-r--r--drivers/sgi/char/sgiserial.c2019
-rw-r--r--drivers/sgi/char/sgiserial.h444
-rw-r--r--drivers/sgi/char/shmiq.c460
-rw-r--r--drivers/sgi/char/streamable.c363
-rw-r--r--drivers/sgi/char/usema.c191
-rw-r--r--drivers/sgi/char/vga_font.c346
-rw-r--r--drivers/sound/lowlevel/awe_wave.c2
57 files changed, 11048 insertions, 1193 deletions
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index 220148670..b76e9af08 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -23,26 +23,28 @@ else
fi
if [ "$CONFIG_PCI" = "y" ]; then
bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
- bool ' PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
- if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
+ bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+ if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Tekram TRM290 DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
- bool ' OPTi 82C621 enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
+ bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
+ bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
+ bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
fi
fi
fi
bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
comment 'Note: most of these also require special kernel boot parameters'
- bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
- bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
- bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
- bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+ bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+ bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+ bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+ bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
fi
- bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
- bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672
fi
fi
fi
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 50e9f6be6..f992a98a5 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -79,11 +79,17 @@ L_OBJS += hd.o
endif
ifeq ($(CONFIG_BLK_DEV_IDE),y)
-LX_OBJS += ide.o
-L_OBJS += ide-probe.o
+ LX_OBJS += ide.o
+ ifeq ($(CONFIG_PROC_FS),y)
+ L_OBJS += ide-proc.o
+ endif
+ L_OBJS += ide-probe.o
else
ifeq ($(CONFIG_BLK_DEV_IDE),m)
MX_OBJS += ide.o
+ ifeq ($(CONFIG_PROC_FS),y)
+ M_OBJS += ide-proc.o
+ endif
M_OBJS += ide-probe.o
endif
endif
@@ -96,6 +102,10 @@ ifeq ($(CONFIG_BLK_DEV_CMD640),y)
L_OBJS += cmd640.o
endif
+ifeq ($(CONFIG_BLK_DEV_IDEPCI),y)
+L_OBJS += ide-pci.o
+endif
+
ifeq ($(CONFIG_BLK_DEV_IDEDMA),y)
L_OBJS += ide-dma.o
endif
@@ -137,6 +147,10 @@ ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
L_OBJS += opti621.o
endif
+ifeq ($(CONFIG_BLK_DEV_NS87415),y)
+L_OBJS += ns87415.o
+endif
+
ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
L_OBJS += ide-disk.o
else
diff --git a/drivers/block/ali14xx.c b/drivers/block/ali14xx.c
index 5f6c0f437..c0c7762d6 100644
--- a/drivers/block/ali14xx.c
+++ b/drivers/block/ali14xx.c
@@ -213,6 +213,7 @@ void init_ali14xx (void)
ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
ide_hwifs[0].mate = &ide_hwifs[1];
ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
/* initialize controller registers */
if (!initRegisters()) {
diff --git a/drivers/block/cmd640.c b/drivers/block/cmd640.c
index e833a2bd0..5ffbefaf0 100644
--- a/drivers/block/cmd640.c
+++ b/drivers/block/cmd640.c
@@ -797,6 +797,7 @@ int ide_probe_for_cmd640x (void)
cmd_hwif1->chipset = ide_cmd640;
cmd_hwif0->mate = cmd_hwif1;
cmd_hwif1->mate = cmd_hwif0;
+ cmd_hwif1->channel = 1;
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
cmd_hwif1->tuneproc = &cmd640_tune_drive;
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
diff --git a/drivers/block/dtc2278.c b/drivers/block/dtc2278.c
index 30988bd99..63b9143b6 100644
--- a/drivers/block/dtc2278.c
+++ b/drivers/block/dtc2278.c
@@ -127,4 +127,5 @@ void init_dtc2278 (void)
ide_hwifs[1].drives[1].no_unmask = 1;
ide_hwifs[0].mate = &ide_hwifs[1];
ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
}
diff --git a/drivers/block/ht6560b.c b/drivers/block/ht6560b.c
index 9d888cfc0..b739f39ee 100644
--- a/drivers/block/ht6560b.c
+++ b/drivers/block/ht6560b.c
@@ -155,7 +155,6 @@ static void ht6560b_selectproc (ide_drive_t *drive)
printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, t, timing);
#endif
}
- OUT_BYTE(drive->select.all,IDE_SELECT_REG);
}
/*
@@ -229,6 +228,7 @@ void init_ht6560b (void)
ide_hwifs[1].serialized = 1;
ide_hwifs[0].mate = &ide_hwifs[1];
ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
} else
printk("\nht6560b: not found\n");
}
diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c
index 02f97ec6f..3a8e48722 100644
--- a/drivers/block/ide-cd.c
+++ b/drivers/block/ide-cd.c
@@ -2,7 +2,7 @@
/*
* linux/drivers/block/ide-cd.c
* Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
- * Copyright (C) 1996, 1997 Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1996-1998 Erik Andersen <andersee@debian.org>
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
@@ -27,13 +27,6 @@
* unless you have a patch to fix it. I am working on it...)
* -Implement ide_cdrom_select_speed using the generic cdrom interface
* -Fix ide_cdrom_reset so that it works (it does nothing right now)
- * -When trying to mount a cdrom with the tray open, you get an billion
- * "tray open or drive not ready" messages until the tray gets closed.
- * This is because ide-cd does not properly return drive_status immediatly,
- * but instead waits for the drive to close first (bad, bad, bad)
- * and keeps on trying, and failing... This bug was revealed by the
- * recent changes to the Uniform CD-ROm driver, and I havn't had a
- * chance to fix it yet.
*
* MOSTLY DONE LIST:
* Query the drive to find what features are available
@@ -185,8 +178,13 @@
* Added identifier so new Sanyo CD-changer works
* Better detection if door locking isn't supported
*
+ * 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml
+ * 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open"
+ *
*************************************************************************/
+#define IDECD_VERSION "4.07"
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -194,11 +192,8 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/malloc.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/cdrom.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -418,7 +413,8 @@ static void cdrom_end_request (int uptodate, ide_drive_t *drive)
(struct packet_command *)
pc->sense_data);
}
-
+ if (rq->cmd == READ && !rq->current_nr_sectors)
+ uptodate = 1;
ide_end_request (uptodate, HWGROUP(drive));
}
@@ -443,7 +439,7 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
sense_key = err >> 4;
if (rq == NULL)
- printk ("%s : missing request in cdrom_decode_status\n",
+ printk ("%s: missing request in cdrom_decode_status\n",
drive->name);
else {
cmd = rq->cmd;
@@ -477,16 +473,13 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
because workman constantly polls the drive
with this command, and we don't want
to uselessly fill up the syslog. */
- if (pc->c[0] != SCMD_READ_SUBCHANNEL) {
+ if (pc->c[0] != SCMD_READ_SUBCHANNEL)
printk ("%s: tray open or drive not ready\n",
drive->name);
- return 1;
- }
} else if (sense_key == UNIT_ATTENTION) {
/* Check for media change. */
cdrom_saw_media_change (drive);
- printk (" %s: media changed\n", drive->name);
- return 0;
+ printk ("%s: media changed\n", drive->name);
} else {
/* Otherwise, print an error. */
ide_dump_status (drive, "packet command error",
@@ -521,7 +514,7 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
cdrom_saw_media_change (drive);
/* Fail the request. */
- printk ("%s : tray open\n", drive->name);
+ printk ("%s: tray open\n", drive->name);
cdrom_end_request (0, drive);
} else if (sense_key == UNIT_ATTENTION) {
/* Media change. */
@@ -591,7 +584,6 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
if (info->dma)
(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
-
if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
ide_set_handler (drive, handler, WAIT_CMD);
OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
@@ -619,7 +611,8 @@ static int cdrom_transfer_packet_command (ide_drive_t *drive,
int stat_dum;
/* Check for errors. */
- if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
+ if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum))
+ return 1;
} else {
/* Otherwise, we must wait for DRQ to get set. */
if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
@@ -746,12 +739,12 @@ static void cdrom_read_intr (ide_drive_t *drive)
/* Check for errors. */
if (dma) {
info->dma = 0;
- if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive)))
+ if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive)))
HWIF(drive)->dmaproc(ide_dma_off, drive);
- (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
}
- if (cdrom_decode_status (drive, 0, &stat)) return;
+ if (cdrom_decode_status (drive, 0, &stat))
+ return;
if (dma) {
if (!dma_error) {
@@ -764,7 +757,6 @@ static void cdrom_read_intr (ide_drive_t *drive)
return;
}
-
/* Read the interrupt reason and the transfer length. */
ireason = IN_BYTE (IDE_NSECTOR_REG);
len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
@@ -779,7 +771,6 @@ static void cdrom_read_intr (ide_drive_t *drive)
cdrom_end_request (0, drive);
} else
cdrom_end_request (1, drive);
-
return;
}
@@ -990,7 +981,8 @@ static void cdrom_seek_intr (ide_drive_t *drive)
int stat;
static int retry = 10;
- if (cdrom_decode_status (drive, 0, &stat)) return;
+ if (cdrom_decode_status (drive, 0, &stat))
+ return;
CDROM_CONFIG_FLAGS(drive)->seeking = 1;
if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
@@ -1088,7 +1080,8 @@ static void cdrom_pc_intr (ide_drive_t *drive)
struct packet_command *pc = (struct packet_command *)rq->buffer;
/* Check for errors. */
- if (cdrom_decode_status (drive, 0, &stat)) return;
+ if (cdrom_decode_status (drive, 0, &stat))
+ return;
/* Read the interrupt reason and the transfer length. */
ireason = IN_BYTE (IDE_NSECTOR_REG);
@@ -1251,8 +1244,11 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
ide_init_drive_cmd (&req);
req.cmd = PACKET_COMMAND;
req.buffer = (char *)pc;
- (void) ide_do_drive_cmd (drive, &req, ide_wait);
-
+ if (ide_do_drive_cmd (drive, &req, ide_wait)) {
+ printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
+ drive->name, req.buffer[0], req.buffer[1]);
+ /* FIXME: we should probably abort/retry or something */
+ }
if (pc->stat != 0) {
/* The request failed. Retry if it was due to a unit
attention status
@@ -2692,7 +2688,7 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots)
static
int ide_cdrom_probe_capabilities (ide_drive_t *drive)
{
- int stat, nslots;
+ int stat, nslots, attempts = 3;
struct {
char pad[8];
struct atapi_capabilities_page cap;
@@ -2703,14 +2699,15 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
if (CDROM_CONFIG_FLAGS (drive)->nec260)
return nslots;
- stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
- (char *)&buf, sizeof (buf), NULL);
- if (stat)
- return nslots;
+ do { /* we seem to get stat=0x01,err=0x00 the first time (??) */
+ if (attempts-- <= 0)
+ return 0;
+ stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+ (char *)&buf, sizeof (buf), NULL);
+ } while (stat);
if (buf.cap.lock == 0)
CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-
if (buf.cap.cd_r_write)
CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
if (buf.cap.cd_rw_write)
@@ -2737,11 +2734,15 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
}
}
- CDROM_STATE_FLAGS (drive)->curent_speed = ntohs(buf.cap.curspeed)/176;
- CDROM_CONFIG_FLAGS (drive)->max_speed = ntohs(buf.cap.maxspeed)/176;
+ 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 */
+ 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;
+ }
- stat=0;
- printk ("%s: ATAPI %dx CDROM",
+ printk ("%s: ATAPI %dX CDROM",
drive->name, CDROM_CONFIG_FLAGS (drive)->max_speed);
if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw)
printk (" CD%s%s",
@@ -2827,7 +2828,7 @@ int ide_cdrom_setup (ide_drive_t *drive)
else if (strcmp (drive->id->model,
"NEC CD-ROM DRIVE:260") == 0 &&
- strcmp (drive->id->fw_rev, "1.01") == 0) {
+ strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
/* Old NEC260 (not R).
This drive was released before the 1.2 version
of the spec. */
@@ -2838,7 +2839,7 @@ int ide_cdrom_setup (ide_drive_t *drive)
}
else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
- strcmp (drive->id->fw_rev, "A1.1") == 0) {
+ strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
/* Wearnes */
CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
@@ -2936,21 +2937,23 @@ static ide_module_t ide_cdrom_module = {
};
static ide_driver_t ide_cdrom_driver = {
+ "ide-cdrom", /* name */
+ IDECD_VERSION, /* version */
ide_cdrom, /* media */
0, /* busy */
1, /* supports_dma */
1, /* supports_dsc_overlap */
ide_cdrom_cleanup, /* cleanup */
ide_do_rw_cdrom, /* do_request */
- NULL, /* ??? or perhaps
- cdrom_end_request? */
+ NULL, /* ??? or perhaps cdrom_end_request? */
ide_cdrom_ioctl, /* ioctl */
ide_cdrom_open, /* open */
ide_cdrom_release, /* release */
ide_cdrom_check_media_change, /* media_change */
NULL, /* pre_reset */
NULL, /* capacity */
- NULL /* special */
+ NULL, /* special */
+ NULL /* proc */
};
diff --git a/drivers/block/ide-cd.h b/drivers/block/ide-cd.h
index afb3e5dbe..53e38e1b1 100644
--- a/drivers/block/ide-cd.h
+++ b/drivers/block/ide-cd.h
@@ -119,11 +119,11 @@ struct ide_cd_config_flags {
__u8 cd_rw : 1; /* Drive can write to CD-R/W media . */
__u8 supp_disc_present: 1; /* Changer can report exact contents
of slots. */
- __u8 max_speed : 4; /* Max speed of the drive */
__u8 seeking : 1; /* Seeking in progress */
__u8 reserved : 6;
+ byte max_speed; /* Max speed of the drive */
};
-#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_cyl))
+#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
/* State flags. These give information about the current state of the
@@ -133,10 +133,10 @@ struct ide_cd_state_flags {
__u8 toc_valid : 1; /* Saved TOC information is current. */
__u8 door_locked : 1; /* We think that the drive door is locked. */
__u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */
- __u8 curent_speed : 4; /* Current speed of the drive */
__u8 reserved : 3;
+ byte current_speed; /* Current speed of the drive */
};
-#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head))
+#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
struct atapi_request_sense {
@@ -385,6 +385,8 @@ struct cdrom_info {
/* Buffer to hold mechanism status and changer slot table. */
struct atapi_changer_info *changer_info;
+ struct ide_cd_config_flags config_flags;
+ struct ide_cd_state_flags state_flags;
/* Per-device info needed by cdrom.c generic driver. */
struct cdrom_device_info devinfo;
diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c
index f75a1a5be..5e178b6cd 100644
--- a/drivers/block/ide-disk.c
+++ b/drivers/block/ide-disk.c
@@ -10,32 +10,6 @@
*
* This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
*
- * From hd.c:
- * |
- * | It traverses the request-list, using interrupts to jump between functions.
- * | As nearly all functions can be called within interrupts, we may not sleep.
- * | Special care is recommended. Have Fun!
- * |
- * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
- * |
- * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- * | in the early extended-partition checks and added DM partitions.
- * |
- * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
- * |
- * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- * | and general streamlining by Mark Lord (mlord@pobox.com).
- *
- * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
- *
- * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
- * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
- * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
- *
- * This was a rewrite of just about everything from hd.c, though some original
- * code is still sprinkled about. Think of it as a major evolution, with
- * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
- *
* Version 1.00 move disk only code from ide.c to ide-disk.c
* support optional byte-swapping of all data
* Version 1.01 fix previous byte-swapping code
@@ -43,6 +17,8 @@
* Verions 1.03 fix display of id->buf_size for big-endian
*/
+#define IDEDISK_VERSION "1.03"
+
#undef REALLY_SLOW_IO /* most systems can safely undef this */
#include <linux/config.h>
@@ -52,12 +28,9 @@
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
#include <linux/delay.h>
@@ -319,7 +292,7 @@ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long bl
OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
#ifdef CONFIG_BLK_DEV_PDC4030
if (IS_PDC4030_DRIVE) {
- if (hwif->is_pdc4030_2 || rq->cmd == READ) {
+ if (hwif->channel != 0 || rq->cmd == READ) {
use_pdc4030_io = 1;
}
}
@@ -494,6 +467,11 @@ static void idedisk_pre_reset (ide_drive_t *drive)
drive->special.b.set_multmode = 1;
}
+static ide_proc_entry_t idedisk_proc[] = {
+ { "geometry", proc_ide_read_geometry, NULL },
+ { NULL, NULL, NULL }
+};
+
int idedisk_init (void);
static ide_module_t idedisk_module = {
IDE_DRIVER_MODULE,
@@ -505,6 +483,8 @@ static ide_module_t idedisk_module = {
* IDE subdriver functions, registered with ide.c
*/
static ide_driver_t idedisk_driver = {
+ "ide-disk", /* name */
+ IDEDISK_VERSION, /* version */
ide_disk, /* media */
0, /* busy */
1, /* supports_dma */
@@ -518,7 +498,8 @@ static ide_driver_t idedisk_driver = {
idedisk_media_change, /* media_change */
idedisk_pre_reset, /* pre_reset */
idedisk_capacity, /* capacity */
- idedisk_special /* special */
+ idedisk_special, /* special */
+ idedisk_proc /* proc */
};
static int idedisk_cleanup (ide_drive_t *drive)
@@ -526,20 +507,6 @@ static int idedisk_cleanup (ide_drive_t *drive)
return ide_unregister_subdriver(drive);
}
-static int idedisk_identify_device (ide_drive_t *drive)
-{
- struct hd_driveid *id = drive->id;
-
- if (id == NULL)
- return 0;
-
- /* SunDisk drives: force one unit */
- if (id->model[0] == 'S' && id->model[1] == 'u' && (drive->select.all & (1<<4)))
- return 1;
-
- return 0;
-}
-
static void idedisk_setup (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
@@ -554,7 +521,7 @@ static void idedisk_setup (ide_drive_t *drive)
drive->removable = 1;
}
- /* SunDisk drives: treat as non-removable */
+ /* SunDisk drives: treat as non-removable; can mess up non-Sun systems! FIXME */
if (id->model[0] == 'S' && id->model[1] == 'u')
drive->removable = 0;
@@ -634,8 +601,12 @@ int idedisk_init (void)
MOD_INC_USE_COUNT;
while ((drive = ide_scan_devices (ide_disk, NULL, failed++)) != NULL) {
- if (idedisk_identify_device (drive))
+
+ /* SunDisk drives: ignore "second" drive; can mess up non-Sun systems! FIXME */
+ struct hd_driveid *id = drive->id;
+ if (id && id->model[0] == 'S' && id->model[1] == 'u' && drive->select.b.unit)
continue;
+
if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
continue;
diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c
index 6a2886f78..bba735580 100644
--- a/drivers/block/ide-dma.c
+++ b/drivers/block/ide-dma.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-dma.c Version 4.06 December 3, 1997
+ * linux/drivers/block/ide-dma.c Version 4.07 December 5, 1997
*
* Copyright (c) 1995-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
@@ -49,9 +49,8 @@
* available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
* (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
*
- * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for fixing the
- * problem with some (all?) ACER motherboards/BIOSs. Hopefully the fix
- * still works here (?).
+ * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
+ * fixing the problem with the BIOS on some Acer motherboards.
*
* Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
* "TX" chipset compatibility and for providing patches for the "TX" chipset.
@@ -69,16 +68,12 @@
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/bios32.h>
-#include <linux/init.h>
#include <asm/io.h>
-#include <asm/dma.h>
+#include <asm/irq.h>
#include "ide.h"
@@ -110,23 +105,19 @@ const char *good_dma_drives[] = {"Micropolis 2112A",
#define PRD_BYTES 8
#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
-static int config_drive_for_dma (ide_drive_t *);
-
/*
* dma_intr() is the handler for disk read/write DMA interrupts
*/
-static void dma_intr (ide_drive_t *drive)
+void ide_dma_intr (ide_drive_t *drive)
{
- byte stat, dma_stat;
int i;
- struct request *rq = HWGROUP(drive)->rq;
- unsigned short dma_base = HWIF(drive)->dma_base;
+ byte stat, dma_stat;
- dma_stat = inb(dma_base+2); /* get DMA status */
- outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */
+ dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
stat = GET_STAT(); /* get drive status */
if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
- if ((dma_stat & 7) == 4) { /* verify good DMA status */
+ if (!dma_stat) {
+ struct request *rq = HWGROUP(drive)->rq;
rq = HWGROUP(drive)->rq;
for (i = rq->nr_sectors; i > 0;) {
i -= rq->current_nr_sectors;
@@ -134,7 +125,7 @@ static void dma_intr (ide_drive_t *drive)
}
return;
}
- printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
+ printk("%s: dma_intr: bad DMA status\n", drive->name);
}
sti();
ide_error(drive, "dma_intr", stat);
@@ -195,23 +186,46 @@ int ide_build_dmatable (ide_drive_t *drive)
unsigned long xcount, bcount = 0x10000 - (addr & 0xffff);
if (bcount > size)
bcount = size;
- *table++ = addr;
+ *table++ = cpu_to_le32(addr);
xcount = bcount & 0xffff;
if (is_trm290_chipset)
xcount = ((xcount >> 2) - 1) << 16;
- *table++ = xcount;
+ *table++ = cpu_to_le32(xcount);
addr += bcount;
size -= bcount;
}
}
} while (bh != NULL);
- if (count) {
- if (!is_trm290_chipset)
- *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */
- return count;
+ if (!count)
+ printk("%s: empty DMA table?\n", drive->name);
+ else if (!is_trm290_chipset)
+ *--table |= cpu_to_le32(0x80000000); /* set End-Of-Table (EOT) bit */
+ return count;
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (id && (id->capability & 1) && !HWIF(drive)->no_autodma) {
+ /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+ if (id->field_valid & 4) /* UltraDMA */
+ if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+ if (id->field_valid & 2) /* regular DMA */
+ if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model))
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
}
- printk("%s: empty DMA table?\n", drive->name);
- return 0;
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
}
/*
@@ -233,7 +247,7 @@ int ide_build_dmatable (ide_drive_t *drive)
int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned long dma_base = hwif->dma_base;
+ unsigned int dma_base = hwif->dma_base;
unsigned int count, reading = 0;
switch (func) {
@@ -243,21 +257,8 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
case ide_dma_on:
drive->using_dma = (func == ide_dma_on);
return 0;
- case ide_dma_abort:
- outb(inb(dma_base)&~1, dma_base); /* stop DMA */
- return 0;
case ide_dma_check:
return config_drive_for_dma (drive);
- case ide_dma_status_bad:
- return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */
- case ide_dma_transferred:
- return 0; /* NOT IMPLEMENTED: number of bytes actually transferred */
- case ide_dma_begin:
- outb(inb(dma_base)|1, dma_base); /* begin DMA */
- return 0;
- default:
- printk("ide_dmaproc: unsupported func: %d\n", func);
- return 1;
case ide_dma_read:
reading = 1 << 3;
case ide_dma_write:
@@ -268,50 +269,37 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */
if (drive->media != ide_disk)
return 0;
- ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);/* issue cmd to drive */
OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
- outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ case ide_dma_begin:
+ outb(inb(dma_base)|1, dma_base); /* start DMA */
return 0;
- }
-}
-
-static int config_drive_for_dma (ide_drive_t *drive)
-{
- const char **list;
- struct hd_driveid *id = drive->id;
- ide_hwif_t *hwif = HWIF(drive);
-
- if (id && (id->capability & 1)) {
- /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
- if (id->field_valid & 4) /* UltraDMA */
- if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
- return hwif->dmaproc(ide_dma_on, drive);
- /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
- if (id->field_valid & 2) /* regular DMA */
- if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
- return hwif->dmaproc(ide_dma_on, drive);
- /* Consult the list of known "good" drives */
- list = good_dma_drives;
- while (*list) {
- if (!strcmp(*list++,id->model))
- return hwif->dmaproc(ide_dma_on, drive);
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ {
+ byte dma_stat = inb(dma_base+2);
+ int rc = (dma_stat & 7) != 4;
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
+ return rc; /* verify good DMA status */
}
+ default:
+ printk("ide_dmaproc: unsupported func: %d\n", func);
+ return 1;
}
- return hwif->dmaproc(ide_dma_off_quietly, drive);
}
-void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports)
+void ide_setup_dma (ide_hwif_t *hwif, unsigned int dma_base, unsigned int num_ports) /* __init */
{
static unsigned long dmatable = 0;
static unsigned leftover = 0;
- printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase + num_ports - 1);
- if (check_region(dmabase, num_ports)) {
+ printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dma_base, dma_base + num_ports - 1);
+ if (check_region(dma_base, num_ports)) {
printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
return;
}
- request_region(dmabase, num_ports, hwif->name);
- hwif->dma_base = dmabase;
+ request_region(dma_base, num_ports, hwif->name);
+ hwif->dma_base = dma_base;
if (leftover < (PRD_ENTRIES * PRD_BYTES)) {
/*
* The BM-DMA uses full 32bit addr, so we can
@@ -328,87 +316,18 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_p
dmatable += (PRD_ENTRIES * PRD_BYTES);
leftover -= (PRD_ENTRIES * PRD_BYTES);
hwif->dmaproc = &ide_dmaproc;
+
if (hwif->chipset != ide_trm290) {
- byte dma_stat = inb(dmabase+2);
- printk(", BIOS DMA settings: %s:%s %s:%s",
- hwif->drives[0].name, (dma_stat & 0x20) ? "yes" : "no ",
- hwif->drives[1].name, (dma_stat & 0x40) ? "yes" : "no");
+ byte dma_stat = inb(dma_base+2);
+ printk(", BIOS settings: %s:%s, %s:%s",
+ hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
+ hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
}
printk("\n");
}
}
-#ifdef CONFIG_BLK_DEV_TRM290
-extern void ide_init_trm290(byte, byte, ide_hwif_t *);
-#define INIT_TRM290 (&ide_init_trm290)
-#else
-#define INIT_TRM290 (NULL)
-#endif /* CONFIG_BLK_DEV_TRM290 */
-
-#ifdef CONFIG_BLK_DEV_OPTI621
-extern void ide_init_opti621(byte, byte, ide_hwif_t *);
-#define INIT_OPTI (&ide_init_opti621)
-#else
-#define INIT_OPTI (NULL)
-#endif /* CONFIG_BLK_DEV_OPTI621 */
-
-#define DEVID_PIIX (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371_1 <<16))
-#define DEVID_PIIX3 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371SB_1 <<16))
-#define DEVID_PIIX4 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371AB <<16))
-#define DEVID_VP_IDE (PCI_VENDOR_ID_VIA |(PCI_DEVICE_ID_VIA_82C586_1 <<16))
-#define DEVID_PDC20246 (PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246 <<16))
-#define DEVID_RZ1000 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1000 <<16))
-#define DEVID_RZ1001 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1001 <<16))
-#define DEVID_CMD640 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_640 <<16))
-#define DEVID_CMD646 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_646 <<16))
-#define DEVID_SIS5513 (PCI_VENDOR_ID_SI |(PCI_DEVICE_ID_SI_5513 <<16))
-#define DEVID_OPTI (PCI_VENDOR_ID_OPTI |(PCI_DEVICE_ID_OPTI_82C621 <<16))
-#define DEVID_OPTI2 (PCI_VENDOR_ID_OPTI |(0xd568 /* from datasheets */ <<16))
-#define DEVID_TRM290 (PCI_VENDOR_ID_TEKRAM |(PCI_DEVICE_ID_TEKRAM_DC290 <<16))
-#define DEVID_NS87410 (PCI_VENDOR_ID_NS |(PCI_DEVICE_ID_NS_87410 <<16))
-#define DEVID_HT6565 (PCI_VENDOR_ID_HOLTEK |(PCI_DEVICE_ID_HOLTEK_6565 <<16))
-
-typedef struct ide_pci_enablebit_s {
- byte reg; /* byte pci reg holding the enable-bit */
- byte mask; /* mask to isolate the enable-bit */
- byte val; /* value of masked reg when "enabled" */
-} ide_pci_enablebit_t;
-
-typedef struct ide_pci_device_s {
- unsigned int id;
- const char *name;
- void (*init_hwif)(byte bus, byte fn, ide_hwif_t *hwif);
- ide_pci_enablebit_t enablebits[2];
-} ide_pci_device_t;
-
-static ide_pci_device_t ide_pci_chipsets[] = {
- {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
- {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
- {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
- {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
- {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
- {DEVID_RZ1000, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
- {DEVID_RZ1001, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
- {DEVID_CMD640, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
- {DEVID_OPTI, "OPTI", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
- {DEVID_OPTI2, "OPTI2", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
- {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} },
- {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} },
- {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
- {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} },
- {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
- {0, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
-
-__initfunc(static ide_pci_device_t *lookup_devid(unsigned int devid))
-{
- ide_pci_device_t *d = ide_pci_chipsets;
- while (d->id && d->id != devid)
- ++d;
- return d;
-}
-
-/* The next two functions were stolen from cmd640.c, with
- a few modifications */
+/* The next two functions were stolen from cmd640.c, with a few modifications */
__initfunc(static void write_pcicfg_dword (byte fn, unsigned short reg, long val))
{
@@ -435,244 +354,50 @@ __initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg))
}
/*
- * Search for an (apparently) unused block of I/O space
- * of "size" bytes in length.
- */
-__initfunc(static short find_free_region (unsigned short size))
-{
- unsigned short i, base = 0xe800;
- for (base = 0xe800; base > 0; base -= 0x800) {
- if (!check_region(base,size)) {
- for (i = 0; i < size; i++) {
- if (inb(base+i) != 0xff)
- goto next;
- }
- return base; /* success */
- }
- next:
- }
- return 0; /* failure */
-}
-
-/*
- * Fetch the Bus-Master I/O Base-Address (BMIBA) from PCI space:
+ * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
*/
-__initfunc(static unsigned int ide_get_or_set_bmiba (byte bus, byte fn, const char *name))
+unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */
{
- unsigned int bmiba = 0;
- unsigned short base;
- int rc;
-
- if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) {
- printk("%s: failed to read BMIBA\n", name);
- } else if ((bmiba &= 0xfff0) == 0) {
- printk("%s: BMIBA is invalid (0x%04x, BIOS problem)\n", name, bmiba);
- base = find_free_region(16);
- if (base) {
- printk("%s: setting BMIBA to 0x%04x\n", name, base);
- pcibios_write_config_dword(bus, fn, 0x20, base | 1);
- pcibios_read_config_dword(bus, fn, 0x20, &bmiba);
- bmiba &= 0xfff0;
- if (bmiba != base) {
+ unsigned int new, dma_base = 0;
+ byte bus = hwif->pci_bus, fn = hwif->pci_fn;
+
+ if (hwif->mate && hwif->mate->dma_base) {
+ dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
+ } else if (pcibios_read_config_dword(bus, fn, 0x20, &dma_base)) {
+ printk("%s: failed to read dma_base\n", name);
+ dma_base = 0;
+ } else if ((dma_base &= ~0xf) == 0 || dma_base == ~0xf) {
+ printk("%s: dma_base is invalid (0x%04x, BIOS problem)\n", name, dma_base);
+ new = ide_find_free_region(16 + extra);
+ hwif->no_autodma = 1; /* default DMA off if we had to configure it here */
+ if (new) {
+ printk("%s: setting dma_base to 0x%04x\n", name, new);
+ new |= 1;
+ (void) pcibios_write_config_dword(bus, fn, 0x20, new);
+ (void) pcibios_read_config_dword(bus, fn, 0x20, &dma_base);
+ if (dma_base != new) {
if (bus == 0) {
printk("%s: operation failed, bypassing BIOS to try again\n", name);
- write_pcicfg_dword(fn, 0x20, base | 1);
- bmiba = read_pcicfg_dword(fn, 0x20) & 0xfff0;
+ write_pcicfg_dword(fn, 0x20, new);
+ dma_base = read_pcicfg_dword(fn, 0x20);
}
- if (bmiba != base) {
+ if (dma_base != new) {
printk("%s: operation failed, DMA disabled\n", name);
- bmiba = 0;
+ dma_base = 0;
}
}
+ dma_base &= ~0xf;
}
}
- return bmiba;
-}
-
-/*
- * Match a PCI IDE port against an entry in ide_hwifs[],
- * based on io_base port if possible.
- */
-__initfunc(ide_hwif_t *ide_match_hwif (unsigned int io_base))
-{
- int h;
- ide_hwif_t *hwif;
-
- /*
- * Look for a hwif with matching io_base specified using
- * parameters to ide_setup().
- */
- for (h = 0; h < MAX_HWIFS; ++h) {
- hwif = &ide_hwifs[h];
- if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
- if (hwif->chipset == ide_generic)
- return hwif; /* a perfect match */
- }
- }
- /*
- * Look for a hwif with matching io_base default value.
- * If chipset is "ide_unknown", then claim that hwif slot.
- * Otherwise, some other chipset has already claimed it.. :(
- */
- for (h = 0; h < MAX_HWIFS; ++h) {
- hwif = &ide_hwifs[h];
- if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
- if (hwif->chipset == ide_unknown)
- return hwif; /* match */
- return NULL; /* already claimed */
- }
- }
- /*
- * Okay, there is no hwif matching our io_base,
- * so we'll just claim an unassigned slot.
- * Give preference to claiming ide2/ide3 before ide0/ide1,
- * just in case there's another interface yet-to-be-scanned
- * which uses ports 1f0/170 (the ide0/ide1 defaults).
- */
- for (h = 0; h < MAX_HWIFS; ++h) {
- int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */
- hwif = &ide_hwifs[hwifs[h]];
- if (hwif->chipset == ide_unknown)
- return hwif; /* pick an unused entry */
- }
- return NULL;
-}
-
-/*
- * ide_setup_pci_device() looks at the primary/secondary interfaces
- * on a PCI IDE device and, if they are enabled, prepares the IDE driver
- * for use with them. This generic code works for most PCI chipsets.
- *
- * One thing that is not standardized is the location of the
- * primary/secondary interface "enable/disable" bits. For chipsets that
- * we "know" about, this information is in the ide_pci_device_t struct;
- * for all other chipsets, we just assume both interfaces are enabled.
- */
-__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int bmiba, ide_pci_device_t *d))
-{
- unsigned int port, at_least_one_hwif_enabled = 0;
- unsigned short base = 0, ctl = 0;
- byte tmp = 0;
- ide_hwif_t *hwif, *mate = NULL;
-
- for (port = 0; port <= 1; ++port) {
- ide_pci_enablebit_t *e = &(d->enablebits[port]);
- if (e->reg) {
- if (pcibios_read_config_byte(bus, fn, e->reg, &tmp)) {
- printk("%s: unable to read pci reg 0x%x\n", d->name, e->reg);
- } else if ((tmp & e->mask) != e->val)
- continue; /* port not enabled */
- }
- if (pcibios_read_config_word(bus, fn, 0x14+(port*8), &ctl))
- ctl = 0;
- if ((ctl &= 0xfffc) == 0)
- ctl = 0x3f4 ^ (port << 7);
- if (pcibios_read_config_word(bus, fn, 0x10+(port*8), &base))
- base = 0;
- if ((base &= 0xfff8) == 0)
- base = 0x1F0 ^ (port << 7);
- if ((hwif = ide_match_hwif(base)) == NULL) {
- printk("%s: no room in hwif table for port %d\n", d->name, port);
- continue;
- }
- hwif->chipset = ide_pci;
- hwif->pci_port = port;
- if (mate) {
- hwif->mate = mate;
- mate->mate = hwif;
- }
- mate = hwif; /* for next iteration */
- if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
- ide_init_hwif_ports(hwif->io_ports, base, NULL);
- hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2;
- }
- if (bmiba) {
- if ((inb(bmiba+2) & 0x80)) { /* simplex DMA only? */
- printk("%s: simplex device: DMA disabled\n", d->name);
- } else { /* supports simultaneous DMA on both channels */
- ide_setup_dma(hwif, bmiba + (8 * port), 8);
- }
- }
- if (d->id) { /* For "known" chipsets, allow other irqs during i/o */
- hwif->drives[0].unmask = 1;
- hwif->drives[1].unmask = 1;
- }
- if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */
- d->init_hwif(bus, fn, hwif);
- at_least_one_hwif_enabled = 1;
- }
- if (!at_least_one_hwif_enabled)
- printk("%s: neither IDE port enabled (BIOS)\n", d->name);
-}
-
-/*
- * ide_scan_pci_device() examines all functions of a PCI device,
- * looking for IDE interfaces and/or devices in ide_pci_chipsets[].
- */
-__initfunc(static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn))
-{
- unsigned int devid, ccode;
- unsigned short pcicmd, class;
- ide_pci_device_t *d;
- byte hedt, progif;
-
- if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt))
- hedt = 0;
- do {
- if (pcibios_read_config_dword(bus, fn, 0x00, &devid)
- || devid == 0xffffffff
- || pcibios_read_config_dword(bus, fn, 0x08, &ccode))
- return;
- d = lookup_devid(devid);
- if (d->name == NULL) /* some chips (cmd640, rz1000) are handled elsewhere */
- continue;
- progif = (ccode >> 8) & 0xff;
- class = ccode >> 16;
- if (d->id || class == PCI_CLASS_STORAGE_IDE) {
- if (d->id)
- printk("%s: IDE device on PCI bus %d function %d\n", d->name, bus, fn);
- else
- printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
- d->name, bus, fn, devid & 0xffff, devid >> 16);
- /*
- * See if IDE ports are enabled
- */
- if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) {
- printk("%s: error accessing PCICMD\n", d->name);
- } else if ((pcicmd & 1) == 0) {
- printk("%s: device disabled (BIOS)\n", d->name);
- } else {
- unsigned int bmiba = 0;
- /*
- * Check for Bus-Master DMA capability
- */
- if (d->id == DEVID_PDC20246 || (class == PCI_CLASS_STORAGE_IDE && (progif & 0x80))) {
- if ((!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name)))) {
- printk("%s: Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, progif=0x%02x, bmiba=0x%04x\n", d->name, pcicmd, progif, bmiba);
- }
- }
- /*
- * Setup the IDE ports
- */
- ide_setup_pci_device(bus, fn, bmiba, d);
- }
- }
- } while (hedt == 0x80 && (++fn & 7));
-}
-
-/*
- * ide_scan_pcibus() gets invoked at boot time from ide.c
- */
-__initfunc(void ide_scan_pcibus (void))
-{
- unsigned int bus, dev;
-
- if (!pcibios_present())
- return;
- for (bus = 0; bus <= 255; ++bus) {
- for (dev = 0; dev <= 31; ++dev) {
- ide_scan_pci_device(bus, dev << 3);
+ if (dma_base) {
+ if (extra) /* PDC20246 */
+ request_region(dma_base+16, extra, name);
+ dma_base += hwif->channel ? 8 : 0;
+ if (inb(dma_base+2) & 0x80) {
+ printk("%s: simplex device: DMA disabled\n", name);
+ dma_base = 0;
}
}
+ return dma_base;
}
diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c
index 2856f330f..5572a07ba 100644
--- a/drivers/block/ide-floppy.c
+++ b/drivers/block/ide-floppy.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-floppy.c Version 0.5 - ALPHA Feb 21, 1997
+ * linux/drivers/block/ide-floppy.c Version 0.8 Feb 21, 1997
*
* Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
*/
@@ -21,8 +21,15 @@
* Use the minimum of the LBA and CHS capacities.
* Avoid hwgroup->rq == NULL on the last irq.
* Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.8 Dec 7 97 Increase irq timeout from 10 to 50 seconds.
+ * Add media write-protect detection.
+ * Issue START command only if TEST UNIT READY fails.
+ * Add work-around for IOMEGA ZIP revision 21.D.
+ * Remove idefloppy_get_capabilities().
*/
+#define IDEFLOPPY_VERSION "0.8"
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -31,12 +38,9 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
@@ -60,6 +64,11 @@
#define IDEFLOPPY_DEBUG_BUGS 1
/*
+ * Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD)
+
+/*
* After each failed packet command we issue a request sense command
* and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
*/
@@ -191,6 +200,7 @@ typedef struct {
int blocks, block_size, bs_factor; /* Current format */
idefloppy_capacity_descriptor_t capacity; /* Last format capacity */
idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */
+ int wp; /* Write protect */
unsigned int flags; /* Status/Action flags */
} idefloppy_floppy_t;
@@ -683,13 +693,12 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
#ifdef CONFIG_BLK_DEV_IDEDMA
if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
- if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+ if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
set_bit (PC_DMA_ERROR, &pc->flags);
} else {
pc->actually_transferred=pc->request_transfer;
idefloppy_update_buffers (drive, pc);
}
- (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */
#if IDEFLOPPY_DEBUG_LOG
printk (KERN_INFO "ide-floppy: DMA finished\n");
#endif /* IDEFLOPPY_DEBUG_LOG */
@@ -728,8 +737,7 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
#ifdef CONFIG_BLK_DEV_IDEDMA
if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
- printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
- HWIF(drive)->dmaproc(ide_dma_off, drive);
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
ide_do_reset (drive);
return;
}
@@ -755,7 +763,7 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
if (temp > pc->buffer_size) {
printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
idefloppy_discard_data (drive,bcount.all);
- ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD);
+ ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
return;
}
#if IDEFLOPPY_DEBUG_LOG
@@ -777,7 +785,7 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
pc->actually_transferred+=bcount.all; /* Update the current position */
pc->current_position+=bcount.all;
- ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD); /* And set the interrupt handler again */
+ ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */
}
static void idefloppy_transfer_pc (ide_drive_t *drive)
@@ -795,7 +803,7 @@ static void idefloppy_transfer_pc (ide_drive_t *drive)
ide_do_reset (drive);
return;
}
- ide_set_handler (drive, &idefloppy_pc_intr, WAIT_CMD); /* Set the interrupt routine */
+ ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */
atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
}
@@ -843,8 +851,7 @@ static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
#ifdef CONFIG_BLK_DEV_IDEDMA
if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
- printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
- HWIF(drive)->dmaproc(ide_dma_off, drive);
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
}
if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
@@ -864,7 +871,7 @@ static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
#endif /* CONFIG_BLK_DEV_IDEDMA */
if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
- ide_set_handler (drive, &idefloppy_transfer_pc, WAIT_CMD);
+ ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD);
OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */
} else {
OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
@@ -934,6 +941,12 @@ static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
pc->c[4] = start;
}
+static void idefloppy_create_test_unit_ready_cmd(idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc(pc);
+ pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
+}
+
static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
{
int block = sector / floppy->bs_factor;
@@ -1041,6 +1054,7 @@ static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
return 1;
}
header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+ floppy->wp = header->wp;
page = (idefloppy_flexible_disk_page_t *) (header + 1);
page->transfer_rate = ntohs (page->transfer_rate);
@@ -1144,13 +1158,21 @@ static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *
MOD_INC_USE_COUNT;
if (drive->usage == 1) {
- idefloppy_create_start_stop_cmd (&pc, 1);
- (void) idefloppy_queue_pc_tail (drive, &pc);
+ idefloppy_create_test_unit_ready_cmd(&pc);
+ if (idefloppy_queue_pc_tail(drive, &pc)) {
+ idefloppy_create_start_stop_cmd (&pc, 1);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ }
if (idefloppy_get_capacity (drive)) {
drive->usage--;
MOD_DEC_USE_COUNT;
return -EIO;
}
+ if (floppy->wp && (filp->f_mode & 2)) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
idefloppy_create_prevent_cmd (&pc, 1);
(void) idefloppy_queue_pc_tail (drive, &pc);
@@ -1243,9 +1265,9 @@ static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
default: sprintf (buffer, "Reserved");break;
}
printk (KERN_INFO "Command Packet Size: %s\n", buffer);
- printk (KERN_INFO "Model: %s\n",id->model);
- printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
- printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+ printk (KERN_INFO "Model: %.40s\n",id->model);
+ printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev);
+ printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no);
printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
@@ -1306,51 +1328,13 @@ static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
}
/*
- * idefloppy_get_capabilities asks the floppy about its various
- * parameters.
- */
-static void idefloppy_get_capabilities (ide_drive_t *drive)
-{
- idefloppy_pc_t pc;
- idefloppy_mode_parameter_header_t *header;
- idefloppy_capabilities_page_t *capabilities;
-
- idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
- if (idefloppy_queue_pc_tail (drive,&pc)) {
- printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
- return;
- }
- header = (idefloppy_mode_parameter_header_t *) pc.buffer;
- capabilities = (idefloppy_capabilities_page_t *) (header + 1);
-
- if (!capabilities->sflp)
- printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
-
-#if IDEFLOPPY_DEBUG_INFO
- printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
- printk (KERN_INFO "Mode Parameter Header:\n");
- printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
- printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
- printk (KERN_INFO "WP - %d\n",header->wp);
-
- printk (KERN_INFO "Capabilities Page:\n");
- printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
- printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
- printk (KERN_INFO "PS - %d\n",capabilities->ps);
- printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
- printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
- printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
- printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
- printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
-#endif /* IDEFLOPPY_DEBUG_INFO */
-}
-
-/*
* Driver initialization.
*/
static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
{
struct idefloppy_id_gcw gcw;
+ int major = HWIF(drive)->major, i;
+ int minor = drive->select.b.unit << PARTN_BITS;
*((unsigned short *) &gcw) = drive->id->config;
drive->driver_data = floppy;
@@ -1360,8 +1344,12 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
floppy->pc = floppy->pc_stack;
if (gcw.drq_type == 1)
set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+ if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 &&
+ strcmp(drive->id->fw_rev, "21.D") == 0) {
+ for (i = 0; i < 1 << PARTN_BITS; i++)
+ max_sectors[major][minor + i] = 64;
+ }
- idefloppy_get_capabilities (drive);
(void) idefloppy_get_capacity (drive);
}
@@ -1387,6 +1375,8 @@ static ide_module_t idefloppy_module = {
* IDE subdriver functions, registered with ide.c
*/
static ide_driver_t idefloppy_driver = {
+ "ide-floppy", /* name */
+ IDEFLOPPY_VERSION, /* version */
ide_floppy, /* media */
0, /* busy */
1, /* supports_dma */
@@ -1400,7 +1390,8 @@ static ide_driver_t idefloppy_driver = {
idefloppy_media_change, /* media_change */
NULL, /* pre_reset */
idefloppy_capacity, /* capacity */
- NULL /* special */
+ NULL, /* special */
+ NULL /* proc */
};
/*
diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c
new file mode 100644
index 000000000..adcf99525
--- /dev/null
+++ b/drivers/block/ide-pci.c
@@ -0,0 +1,382 @@
+/*
+ * linux/drivers/block/ide-pci.c Version 1.00 December 8, 1997
+ *
+ * Copyright (c) 1995-1998 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This modules provides support for automatic detection and
+ * configuration of all PCI IDE interfaces present in a system.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide.h"
+
+#define DEVID_PIIX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1})
+#define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1})
+#define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB})
+#define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1})
+#define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246})
+#define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000})
+#define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001})
+#define DEVID_CMD640 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640})
+#define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646})
+#define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513})
+#define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621})
+#define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558})
+#define DEVID_OPTI621X ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, 0xd568}) /* from datasheets */
+#define DEVID_TRM290 ((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290})
+#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410})
+#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415})
+#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565})
+
+#define IDE_IGNORE ((void *)-1)
+
+#ifdef CONFIG_BLK_DEV_TRM290
+extern void ide_init_trm290(ide_hwif_t *);
+#define INIT_TRM290 &ide_init_trm290
+#else
+#define INIT_TRM290 IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+extern void ide_init_opti621(ide_hwif_t *);
+#define INIT_OPTI621 &ide_init_opti621
+#else
+#define INIT_OPTI621 NULL
+#endif
+
+#ifdef CONFIG_BLK_DEV_NS87415
+extern void ide_init_ns87415(ide_hwif_t *);
+#define INIT_NS87415 &ide_init_ns87415
+#else
+#define INIT_NS87415 IDE_IGNORE
+#endif
+
+#ifdef CONFIG_BLK_DEV_RZ1000
+extern void ide_init_rz1000(ide_hwif_t *);
+#define INIT_RZ1000 &ide_init_rz1000
+#else
+#define INIT_RZ1000 IDE_IGNORE
+#endif
+
+typedef struct ide_pci_enablebit_s {
+ byte reg; /* byte pci reg holding the enable-bit */
+ byte mask; /* mask to isolate the enable-bit */
+ byte val; /* value of masked reg when "enabled" */
+} ide_pci_enablebit_t;
+
+typedef struct ide_pci_device_s {
+ ide_pci_devid_t devid;
+ const char *name;
+ void (*init_hwif)(ide_hwif_t *hwif);
+ ide_pci_enablebit_t enablebits[2];
+} ide_pci_device_t;
+
+static ide_pci_device_t ide_pci_chipsets[] __initdata = {
+ {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
+ {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
+ {DEVID_RZ1000, "RZ1000", INIT_RZ1000, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_RZ1001, "RZ1001", INIT_RZ1000, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_CMD640, "CMD640", IDE_IGNORE, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} },
+ {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} },
+ {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} },
+ {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_OPTI621, "OPTI621", INIT_OPTI621, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
+ {DEVID_OPTI621X,"OPTI621X", INIT_OPTI621, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
+ {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_NS87415, "NS87415", INIT_NS87415, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
+
+/*
+ * Search for an (apparently) unused block of I/O space
+ * of "size" bytes in length. Ideally we ought to do a pass
+ * through pcicfg space to eliminate ports already allocated
+ * by the BIOS, to avoid conflicts later in the init cycle,
+ * but we don't. FIXME
+ */
+unsigned int ide_find_free_region (unsigned short size) /* __init */
+{
+ static unsigned short base = 0x5800; /* it works for me */
+ unsigned short i;
+
+ for (; base > 0; base -= 0x200) {
+ if (!check_region(base,size)) {
+ for (i = 0; i < size; i++) {
+ if (inb(base+i) != 0xff)
+ goto next;
+ }
+ return base; /* success */
+ }
+ next:
+ }
+ return 0; /* failure */
+}
+
+/*
+ * Match a PCI IDE port against an entry in ide_hwifs[],
+ * based on io_base port if possible.
+ */
+__initfunc(static ide_hwif_t *ide_match_hwif (unsigned int io_base, const char *name))
+{
+ int h;
+ ide_hwif_t *hwif;
+
+ /*
+ * Look for a hwif with matching io_base specified using
+ * parameters to ide_setup().
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_generic)
+ return hwif; /* a perfect match */
+ }
+ }
+ /*
+ * Look for a hwif with matching io_base default value.
+ * If chipset is "ide_unknown", then claim that hwif slot.
+ * Otherwise, some other chipset has already claimed it.. :(
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* match */
+ printk("%s: port 0x%04x already claimed by %s\n", name, io_base, hwif->name);
+ return NULL; /* already claimed */
+ }
+ }
+ /*
+ * Okay, there is no hwif matching our io_base,
+ * so we'll just claim an unassigned slot.
+ * Give preference to claiming other slots before claiming ide0/ide1,
+ * just in case there's another interface yet-to-be-scanned
+ * which uses ports 1f0/170 (the ide0/ide1 defaults).
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */
+ hwif = &ide_hwifs[hwifs[h]];
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* pick an unused entry */
+ }
+ printk("%s: too many IDE interfaces, no room in table\n", name);
+ return NULL;
+}
+
+__initfunc(static int ide_setup_pci_baseregs (byte bus, byte fn, const char *name))
+{
+ unsigned int base, readback;
+ byte reg, progif = 0;
+
+ /*
+ * Place both IDE interfaces into PCI "native" mode:
+ */
+ if (pcibios_read_config_byte(bus, fn, 0x09, &progif) || (progif & 5) != 5) {
+ if ((progif & 0xa) != 0xa) {
+ printk("%s: device not capable of full native PCI mode\n", name);
+ return 1;
+ }
+ printk("%s: placing both ports into native PCI mode\n", name);
+ (void) pcibios_write_config_byte(bus, fn, 0x09, progif|5);
+ if (pcibios_read_config_byte(bus, fn, 0x09, &progif) || (progif & 5) != 5) {
+ printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif);
+ return 1;
+ }
+ }
+ /*
+ * Setup base registers for IDE command/control spaces for each interface:
+ */
+ if (!(base = ide_find_free_region(32)))
+ return 1;
+ for (reg = 0x10; reg <= 0x1c; reg += 4, base += 8) {
+ (void) pcibios_write_config_dword(bus, fn, reg, base|1);
+ if (pcibios_read_config_dword(bus, fn, reg, &readback) || (readback &= ~1) != base) {
+ printk("%s: readback failed for basereg 0x%02x: wrote 0x%04x, read 0x%x04\n", name, reg, base, readback);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ide_setup_pci_device() looks at the primary/secondary interfaces
+ * on a PCI IDE device and, if they are enabled, prepares the IDE driver
+ * for use with them. This generic code works for most PCI chipsets.
+ *
+ * One thing that is not standardized is the location of the
+ * primary/secondary interface "enable/disable" bits. For chipsets that
+ * we "know" about, this information is in the ide_pci_device_t struct;
+ * for all other chipsets, we just assume both interfaces are enabled.
+ */
+__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int ccode, ide_pci_device_t *d))
+{
+ unsigned int port, at_least_one_hwif_enabled = 0, no_autodma = 0;
+ unsigned short pcicmd = 0;
+ byte tmp = 0, progif = 0, pciirq = 0;
+ ide_hwif_t *hwif, *mate = NULL;
+
+ if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
+ || pcibios_read_config_byte(bus, fn, 0x09, &progif)
+ || pcibios_read_config_byte(bus, fn, 0x3c, &pciirq))
+ {
+ printk("%s: error accessing PCI regs\n", d->name);
+ return;
+ }
+ if (!(pcicmd & 1)) { /* is device disabled? */
+ /*
+ * PnP BIOS was *supposed* to have set this device up for us,
+ * but we can do it ourselves, so long as the BIOS has assigned an IRQ
+ * (or possibly the device is using a "legacy header" for IRQs).
+ * Maybe the user deliberately *disabled* the device,
+ * but we'll eventually ignore it again if no drives respond.
+ */
+ if (ide_setup_pci_baseregs(bus, fn, d->name)
+ || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1)
+ || pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
+ || !(pcicmd & 1))
+ {
+ printk("%s: device disabled (BIOS)\n", d->name);
+ return;
+ }
+ no_autodma = 1; /* default DMA off if we had to configure it here */
+ printk("%s: device enabled (Linux)\n", d->name);
+ }
+ if (!pciirq || pciirq >= NR_IRQS) { /* is pciirq invalid? */
+ if (pciirq || (progif & 0x5)) /* don't complain if using "legacy" mode */
+ printk("%s: BIOS returned %d for IRQ (ignored)\n", d->name, pciirq);
+ pciirq = 0; /* probe for it instead */
+ }
+ /*
+ * Set up the IDE ports
+ */
+ for (port = 0; port <= 1; ++port) {
+ unsigned int base = 0, ctl = 0;
+ ide_pci_enablebit_t *e = &(d->enablebits[port]);
+ if (e->reg && (pcibios_read_config_byte(bus, fn, e->reg, &tmp) || (tmp & e->mask) != e->val))
+ continue; /* port not enabled */
+ if (pcibios_read_config_dword(bus, fn, 0x14+(port*8), &ctl) || (ctl &= ~3) == 0)
+ ctl = port ? 0x374 : 0x3f4; /* use default value */
+ if (pcibios_read_config_dword(bus, fn, 0x10+(port*8), &base) || (base &= ~7) == 0)
+ base = port ? 0x170 : 0x1f0; /* use default value */
+ if ((hwif = ide_match_hwif(base, d->name)) == NULL)
+ continue; /* no room in ide_hwifs[] */
+ if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
+ ide_init_hwif_ports(hwif->io_ports, base, NULL);
+ hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2;
+ }
+ hwif->chipset = ide_pci;
+ hwif->pci_bus = bus;
+ hwif->pci_fn = fn;
+ hwif->pci_devid = d->devid;
+ hwif->channel = port;
+ hwif->irq = pciirq;
+ if (mate) {
+ hwif->mate = mate;
+ mate->mate = hwif;
+ }
+ if (no_autodma)
+ hwif->no_autodma = 1;
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || ((ccode >> 16) == PCI_CLASS_STORAGE_IDE && (ccode & 0x8000))) {
+ unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0;
+ unsigned int dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
+ if (dma_base && !(pcicmd & 4)) {
+ /*
+ * Set up BM-DMA capability (PnP BIOS should have done this)
+ */
+ hwif->no_autodma = 1; /* default DMA off if we had to configure it here */
+ (void) pcibios_write_config_word(bus, fn, 0x04, (pcicmd|4));
+ if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd) || !(pcicmd & 4)) {
+ printk("%s: %s error updating PCICMD\n", hwif->name, d->name);
+ dma_base = 0;
+ }
+ }
+ if (dma_base)
+ ide_setup_dma(hwif, dma_base, 8);
+ else
+ printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04x\n",
+ hwif->name, d->name, pcicmd, ccode, dma_base);
+ }
+#endif /* CONFIG_BLK_DEV_IDEDMA */
+ if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */
+ d->init_hwif(hwif);
+ mate = hwif;
+ at_least_one_hwif_enabled = 1;
+ }
+ if (!at_least_one_hwif_enabled)
+ printk("%s: neither IDE port enabled (BIOS)\n", d->name);
+}
+
+/*
+ * ide_scan_pci_device() examines all functions of a PCI device,
+ * looking for IDE interfaces and/or devices in ide_pci_chipsets[].
+ * We cannot use pcibios_find_class() cuz it doesn't work in all systems.
+ */
+static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn)
+{
+ unsigned int ccode;
+ ide_pci_devid_t devid;
+ ide_pci_device_t *d;
+ byte hedt;
+
+ if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt))
+ hedt = 0;
+ do {
+ if (pcibios_read_config_word(bus, fn, 0x00, &devid.vid)
+ || devid.vid == 0xffff
+ || pcibios_read_config_word(bus, fn, 0x02, &devid.did)
+ || IDE_PCI_DEVID_EQ(devid, IDE_PCI_DEVID_NULL)
+ || pcibios_read_config_dword(bus, fn, 0x08, &ccode))
+ return;
+ for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
+ if (d->init_hwif == IDE_IGNORE)
+ printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name);
+ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(fn & 1))
+ continue; /* OPTI Viper-M uses same devid for functions 0 and 1 */
+ else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (ccode >> 16) == PCI_CLASS_STORAGE_IDE) {
+ if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
+ printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
+ d->name, bus, fn, devid.vid, devid.did);
+ else
+ printk("%s: PCI bus %d function %d\n", d->name, bus, fn);
+ ide_setup_pci_device(bus, fn, ccode, d);
+ }
+ } while (hedt == 0x80 && (++fn & 7));
+}
+
+/*
+ * ide_scan_pcibus() gets invoked at boot time from ide.c
+ *
+ * Loops over all PCI devices on all PCI buses, invoking ide_scan_pci_device().
+ * We cannot use pcibios_find_class() cuz it doesn't work in all systems.
+ */
+void ide_scan_pcibus (void) /* __init */
+{
+ unsigned int bus, dev;
+
+ if (!pcibios_present())
+ return;
+ for (bus = 0; bus <= 255; ++bus) {
+ for (dev = 0; dev < 256; dev += 8) {
+ ide_scan_pci_device(bus, dev);
+ }
+ }
+}
+
diff --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c
index 9ae187b7e..8e9336037 100644
--- a/drivers/block/ide-probe.c
+++ b/drivers/block/ide-probe.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/block/ide-probe.c Version 1.02 Jul 29, 1997
+ * linux/drivers/block/ide-probe.c Version 1.03 Dec 5, 1997
*
- * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
+ * Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
*/
/*
@@ -10,36 +10,11 @@
*
* This is the IDE probe module, as evolved from hd.c and ide.c.
*
- * From hd.c:
- * |
- * | It traverses the request-list, using interrupts to jump between functions.
- * | As nearly all functions can be called within interrupts, we may not sleep.
- * | Special care is recommended. Have Fun!
- * |
- * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
- * |
- * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- * | in the early extended-partition checks and added DM partitions.
- * |
- * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
- * |
- * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- * | and general streamlining by Mark Lord (mlord@pobox.com).
- *
- * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
- *
- * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
- * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
- * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
- *
- * This was a rewrite of just about everything from hd.c, though some original
- * code is still sprinkled about. Think of it as a major evolution, with
- * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
- *
* Version 1.00 move drive probing code from ide.c to ide-probe.c
* Version 1.01 fix compilation problem for m68k
* Version 1.02 increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot
- * by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * Version 1.03 fix for (hwif->chipset == ide_4drives)
*/
#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -51,12 +26,9 @@
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
#include <linux/delay.h>
@@ -105,6 +77,7 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+ id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */
drive->present = 1;
printk("%s: %s, ", drive->name, id->model);
@@ -115,7 +88,7 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
byte type = (id->config >> 8) & 0x1f;
printk("ATAPI ");
#ifdef CONFIG_BLK_DEV_PDC4030
- if (HWIF(drive)->is_pdc4030_2) {
+ if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) {
printk(" -- not supported on 2nd Promise port\n");
drive->present = 0;
return;
@@ -160,8 +133,8 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
*/
static void delay_50ms (void)
{
- unsigned long timer = jiffies + ((HZ + 19)/20) + 1;
- while (timer > jiffies);
+ unsigned long timeout = jiffies + ((HZ + 19)/20) + 1;
+ while (0 < (signed long)(timeout - jiffies));
}
/*
@@ -180,6 +153,7 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
ide_ioreg_t hd_status;
unsigned long timeout;
unsigned long irqs = 0;
+ byte s, a;
if (!HWIF(drive)->irq) { /* already got an IRQ? */
probe_irq_off(probe_irq_on()); /* clear dangling irqs */
@@ -188,9 +162,11 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
}
delay_50ms(); /* take a deep breath */
- if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
- printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
- hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
+ a = IN_BYTE(IDE_ALTSTATUS_REG);
+ s = IN_BYTE(IDE_STATUS_REG);
+ if ((a ^ s) & ~INDEX_STAT) {
+ printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a);
+ hd_status = IDE_STATUS_REG; /* ancient Seagate drives, broken interfaces */
} else
hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
@@ -208,7 +184,7 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
timeout += jiffies;
do {
- if (jiffies > timeout) {
+ if (0 < (signed long)(jiffies - timeout)) {
if (irqs)
(void) probe_irq_off(irqs);
return 1; /* drive timed-out */
@@ -285,9 +261,11 @@ static int do_probe (ide_drive_t *drive, byte cmd)
SELECT_DRIVE(hwif,drive);
delay_50ms();
if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
- OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
- delay_50ms(); /* allow BUSY_STAT to assert & clear */
- return 3; /* no i/f present: avoid killing ethernet cards */
+ if (drive->select.b.unit != 0) {
+ SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */
+ delay_50ms(); /* allow BUSY_STAT to assert & clear */
+ }
+ return 3; /* no i/f present: mmm.. this should be a 4 -ml */
}
if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
@@ -302,7 +280,7 @@ static int do_probe (ide_drive_t *drive, byte cmd)
rc = 3; /* not present or maybe ATAPI */
}
if (drive->select.b.unit != 0) {
- OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */
delay_50ms();
(void) GET_STAT(); /* ensure drive irq is clear */
}
@@ -367,7 +345,7 @@ static void probe_cmos_for_drives (ide_hwif_t *hwif)
int unit;
#ifdef CONFIG_BLK_DEV_PDC4030
- if (hwif->is_pdc4030_2)
+ if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
return;
#endif /* CONFIG_BLK_DEV_PDC4030 */
outb_p(0x12,0x70); /* specify CMOS address 0x12 */
@@ -400,12 +378,12 @@ static void probe_hwif (ide_hwif_t *hwif)
return;
if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
probe_cmos_for_drives (hwif);
+ if ((hwif->chipset != ide_4drives || !hwif->mate->present)
#if CONFIG_BLK_DEV_PDC4030
- if (!hwif->is_pdc4030_2 &&
- (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) {
-#else
- if (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)) {
+ && (hwif->chipset != ide_pdc4030 || hwif->channel == 0)
#endif /* CONFIG_BLK_DEV_PDC4030 */
+ && (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)))
+ {
int msgout = 0;
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
@@ -431,8 +409,10 @@ static void probe_hwif (ide_hwif_t *hwif)
(void) probe_for_drive (drive);
if (drive->present && !hwif->present) {
hwif->present = 1;
- ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
- ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+ if (hwif->chipset != ide_4drives || !hwif->mate->present) {
+ ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
+ ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+ }
}
}
if (hwif->reset) {
@@ -446,7 +426,8 @@ static void probe_hwif (ide_hwif_t *hwif)
do {
delay_50ms();
stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
- } while ((stat & BUSY_STAT) && jiffies < timeout);
+ } while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
+
}
restore_flags(flags);
for (unit = 0; unit < MAX_DRIVES; ++unit) {
@@ -521,13 +502,11 @@ static int init_irq (ide_hwif_t *hwif)
save_match(hwif, h, &match);
}
if (hwif->serialized) {
- ide_hwif_t *mate = &ide_hwifs[hwif->index^1];
- if (index == mate->index || h->irq == mate->irq)
+ if (hwif->mate && hwif->mate->irq == h->irq)
save_match(hwif, h, &match);
}
if (h->serialized) {
- ide_hwif_t *mate = &ide_hwifs[h->index^1];
- if (hwif->irq == mate->irq)
+ if (h->mate && hwif->irq == h->mate->irq)
save_match(hwif, h, &match);
}
}
@@ -603,7 +582,7 @@ static void init_gendisk (ide_hwif_t *hwif)
{
struct gendisk *gd, **gdp;
unsigned int unit, units, minors;
- int *bs;
+ int *bs, *max_sect;
/* figure out maximum drive number on the interface */
for (units = MAX_DRIVES; units > 0; --units) {
@@ -615,13 +594,17 @@ static void init_gendisk (ide_hwif_t *hwif)
gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
+ max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL);
memset(gd->part, 0, minors * sizeof(struct hd_struct));
/* cdroms and msdos f/s are examples of non-1024 blocksizes */
blksize_size[hwif->major] = bs;
- for (unit = 0; unit < minors; ++unit)
+ max_sectors[hwif->major] = max_sect;
+ for (unit = 0; unit < minors; ++unit) {
*bs++ = BLOCK_SIZE;
+ *max_sect++ = 244;
+ }
for (unit = 0; unit < units; ++unit)
hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
@@ -640,9 +623,8 @@ static void init_gendisk (ide_hwif_t *hwif)
hwif->gd = *gdp = gd; /* link onto tail of list */
}
-static int hwif_init (int h)
+static int hwif_init (ide_hwif_t *hwif)
{
- ide_hwif_t *hwif = &ide_hwifs[h];
void (*rfn)(void);
if (!hwif->present)
@@ -692,7 +674,6 @@ static int hwif_init (int h)
return hwif->present;
}
-int ideprobe_init (void);
static ide_module_t ideprobe_module = {
IDE_PROBE_MODULE,
ideprobe_init,
@@ -713,9 +694,11 @@ int ideprobe_init (void)
* Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
*/
for (index = 0; index < MAX_HWIFS; ++index)
- if (probe[index]) probe_hwif (&ide_hwifs[index]);
+ if (probe[index])
+ probe_hwif(&ide_hwifs[index]);
for (index = 0; index < MAX_HWIFS; ++index)
- if (probe[index]) hwif_init (index);
+ if (probe[index])
+ hwif_init(&ide_hwifs[index]);
ide_register_module(&ideprobe_module);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/block/ide-proc.c b/drivers/block/ide-proc.c
new file mode 100644
index 000000000..55ad22521
--- /dev/null
+++ b/drivers/block/ide-proc.c
@@ -0,0 +1,505 @@
+/*
+ * linux/drivers/block/proc_ide.c Version 1.01 December 12, 1997
+ *
+ * Copyright (C) 1997-1998 Mark Lord
+ */
+
+/*
+ * This is the /proc/ide/ filesystem implementation.
+ *
+ * The major reason this exists is to provide sufficient access
+ * to driver and config data, such that user-mode programs can
+ * be developed to handle chipset tuning for most PCI interfaces.
+ * This should provide better utilities, and less kernel bloat.
+ *
+ * The entire pci config space for a PCI interface chipset can be
+ * retrieved by just reading it. e.g. "cat /proc/ide3/pci"
+ *
+ * To modify registers, do something like:
+ * echo "40:88" >/proc/ide/ide3/pci
+ * That expression writes 0x88 to pci config register 0x40
+ * on the chip which controls ide3. Multiple tuples can be issued,
+ * and the writes will be completed as an atomic set:
+ * echo "40:88 41:35 42:00 43:00" >/proc/ide/ide3/pci
+ * All numbers must be pairs of ascii hex digits.
+ *
+ * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY
+ * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the
+ * returned data as 256 16-bit words. The "hdparm" utility will
+ * be updated someday soon to use this mechanism.
+ *
+ * Feel free to develop and distribute fancy GUI configuration
+ * utilities for you favorite PCI chipsets. I'll be working on
+ * one for the Promise 20246 someday soon. -ml
+ *
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/ctype.h>
+#include "ide.h"
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/*
+ * Standard exit stuff:
+ */
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
+{ \
+ len -= off; \
+ if (len < count) { \
+ *eof = 1; \
+ if (len <= 0) \
+ return 0; \
+ } else \
+ len = count; \
+ *start = page + off; \
+ return len; \
+}
+
+
+#ifdef CONFIG_PCI
+
+static int ide_getxdigit(char c)
+{
+ int digit;
+ if (isdigit(c))
+ digit = c - '0';
+ else if (isxdigit(c))
+ digit = tolower(c) - 'a' + 10;
+ else
+ digit = -1;
+ return digit;
+}
+
+
+static int xx_xx_parse_error (const char *start, unsigned long maxlen)
+{
+ char errbuf[7];
+ int i, len = MIN(6, maxlen);
+ for (i = 0; i < len; ++i) {
+ char c = start[i];
+ if (!c || c == '\n')
+ c = '\0';
+ else if (iscntrl(c))
+ c = '?';
+ errbuf[i] = c;
+ }
+ errbuf[i] = '\0';
+ printk("proc_ide: error: expected 'xx:xx', but got '%s'\n", errbuf);
+ return -EINVAL;
+}
+
+static int proc_ide_write_pci
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *)data;
+ int for_real = 0;
+ unsigned long n, flags;
+ const char *start;
+
+ if (!suser())
+ return -EACCES;
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+ /*
+ * Do one full pass to verify all parameters,
+ * then do another to actually write the pci regs.
+ */
+ save_flags(flags);
+ do {
+ const char *p = buffer;
+ n = count;
+ if (for_real) {
+ unsigned long timeout = jiffies + (3 * HZ);
+ cli(); /* ensure all PCI writes are done together */
+ while (((ide_hwgroup_t *)(hwif->hwgroup))->active || (hwif->mate && ((ide_hwgroup_t *)(hwif->mate->hwgroup))->active)) {
+ sti();
+ if (0 < (signed long)(timeout - jiffies)) {
+ printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+ return -EBUSY;
+ }
+ cli();
+ }
+ }
+ while (n) {
+ int d1, d2, rc;
+ byte reg, val;
+ start = p;
+#if 0
+ printk("loop(%d): n=%ld, input=%.5s\n", for_real, n, p);
+#endif
+ if (n < 5)
+ goto parse_error;
+ if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+ goto parse_error;
+ reg = (d1 << 4) | d2;
+ if (*p++ != ':')
+ goto parse_error;
+ if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+ goto parse_error;
+ val = (d1 << 4) | d2;
+ if (n > 5 && !isspace(*p))
+ goto parse_error;
+ n -= 5;
+ while (n && isspace(*p)) {
+ --n;
+ ++p;
+ }
+ if (for_real) {
+#if 0
+ printk("proc_ide_write_pci: reg=0x%02x, val=0x%02x\n", reg, val);
+#endif
+ rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
+ if (rc) {
+ restore_flags(flags);
+ printk("proc_ide_write_pci: error writing bus %d fn %d reg 0x%02x value 0x%02x\n",
+ hwif->pci_bus, hwif->pci_fn, reg, val);
+ printk("proc_ide_write_pci: %s\n", pcibios_strerror(rc));
+ return -EIO;
+ }
+ }
+ }
+ } while (!for_real++);
+ restore_flags(flags);
+ return count;
+parse_error:
+ restore_flags(flags);
+ return xx_xx_parse_error(start, n);
+}
+
+static int proc_ide_read_pci
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *)data;
+ char *out = page;
+ int len, reg = 0;
+
+ out += sprintf(out, "Bus %d Function %d Vendor %04x Device %04x Channel %d\n",
+ hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
+ do {
+ byte val;
+ int rc = pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, reg, &val);
+ if (rc) {
+ printk("proc_ide_read_pci: error reading bus %d fn %d reg 0x%02x\n",
+ hwif->pci_bus, hwif->pci_fn, reg);
+ printk("proc_ide_read_pci: %s\n", pcibios_strerror(rc));
+ return -EIO;
+ out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
+ } else
+ out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
+ } while (reg < 0x100);
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_imodel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+ const char *vids, *dids;
+
+ vids = pci_strvendor(hwif->pci_devid.vid);
+ dids = pci_strdev(hwif->pci_devid.vid, hwif->pci_devid.did);
+ len = sprintf(page,"%s: %s\n", vids ? vids : "(none)", dids ? dids : "(none)");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+#endif /* CONFIG_PCI */
+
+static int proc_ide_read_type
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+ const char *name;
+
+ switch (hwif->chipset) {
+ case ide_unknown: name = "(none)"; break;
+ case ide_generic: name = "generic"; break;
+ case ide_pci: name = "pci"; break;
+ case ide_cmd640: name = "cmd640"; break;
+ case ide_dtc2278: name = "dtc2278"; break;
+ case ide_ali14xx: name = "ali14xx"; break;
+ case ide_qd6580: name = "qd6580"; break;
+ case ide_umc8672: name = "umc8672"; break;
+ case ide_ht6560b: name = "ht6560b"; break;
+ case ide_pdc4030: name = "pdc4030"; break;
+ case ide_rz1000: name = "rz1000"; break;
+ case ide_trm290: name = "trm290"; break;
+ case ide_4drives: name = "4drives"; break;
+ default: name = "(unknown)"; break;
+ }
+ len = sprintf(page, "%s\n", name);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_mate
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+
+ len = sprintf(page, "%s\n", hwif->mate->name);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_channel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_hwif_t *hwif = (ide_hwif_t *) data;
+ int len;
+
+ page[0] = hwif->channel ? '1' : '0';
+ page[1] = '\n';
+ len = 2;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_get_identify (ide_drive_t *drive, byte *buf)
+{
+ struct request rq;
+ byte *end;
+
+ ide_init_drive_cmd(&rq);
+ rq.buffer = buf;
+ *buf++ = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY;
+ *buf++ = 0;
+ *buf++ = 0;
+ *buf++ = 1;
+ end = buf + (SECTOR_WORDS * 4);
+ while (buf != end)
+ *buf++ = 0; /* pre-zero it, in case identify fails */
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ return 0;
+}
+
+static int proc_ide_read_identify
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (!proc_ide_get_identify(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_settings
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ out += sprintf(out,"multcount %i\n", drive->mult_count);
+ out += sprintf(out,"io_32bit %i\n", drive->io_32bit);
+ out += sprintf(out,"unmaskirq %i\n", drive->unmask);
+ out += sprintf(out,"using_dma %i\n", drive->using_dma);
+ out += sprintf(out,"nowerr %i\n", drive->bad_wstat == BAD_R_STAT);
+ out += sprintf(out,"keepsettings %i\n", drive->keep_settings);
+ out += sprintf(out,"nice %i/%i/%i\n", drive->nice0, drive->nice1, drive->nice2);
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+int proc_ide_read_capacity
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ int len;
+
+ len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+int proc_ide_read_geometry
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ out += sprintf(out,"physical %hi/%hi/%hi\n", drive->cyl, drive->head, drive->sect);
+ out += sprintf(out,"logical %hi/%hi/%hi\n", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_dmodel
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ struct hd_driveid *id = drive->id;
+ int len;
+
+ len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_driver
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ ide_driver_t *driver = (ide_driver_t *) drive->driver;
+ int len;
+
+ if (!driver)
+ len = sprintf(page, "(none)\n");
+ else
+ len = sprintf(page, "%s version %s\n", driver->name, driver->version);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_ide_read_media
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ const char *media;
+ int len;
+
+ switch (drive->media) {
+ case ide_disk: media = "disk\n";
+ break;
+ case ide_cdrom: media = "cdrom\n";
+ break;
+ case ide_tape: media = "tape\n";
+ break;
+ case ide_floppy:media = "floppy\n";
+ break;
+ default: media = "UNKNOWN\n";
+ break;
+ }
+ strcpy(page,media);
+ len = strlen(media);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+
+static ide_proc_entry_t generic_drive_entries[] = {
+ { "capacity", proc_ide_read_capacity, NULL },
+ { "driver", proc_ide_read_driver, NULL },
+ { "identify", proc_ide_read_identify, NULL },
+ { "media", proc_ide_read_media, NULL },
+ { "model", proc_ide_read_dmodel, NULL },
+ { "settings", proc_ide_read_settings, NULL },
+ { NULL, NULL, NULL }
+};
+
+void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
+{
+ struct proc_dir_entry *ent;
+
+ if (!drive->proc || !p)
+ return;
+ while (p->name != NULL) {
+ ent = create_proc_entry(p->name, 0, drive->proc);
+ if (!ent) return;
+ ent->data = drive;
+ ent->read_proc = p->read_proc;
+ ent->write_proc = p->write_proc;
+ p++;
+ }
+}
+
+void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
+{
+ if (!drive->proc || !p)
+ return;
+ while (p->name != NULL) {
+ remove_proc_entry(p->name, drive->proc);
+ p++;
+ }
+}
+
+static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent)
+{
+ int d;
+
+ for (d = 0; d < MAX_DRIVES; d++) {
+ ide_drive_t *drive = &hwif->drives[d];
+
+ if (!drive->present)
+ continue;
+ drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
+ if (drive->proc)
+ ide_add_proc_entries(drive, generic_drive_entries);
+ }
+}
+
+static void create_proc_ide_interfaces (struct proc_dir_entry *parent)
+{
+ int h;
+ struct proc_dir_entry *hwif_ent, *ent;
+
+ for (h = 0; h < MAX_HWIFS; h++) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+
+ if (!hwif->present)
+ continue;
+ hwif_ent = create_proc_entry(hwif->name, S_IFDIR, parent);
+ if (!hwif_ent) return;
+#ifdef CONFIG_PCI
+ if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
+ ent = create_proc_entry("pci", 0, hwif_ent);
+ if (!ent) return;
+ ent->data = hwif;
+ ent->read_proc = proc_ide_read_pci;
+ ent->write_proc = proc_ide_write_pci;;
+
+ ent = create_proc_entry("model", 0, hwif_ent);
+ if (!ent) return;
+ ent->data = hwif;
+ ent->read_proc = proc_ide_read_imodel;
+ }
+#endif /* CONFIG_PCI */
+ ent = create_proc_entry("channel", 0, hwif_ent);
+ if (!ent) return;
+ ent->data = hwif;
+ ent->read_proc = proc_ide_read_channel;
+
+ if (hwif->mate && hwif->mate->present) {
+ ent = create_proc_entry("mate", 0, hwif_ent);
+ if (!ent) return;
+ ent->data = hwif;
+ ent->read_proc = proc_ide_read_mate;
+ }
+
+ ent = create_proc_entry("type", 0, hwif_ent);
+ if (!ent) return;
+ ent->data = hwif;
+ ent->read_proc = proc_ide_read_type;
+
+ create_proc_ide_drives(hwif, hwif_ent);
+ }
+}
+
+void proc_ide_init(void)
+{
+ struct proc_dir_entry *ent;
+ ent = create_proc_entry("ide", S_IFDIR, 0);
+ if (!ent) return;
+ create_proc_ide_interfaces(ent);
+}
diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c
index e9d7f1433..a30fc9a58 100644
--- a/drivers/block/ide-tape.c
+++ b/drivers/block/ide-tape.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-tape.c Version 1.11 - BETA Dec 2, 1996
+ * linux/drivers/block/ide-tape.c Version 1.12 Dec 7, 1997
*
* Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
*
@@ -206,6 +206,11 @@
* Use ide_stall_queue() for DSC overlap.
* Use the maximum speed rather than the current speed
* to compute the request service time.
+ * Ver 1.12 Dec 7 97 Fix random memory overwriting and/or last block data
+ * corruption, which could occur if the total number
+ * of bytes written to the tape was not an integral
+ * number of tape blocks.
+ * Add support for INTERRUPT DRQ devices.
*
* Here are some words from the first releases of hd.c, which are quoted
* in ide.c and apply here as well:
@@ -315,6 +320,8 @@
* sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
*/
+#define IDETAPE_VERSION "1.12"
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -323,12 +330,9 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
@@ -692,6 +696,7 @@ typedef struct {
#define IDETAPE_PIPELINE_ERROR 3 /* Error detected in a pipeline stage */
#define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */
#define IDETAPE_FILEMARK 5 /* Currently on a filemark */
+#define IDETAPE_DRQ_INTERRUPT 6 /* DRQ interrupt device */
/*
* Supported ATAPI tape drives packet commands
@@ -1152,11 +1157,6 @@ static void idetape_postpone_request (ide_drive_t *drive)
*/
static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq)
{
- unsigned int major = HWIF(drive)->major;
- struct blk_dev_struct *bdev = &blk_dev[major];
-
- bdev->current_request=HWGROUP (drive)->rq; /* Since we may have taken it out */
-
ide_init_drive_cmd (rq);
rq->buffer = (char *) pc;
rq->cmd = IDETAPE_PC_RQ1;
@@ -1542,16 +1542,12 @@ static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
ide_drive_t *drive = hwgroup->drive;
struct request *rq = hwgroup->rq;
idetape_tape_t *tape = drive->driver_data;
- unsigned int major = HWIF(drive)->major;
- struct blk_dev_struct *bdev = &blk_dev[major];
int error;
#if IDETAPE_DEBUG_LOG
printk (KERN_INFO "Reached idetape_end_request\n");
#endif /* IDETAPE_DEBUG_LOG */
- bdev->current_request=rq; /* Since we may have taken it out */
-
switch (uptodate) {
case 0: error = IDETAPE_ERROR_GENERAL; break;
case 1: error = 0; break;
@@ -1723,18 +1719,23 @@ static void idetape_pc_intr (ide_drive_t *drive)
#ifdef CONFIG_BLK_DEV_IDEDMA
if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
- if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
- set_bit (PC_DMA_ERROR, &pc->flags);
+ if (HWIF(drive)->dmaproc(ide_dma_end, drive)) {
/*
- * We will currently correct the following in
- * idetape_analyze_error.
+ * A DMA error is sometimes expected. For example,
+ * if the tape is crossing a filemark during a
+ * READ command, it will issue an irq and position
+ * itself before the filemark, so that only a partial
+ * data transfer will occur (which causes the DMA
+ * error). In that case, we will later ask the tape
+ * how much bytes of the original request were
+ * actually transferred (we can't receive that
+ * information from the DMA engine on most chipsets).
*/
- pc->actually_transferred=HWIF(drive)->dmaproc(ide_dma_transferred, drive);
+ set_bit (PC_DMA_ERROR, &pc->flags);
} else {
pc->actually_transferred=pc->request_transfer;
idetape_update_buffers (pc);
}
- (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */
#if IDETAPE_DEBUG_LOG
printk (KERN_INFO "ide-tape: DMA finished\n");
#endif /* IDETAPE_DEBUG_LOG */
@@ -1780,7 +1781,7 @@ static void idetape_pc_intr (ide_drive_t *drive)
if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
- HWIF(drive)->dmaproc(ide_dma_off, drive);
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
ide_do_reset (drive);
return;
}
@@ -1873,11 +1874,31 @@ static void idetape_pc_intr (ide_drive_t *drive)
* we will handle the next request.
*
*/
+
+static void idetape_transfer_pc(ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->pc;
+ idetape_ireason_reg_t ireason;
+
+ if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+ printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+ return;
+ }
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+ ide_do_reset (drive);
+ return;
+ }
+ ide_set_handler(drive, &idetape_pc_intr, WAIT_CMD); /* Set the interrupt routine */
+ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */
+}
+
static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
{
idetape_tape_t *tape = drive->driver_data;
idetape_bcount_reg_t bcount;
- idetape_ireason_reg_t ireason;
int dma_ok=0;
#if IDETAPE_DEBUG_BUGS
@@ -1918,7 +1939,7 @@ static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
#ifdef CONFIG_BLK_DEV_IDEDMA
if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
- HWIF(drive)->dmaproc(ide_dma_off, drive);
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
}
if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
@@ -1929,35 +1950,19 @@ static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
OUT_BYTE (drive->select.all,IDE_SELECT_REG);
-
- ide_set_handler (drive, &idetape_pc_intr, WAIT_CMD); /* Set the interrupt routine */
- OUT_BYTE (WIN_PACKETCMD,IDE_COMMAND_REG); /* Issue the packet command */
-
- if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { /* Wait for DRQ to be ready - Assuming Accelerated DRQ */
- /*
- * We currently only support tape drives which report
- * accelerated DRQ assertion. For this case, specs
- * allow up to 50us. We really shouldn't get here.
- *
- * ??? Still needs to think what to do if we reach
- * here anyway.
- */
- printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
- return;
- }
- ireason.all=IN_BYTE (IDE_IREASON_REG);
- if (!ireason.b.cod || ireason.b.io) {
- printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
- ide_do_reset (drive);
- return;
- }
- atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */
#ifdef CONFIG_BLK_DEV_IDEDMA
if (dma_ok) { /* Begin DMA, if necessary */
set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
}
#endif /* CONFIG_BLK_DEV_IDEDMA */
+ if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
+ ide_set_handler(drive, &idetape_transfer_pc, WAIT_CMD);
+ OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+ } else {
+ OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG);
+ idetape_transfer_pc(drive);
+ }
}
static void idetape_media_access_finished (ide_drive_t *drive)
@@ -2179,7 +2184,6 @@ static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned
{
idetape_tape_t *tape = drive->driver_data;
idetape_pc_t *pc;
- struct blk_dev_struct *bdev = &blk_dev[HWIF(drive)->major];
struct request *postponed_rq = tape->postponed_rq;
idetape_status_reg_t status;
@@ -2198,32 +2202,6 @@ static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned
}
/*
- * This is an important point. We will try to remove our request
- * from the block device request queue while we service the
- * request. Note that the request must be returned to
- * bdev->current_request before the next call to
- * ide_end_drive_cmd or ide_do_drive_cmd to conform with the
- * normal behavior of the IDE driver, which leaves the active
- * request in bdev->current_request during I/O.
- *
- * This will eliminate fragmentation of disk/cdrom requests
- * around a tape request, now that we are using ide_next to
- * insert pending pipeline requests, since we have only one
- * ide-tape.c data request in the device request queue, and
- * thus once removed, ll_rw_blk.c will only see requests from
- * the other device.
- *
- * The potential fragmentation inefficiency was pointed to me
- * by Mark Lord.
- *
- * Uhuh.. the following "fix" is actually not entirely correct.
- * Some day we should probably move to a per device request
- * queue, rather than per interface.
- */
- if (rq->next != NULL && rq->rq_dev != rq->next->rq_dev)
- bdev->current_request=rq->next;
-
- /*
* Retry a failed packet command
*/
if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
@@ -2612,8 +2590,8 @@ static void idetape_empty_write_pipeline (ide_drive_t *drive)
if (tape->merge_stage_size % tape->tape_block_size) {
blocks++;
i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size;
- memset (tape->merge_stage->bh->b_data + tape->merge_stage->bh->b_count, 0, i);
- tape->merge_stage->bh->b_count += i;
+ memset (tape->bh->b_data + tape->bh->b_count, 0, i);
+ tape->bh->b_count += i;
}
(void) idetape_add_chrdev_write_request (drive, blocks);
tape->merge_stage_size = 0;
@@ -3378,9 +3356,9 @@ static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
case 1: printk (KERN_INFO "16 bytes\n");break;
default: printk (KERN_INFO "Reserved\n");break;
}
- printk (KERN_INFO "Model: %s\n",id->model);
- printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
- printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+ printk (KERN_INFO "Model: %.40s\n",id->model);
+ printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev);
+ printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no);
printk (KERN_INFO "Write buffer size: %d bytes\n",id->buf_size*512);
printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
@@ -3532,6 +3510,7 @@ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
ide_hwif_t *hwif = HWIF(drive);
unsigned long t1, tmid, tn, t;
u16 speed;
+ struct idetape_id_gcw gcw;
drive->driver_data = tape;
drive->ready_stat = 0; /* An ATAPI device ignores DRDY */
@@ -3543,6 +3522,9 @@ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
tape->chrdev_direction = idetape_direction_none;
tape->pc = tape->pc_stack;
tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+ *((unsigned short *) &gcw) = drive->id->config;
+ if (gcw.drq_type == 1)
+ set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
idetape_get_mode_sense_results (drive);
@@ -3635,6 +3617,8 @@ static ide_module_t idetape_module = {
* IDE subdriver functions, registered with ide.c
*/
static ide_driver_t idetape_driver = {
+ "ide-tape", /* name */
+ IDETAPE_VERSION, /* version */
ide_tape, /* media */
1, /* busy */
1, /* supports_dma */
@@ -3648,7 +3632,8 @@ static ide_driver_t idetape_driver = {
NULL, /* media_change */
idetape_pre_reset, /* pre_reset */
NULL, /* capacity */
- NULL /* special */
+ NULL, /* special */
+ NULL /* proc */
};
/*
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index 50af139cd..3b9a17283 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide.c Version 6.05 November 30, 1997
+ * linux/drivers/block/ide.c Version 6.11 December 5, 1997
*
* Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
*/
@@ -95,6 +95,9 @@
* added support for BIOS-enabled UltraDMA
* rename all "promise" things to "pdc4030"
* fix EZ-DRIVE handling on small disks
+ * Version 6.11 fix probe error in ide_scan_devices()
+ * fix ancient "jiffies" polling bugs
+ * mask all hwgroup interrupts on each irq entry
*
* Some additional driver compile-time options are in ide.h
*
@@ -111,18 +114,14 @@
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/mm.h>
-#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
-#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/init.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
@@ -312,11 +311,12 @@ void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
if (io_32bit) {
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
+ unsigned long flags;
+ save_flags(flags);
cli();
do_vlb_sync(IDE_NSECTOR_REG);
insl(IDE_DATA_REG, buffer, wcount);
- if (drive->unmask)
- sti();
+ restore_flags(flags);
} else
#endif /* SUPPORT_VLB_SYNC */
insl(IDE_DATA_REG, buffer, wcount);
@@ -344,11 +344,12 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
if (io_32bit) {
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
+ unsigned long flags;
+ save_flags(flags);
cli();
do_vlb_sync(IDE_NSECTOR_REG);
outsl(IDE_DATA_REG, buffer, wcount);
- if (drive->unmask)
- sti();
+ restore_flags(flags);
} else
#endif /* SUPPORT_VLB_SYNC */
outsl(IDE_DATA_REG, buffer, wcount);
@@ -469,13 +470,13 @@ static void atapi_reset_pollfunc (ide_drive_t *drive)
ide_hwgroup_t *hwgroup = HWGROUP(drive);
byte stat;
- OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ SELECT_DRIVE(HWIF(drive),drive);
udelay (10);
if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
printk("%s: ATAPI reset complete\n", drive->name);
} else {
- if (jiffies < hwgroup->poll_timeout) {
+ if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
return; /* continue polling */
}
@@ -500,7 +501,7 @@ static void reset_pollfunc (ide_drive_t *drive)
byte tmp;
if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
- if (jiffies < hwgroup->poll_timeout) {
+ if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
ide_set_handler (drive, &reset_pollfunc, HZ/20);
return; /* continue polling */
}
@@ -542,7 +543,7 @@ static void pre_reset (ide_drive_t *drive)
drive->unmask = 0;
drive->io_32bit = 0;
if (drive->using_dma)
- HWIF(drive)->dmaproc(ide_dma_off, drive);
+ (void) HWIF(drive)->dmaproc(ide_dma_off, drive);
}
if (drive->driver != NULL)
DRIVER(drive)->pre_reset(drive);
@@ -576,7 +577,7 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
/* For an ATAPI device, first try an ATAPI SRST. */
if (drive->media != ide_disk && !do_not_try_atapi) {
pre_reset(drive);
- OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ SELECT_DRIVE(hwif,drive);
udelay (20);
OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
@@ -808,7 +809,7 @@ static void drive_cmd_intr (ide_drive_t *drive)
{
struct request *rq = HWGROUP(drive)->rq;
byte *args = (byte *) rq->buffer;
- byte stat = GET_STAT();
+ byte test, stat = GET_STAT();
ide_sti();
if ((stat & DRQ_STAT) && args && args[3]) {
@@ -818,7 +819,10 @@ static void drive_cmd_intr (ide_drive_t *drive)
drive->io_32bit = io_32bit;
stat = GET_STAT();
}
- if (OK_STAT(stat,READY_STAT,BAD_STAT))
+ test = stat;
+ if (drive->media == ide_cdrom)
+ test = stat &~BUSY_STAT;
+ if (OK_STAT(test,READY_STAT,BAD_STAT))
ide_end_drive_cmd (drive, stat, GET_ERR());
else
ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
@@ -870,7 +874,7 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
ide_sti();
timeout += jiffies;
while ((stat = GET_STAT()) & BUSY_STAT) {
- if (jiffies > timeout) {
+ if (0 < (signed long)(jiffies - timeout)) {
restore_flags(flags);
ide_error(drive, "status timeout", stat);
return 1;
@@ -1007,7 +1011,7 @@ repeat:
do {
if (!drive->queue)
continue;
- if (drive->sleep && drive->sleep > jiffies)
+ if (drive->sleep && 0 < (signed long)(drive->sleep - jiffies))
continue;
if (!best) {
best = drive;
@@ -1019,7 +1023,7 @@ repeat:
best = drive;
} while ((drive = drive->next) != hwgroup->drive);
if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) {
- long t = (signed) (WAKEUP(best) - jiffies);
+ long t = (signed) (WAKEUP(best) - jiffies); /* BUGGY? */
if (t >= WAIT_MIN_SLEEP) {
/*
* We *may* have some time to spare, but first let's see if
@@ -1029,7 +1033,7 @@ repeat:
do {
if (drive->sleep) /* this drive tried to be nice to us */
continue;
- if (WAKEUP(drive) > jiffies - best->service_time && WAKEUP(drive) < jiffies + t) {
+ if (WAKEUP(drive) > (jiffies - best->service_time) && WAKEUP(drive) < (jiffies + t)) { /* BUGGY? */
ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
goto repeat;
}
@@ -1057,7 +1061,7 @@ static inline void ide_leave_hwgroup (ide_hwgroup_t *hwgroup)
sleep = drive->sleep;
} while ((drive = drive->next) != hwgroup->drive);
if (sleep) {
- if (sleep < jiffies + WAIT_MIN_SLEEP)
+ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
sleep = jiffies + WAIT_MIN_SLEEP;
hwgroup->timer.expires = sleep;
add_timer(&hwgroup->timer);
@@ -1189,7 +1193,7 @@ void ide_timer_expiry (unsigned long data)
handler(drive);
else { /* abort the operation */
if (hwgroup->hwif->dmaproc)
- (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+ (void) hwgroup->hwif->dmaproc (ide_dma_end, drive);
ide_error(drive, "irq timeout", GET_STAT());
}
cli();
@@ -1229,7 +1233,6 @@ void ide_timer_expiry (unsigned long data)
static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
{
byte stat;
- unsigned int unit;
ide_hwif_t *hwif = hwgroup->hwif;
/*
@@ -1237,27 +1240,18 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
*/
do {
if (hwif->irq == irq) {
- for (unit = 0; unit < MAX_DRIVES; ++unit) {
- ide_drive_t *drive = &hwif->drives[unit];
- if (!drive->present)
- continue;
- SELECT_DRIVE(hwif,drive);
- udelay(100); /* Ugly, but wait_stat() may not be safe here */
- if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) {
- /* Try to not flood the console with msgs */
- static unsigned long last_msgtime = 0;
- if ((last_msgtime + (HZ/2)) < jiffies) {
- last_msgtime = jiffies;
- (void) ide_dump_status(drive, "unexpected_intr", stat);
- }
+ stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+ if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+ /* Try to not flood the console with msgs */
+ static unsigned long last_msgtime = 0;
+ if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
+ last_msgtime = jiffies;
+ printk("%s%s: unexpected interrupt, status=0x%02x\n",
+ hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat);
}
- if ((stat & DRQ_STAT))
- try_to_flush_leftover_data(drive);
}
}
} while ((hwif = hwif->next) != hwgroup->hwif);
- SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
- udelay(100); /* Ugly, but wait_stat() may not be safe here */
}
/*
@@ -1266,14 +1260,28 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
ide_hwgroup_t *hwgroup = dev_id;
+ ide_hwif_t *hwif = hwgroup->hwif;
ide_handler_t *handler;
- if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_STATUS_OFFSET],
- hwgroup->hwif->io_ports[IDE_IRQ_OFFSET]))
+ if (!ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]))
return;
-
- if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
+ do {
+ if (hwif->irq != irq) disable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+ if (irq == hwif->irq && (handler = hwgroup->handler) != NULL) {
ide_drive_t *drive = hwgroup->drive;
+#if 1 /* temporary, remove later -- FIXME */
+ {
+ struct request *rq = hwgroup->rq;
+ if (rq != NULL
+ &&( MAJOR(rq->rq_dev) != HWIF(drive)->major
+ || (MINOR(rq->rq_dev) >> PARTN_BITS) != drive->select.b.unit))
+ {
+ printk("ide_intr: got IRQ from wrong device: email mlord@pobox.com!!\n");
+ return;
+ }
+ }
+#endif /* temporary */
hwgroup->handler = NULL;
del_timer(&(hwgroup->timer));
if (drive->unmask)
@@ -1289,6 +1297,10 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
unexpected_intr(irq, hwgroup);
}
cli();
+ hwif = hwgroup->hwif;
+ do {
+ if (hwif->irq != irq) enable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hwgroup->hwif);
}
/*
@@ -1627,6 +1639,7 @@ void ide_unregister (unsigned int index)
*/
unregister_blkdev(hwif->major, hwif->name);
kfree(blksize_size[hwif->major]);
+ kfree(max_sectors[hwif->major]);
blk_dev[hwif->major].request_fn = NULL;
blk_dev[hwif->major].data = NULL;
blk_dev[hwif->major].queue = NULL;
@@ -1686,13 +1699,15 @@ found:
static int ide_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- int err;
+ int err, major, minor;
ide_drive_t *drive;
unsigned long flags;
struct request rq;
+ kdev_t dev;
- if (!inode || !(inode->i_rdev))
+ if (!inode || !(dev = inode->i_rdev))
return -EINVAL;
+ major = MAJOR(dev); minor = MINOR(dev);
if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENODEV;
ide_init_drive_cmd (&rq);
@@ -1726,6 +1741,23 @@ static int ide_ioctl (struct inode *inode, struct file *file,
case BLKGETSIZE: /* Return device size */
return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
+ case BLKFRASET:
+ if (!suser()) return -EACCES;
+ max_readahead[major][minor] = arg;
+ return 0;
+
+ case BLKFRAGET:
+ return put_user(max_readahead[major][minor], (long *) arg);
+
+ case BLKSECTSET:
+ if (!suser()) return -EACCES;
+ if (!arg || arg > 0xff) return -EINVAL;
+ max_sectors[major][minor] = arg;
+ return 0;
+
+ case BLKSECTGET:
+ return put_user(max_sectors[major][minor], (long *) arg);
+
case BLKRRPART: /* Re-read partition tables */
if (!suser()) return -EACCES;
return ide_revalidate_disk(inode->i_rdev);
@@ -2074,6 +2106,9 @@ __initfunc(static int match_parm (char *s, const char *keywords[], int vals[], i
* This is the default for most chipsets,
* except the cmd640.
* "idex=serialize" : do not overlap operations on idex and ide(x^1)
+ * "idex=four" : four drives on idex and ide(x^1) share same ports
+ * "idex=reset" : reset interface before first use
+ * "idex=nodma" : do not enable DMA by default on either drive
*
* The following are valid ONLY on ide0,
* and the defaults for the base,ctl ports must not be altered.
@@ -2175,8 +2210,8 @@ __initfunc(void ide_setup (char *s))
/*
* Be VERY CAREFUL changing this: note hardcoded indexes below
*/
- const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "qd6580",
- "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "four", "reset", NULL};
+ const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "reset", "nodma", "four",
+ "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
hw = s[3] - '0';
hwif = &ide_hwifs[hw];
i = match_parm(&s[4], ide_words, vals, 3);
@@ -2184,42 +2219,19 @@ __initfunc(void ide_setup (char *s))
/*
* Cryptic check to ensure chipset not already set for hwif:
*/
- if (i > 0 || (i <= -5 && i != -13)) {
+ if (i > 0 || i <= -7) { /* is parameter a chipset name? */
if (hwif->chipset != ide_unknown)
- goto bad_option;
- if (i <= -5) {
- if (ide_hwifs[1].chipset != ide_unknown)
- goto bad_option;
- /*
- * Interface keywords work only for ide0:
- */
- if (hw != 0)
- goto bad_hwif;
- printk("\n");
- }
+ goto bad_option; /* chipset already specified */
+ if (i != -7 && hw != 0)
+ goto bad_hwif; /* chipset drivers are for "ide0=" only */
+ if (ide_hwifs[hw^1].chipset != ide_unknown)
+ goto bad_option; /* chipset for 2nd port already specified */
+ printk("\n");
}
switch (i) {
- case -13: /* "reset" */
- hwif->reset = 1;
- goto done;
-#ifdef CONFIG_BLK_DEV_4DRIVES
- case -12: /* "four" drives on one set of ports */
- {
- ide_hwif_t *mate = &ide_hwifs[hw^1];
- mate->mate = hwif;
- hwif->mate = mate;
- hwif->chipset = mate->chipset = ide_4drives;
- hwif->serialized = mate->serialized = 1;
- mate->drives[0].select.all ^= 0x20;
- mate->drives[1].select.all ^= 0x20;
- memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
- mate->irq = hwif->irq;
- goto done;
- }
-#endif /* CONFIG_BLK_DEV_4DRIVES */
#ifdef CONFIG_BLK_DEV_PDC4030
- case -11: /* "dc4030" */
+ case -14: /* "dc4030" */
{
extern void setup_pdc4030(ide_hwif_t *);
setup_pdc4030(hwif);
@@ -2227,7 +2239,7 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_PDC4030 */
#ifdef CONFIG_BLK_DEV_ALI14XX
- case -10: /* "ali14xx" */
+ case -13: /* "ali14xx" */
{
extern void init_ali14xx (void);
init_ali14xx();
@@ -2235,7 +2247,7 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_ALI14XX */
#ifdef CONFIG_BLK_DEV_UMC8672
- case -9: /* "umc8672" */
+ case -12: /* "umc8672" */
{
extern void init_umc8672 (void);
init_umc8672();
@@ -2243,7 +2255,7 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_UMC8672 */
#ifdef CONFIG_BLK_DEV_DTC2278
- case -8: /* "dtc2278" */
+ case -11: /* "dtc2278" */
{
extern void init_dtc2278 (void);
init_dtc2278();
@@ -2251,7 +2263,7 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_DTC2278 */
#ifdef CONFIG_BLK_DEV_CMD640
- case -7: /* "cmd640_vlb" */
+ case -10: /* "cmd640_vlb" */
{
extern int cmd640_vlb; /* flag for cmd640.c */
cmd640_vlb = 1;
@@ -2259,7 +2271,7 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_CMD640 */
#ifdef CONFIG_BLK_DEV_HT6560B
- case -6: /* "ht6560b" */
+ case -9: /* "ht6560b" */
{
extern void init_ht6560b (void);
init_ht6560b();
@@ -2267,13 +2279,31 @@ __initfunc(void ide_setup (char *s))
}
#endif /* CONFIG_BLK_DEV_HT6560B */
#if CONFIG_BLK_DEV_QD6580
- case -5: /* "qd6580" (has secondary i/f) */
+ case -8: /* "qd6580" */
{
extern void init_qd6580 (void);
init_qd6580();
goto done;
}
#endif /* CONFIG_BLK_DEV_QD6580 */
+#ifdef CONFIG_BLK_DEV_4DRIVES
+ case -7: /* "four" drives on one set of ports */
+ {
+ ide_hwif_t *mate = &ide_hwifs[hw^1];
+ mate->drives[0].select.all ^= 0x20;
+ mate->drives[1].select.all ^= 0x20;
+ hwif->chipset = mate->chipset = ide_4drives;
+ mate->irq = hwif->irq;
+ memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
+ goto do_serialize;
+ }
+#endif /* CONFIG_BLK_DEV_4DRIVES */
+ case -6: /* nodma */
+ hwif->no_autodma = 1;
+ goto done;
+ case -5: /* "reset" */
+ hwif->reset = 1;
+ goto done;
case -4: /* "noautotune" */
hwif->drives[0].autotune = 2;
hwif->drives[1].autotune = 2;
@@ -2284,8 +2314,9 @@ __initfunc(void ide_setup (char *s))
goto done;
case -2: /* "serialize" */
do_serialize:
- ide_hwifs[hw].serialized = 1; /* serialize */
- ide_hwifs[hw^1].serialized = 1; /* with mate */
+ hwif->mate = &ide_hwifs[hw^1];
+ hwif->mate->mate = hwif;
+ hwif->serialized = hwif->mate->serialized = 1;
goto done;
case -1: /* "noprobe" */
@@ -2395,44 +2426,37 @@ int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
/*
* probe_for_hwifs() finds/initializes "known" IDE interfaces
- *
- * This routine should ideally be using pcibios_find_class() to find all
- * PCI IDE interfaces, but that function causes some systems to "go weird".
*/
__initfunc(static void probe_for_hwifs (void))
{
#ifdef CONFIG_PCI
- /*
- * Find/initialize PCI IDE interfaces
- */
if (pcibios_present())
{
-#ifdef CONFIG_BLK_DEV_IDEDMA
- {
- extern void ide_scan_pcibus(void);
- ide_scan_pcibus();
- }
-#endif
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ ide_scan_pcibus();
+#else
#ifdef CONFIG_BLK_DEV_RZ1000
{
extern void ide_probe_for_rz100x(void);
ide_probe_for_rz100x();
}
-#endif
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
}
-#endif /* CONFIG_PCI */
+#endif /* CONFIG_PCI */
+
#ifdef CONFIG_BLK_DEV_CMD640
{
extern void ide_probe_for_cmd640x(void);
ide_probe_for_cmd640x();
}
-#endif
+#endif /* CONFIG_BLK_DEV_CMD640 */
#ifdef CONFIG_BLK_DEV_PDC4030
{
extern int init_pdc4030(void);
(void) init_pdc4030();
}
-#endif
+#endif /* CONFIG_BLK_DEV_PDC4030 */
}
__initfunc(void ide_init_builtin_drivers (void))
@@ -2460,6 +2484,10 @@ __initfunc(void ide_init_builtin_drivers (void))
#endif /* __mc68000__ */
#endif /* CONFIG_BLK_DEV_IDE */
+#ifdef CONFIG_PROC_FS
+ proc_ide_init();
+#endif
+
/*
* Attempt to match drivers for the available drives
*/
@@ -2552,18 +2580,19 @@ static void setup_driver_defaults (ide_drive_t *drive)
ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
{
unsigned int unit, index, i;
- ide_drive_t *drive;
for (index = 0; index < MAX_HWIFS; ++index)
if (ide_hwifs[index].present) goto search;
ide_init_module(IDE_PROBE_MODULE);
search:
for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
- for (unit = 0; unit < MAX_DRIVES; ++unit) {
- drive = &ide_hwifs[index].drives[unit];
- if (drive->present && drive->media == media &&
- drive->driver == driver && ++i > n)
- return drive;
+ ide_hwif_t *hwif = &ide_hwifs[index];
+ if (hwif->present) {
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+ return drive;
+ }
}
}
return NULL;
@@ -2590,6 +2619,7 @@ int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int versio
drive->nice1 = 1;
}
drive->revalidate = 1;
+ ide_add_proc_entries(drive, driver->proc);
return 0;
}
@@ -2603,6 +2633,7 @@ int ide_unregister_subdriver (ide_drive_t *drive)
restore_flags(flags);
return 1;
}
+ ide_remove_proc_entries(drive, DRIVER(drive)->proc);
drive->driver = NULL;
restore_flags(flags);
return 0;
@@ -2694,6 +2725,9 @@ EXPORT_SYMBOL(ide_end_request);
EXPORT_SYMBOL(ide_revalidate_disk);
EXPORT_SYMBOL(ide_cmd);
EXPORT_SYMBOL(ide_stall_queue);
+EXPORT_SYMBOL(ide_add_proc_entries);
+EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(proc_ide_read_geometry);
EXPORT_SYMBOL(ide_register);
EXPORT_SYMBOL(ide_unregister);
diff --git a/drivers/block/ide.h b/drivers/block/ide.h
index dd3549ac9..dfcb80fa5 100644
--- a/drivers/block/ide.h
+++ b/drivers/block/ide.h
@@ -7,10 +7,15 @@
*/
#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
#include <asm/ide.h>
/*
- * This is the multiple IDE interface driver, as evolved from hd.c.
+ * This is the multiple IDE interface driver, as evolved from hd.c.
* It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
* There can be up to two drives per interface, as per the ATA-2 spec.
*
@@ -22,7 +27,7 @@
/******************************************************************************
* IDE driver configuration options (play with these as desired):
- *
+ *
* REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
*/
#undef REALLY_FAST_IO /* define if ide ports are perfect */
@@ -164,18 +169,13 @@ typedef unsigned char byte; /* used everywhere */
#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
#define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */
-#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_TRM290)
#define SELECT_DRIVE(hwif,drive) \
{ \
if (hwif->selectproc) \
hwif->selectproc(drive); \
- else \
- OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \
+ OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \
}
-#else
-#define SELECT_DRIVE(hwif,drive) OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]);
-#endif /* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PDC4030 */
-
+
/*
* Now for the data we need to maintain per-drive: ide_drive_t
*/
@@ -248,7 +248,7 @@ typedef struct ide_drive_s {
byte bios_sect __attribute__ ((aligned (8))); /* BIOS/fdisk/LILO sectors per track */
unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
unsigned short cyl; /* "real" number of cyls */
- unsigned int timing_data; /* for use by tuneproc()'s */
+ unsigned int drive_data; /* for use by tuneproc/selectproc as needed */
void *hwif; /* actually (ide_hwif_t *) */
struct wait_queue *wqueue; /* used to wait for drive in open() */
struct hd_driveid *id; /* drive model identification info */
@@ -256,6 +256,7 @@ typedef struct ide_drive_s {
char name[4]; /* drive name, such as "hda" */
void *driver; /* (ide_driver_t *) */
void *driver_data; /* extra driver data */
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
} ide_drive_t;
/*
@@ -269,12 +270,9 @@ typedef struct ide_drive_s {
* Returns 1 if DMA read/write could not be started, in which case the caller
* should either try again later, or revert to PIO for the current request.
*/
-typedef enum { ide_dma_read = 0, ide_dma_write = 1,
- ide_dma_abort = 2, ide_dma_check = 3,
- ide_dma_status_bad = 4, ide_dma_transferred = 5,
- ide_dma_begin = 6, ide_dma_on = 7,
- ide_dma_off = 8, ide_dma_off_quietly = 9 }
- ide_dma_action_t;
+typedef enum { ide_dma_read, ide_dma_write, ide_dma_begin, ide_dma_end,
+ ide_dma_check, ide_dma_on, ide_dma_off, ide_dma_off_quietly
+ } ide_dma_action_t;
typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
@@ -293,7 +291,7 @@ typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
/*
- * This is used to provide HT6560B & PDC4030 & TRM290 interface support.
+ * This is used to provide support for strange interfaces
*/
typedef void (ide_selectproc_t) (ide_drive_t *);
@@ -305,8 +303,16 @@ typedef enum { ide_unknown, ide_generic, ide_pci,
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd6580, ide_umc8672, ide_ht6560b,
ide_pdc4030, ide_rz1000, ide_trm290,
- ide_4drives }
- hwif_chipset_t;
+ ide_4drives
+ } hwif_chipset_t;
+
+typedef struct ide_pci_devid_s {
+ unsigned short vid;
+ unsigned short did;
+} ide_pci_devid_t;
+
+#define IDE_PCI_DEVID_NULL ((ide_pci_devid_t){0,0})
+#define IDE_PCI_DEVID_EQ(a,b) (a.vid == b.vid && a.did == b.did)
typedef struct hwif_s {
struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
@@ -315,13 +321,14 @@ typedef struct hwif_s {
ide_drive_t drives[MAX_DRIVES]; /* drive info */
struct gendisk *gd; /* gendisk structure */
ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
-#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_TRM290)
ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
-#endif
ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
unsigned long *dmatable; /* dma physical region descriptor table */
struct hwif_s *mate; /* other hwif from same PCI chip */
- unsigned short dma_base; /* base addr for dma ports (triton) */
+ unsigned int dma_base; /* base addr for dma ports */
+ unsigned int config_data; /* for use by chipset-specific code */
+ unsigned int select_data; /* for use by chipset-specific code */
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
int irq; /* our irq number */
byte major; /* our major number */
char name[6]; /* name of interface, eg. "ide0" */
@@ -331,11 +338,12 @@ typedef struct hwif_s {
unsigned present : 1; /* this interface exists */
unsigned serialized : 1; /* serialized operation with mate hwif */
unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
-#ifdef CONFIG_BLK_DEV_PDC4030
- unsigned is_pdc4030_2: 1;/* 2nd i/f on pdc4030 */
-#endif /* CONFIG_BLK_DEV_PDC4030 */
- unsigned reset : 1; /* reset after probe */
- unsigned pci_port : 1; /* for dual-port chips: 0=primary, 1=secondary */
+ unsigned reset : 1; /* reset after probe */
+ unsigned no_autodma : 1; /* don't automatically enable DMA at boot */
+ byte channel; /* for dual-port chips: 0=primary, 1=secondary */
+ byte pci_bus; /* for pci chipsets */
+ byte pci_fn; /* for pci chipsets */
+ ide_pci_devid_t pci_devid; /* for pci chipsets: {VID,DID} */
#if (DISK_RECOVERY_TIME > 0)
unsigned long last_time; /* time when previous rq was done */
#endif
@@ -358,6 +366,20 @@ typedef struct hwgroup_s {
} ide_hwgroup_t;
/*
+ * /proc/ide interface
+ */
+typedef struct {
+ char *name;
+ read_proc_t *read_proc;
+ write_proc_t *write_proc;
+} ide_proc_entry_t;
+
+void proc_ide_init(void);
+void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
+void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
+read_proc_t proc_ide_read_geometry;
+
+/*
* Subdrivers support.
*/
#define IDE_SUBDRIVER_VERSION 1
@@ -374,6 +396,8 @@ typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
typedef void (ide_special_proc)(ide_drive_t *);
typedef struct ide_driver_s {
+ const char *name;
+ const char *version;
byte media;
unsigned busy : 1;
unsigned supports_dma : 1;
@@ -388,6 +412,7 @@ typedef struct ide_driver_s {
ide_pre_reset_proc *pre_reset;
ide_capacity_proc *capacity;
ide_special_proc *special;
+ ide_proc_entry_t *proc;
} ide_driver_t;
#define DRIVER(drive) ((ide_driver_t *)((drive)->driver))
@@ -410,7 +435,7 @@ typedef struct ide_module_s {
/*
* ide_hwifs[] is the master data structure used to keep track
* of just about everything in ide.c. Whenever possible, routines
- * should be using pointers to a drive (ide_drive_t *) or
+ * should be using pointers to a drive (ide_drive_t *) or
* pointers to a hwif (ide_hwif_t *), rather than indexing this
* structure directly (the allocation/layout may change!).
*
@@ -533,7 +558,7 @@ typedef enum
* If action is ide_next, then the rq is queued immediately after
* the currently-being-processed-request (if any), and the function
* returns without waiting for the new rq to be completed. As above,
- * This is VERY DANGEROUS, and is intended for careful use by the
+ * This is VERY DANGEROUS, and is intended for careful use by the
* ATAPI tape/cdrom driver code.
*
* If action is ide_end, then the rq is queued at the end of the
@@ -542,7 +567,7 @@ typedef enum
* use by the ATAPI tape/cdrom driver code.
*/
int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
-
+
/*
* Clean up after success/failure of an explicit drive cmd.
* stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
@@ -617,10 +642,16 @@ ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n);
int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
int ide_unregister_subdriver (ide_drive_t *drive);
+#ifdef CONFIG_BLK_DEV_IDEPCI
+unsigned int ide_find_free_region (unsigned short size) __init;
+void ide_scan_pcibus (void) __init;
+#endif
#ifdef CONFIG_BLK_DEV_IDEDMA
int ide_build_dmatable (ide_drive_t *drive);
+void ide_dma_intr (ide_drive_t *drive);
int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
-void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports);
+void ide_setup_dma (ide_hwif_t *hwif, unsigned int dmabase, unsigned int num_ports) __init;
+unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
#endif
#ifdef CONFIG_BLK_DEV_IDE
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 925423581..d4a83f9a1 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -87,6 +87,18 @@ int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
*/
int * max_readahead[MAX_BLKDEV] = { NULL, NULL, };
+/*
+ * Max number of sectors per request
+ */
+int * max_sectors[MAX_BLKDEV] = { NULL, NULL, };
+
+static inline int get_max_sectors(kdev_t dev)
+{
+ if (!max_sectors[MAJOR(dev)])
+ return 244; /* 254? */
+ return max_sectors[MAJOR(dev)][MINOR(dev)];
+}
+
static inline struct request **get_queue(kdev_t dev)
{
int major = MAJOR(dev);
@@ -300,9 +312,7 @@ void add_request(struct blk_dev_struct * dev, struct request * req)
sti();
}
-#define MAX_SECTORS 244
-
-static inline void attempt_merge (struct request *req)
+static inline void attempt_merge (struct request *req, int max_sectors)
{
struct request *next = req->next;
@@ -310,7 +320,7 @@ static inline void attempt_merge (struct request *req)
return;
if (req->sector + req->nr_sectors != next->sector)
return;
- if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+ if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors)
return;
req->bhtail->b_reqnext = next->bh;
req->bhtail = next->bhtail;
@@ -324,7 +334,7 @@ void make_request(int major,int rw, struct buffer_head * bh)
{
unsigned int sector, count;
struct request * req;
- int rw_ahead, max_req;
+ int rw_ahead, max_req, max_sectors;
count = bh->b_size >> 9;
sector = bh->b_rsector;
@@ -391,6 +401,7 @@ void make_request(int major,int rw, struct buffer_head * bh)
/*
* Try to coalesce the new request with old requests
*/
+ max_sectors = get_max_sectors(bh->b_rdev);
cli();
req = *get_queue(bh->b_rdev);
if (!req) {
@@ -428,7 +439,7 @@ void make_request(int major,int rw, struct buffer_head * bh)
continue;
if (req->cmd != rw)
continue;
- if (req->nr_sectors >= MAX_SECTORS)
+ if (req->nr_sectors + count > max_sectors)
continue;
if (req->rq_dev != bh->b_rdev)
continue;
@@ -438,7 +449,7 @@ void make_request(int major,int rw, struct buffer_head * bh)
req->bhtail = bh;
req->nr_sectors += count;
/* Can we now merge this req with the next? */
- attempt_merge(req);
+ attempt_merge(req, max_sectors);
/* or to the beginning? */
} else if (req->sector - count == sector) {
bh->b_reqnext = req->bh;
@@ -684,6 +695,7 @@ __initfunc(int blk_dev_init(void))
}
memset(ro_bits,0,sizeof(ro_bits));
memset(max_readahead, 0, sizeof(max_readahead));
+ memset(max_sectors, 0, sizeof(max_sectors));
#ifdef CONFIG_AMIGA_Z2RAM
z2_init();
#endif
diff --git a/drivers/block/ns87415.c b/drivers/block/ns87415.c
new file mode 100644
index 000000000..62ae86f61
--- /dev/null
+++ b/drivers/block/ns87415.c
@@ -0,0 +1,228 @@
+/*
+ * linux/drivers/block/ns87415.c Version 1.00 December 7, 1997
+ *
+ * Copyright (C) 1997-1998 Mark Lord
+ *
+ * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/io.h>
+#include "ide.h"
+
+
+#undef INCLUDE_OBSOLETE_NS87514_STUFF /* define this if you absolutely *need* the timings stuff */
+
+
+#ifdef INCLUDE_OBSOLETE_NS87514_STUFF
+/*
+ * This part adapted from code from David S. Miller (davem@caipfs.rutgers.edu)
+ * which was in turn adapted from code from Mark Lord.
+ *
+ * Here as a temporary measure only. Will be removed once /proc/ide/ is working.
+ */
+#include "ide_modes.h"
+
+static void ns87415_program_modes(ide_drive_t *drive, byte active_count, byte recovery_count)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ byte cfg_reg, regval;
+
+ cfg_reg = (0x44 + (8 * HWIF(drive)->channel) + (4 * drive->select.b.unit));
+
+ /* set identical PIO timings for read/write */
+ regval = (17 - active_count) | ((16 - recovery_count) << 4);
+ pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, cfg_reg, regval);
+ pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, cfg_reg + 1, regval);
+}
+
+static void set_ide_modes(ide_drive_t *drive, ide_pio_data_t *d, int bus_speed)
+{
+ int setup_time, active_time, cycle_time = d->cycle_time;
+ byte setup_count, active_count, pio_mode = d->pio_mode;
+ byte recovery_count, recovery_count2, cycle_count;
+ int recovery_time, clock_time;
+
+ if(pio_mode > 5)
+ pio_mode = 5;
+
+ setup_time = ide_pio_timings[pio_mode].setup_time;
+ active_time = ide_pio_timings[pio_mode].active_time;
+
+ recovery_time = cycle_time - (setup_time + active_time);
+ clock_time = 1000 / bus_speed;
+
+ cycle_count = (cycle_time + clock_time - 1) / clock_time;
+ setup_count = (setup_time + clock_time - 1) / clock_time;
+ active_count = (active_time + clock_time - 1) / clock_time;
+
+ if(active_count < 2)
+ active_count = 2;
+
+ recovery_count = (recovery_time + clock_time - 1) / clock_time;
+ recovery_count2 = cycle_count - (setup_count + active_count);
+
+ if(recovery_count2 > recovery_count)
+ recovery_count = recovery_count2;
+ if(recovery_count < 2)
+ recovery_count = 2;
+ if(recovery_count > 17) {
+ active_count += recovery_count - 17;
+ recovery_count = 17;
+ }
+
+ if(active_count > 16)
+ active_count = 16;
+ if(recovery_count > 16)
+ recovery_count = 16;
+
+ printk("active[%d CLKS] recovery[%d CLKS]\n", active_count, recovery_count);
+
+ ns87415_program_modes(drive, active_count, recovery_count);
+}
+
+/* Configure for best PIO mode. */
+static void ns87415_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+ ide_pio_data_t d;
+ int bus_speed = ide_system_bus_speed();
+
+ switch(mode_wanted) {
+ case 6:
+ case 7:
+ /* Changes to Fast-devsel are unsupported. */
+ return;
+
+ case 8:
+ case 9:
+ mode_wanted &= 1;
+ /* XXX set_prefetch_mode(index, mode_wanted); */
+ printk("%s: %sbled NS87415 prefetching...\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ };
+
+ (void) ide_get_best_pio_mode(drive, mode_wanted, 5, &d);
+
+ printk("%s: selected NS87415 PIO mode%d (%dns)%s ",
+ drive->name, d.pio_mode, d.cycle_time,
+ d.overridden ? " (overriding vendor mode)" : "");
+
+ set_ide_modes(drive, &d, bus_speed);
+}
+#endif /* INCLUDE_OBSOLETE_NS87514_STUFF */
+
+static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = {0};
+
+/*
+ * This routine either enables/disables (according to drive->present)
+ * the IRQ associated with the port (HWIF(drive)),
+ * and selects either PIO or DMA handshaking for the next I/O operation.
+ */
+static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int bit, new, *old = (unsigned int *) hwif->select_data;
+ unsigned int flags;
+
+ save_flags(flags);
+ cli();
+
+ new = *old;
+
+ /* adjust IRQ enable bit */
+ bit = 1 << (8 + hwif->channel);
+ new = drive->present ? (new | bit) : (new & ~bit);
+
+ /* select PIO or DMA */
+ bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1));
+ new = use_dma ? (new | bit) : (new & ~bit);
+
+ if (new != *old) {
+ *old = new;
+ (void) pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, new);
+ }
+ restore_flags(flags);
+}
+
+static void ns87415_selectproc (ide_drive_t *drive)
+{
+ ns87415_prepare_drive (drive, drive->using_dma);
+}
+
+static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+
+ switch (func) {
+ case ide_dma_end: /* returns 1 on error, 0 otherwise */
+ {
+ byte dma_stat = inb(hwif->dma_base+2);
+ int rc = (dma_stat & 7) != 4;
+ outb(7, hwif->dma_base); /* from errata: stop DMA, clear INTR & ERROR */
+ outb(dma_stat|6, hwif->dma_base+2); /* clear the INTR & ERROR bits */
+ return rc; /* verify good DMA status */
+ }
+ case ide_dma_write:
+ case ide_dma_read:
+ ns87415_prepare_drive(drive, 1); /* select DMA xfer */
+ if (!ide_dmaproc(func, drive)) /* use standard DMA stuff */
+ return 0;
+ ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */
+ return 1;
+ default:
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+ }
+}
+
+__initfunc(void ide_init_ns87415 (ide_hwif_t *hwif))
+{
+ unsigned int ctrl, progif, using_inta;
+
+ /*
+ * We cannot probe for IRQ: both ports share common IRQ on INTA.
+ * Also, leave IRQ masked during drive probing, to prevent infinite
+ * interrupts from a potentially floating INTA..
+ *
+ * IRQs get unmasked in selectproc when drive is first used.
+ */
+ (void) pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, &ctrl);
+ (void) pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, &progif);
+ /* is irq in "native" mode? */
+ using_inta = progif & (1 << (hwif->channel << 1));
+ if (!using_inta)
+ using_inta = ctrl & (1 << (4 + hwif->channel));
+ (void) pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, ctrl);
+ if (hwif->mate) {
+ hwif->select_data = hwif->mate->select_data;
+ } else {
+ hwif->select_data = (unsigned int) &ns87415_control[ns87415_count++];
+ ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */
+ if (using_inta)
+ ctrl &= ~(1 << 6); /* unmask INTA */
+ *((unsigned int *)hwif->select_data) = ctrl;
+ /*
+ * Set prefetch size to 512 bytes for both ports,
+ * but don't turn on/off prefetching here.
+ */
+ pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, 0x55, 0xee);
+ }
+ if (!using_inta)
+ hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */
+ else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+ hwif->irq = hwif->mate->irq; /* share IRQ with mate */
+
+ hwif->dmaproc = &ns87415_dmaproc;
+ hwif->selectproc = &ns87415_selectproc;
+#ifdef INCLUDE_OBSOLETE_NS87514_STUFF
+ hwif->tuneproc = &ns87415_tuneproc;
+#endif /* INCLUDE_OBSOLETE_NS87514_STUFF */
+}
diff --git a/drivers/block/opti621.c b/drivers/block/opti621.c
index 0e0b23758..a4c94b0e6 100644
--- a/drivers/block/opti621.c
+++ b/drivers/block/opti621.c
@@ -110,7 +110,7 @@ int reg_base;
/* there are stored pio numbers from other calls of opti621_tune_drive */
static void compute_pios(ide_drive_t *drive, byte pio)
-/* Store values into drive->timing_data
+/* Store values into drive->drive_data
* second_contr - 0 for primary controller, 1 for secondary
* slave_drive - 0 -> pio is for master, 1 -> pio is for slave
* pio - PIO mode for selected drive (for other we don't know)
@@ -119,17 +119,17 @@ static void compute_pios(ide_drive_t *drive, byte pio)
int d;
ide_hwif_t *hwif = HWIF(drive);
- drive->timing_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
+ drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
for (d = 0; d < 2; ++d) {
drive = &hwif->drives[d];
if (drive->present) {
- if (drive->timing_data == PIO_DONT_KNOW)
- drive->timing_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
+ if (drive->drive_data == PIO_DONT_KNOW)
+ drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL);
#ifdef OPTI621_DEBUG
- printk("%s: Selected PIO mode %d\n", drive->name, drive->timing_data);
+ printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data);
#endif
} else {
- drive->timing_data = PIO_NOT_EXIST;
+ drive->drive_data = PIO_NOT_EXIST;
}
}
}
@@ -192,7 +192,7 @@ static void compute_clocks(int pio, pio_clocks_t *clks)
clks->address_time = cmpt_clk(adr_setup, bus_speed);
clks->data_time = cmpt_clk(data_pls, bus_speed);
clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
- -adr_setup-data_pls, bus_speed);
+ - adr_setup-data_pls, bus_speed);
if (clks->address_time<1) clks->address_time = 1;
if (clks->address_time>4) clks->address_time = 4;
if (clks->data_time<1) clks->data_time = 1;
@@ -220,10 +220,10 @@ static void opti621_tune_drive (ide_drive_t *drive, byte pio)
byte cycle1, cycle2, misc;
ide_hwif_t *hwif = HWIF(drive);
- /* set drive->timing_data for both drives */
+ /* set drive->drive_data for both drives */
compute_pios(drive, pio);
- pio1 = hwif->drives[0].timing_data;
- pio2 = hwif->drives[1].timing_data;
+ pio1 = hwif->drives[0].drive_data;
+ pio2 = hwif->drives[1].drive_data;
compute_clocks(pio1, &first);
compute_clocks(pio2, &second);
@@ -274,11 +274,11 @@ static void opti621_tune_drive (ide_drive_t *drive, byte pio)
}
/*
- * ide_init_opti621() is Called from idedma.c once for each hwif found at boot.
+ * ide_init_opti621() is called once for each hwif found at boot.
*/
-void ide_init_opti621 (byte bus, byte fn, ide_hwif_t *hwif)
+void ide_init_opti621 (ide_hwif_t *hwif)
{
- hwif->drives[0].timing_data = PIO_DONT_KNOW;
- hwif->drives[1].timing_data = PIO_DONT_KNOW;
+ hwif->drives[0].drive_data = PIO_DONT_KNOW;
+ hwif->drives[1].drive_data = PIO_DONT_KNOW;
hwif->tuneproc = &opti621_tune_drive;
}
diff --git a/drivers/block/pdc4030.c b/drivers/block/pdc4030.c
index c161e58b6..30baff570 100644
--- a/drivers/block/pdc4030.c
+++ b/drivers/block/pdc4030.c
@@ -72,9 +72,7 @@ static void promise_selectproc (ide_drive_t *drive)
{
unsigned int number;
- OUT_BYTE(drive->select.all,IDE_SELECT_REG);
- udelay(1); /* paranoia */
- number = ((HWIF(drive)->is_pdc4030_2)<<1) + drive->select.b.unit;
+ number = (HWIF(drive)->channel << 1) + drive->select.b.unit;
OUT_BYTE(number,IDE_FEATURE_REG);
}
@@ -134,7 +132,7 @@ int init_pdc4030 (void)
drive = &hwif->drives[0];
second_hwif = &ide_hwifs[hwif->index+1];
- if(hwif->is_pdc4030_2) /* we've already been found ! */
+ if(hwif->chipset == ide_pdc4030) /* we've already been found ! */
return 1;
if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
@@ -172,6 +170,7 @@ int init_pdc4030 (void)
hwif->chipset = second_hwif->chipset = ide_pdc4030;
hwif->mate = second_hwif;
second_hwif->mate = hwif;
+ second_hwif->channel = 1;
hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
/* Shift the remaining interfaces down by one */
for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
@@ -182,7 +181,6 @@ int init_pdc4030 (void)
h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET];
h->noprobe = (h-1)->noprobe;
}
- second_hwif->is_pdc4030_2 = 1;
ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL);
second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET];
second_hwif->irq = hwif->irq;
diff --git a/drivers/block/qd6580.c b/drivers/block/qd6580.c
index 296aef3a7..9c13bfea9 100644
--- a/drivers/block/qd6580.c
+++ b/drivers/block/qd6580.c
@@ -65,4 +65,5 @@ void init_qd6580 (void)
ide_hwifs[0].tuneproc = &tune_qd6580;
ide_hwifs[0].mate = &ide_hwifs[1];
ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
}
diff --git a/drivers/block/rz1000.c b/drivers/block/rz1000.c
index b7270c559..670fe3e58 100644
--- a/drivers/block/rz1000.c
+++ b/drivers/block/rz1000.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/block/rz1000.c Version 0.03 Mar 20, 1996
+ * linux/drivers/block/rz1000.c Version 0.05 December 8, 1997
*
- * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ * Copyright (C) 1995-1998 Linus Torvalds & author (see below)
*/
/*
@@ -9,6 +9,8 @@
*
* This file provides support for disabling the buggy read-ahead
* mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ *
+ * Dunno if this fixes both ports, or only the primary port (?).
*/
#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -26,21 +28,40 @@
#include <linux/pci.h>
#include "ide.h"
-static void init_rz1000 (byte bus, byte fn, const char *name)
+#ifdef CONFIG_BLK_DEV_IDEPCI
+
+__initfunc(void ide_init_rz1000 (ide_hwif_t *hwif)) /* called from ide-pci.c */
+{
+ unsigned short reg;
+
+ hwif->chipset = ide_rz1000;
+ if (!pcibios_read_config_word (hwif->pci_bus, hwif->pci_fn, 0x40, &reg)
+ && !pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, 0x40, reg & 0xdfff))
+ {
+ printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name);
+ } else {
+ hwif->serialized = 1;
+ hwif->drives[0].no_unmask = 1;
+ hwif->drives[1].no_unmask = 1;
+ printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name);
+ }
+}
+
+#else
+
+__initfunc(static void init_rz1000 (byte bus, byte fn, const char *name))
{
unsigned short reg, h;
- printk("%s: buggy IDE controller: ", name);
if (!pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg) && !(reg & 1)) {
- printk("disabled (BIOS)\n");
+ printk("%s: buggy IDE controller disabled (BIOS)\n", name);
return;
}
if (!pcibios_read_config_word (bus, fn, 0x40, &reg)
&& !pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff))
{
- printk("disabled read-ahead\n");
+ printk("IDE: disabled chipset read-ahead (buggy %s)\n", name);
} else {
- printk("\n");
for (h = 0; h < MAX_HWIFS; ++h) {
ide_hwif_t *hwif = &ide_hwifs[h];
if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
@@ -50,13 +71,15 @@ static void init_rz1000 (byte bus, byte fn, const char *name)
hwif->serialized = 1;
hwif->drives[0].no_unmask = 1;
hwif->drives[1].no_unmask = 1;
- printk(" %s: serialized, disabled unmasking\n", hwif->name);
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+ hwif->channel = 1;
+ printk("%s: serialized, disabled unmasking (buggy %s)\n", hwif->name, name);
}
}
}
}
-void ide_probe_for_rz100x (void)
+__initfunc(void ide_probe_for_rz100x (void)) /* called from ide.c */
{
byte index, bus, fn;
@@ -65,3 +88,5 @@ void ide_probe_for_rz100x (void)
for (index = 0; !pcibios_find_device (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, index, &bus, &fn); ++index)
init_rz1000 (bus, fn, "RZ1001");
}
+
+#endif CONFIG_BLK_DEV_IDEPCI
diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c
index 7b8fb308c..f88f597d8 100644
--- a/drivers/block/trm290.c
+++ b/drivers/block/trm290.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/trm290.c Version 1.00 December 3, 1997
+ * linux/drivers/block/trm290.c Version 1.01 December 5, 1997
*
* Copyright (c) 1997-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
@@ -34,18 +34,20 @@
* The configuration registers are addressed in normal I/O port space
* and are used as follows:
*
- * 0x3df2 when WRITTEN: chiptest register (byte, write-only)
+ * trm290_base depends on jumper settings, and is probed for by ide-dma.c
+ *
+ * trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
* bit7 must always be written as "1"
* bits6-2 undefined
* bit1 1=legacy_compatible_mode, 0=native_pci_mode
* bit0 1=test_mode, 0=normal(default)
*
- * 0x3df2 when READ: status register (byte, read-only)
+ * trm290_base+2 when READ: status register (byte, read-only)
* bits7-2 undefined
* bit1 channel0 busmaster interrupt status 0=none, 1=asserted
* bit0 channel0 interrupt status 0=none, 1=asserted
*
- * 0x3df3 Interrupt mask register
+ * trm290_base+3 Interrupt mask register
* bits7-5 undefined
* bit4 legacy_header: 1=present, 0=absent
* bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
@@ -53,7 +55,7 @@
* bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
* bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
*
- * 0x3df1 "CPR" Config Pointer Register (byte)
+ * trm290_base+1 "CPR" Config Pointer Register (byte)
* bit7 1=autoincrement CPR bits 2-0 after each access of CDR
* bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
* bit5 0=enabled master burst access (default), 1=disable (write only)
@@ -61,7 +63,7 @@
* bit3 0=primary IDE channel, 1=secondary IDE channel
* bits2-0 register index for accesses through CDR port
*
- * 0x3df0 "CDR" Config Data Register (word)
+ * trm290_base+0 "CDR" Config Data Register (word)
* two sets of seven config registers,
* selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
* each index defined below:
@@ -130,98 +132,137 @@
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+
#include <asm/io.h>
#include "ide.h"
-static void select_dma_or_pio(ide_hwif_t *hwif, int dma)
+static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
{
- static int previous[2] = {-1,-1};
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int reg;
unsigned long flags;
- if (previous[hwif->pci_port] != dma) {
- unsigned short cfg1 = dma ? 0xa3 : 0x21;
- previous[hwif->pci_port] = dma;
- save_flags(flags);
- cli();
- outb(0x51|(hwif->pci_port<<3),0x3df1);
- outw(cfg1,0x3df0);
- restore_flags(flags);
+ /* select PIO or DMA */
+ reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
+
+ save_flags(flags);
+ cli();
+
+ if (reg != hwif->select_data) {
+ hwif->select_data = reg;
+ outb(0x51|(hwif->channel<<3), hwif->config_data+1); /* set PIO/DMA */
+ outw(reg & 0xff, hwif->config_data);
+ }
+
+ /* enable IRQ if not probing */
+ if (drive->present) {
+ reg = inw(hwif->config_data+3) & 0x13;
+ reg &= ~(1 << hwif->channel);
+ outw(reg, hwif->config_data+3);
}
+
+ restore_flags(flags);
}
-/*
- * trm290_dma_intr() is the handler for trm290 disk read/write DMA interrupts
- */
-static void trm290_dma_intr (ide_drive_t *drive)
+static void trm290_selectproc (ide_drive_t *drive)
{
- byte stat;
- int i;
- struct request *rq = HWGROUP(drive)->rq;
-
- stat = GET_STAT(); /* get drive status */
- if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
- unsigned short dma_stat = inw(HWIF(drive)->dma_base + 2);
- if (dma_stat == 0x00ff) {
- rq = HWGROUP(drive)->rq;
- for (i = rq->nr_sectors; i > 0;) {
- i -= rq->current_nr_sectors;
- ide_end_request(1, HWGROUP(drive));
- }
- return;
- }
- printk("%s: bad trm290 DMA status: 0x%04x\n", drive->name, dma_stat);
- }
- sti();
- ide_error(drive, "dma_intr", stat);
+ trm290_prepare_drive(drive, drive->using_dma);
}
static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned int count, reading = 1 << 1;
+ unsigned int count, reading = 2, writing = 0;
- if (drive->media == ide_disk) {
- switch (func) {
- case ide_dma_write:
- reading = 0;
+ switch (func) {
+ case ide_dma_write:
+ reading = 0;
+ writing = 1;
#ifdef TRM290_NO_DMA_WRITES
- break; /* always use PIO for writes */
+ break; /* always use PIO for writes */
#endif
- case ide_dma_read:
- if (!(count = ide_build_dmatable(drive)))
- break; /* try PIO instead of DMA */
- select_dma_or_pio(hwif, 1); /* select DMA mode */
- outl_p(virt_to_bus(hwif->dmatable)|reading, hwif->dma_base);
- outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
- ide_set_handler(drive, &trm290_dma_intr, WAIT_CMD);
- OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ case ide_dma_read:
+ if (!(count = ide_build_dmatable(drive)))
+ break; /* try PIO instead of DMA */
+ trm290_prepare_drive(drive, 1); /* select DMA xfer */
+ outl(virt_to_bus(hwif->dmatable)|reading|writing, hwif->dma_base);
+ outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
+ if (drive->media != ide_disk)
return 0;
- default:
- return ide_dmaproc(func, drive);
- }
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ case ide_dma_begin:
+ return 0;
+ case ide_dma_end:
+ return (inw(hwif->dma_base+2) != 0x00ff);
+ default:
+ return ide_dmaproc(func, drive);
}
- select_dma_or_pio(hwif, 0); /* force PIO mode for this operation */
+ trm290_prepare_drive(drive, 0); /* select PIO xfer */
return 1;
}
-static void trm290_selectproc (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = HWIF(drive);
-
- select_dma_or_pio(hwif, drive->using_dma);
- OUT_BYTE(drive->select.all, hwif->io_ports[IDE_SELECT_OFFSET]);
-}
-
/*
* Invoked from ide-dma.c at boot time.
*/
-__initfunc(void ide_init_trm290 (byte bus, byte fn, ide_hwif_t *hwif))
+__initfunc(void ide_init_trm290 (ide_hwif_t *hwif))
{
+ unsigned int cfgbase = 0;
+ unsigned long flags;
+ byte reg, progif;
+
hwif->chipset = ide_trm290;
- hwif->selectproc = trm290_selectproc;
- hwif->drives[0].autotune = 2; /* play it safe */
- hwif->drives[1].autotune = 2; /* play it safe */
- ide_setup_dma(hwif, hwif->pci_port ? 0x3d74 : 0x3df4, 2);
+ if (!pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, 0x09, &progif) && (progif & 5)
+ && !pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x20, &cfgbase) && cfgbase)
+ {
+ hwif->config_data = cfgbase & ~1;
+ printk("TRM290: chip config base at 0x%04x\n", hwif->config_data);
+ } else {
+ hwif->config_data = 0x3df4;
+ printk("TRM290: using default config base at 0x%04x\n", hwif->config_data);
+ }
+
+ save_flags(flags);
+ cli();
+ /* put config reg into first byte of hwif->select_data */
+ outb(0x51|(hwif->channel<<3), hwif->config_data+1);
+ hwif->select_data = 0x21; /* select PIO as default */
+ outb(hwif->select_data, hwif->config_data);
+ reg = inb(hwif->config_data+3); /* get IRQ info */
+ reg = (reg & 0x10) | 0x03; /* mask IRQs for both ports */
+ outb(reg, hwif->config_data+3);
+ restore_flags(flags);
+
+ if ((reg & 0x10))
+ hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */
+ else if (!hwif->irq && hwif->mate && hwif->mate->irq)
+ hwif->irq = hwif->mate->irq; /* sharing IRQ with mate */
+ ide_setup_dma(hwif, (hwif->channel ? hwif->config_data ^ 0x0080 : hwif->config_data) + 4, 2);
hwif->dmaproc = &trm290_dmaproc;
+ hwif->selectproc = &trm290_selectproc;
+ hwif->no_autodma = 1; /* play it safe for now */
+#if 1
+ {
+ /*
+ * My trm290-based card doesn't seem to work with all possible values
+ * for the control basereg, so this kludge ensures that we use only
+ * values that are known to work. Ugh. -ml
+ */
+ unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4;
+ static unsigned short next_offset = 0;
+
+ outb(0x54|(hwif->channel<<3), hwif->config_data+1);
+ old = inw(hwif->config_data) & ~1;
+ if (old != compat && inb(old+2) == 0xff) {
+ compat += (next_offset += 0x400); /* leave lower 10 bits untouched */
+ hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2; /* FIXME: should do a check_region */
+ outw(compat|1, hwif->config_data);
+ printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1);
+ }
+ }
+#endif
}
diff --git a/drivers/block/umc8672.c b/drivers/block/umc8672.c
index 628e13bb5..8856dad35 100644
--- a/drivers/block/umc8672.c
+++ b/drivers/block/umc8672.c
@@ -153,4 +153,5 @@ void init_umc8672 (void) /* called from ide.c */
ide_hwifs[1].tuneproc = &tune_umc;
ide_hwifs[0].mate = &ide_hwifs[1];
ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].channel = 1;
}
diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c
index 849487b19..c17ae5e14 100644
--- a/drivers/char/pc_keyb.c
+++ b/drivers/char/pc_keyb.c
@@ -17,6 +17,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/kbd_ll.h>
+#include <linux/delay.h>
#include <asm/keyboard.h>
#include <asm/bitops.h>
@@ -205,16 +206,21 @@ static volatile unsigned char resend = 0;
/*
* Wait for keyboard controller input buffer is empty.
+ *
+ * Don't use 'jiffies' so that we don't depend on
+ * interrupts..
*/
static inline void kb_wait(void)
{
- unsigned long start = jiffies;
+ unsigned long timeout = KBC_TIMEOUT;
do {
if (! (kbd_read_status() & KBD_STAT_IBF))
return;
- } while (jiffies - start < KBC_TIMEOUT);
+ udelay(1000);
+ timeout--;
+ } while (timeout);
#ifdef KBD_REPORT_TIMEOUTS
printk(KERN_WARNING "Keyboard timed out\n");
#endif
@@ -544,30 +550,35 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* send_data sends a character to the keyboard and waits
* for an acknowledge, possibly retrying if asked to. Returns
* the success status.
+ *
+ * Don't use 'jiffies', so that we don't depend on interrupts
*/
static int send_data(unsigned char data)
{
int retries = 3;
- unsigned long start;
do {
+ unsigned long timeout = KBD_TIMEOUT;
+
kb_wait();
acknowledge = 0;
resend = 0;
reply_expected = 1;
kbd_write_output(data);
kbd_pause();
- start = jiffies;
- do {
+ for (;;) {
if (acknowledge)
return 1;
- if (jiffies - start >= KBD_TIMEOUT) {
+ if (resend)
+ break;
+ udelay(1000);
+ if (!--timeout) {
#ifdef KBD_REPORT_TIMEOUTS
printk(KERN_WARNING "Keyboard timeout\n");
#endif
return 0;
}
- } while (!resend);
+ }
} while (retries-- > 0);
#ifdef KBD_REPORT_TIMEOUTS
printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n");
diff --git a/drivers/char/pc_keyb.h b/drivers/char/pc_keyb.h
index ffbfc49e2..a70763bf0 100644
--- a/drivers/char/pc_keyb.h
+++ b/drivers/char/pc_keyb.h
@@ -15,9 +15,9 @@
#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */
#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */
-#define KBD_INIT_TIMEOUT HZ /* Timeout for initializing the keyboard */
-#define KBC_TIMEOUT (HZ/4) /* Timeout for sending to keyboard controller */
-#define KBD_TIMEOUT (HZ/4) /* Timeout for keyboard command acknowledge */
+#define KBD_INIT_TIMEOUT HZ /* Timeout in jiffies for initializing the keyboard */
+#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */
+#define KBD_TIMEOUT 250 /* Timeout in ms for keyboard command acknowledge */
/*
* Internal variables of the driver
diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c
index 207fc4cf4..2226f9f3c 100644
--- a/drivers/char/pcwd.c
+++ b/drivers/char/pcwd.c
@@ -30,6 +30,7 @@
* code bits, and added compatibility to 2.1.x.
* 970912 Enabled board on open and disable on close.
* 971107 Took account of recent VFS changes (broke read).
+ * 971210 Disable board on initialisation in case board already ticking.
*/
#include <linux/module.h>
@@ -579,6 +580,12 @@ __initfunc(int pcwatchdog_init(void))
pcwd_showprevstate();
+ /* Disable the board */
+ if (revision == PCWD_REVISION_C) {
+ outb_p(0xA5, current_readport + 3);
+ outb_p(0xA5, current_readport + 3);
+ }
+
if (revision == PCWD_REVISION_A)
request_region(current_readport, 2, "PCWD Rev.A (Berkshire)");
else
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index ae1f06c99..d645ce165 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -323,7 +323,7 @@ inline static void adapter_reset(struct device *dev)
* never happen in theory, but seems to occur occasionally if the card gets
* prodded at the wrong time.
*/
-static inline void check_dma(struct device *dev)
+static inline void check_3c505_dma(struct device *dev)
{
elp_device *adapter = dev->priv;
if (adapter->dmaing && (jiffies > (adapter->current_dma.start_time + 10))) {
@@ -406,7 +406,7 @@ static int send_pcb(struct device *dev, pcb_struct * pcb)
int timeout;
elp_device *adapter = dev->priv;
- check_dma(dev);
+ check_3c505_dma(dev);
if (adapter->dmaing && adapter->current_dma.direction == 0)
return FALSE;
@@ -723,7 +723,7 @@ static void elp_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
}
} else {
/* has one timed out? */
- check_dma(dev);
+ check_3c505_dma(dev);
}
sti();
@@ -1088,7 +1088,7 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
return 1;
}
- check_dma(dev);
+ check_3c505_dma(dev);
/*
* if the transmitter is still busy, we have a transmit timeout...
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index c2fa5b507..4202f8ef6 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1,31 +1,60 @@
-/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */
/*
- Written 1995 by Donald Becker.
+ Written 1996-1997 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
- This driver is for the 3Com "Vortex" series ethercards. Members of
- the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
- EtherLink.
+ This driver is for the 3Com "Vortex" and "Boomerang" series ethercards.
+ Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597
+ and the EtherLink XL 3c900 and 3c905 cards.
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
*/
-static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
+static char *version =
+"3c59x.c:v0.46B 9/25/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
-/* "Knobs" that turn on special features. */
-/* Enable the experimental automatic media selection code. */
+/* "Knobs" that adjust features and parameters. */
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1512 effectively disables this feature. */
+static const rx_copybreak = 200;
+/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
+static const mtu = 1500;
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+
+/* Enable the automatic media selection code -- usually set. */
#define AUTOMEDIA 1
-/* Allow the use of bus master transfers instead of programmed-I/O for the
- Tx process. Bus master transfers are always disabled by default, but
- iff this is set they may be turned on using 'options'. */
+/* Allow the use of fragment bus master transfers instead of only
+ programmed-I/O for Vortex cards. Full-bus-master transfers are always
+ enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
+ the feature may be turned on using 'options'. */
#define VORTEX_BUS_MASTER
+
+/* A few values that may be tweaked. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 32
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -36,16 +65,10 @@ static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
-#include <linux/init.h>
-
-#include <linux/delay.h>
-
-#ifdef CONFIG_PCI
#include <linux/pci.h>
#include <linux/bios32.h>
-#endif
-
#include <linux/timer.h>
+#include <asm/irq.h> /* For NR_IRQS only. */
#include <asm/bitops.h>
#include <asm/io.h>
@@ -53,27 +76,60 @@ static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+ This is only in the support-all-kernels source code. */
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h> /* Redundant above, here for easy clean-up. */
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x) /* What to put in timer->expires. */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#if defined(__alpha)
+#error "The Alpha architecture is only support with kernel version 2.0."
+#endif
+#define virt_to_bus(addr) ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else /* 1.3.0 and later */
#define RUN_AT(x) (jiffies + (x))
#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+#ifdef SA_SHIRQ
#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#else
+#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
/* "Knobs" for adjusting internal parameters. */
/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define VORTEX_DEBUG 2
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
- limited cases. */
-#define WAIT_TX_AVAIL 200
+#define VORTEX_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+ debugging. */
+static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;
/* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT 40 /* Time in jiffies before concluding Tx hung */
-/* The total size is twice that of the original EtherLinkIII series: the
- runtime register window, window 1, is now always mapped in. */
+/* The Vortex size is twice that of the original EtherLinkIII series: the
+ runtime register window, window 1, is now always mapped in.
+ The Boomerang size is twice as large as the Vortex -- it has additional
+ bus master control registers. */
#define VORTEX_TOTAL_SIZE 0x20
+#define BOOMERANG_TOTAL_SIZE 0x40
#ifdef HAVE_DEVLIST
struct netdev_entry tc59x_drv =
@@ -86,27 +142,40 @@ static int vortex_debug = VORTEX_DEBUG;
static int vortex_debug = 1;
#endif
-#ifdef CONFIG_PCI
-static int product_ids[] __initdata = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
-#endif
+/* Set iff a MII transceiver on any interface requires mdio preamble. */
+static char mii_preamble_required = 0;
+/* Caution! These entries must be consistent, with the EISA ones last. */
+static const int product_ids[] = {
+ 0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0, 0};
static const char *product_names[] = {
"3c590 Vortex 10Mbps",
"3c595 Vortex 100baseTX",
"3c595 Vortex 100baseT4",
"3c595 Vortex 100base-MII",
- "EISA Vortex 3c597",
+ "3c900 Boomerang 10baseT",
+ "3c900 Boomerang 10Mbps/Combo",
+ "3c905 Boomerang 100baseTx",
+ "3c905 Boomerang 100baseT4",
+ "3c592 EISA 10mbps Demon/Vortex",
+ "3c597 EISA Fast Demon/Vortex",
};
-#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */
+#define DEMON10_INDEX 8
+#define DEMON100_INDEX 9
/*
Theory of Operation
I. Board Compatibility
-This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
-10/100baseT adapter. It also works with the 3c590, a similar product
-with only a 10Mbs interface.
+This device driver is designed for the 3Com FastEtherLink and FastEtherLink
+XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs
+versions of the FastEtherLink cards. The supported product IDs are
+ 3c590, 3c592, 3c595, 3c597, 3c900, 3c905
+
+The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
+the kernel source or available from
+ cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
II. Board-specific settings
@@ -122,12 +191,33 @@ The 3c59x series use an interface that's very similar to the previous 3c5x9
series. The primary interface is two programmed-I/O FIFOs, with an
alternate single-contiguous-region bus-master transfer (see next).
+The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
+lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
+DEC Tulip and Intel Speedo3. The first chip version retains a compatible
+programmed-I/O interface that will be removed in the 'B' and subsequent
+revisions.
+
One extension that is advertised in a very large font is that the adapters
-are capable of being bus masters. Unfortunately this capability is only for
-a single contiguous region making it less useful than the list of transfer
-regions available with the DEC Tulip or AMD PCnet. Given the significant
-performance impact of taking an extra interrupt for each transfer, using
-DMA transfers is a win only with large blocks.
+are capable of being bus masters. On the Vortex chip this capability was
+only for a single contiguous region making it far less useful than the full
+bus master capability. There is a significant performance impact of taking
+an extra interrupt or polling for the completion of each transfer, as well
+as difficulty sharing the single transfer engine between the transmit and
+receive threads. Using DMA transfers is a win only with large blocks or
+with the flawed versions of the Intel Orion motherboard PCI controller.
+
+The Boomerang chip's full-bus-master interface is useful, and has the
+currently-unused advantages over other similar chips that queued transmit
+packets may be reordered and receive buffer groups are associated with a
+single frame.
+
+With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
+Tather than a fixed intermediate receive buffer, this scheme allocates
+full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as
+the copying breakpoint: it is chosen to trade-off the memory wasted by
+passing the full-sized skbuff to the queue layer for all frames vs. the
+copying cost of copying a frame to a correctly-sized skbuff.
+
IIIC. Synchronization
The driver runs as two independent, single-threaded flows of control. One
@@ -137,8 +227,8 @@ threaded by the hardware and other software.
IV. Notes
-Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
-3c590 and 3c595 boards.
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development
+3c590, 3c595, and 3c900 boards.
The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
the EISA version is called "Demon". According to Terry these names come
from rides at the local amusement park.
@@ -169,8 +259,10 @@ limit of 4K.
enum vortex_cmd {
TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
- RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
- TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11,
+ UpStall = 6<<11, UpUnstall = (6<<11)+1,
+ DownStall = (6<<11)+2, DownUnstall = (6<<11)+3,
+ RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
SetTxThreshold = 18<<11, SetTxStart = 19<<11,
@@ -185,7 +277,8 @@ enum RxFilter {
enum vortex_status {
IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
- IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+ IntReq = 0x0040, StatsFull = 0x0080,
+ DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10,
DMAInProgress = 1<<11, /* DMA controller is still busy.*/
CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/
};
@@ -200,6 +293,7 @@ enum Window1 {
enum Window0 {
Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
Wn0EepromData = 12, /* Window 0: EEPROM results register. */
+ IntrStatus=0x0E, /* Valid in all windows. */
};
enum Win0_EEPROM_bits {
EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
@@ -222,12 +316,12 @@ union wn3_config {
unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
int pad8:8;
unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
- int pad24:8;
+ int pad24:7;
} u;
};
-enum Window4 {
- Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */
+enum Window4 { /* Window 4: Xcvr/media bits. */
+ Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
};
enum Win4_Media_bits {
Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
@@ -238,12 +332,54 @@ enum Win4_Media_bits {
enum Window7 { /* Window 7: Bus Master control. */
Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
};
+/* Boomerang bus master control registers. */
+enum MasterCtrl {
+ PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c,
+ TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38,
+};
+
+/* The Rx and Tx descriptor lists.
+ Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
+ alignment contraint on tx_ring[] and rx_ring[]. */
+#define LAST_FRAG 0x80000000 /* Last Addr/Len pair in descriptor. */
+struct boom_rx_desc {
+ u32 next; /* Last entry points to 0. */
+ s32 status;
+ u32 addr; /* Up to addr/len possible.. */
+ s32 length; /* set high bit to indicate last pair. */
+};
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+ RxDComplete=0x00008000, RxDError=0x4000,
+ /* See boomerang_rx() for actual error bits */
+};
+
+struct boom_tx_desc {
+ u32 next; /* Last entry points to 0. */
+ s32 status; /* bits 0:12 length, others see below. */
+ u32 addr;
+ s32 length;
+};
+
+/* Values for the Tx status entry. */
+enum tx_desc_status {
+ CRCDisable=0x2000, TxDComplete=0x8000,
+ TxIntrUploaded=0x80000000, /* IRQ when in FIFO, but maybe not sent. */
+};
struct vortex_private {
char devname[8]; /* "ethN" string, also for kernel debug. */
const char *product_name;
struct device *next_module;
- struct net_device_stats stats;
+ /* The Rx and Tx rings are here to keep them quad-word-aligned. */
+ struct boom_rx_desc rx_ring[RX_RING_SIZE];
+ struct boom_tx_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of transmit- and receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct enet_statistics stats;
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
struct timer_list timer; /* Media selection timer. */
int options; /* User-settable misc. driver options. */
@@ -251,12 +387,23 @@ struct vortex_private {
unsigned int available_media:8, /* From Wn3_Options */
media_override:3, /* Passed-in media type. */
default_media:3, /* Read from the EEPROM. */
- full_duplex:1, bus_master:1, autoselect:1;
+ full_duplex:1, autoselect:1,
+ bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
+ tx_full:1;
+ u16 capabilities; /* Adapter capabilities word. */
+ u16 info1, info2; /* Software information information. */
+ unsigned char phys[2]; /* MII device addresses. */
};
/* The action to take with a media selection timer tick.
Note that we deviate from the 3Com order by checking 10base2 before AUI.
*/
+enum xcvr_types {
+ XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+ XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8,
+};
+
static struct media_table {
char *name;
unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
@@ -264,30 +411,44 @@ static struct media_table {
next:8; /* The media type to try next. */
short wait; /* Time before we check media status. */
} media_tbl[] = {
- { "10baseT", Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
- { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
- { "undefined", 0, 0x80, 0 /* Undefined */, 0},
- { "10base2", 0, 0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
- { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
- { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
- { "MII", 0, 0x40, 0 /* MII->10baseT */, (14*HZ)/10},
- { "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 0},
- { "Default", 0, 0xFF, 0 /* Use default */, 0},
+ { "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10},
+ { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10},
+ { "undefined", 0, 0x80, XCVR_10baseT, 10000},
+ { "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10},
+ { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10},
+ { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10},
+ { "MII", 0, 0x40, XCVR_10baseT, 3*HZ },
+ { "undefined", 0, 0x01, XCVR_10baseT, 10000},
+ { "Default", 0, 0xFF, XCVR_10baseT, 10000},
};
static int vortex_scan(struct device *dev);
-static int vortex_found_device(struct device *dev, int ioaddr, int irq,
- int product_index, int options);
+static struct device *vortex_found_device(struct device *dev, int ioaddr,
+ int irq, int product_index,
+ int options, int card_idx);
static int vortex_probe1(struct device *dev);
static int vortex_open(struct device *dev);
+static void mdio_sync(int ioaddr, int bits);
+static int mdio_read(int ioaddr, int phy_id, int location);
+#ifdef HAVE_PRIVATE_IOCTL
+static void mdio_write(int ioaddr, int phy_id, int location, int value);
+#endif
static void vortex_timer(unsigned long arg);
static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev);
static int vortex_rx(struct device *dev);
+static int boomerang_rx(struct device *dev);
static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
static int vortex_close(struct device *dev);
static void update_stats(int addr, struct device *dev);
-static struct net_device_stats *vortex_get_stats(struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
static void set_rx_mode(struct device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+#endif
+#ifndef NEW_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
/* Unlike the other PCI cards the 59x cards don't need a large contiguous
@@ -307,11 +468,15 @@ static void set_rx_mode(struct device *dev);
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
/* Note: this is the only limit on the number of cards supported!! */
static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+/* Variables to work-around the Compaq PCI BIOS32 problem. */
+static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
#ifdef MODULE
static int debug = -1;
-/* A list of all installed Vortex devices, for removing the driver module. */
-static struct device *root_vortex_dev = NULL;
int
init_module(void)
@@ -329,7 +494,7 @@ init_module(void)
}
#else
-__initfunc(int tc59x_probe(struct device *dev))
+int tc59x_probe(struct device *dev)
{
int cards_found = 0;
@@ -342,100 +507,148 @@ __initfunc(int tc59x_probe(struct device *dev))
}
#endif /* not MODULE */
-__initfunc(static int vortex_scan(struct device *dev))
+static int vortex_scan(struct device *dev)
{
int cards_found = 0;
-#ifdef CONFIG_PCI
+#ifndef NO_PCI /* Allow an EISA-only driver. */
+ /* Ideally we would detect all cards in slot order. That would
+ be best done a central PCI probe dispatch, which wouldn't work
+ well with the current structure. So instead we detect 3Com cards
+ in slot order. */
if (pcibios_present()) {
static int pci_index = 0;
- static int board_index = 0;
- for (; product_ids[board_index]; board_index++, pci_index = 0) {
- for (; pci_index < 16; pci_index++) {
- unsigned char pci_bus, pci_device_fn, pci_irq_line;
- unsigned char pci_latency;
- unsigned int pci_ioaddr;
- unsigned short pci_command;
-
- if (pcibios_find_device(TCOM_VENDOR_ID,
- product_ids[board_index], pci_index,
- &pci_bus, &pci_device_fn))
+ unsigned char pci_bus, pci_device_fn;
+
+ for (;pci_index < 0xff; pci_index++) {
+ unsigned char pci_irq_line, pci_latency;
+ unsigned short pci_command, vendor, device;
+ unsigned int pci_ioaddr;
+
+ int board_index = 0;
+ if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
+ pci_index, &pci_bus, &pci_device_fn)
+ != PCIBIOS_SUCCESSFUL)
+ break;
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_DEVICE_ID, &device);
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+
+ if (vendor != TCOM_VENDOR_ID)
+ continue;
+
+ for (board_index = 0; product_ids[board_index]; board_index++) {
+ if (device == product_ids[board_index])
break;
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq_line);
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &pci_ioaddr);
- /* Remove I/O space marker in bit 0. */
- pci_ioaddr &= ~3;
+ }
+ if (product_ids[board_index] == 0) {
+ printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:"
+ " not configured.\n", device);
+ continue;
+ }
+ if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE))
+ continue;
-#ifdef VORTEX_BUS_MASTER
+ dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
+ board_index, dev && dev->mem_start
+ ? dev->mem_start : options[cards_found],
+ cards_found);
+
+ if (dev) {
/* Get and check the bus-master and latency values.
Some PCI BIOSes fail to set the master-enable bit, and
the latency timer must be set to the maximum value to avoid
data corruption that occurs when the timer expires during
- a transfer. Yes, it's a bug. */
+ a transfer -- a bug in the Vortex chip. */
pcibios_read_config_word(pci_bus, pci_device_fn,
PCI_COMMAND, &pci_command);
if ( ! (pci_command & PCI_COMMAND_MASTER)) {
- printk(" PCI Master Bit has not been set! Setting...\n");
+ printk("%s: PCI Master Bit has not been set! "
+ " Setting...\n", dev->name);
pci_command |= PCI_COMMAND_MASTER;
pcibios_write_config_word(pci_bus, pci_device_fn,
PCI_COMMAND, pci_command);
}
pcibios_read_config_byte(pci_bus, pci_device_fn,
PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency != 255) {
- printk(" Overriding PCI latency timer (CFLT) setting of"
- " %d, new value is 255.\n", pci_latency);
+ if (pci_latency != 248) {
+ printk("%s: Overriding PCI latency"
+ " timer (CFLT) setting of %d, new value is 248.\n",
+ dev->name, pci_latency);
pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, 255);
+ PCI_LATENCY_TIMER, 248);
}
-#endif /* VORTEX_BUS_MASTER */
- vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index,
- dev && dev->mem_start ? dev->mem_start
- : options[cards_found]);
dev = 0;
cards_found++;
}
}
}
-#endif /* CONFIG_PCI */
+#endif /* NO_PCI */
/* Now check all slots of the EISA bus. */
if (EISA_bus) {
static int ioaddr = 0x1000;
for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ int product_id, product_index;
+ if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
+ continue;
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
/* Check for a product that we support, 3c59{2,7} any rev. */
- if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 /* 597 */
- && (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059) /* 592 */
+ product_id = inw(ioaddr + 0xC82) & 0xF0FF;
+ if (product_id == 0x7059) /* 597 */
+ product_index = DEMON100_INDEX;
+ else if (product_id == 0x2059) /* 592 */
+ product_index = DEMON10_INDEX;
+ else
continue;
vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
- DEMON_INDEX, dev && dev->mem_start
- ? dev->mem_start : options[cards_found]);
+ product_index, dev && dev->mem_start
+ ? dev->mem_start : options[cards_found],
+ cards_found);
dev = 0;
cards_found++;
}
}
+ /* Special code to work-around the Compaq PCI BIOS32 problem. */
+ if (compaq_ioaddr) {
+ vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
+ dev && dev->mem_start ? dev->mem_start
+ : options[cards_found], cards_found);
+ cards_found++;
+ dev = 0;
+ }
+
+ /* Finally check for a 3c515 on the ISA bus. */
+ /* (3c515 support omitted on this version.) */
+
return cards_found;
}
-__initfunc(static int vortex_found_device(struct device *dev, int ioaddr, int irq,
- int product_index, int options))
+static struct device *
+vortex_found_device(struct device *dev, int ioaddr, int irq,
+ int product_index, int options, int card_idx)
{
struct vortex_private *vp;
#ifdef MODULE
/* Allocate and fill new device structure. */
int dev_size = sizeof(struct device) +
- sizeof(struct vortex_private);
-
+ sizeof(struct vortex_private) + 15; /* Pad for alignment */
+
dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
memset(dev, 0, dev_size);
- dev->priv = ((void *)dev) + sizeof(struct device);
+ /* Align the Rx and Tx ring entries. */
+ dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
vp = (struct vortex_private *)dev->priv;
dev->name = vp->devname; /* An empty string. */
dev->base_addr = ioaddr;
@@ -443,28 +656,36 @@ __initfunc(static int vortex_found_device(struct device *dev, int ioaddr, int ir
dev->init = vortex_probe1;
vp->product_name = product_names[product_index];
vp->options = options;
+ if (card_idx >= 0) {
+ if (full_duplex[card_idx] >= 0)
+ vp->full_duplex = full_duplex[card_idx];
+ } else
+ vp->full_duplex = (options >= 0 && (options & 0x10) ? 1 : 0);
+
if (options >= 0) {
- vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
- vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->media_override = ((options & 7) == XCVR_10baseTOnly) ?
+ XCVR_10baseT : options & 7;
vp->bus_master = (options & 16) ? 1 : 0;
} else {
vp->media_override = 7;
- vp->full_duplex = 0;
vp->bus_master = 0;
}
ether_setup(dev);
vp->next_module = root_vortex_dev;
root_vortex_dev = dev;
if (register_netdev(dev) != 0)
- return -EIO;
+ return 0;
#else /* not a MODULE */
if (dev) {
+ /* Caution: quad-word alignment required for rings! */
dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
memset(dev->priv, 0, sizeof (struct vortex_private));
}
dev = init_etherdev(dev, sizeof(struct vortex_private));
dev->base_addr = ioaddr;
dev->irq = irq;
+ dev->mtu = mtu;
+
vp = (struct vortex_private *)dev->priv;
vp->product_name = product_names[product_index];
vp->options = options;
@@ -480,13 +701,14 @@ __initfunc(static int vortex_found_device(struct device *dev, int ioaddr, int ir
vortex_probe1(dev);
#endif /* MODULE */
- return 0;
+ return dev;
}
-__initfunc(static int vortex_probe1(struct device *dev))
+static int vortex_probe1(struct device *dev)
{
int ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
printk("%s: 3Com %s at %#3x,", dev->name,
@@ -494,27 +716,33 @@ __initfunc(static int vortex_probe1(struct device *dev))
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 0x18; i++) {
short *phys_addr = (short *)dev->dev_addr;
int timer;
- outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+ outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
/* Pause for at least 162 us. for the read to take place. */
- for (timer = 162*4 + 400; timer >= 0; timer--) {
- udelay(1);
+ for (timer = 4; timer >= 0; timer--) {
+ udelay(162);
if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
break;
}
- phys_addr[i] = htons(inw(ioaddr + Wn0EepromData));
+ eeprom[i] = inw(ioaddr + Wn0EepromData);
+ checksum ^= eeprom[i];
+ if (i >= 10 && i < 13)
+ phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData));
}
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00)
+ printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
for (i = 0; i < 6; i++)
printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
printk(", IRQ %d\n", dev->irq);
/* Tell them about an invalid IRQ. */
- if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
- printk(" *** Warning: this IRQ is unlikely to work!\n");
+ if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
+ printk(" *** Warning: this IRQ is unlikely to work! ***\n");
{
- char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+ char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
union wn3_config config;
EL3WINDOW(3);
vp->available_media = inw(ioaddr + Wn3_Options);
@@ -532,8 +760,46 @@ __initfunc(static int vortex_probe1(struct device *dev))
vp->default_media = config.u.xcvr;
vp->autoselect = config.u.autoselect;
}
+ if (vp->media_override != 7) {
+ printk(" Media override to transceiver type %d (%s).\n",
+ vp->media_override, media_tbl[vp->media_override].name);
+ dev->if_port = vp->media_override;
+ }
- /* We do a request_region() only to register /proc/ioports info. */
+ if (dev->if_port == XCVR_MII) {
+ int phy, phy_idx = 0;
+ EL3WINDOW(4);
+ for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
+ int mii_status;
+ mdio_sync(ioaddr, 32);
+ mii_status = mdio_read(ioaddr, phy, 0);
+ if (mii_status != 0xffff) {
+ vp->phys[phy_idx++] = phy;
+ printk("%s: MII transceiver found at address %d.\n",
+ dev->name, phy);
+ mdio_sync(ioaddr, 32);
+ if ((mdio_read(ioaddr, phy, 1) & 0x0040) == 0)
+ mii_preamble_required = 1;
+ }
+ }
+ if (phy_idx == 0) {
+ printk("%s: ***WARNING*** No MII transceivers found!\n",
+ dev->name);
+ vp->phys[0] = 0;
+ }
+ }
+
+ vp->info1 = eeprom[13];
+ vp->info2 = eeprom[15];
+ vp->capabilities = eeprom[16];
+ if ((vp->capabilities & 0x20) && vp->bus_master) {
+ vp->full_bus_master_tx = 1;
+ printk(" Enabling bus-master transmits and %s receives.\n",
+ (vp->info2 & 1) ? "early" : "whole-frame" );
+ vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
+ }
+
+ /* We do a request_region() to register /proc/ioports info. */
request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
/* The 3c59x-specific entries in the device structure. */
@@ -541,10 +807,97 @@ __initfunc(static int vortex_probe1(struct device *dev))
dev->hard_start_xmit = &vortex_start_xmit;
dev->stop = &vortex_close;
dev->get_stats = &vortex_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &vortex_ioctl;
+#endif
+#ifdef NEW_MULTICAST
dev->set_multicast_list = &set_rx_mode;
+#else
+ dev->set_multicast_list = &set_multicast_list;
+#endif
return 0;
}
+
+/* Read and write the MII registers using software-generated serial
+ MDIO protocol. The maxium data clock rate is 2.5 Mhz. */
+#define mdio_delay() udelay(1)
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN 0x00
+
+static void mdio_sync(int ioaddr, int bits)
+{
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (-- bits >= 0) {
+ outw(MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+}
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ unsigned int retval = 0;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the read command bits out. */
+ for (i = 14; i >= 0; i--) {
+ int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ mdio_delay();
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ return retval>>1 & 0xffff;
+}
+
+static void mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int i;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ mdio_delay();
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Leave the interface idle. */
+ for (i = 1; i >= 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+
+ return;
+}
static int
@@ -557,8 +910,6 @@ vortex_open(struct device *dev)
/* Before initializing select the active media port. */
EL3WINDOW(3);
- if (vp->full_duplex)
- outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
config.i = inl(ioaddr + Wn3_Config);
if (vp->media_override != 7) {
@@ -569,7 +920,7 @@ vortex_open(struct device *dev)
dev->if_port = vp->media_override;
} else if (vp->autoselect) {
/* Find first available media type, starting with 100baseTx. */
- dev->if_port = 4;
+ dev->if_port = XCVR_100baseTx;
while (! (vp->available_media & media_tbl[dev->if_port].mask))
dev->if_port = media_tbl[dev->if_port].next;
@@ -588,6 +939,30 @@ vortex_open(struct device *dev)
config.u.xcvr = dev->if_port;
outl(config.i, ioaddr + Wn3_Config);
+ if (dev->if_port == XCVR_MII) {
+ int mii_reg1, mii_reg5;
+ /* We cheat here: we know that we are using the 83840 transceiver
+ which summarizes the FD status in an extended register. */
+ EL3WINDOW(4);
+ /* Read BMSR (reg1) only to clear old status. */
+ mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+ mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
+ if (mii_reg5 == 0xffff || mii_reg5 == 0x0000)
+ ; /* No MII device or no link partner report */
+ else if ((mii_reg5 & 0x0100) != 0 /* 100baseTx-FD */
+ || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */
+ vp->full_duplex = 1;
+ if (vortex_debug > 1)
+ printk("%s: MII #%d status %4.4x, link partner capability %4.4x,"
+ " setting %s-duplex.\n", dev->name, vp->phys[0],
+ mii_reg1, mii_reg5, vp->full_duplex ? "full" : "half");
+ EL3WINDOW(3);
+ }
+
+ /* Set the full-duplex bit. */
+ outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+ (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+
if (vortex_debug > 1) {
printk("%s: vortex_open() InternalConfig %8.8x.\n",
dev->name, config.i);
@@ -595,22 +970,32 @@ vortex_open(struct device *dev)
outw(TxReset, ioaddr + EL3_CMD);
for (i = 20; i >= 0 ; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(RxReset, ioaddr + EL3_CMD);
/* Wait a few ticks for the RxReset command to complete. */
for (i = 20; i >= 0 ; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+#ifdef SA_SHIRQ
/* Use the now-standard shared IRQ implementation. */
if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ,
vp->product_name, dev)) {
return -EAGAIN;
}
+#else
+ if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL)
+ return -EAGAIN;
+ irq2dev_map[dev->irq] = dev;
+ if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+#endif
if (vortex_debug > 1) {
EL3WINDOW(4);
@@ -625,7 +1010,7 @@ vortex_open(struct device *dev)
for (; i < 12; i+=2)
outw(0, ioaddr + i);
- if (dev->if_port == 3)
+ if (dev->if_port == XCVR_10base2)
/* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD);
EL3WINDOW(4);
@@ -635,18 +1020,52 @@ vortex_open(struct device *dev)
/* Switch to the stats window, and clear all stats by reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6);
- for (i = 0; i < 10; i++)
+ for (i = 0; i < 10; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12);
/* New: On the Vortex we must also clear the BadSSD counter. */
EL3WINDOW(4);
inb(ioaddr + 12);
+ /* ..and on the Boomerang we enable the extra statistics bits. */
+ outw(0x0040, ioaddr + Wn4_NetDiag);
/* Switch to register set 7 for normal use. */
EL3WINDOW(7);
- /* Set receiver mode: presumably accept b-case and phys addr only. */
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ vp->cur_rx = vp->dirty_rx = 0;
+ /* Initialize the RxEarly register as recommended. */
+ outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ outl(0x0020, ioaddr + PktStatus);
+ if (vortex_debug > 2)
+ printk("%s: Filling in the Rx ring.\n", dev->name);
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+ vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
+ vp->rx_ring[i].status = 0; /* Clear complete bit. */
+ vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
+ skb = dev_alloc_skb(PKT_BUF_SZ);
+ vp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break; /* Bad news! */
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+ }
+ vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+ outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
+ }
+ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
+ dev->hard_start_xmit = &boomerang_start_xmit;
+ vp->cur_tx = vp->dirty_tx = 0;
+ outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+ /* Clear the Tx ring. */
+ for (i = 0; i < TX_RING_SIZE; i++)
+ vp->tx_skbuff[i] = 0;
+ outl(0, ioaddr + DownListPtr);
+ }
+ /* Set reciever mode: presumably accept b-case and phys addr only. */
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
@@ -657,12 +1076,18 @@ vortex_open(struct device *dev)
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
/* Allow status bits to be seen. */
- outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull|TxComplete|
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0),
+ ioaddr + EL3_CMD);
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
- | DMADone, ioaddr + EL3_CMD);
+ | AdapterFailure | TxComplete
+ | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+ ioaddr + EL3_CMD);
MOD_INC_USE_COUNT;
@@ -688,7 +1113,7 @@ static void vortex_timer(unsigned long data)
EL3WINDOW(4);
media_status = inw(ioaddr + Wn4_Media);
switch (dev->if_port) {
- case 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */
+ case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx:
if (media_status & Media_LnkBeat) {
ok = 1;
if (vortex_debug > 1)
@@ -697,8 +1122,20 @@ static void vortex_timer(unsigned long data)
} else if (vortex_debug > 1)
printk("%s: Media %s is has no link beat, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
-
+
break;
+ case XCVR_MII:
+ {
+ int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+ int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
+ if (vortex_debug > 1)
+ printk("%s: MII #%d status register is %4.4x, "
+ "link partner capability %4.4x.\n",
+ dev->name, vp->phys[0], mii_reg1, mii_reg5);
+ if (mii_reg1 & 0x0004)
+ ok = 1;
+ break;
+ }
default: /* Other media types handled by Tx timeouts. */
if (vortex_debug > 1)
printk("%s: Media %s is has no indication, %x.\n",
@@ -711,7 +1148,7 @@ static void vortex_timer(unsigned long data)
do {
dev->if_port = media_tbl[dev->if_port].next;
} while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
- if (dev->if_port == 8) { /* Go back to default. */
+ if (dev->if_port == XCVR_Default) { /* Go back to default. */
dev->if_port = vp->default_media;
if (vortex_debug > 1)
printk("%s: Media selection failing, using default %s port.\n",
@@ -731,7 +1168,8 @@ static void vortex_timer(unsigned long data)
config.u.xcvr = dev->if_port;
outl(config.i, ioaddr + Wn3_Config);
- outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+ outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
+ ioaddr + EL3_CMD);
}
EL3WINDOW(old_window);
} restore_flags(flags);
@@ -743,66 +1181,118 @@ static void vortex_timer(unsigned long data)
return;
}
-static int
-vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+static void vortex_tx_timeout(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int ioaddr = dev->base_addr;
+ int i;
- /* Part of the following code is inspired by code from Giuseppe Ciaccio,
- ciaccio@disi.unige.it.
- It works around a ?bug? in the 8K Vortex that only occurs on some
- systems: the TxAvailable interrupt seems to be lost.
- The ugly work-around is to busy-wait for room available in the Tx
- buffer before deciding the transmitter is actually hung.
- This busy-wait should never really occur, since the problem is that
- there actually *is* room in the Tx FIFO.
-
- This pointed out an optimization -- we can ignore dev->tbusy if
- we actually have room for this packet.
- */
-
-#if 0
- /* unstable optimization */
- if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */
- dev->tbusy = 0; /* Fake out the check below. */
- else
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TxStatus),
+ inw(ioaddr + EL3_STATUS));
+ /* Slight code bloat to be user friendly. */
+ if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
+ printk("%s: Transmitter encountered 16 collisions --"
+ " network cable problem?\n", dev->name);
+ if (inw(ioaddr + EL3_STATUS) & IntLatch) {
+ printk("%s: Interrupt posted but not handled --"
+ " IRQ blocked by another device?\n", dev->name);
+ /* Bad idea here.. but we might as well handle a few events. */
+ vortex_interrupt IRQ(dev->irq, dev, 0);
+ }
+#ifndef final_version
+ if (vp->full_bus_master_tx) {
+ printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n",
+ vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx);
+ printk(" Transmit list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
+ &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ printk(" %d: @%p length %8.8x status %8.8x\n", i,
+ &vp->tx_ring[i],
+ vp->tx_ring[i].length,
+ vp->tx_ring[i].status);
+ }
+ }
+#ifdef notdef
+ if (vp->full_bus_master_rx) {
+ printk(" Switching to non-bus-master receives.\n");
+ outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ RxComplete | (vp->bus_master ? DMADone : 0),
+ ioaddr + EL3_CMD);
+ }
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0 ; i--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
#endif
- if (dev->tbusy) {
- /* Transmitter timeout, serious problems. */
- int tickssofar = jiffies - dev->trans_start;
- int i;
-
- if (tickssofar < 2) /* We probably aren't empty. */
- return 1;
- /* Wait a while to see if there really is room. */
- for (i = WAIT_TX_AVAIL; i >= 0; i--)
- if (inw(ioaddr + TxFree) > skb->len)
- break;
- if ( i < 0) {
- if (tickssofar < TX_TIMEOUT)
- return 1;
- printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
- dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0 ; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) break;
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->trans_start = jiffies;
- dev->tbusy = 0;
- vp->stats.tx_errors++;
+#endif
+ if (vp->full_bus_master_tx) {
+ /* Change 6/25/97 Michael Sievers sieversm@mail.desy.de
+ The card has been resetted, but the Tx Ring is still full.
+ Since the card won't know where to resume, 'update' the
+ Tx Ring. Probably, we'll lose 16 packets this way, these
+ will be accounted for as 'dropped'. The code to update the
+ Tx Ring is taken from the Interrupt handler. */
+
+ unsigned int dirty_tx = vp->dirty_tx;
+
+ if (vortex_debug > 0)
+ printk("%s: Freeing Tx ring entries:", dev->name);
+ while (vp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ if (inl(ioaddr + DownListPtr) ==
+ virt_to_bus(&vp->tx_ring[entry]))
+ break; /* It still hasn't been processed. */
+ if (vp->tx_skbuff[entry]) {
+ if (vortex_debug > 0)
+ printk(" %d\n", entry);
+ dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE);
+ vp->tx_skbuff[entry] = 0;
vp->stats.tx_dropped++;
- return 0; /* Yes, silently *drop* the packet! */
}
- dev->tbusy = 0;
+ if (vortex_debug > 0)
+ printk(".\n");
+ vp->stats.tx_errors++;
+ dirty_tx++;
+ }
+ vp->dirty_tx = dirty_tx;
+ vp->tx_full= 0;
+ } else { /* not bus-master, no Tx ring to clear */
+ vp->stats.tx_errors++;
+ vp->stats.tx_dropped++;
}
+
+ /* Issue Tx Enable */
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies;
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ /* The TxFreeThreshold has to be set again after a reset! */
+ if (vp->full_bus_master_tx) {
+ outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+ /* This is to be sure that all bus-master Tx features
+ are correctly re-initialized after a reset, although
+ the DownListPtr should already be 0 at this point. */
+ outl(0, ioaddr + DownListPtr);
+ }
+ /* finally, allow new Transmits */
+ dev->tbusy = 0;
+ /* End of Michael Sievers <sieversm@mail.desy.de> changes. */
+}
+
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
- If this ever occurs the queue layer is doing something evil! */
if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
+ if (jiffies - dev->trans_start >= TX_TIMEOUT)
+ vortex_tx_timeout(dev);
return 1;
}
@@ -855,7 +1345,7 @@ vortex_start_xmit(struct sk_buff *skb, struct device *dev)
int j;
outw(TxReset, ioaddr + EL3_CMD);
for (j = 20; j >= 0 ; j--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
}
outw(TxEnable, ioaddr + EL3_CMD);
@@ -866,16 +1356,80 @@ vortex_start_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
+static int
+boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ if (jiffies - dev->trans_start >= TX_TIMEOUT)
+ vortex_tx_timeout(dev);
+ return 1;
+ } else {
+ /* Calculate the next Tx descriptor entry. */
+ int entry = vp->cur_tx % TX_RING_SIZE;
+ struct boom_tx_desc *prev_entry =
+ &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE];
+ unsigned long flags;
+ int i;
+
+ if (vortex_debug > 3)
+ printk("%s: Trying to send a packet, Tx index %d.\n",
+ dev->name, vp->cur_tx);
+ if (vp->tx_full) {
+ if (vortex_debug >0)
+ printk("%s: Tx Ring full, refusing to send buffer.\n",
+ dev->name);
+ return 1;
+ }
+ /* end change 06/25/97 M. Sievers */
+ vp->tx_skbuff[entry] = skb;
+ vp->tx_ring[entry].next = 0;
+ vp->tx_ring[entry].addr = virt_to_bus(skb->data);
+ vp->tx_ring[entry].length = skb->len | LAST_FRAG;
+ vp->tx_ring[entry].status = skb->len | TxIntrUploaded;
+
+ save_flags(flags);
+ cli();
+ outw(DownStall, ioaddr + EL3_CMD);
+ /* Wait for the stall to complete. */
+ for (i = 60; i >= 0 ; i--)
+ if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
+ break;
+ prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
+ if (inl(ioaddr + DownListPtr) == 0) {
+ outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
+ queued_packet++;
+ }
+ outw(DownUnstall, ioaddr + EL3_CMD);
+ restore_flags(flags);
+
+ vp->cur_tx++;
+ if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
+ vp->tx_full = 1;
+ else { /* Clear previous interrupt enable. */
+ prev_entry->status &= ~TxIntrUploaded;
+ dev->tbusy = 0;
+ }
+ dev->trans_start = jiffies;
+ return 0;
+ }
+}
+
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
{
- /* Use the now-standard shared IRQ implementation. */
+#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */
struct device *dev = dev_id;
+#else
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
struct vortex_private *lp;
int ioaddr, status;
int latency;
- int i = 0;
+ int i = max_interrupt_work;
if (dev->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
@@ -890,17 +1444,20 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
if (vortex_debug > 4)
printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
status, latency);
- if ((status & 0xE000) != 0xE000) {
+#ifdef notdef
+ /* This code guard against bogus hangs, but fails with shared IRQs. */
+ if ((status & ~0xE000) == 0x0000) {
static int donedidthis=0;
/* Some interrupt controllers store a bogus interrupt from boot-time.
Ignore a single early interrupt, but don't hang the machine for
other interrupt problems. */
- if (donedidthis++ > 1) {
+ if (donedidthis++ > 100) {
printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
dev->name, status, dev->start);
FREE_IRQ(dev->irq, dev);
}
}
+#endif
do {
if (vortex_debug > 5)
@@ -917,13 +1474,53 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
dev->tbusy = 0;
mark_bh(NET_BH);
}
+ if (status & TxComplete) { /* Really "TxError" for us. */
+ unsigned char tx_status = inb(ioaddr + TxStatus);
+ /* Presumably a tx-timeout. We must merely re-enable. */
+ if (vortex_debug > 2
+ || (tx_status != 0x88 && vortex_debug > 0))
+ printk("%s: Transmit error, Tx status register %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x04) lp->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+ outb(0, ioaddr + TxStatus);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (status & DownComplete) {
+ unsigned int dirty_tx = lp->dirty_tx;
+
+ while (lp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ if (inl(ioaddr + DownListPtr) ==
+ virt_to_bus(&lp->tx_ring[entry]))
+ break; /* It still hasn't been processed. */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+ lp->tx_skbuff[entry] = 0;
+ }
+ /* lp->stats.tx_packets++; Counted below. */
+ dirty_tx++;
+ }
+ lp->dirty_tx = dirty_tx;
+ outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
+ if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
+ lp->tx_full= 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
#ifdef VORTEX_BUS_MASTER
if (status & DMADone) {
outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
dev->tbusy = 0;
+ dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */
mark_bh(NET_BH);
}
#endif
+ if (status & UpComplete) {
+ boomerang_rx(dev);
+ outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ }
if (status & (AdapterFailure | RxEarly | StatsFull)) {
/* Handle all uncommon interrupts at once. */
if (status & RxEarly) { /* Rx early is unused. */
@@ -949,27 +1546,45 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
printk(" %2.2x", inb(ioaddr+reg));
}
EL3WINDOW(7);
- outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+ outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure
+ | UpComplete | DownComplete | TxComplete,
+ ioaddr + EL3_CMD);
DoneDidThat++;
}
}
if (status & AdapterFailure) {
- /* Adapter failure requires Rx reset and reinit. */
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+ if (vortex_debug > 0)
+ printk("%s: Host error, FIFO diagnostic register %4.4x.\n",
+ dev->name, fifo_diag);
+ /* Adapter failure requires Tx/Rx reset and reinit. */
+ if (fifo_diag & 0x0400) {
+ int j;
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 20; j >= 0 ; j--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ if (fifo_diag & 0x2000) {
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
}
}
- if (++i > 10) {
- printk("%s: Infinite loop in interrupt, status %4.4x. "
+ if (--i < 0) {
+ printk("%s: Too much work in interrupt, status %4.4x. "
"Disabling functions (%4.4x).\n",
- dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+ dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
/* Disable all pending interrupts. */
- outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
+ outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
break;
}
/* Acknowledge the IRQ. */
@@ -998,7 +1613,7 @@ vortex_rx(struct device *dev)
while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
if (rx_status & 0x4000) { /* Error, update stats. */
unsigned char rx_error = inb(ioaddr + RxErrors);
- if (vortex_debug > 4)
+ if (vortex_debug > 2)
printk(" Rx error: status %2.2x.\n", rx_error);
vp->stats.rx_errors++;
if (rx_error & 0x01) vp->stats.rx_over_errors++;
@@ -1017,28 +1632,36 @@ vortex_rx(struct device *dev)
pkt_len, rx_status);
if (skb != NULL) {
skb->dev = dev;
+#if LINUX_VERSION_CODE >= 0x10300
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
(pkt_len + 3) >> 2);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
+#else
+ skb->len = pkt_len;
+ /* 'skb->data' points to the start of sk_buff data area. */
+ insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2);
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+#endif /* KERNEL_1_3_0 */
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ vp->stats.rx_packets++;
/* Wait a limited time to go to next packet. */
for (i = 200; i >= 0; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
- vp->stats.rx_packets++;
continue;
} else if (vortex_debug)
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
dev->name, pkt_len);
}
- vp->stats.rx_dropped++;
outw(RxDiscard, ioaddr + EL3_CMD);
+ vp->stats.rx_dropped++;
/* Wait a limited time to skip this packet. */
for (i = 200; i >= 0; i--)
- if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
}
@@ -1046,17 +1669,111 @@ vortex_rx(struct device *dev)
}
static int
+boomerang_rx(struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int entry = vp->cur_rx % RX_RING_SIZE;
+ int ioaddr = dev->base_addr;
+ int rx_status;
+
+ if (vortex_debug > 5)
+ printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+ while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
+ if (rx_status & RxDError) { /* Error, update stats. */
+ unsigned char rx_error = rx_status >> 16;
+ if (vortex_debug > 2)
+ printk(" Rx error: status %2.2x.\n", rx_error);
+ vp->stats.rx_errors++;
+ if (rx_error & 0x01) vp->stats.rx_over_errors++;
+ if (rx_error & 0x02) vp->stats.rx_length_errors++;
+ if (rx_error & 0x04) vp->stats.rx_frame_errors++;
+ if (rx_error & 0x08) vp->stats.rx_crc_errors++;
+ if (rx_error & 0x10) vp->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ short pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ if (vortex_debug > 4)
+ printk("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+
+ /* Check if the packet is long enough to just accept without
+ copying to a properly sized skbuff. */
+ if (pkt_len < rx_copybreak
+ && (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ memcpy(skb_put(skb, pkt_len),
+ bus_to_virt(vp->rx_ring[entry].addr),
+ pkt_len);
+ rx_copy++;
+ } else{
+ void *temp;
+ /* Pass up the skbuff already on the Rx ring. */
+ skb = vp->rx_skbuff[entry];
+ vp->rx_skbuff[entry] = NULL;
+ temp = skb_put(skb, pkt_len);
+ /* Remove this checking code for final release. */
+ if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
+ printk("%s: Warning -- the skbuff addresses do not match"
+ " in boomerang_rx: %p vs. %p / %p.\n", dev->name,
+ bus_to_virt(vp->rx_ring[entry].addr),
+ skb->head, temp);
+ rx_nocopy++;
+ }
+#if LINUX_VERSION_CODE > 0x10300
+ skb->protocol = eth_type_trans(skb, dev);
+#else
+ skb->len = pkt_len;
+#endif
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ vp->stats.rx_packets++;
+ }
+ entry = (++vp->cur_rx) % RX_RING_SIZE;
+ }
+ /* Refill the Rx ring buffers. */
+ for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = vp->dirty_rx % RX_RING_SIZE;
+ if (vp->rx_skbuff[entry] == NULL) {
+ skb = dev_alloc_skb(PKT_BUF_SZ);
+ if (skb == NULL)
+ break; /* Bad news! */
+ skb->dev = dev; /* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ vp->rx_ring[entry].addr = virt_to_bus(skb->tail);
+#else
+ vp->rx_ring[entry].addr = virt_to_bus(skb->data);
+#endif
+ vp->rx_skbuff[entry] = skb;
+ }
+ vp->rx_ring[entry].status = 0; /* Clear complete bit. */
+ }
+ return 0;
+}
+
+static int
vortex_close(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int ioaddr = dev->base_addr;
+ int i;
dev->start = 0;
dev->tbusy = 1;
- if (vortex_debug > 1)
+ if (vortex_debug > 1) {
printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+ printk("%s: vortex close stats: rx_nocopy %d rx_copy %d"
+ " tx_queued %d.\n",
+ dev->name, rx_nocopy, rx_copy, queued_packet);
+ }
del_timer(&vp->timer);
@@ -1067,27 +1784,57 @@ vortex_close(struct device *dev)
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
- if (dev->if_port == 3)
+ if (dev->if_port == XCVR_10base2)
/* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD);
- FREE_IRQ(dev->irq, dev);
+#ifdef SA_SHIRQ
+ free_irq(dev->irq, dev);
+#else
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
update_stats(ioaddr, dev);
+ if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
+ outl(0, ioaddr + UpListPtr);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (vp->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+ vp->rx_skbuff[i]->free = 1;
+#endif
+ dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE);
+ vp->rx_skbuff[i] = 0;
+ }
+ }
+ if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
+ outl(0, ioaddr + DownListPtr);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ if (vp->tx_skbuff[i]) {
+ dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE);
+ vp->tx_skbuff[i] = 0;
+ }
+ }
+
MOD_DEC_USE_COUNT;
return 0;
}
-static struct net_device_stats *vortex_get_stats(struct device *dev)
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
unsigned long flags;
- save_flags(flags);
- cli();
- update_stats(dev->base_addr, dev);
- restore_flags(flags);
+ if (dev->start) {
+ save_flags(flags);
+ cli();
+ update_stats(dev->base_addr, dev);
+ restore_flags(flags);
+ }
return &vp->stats;
}
@@ -1129,6 +1876,37 @@ static void update_stats(int ioaddr, struct device *dev)
return;
}
+#ifdef HAVE_PRIVATE_IOCTL
+static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+ int phy = vp->phys[0] & 0x1f;
+
+ if (vortex_debug > 2)
+ printk("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+ dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+ data[0], data[1], data[2], data[3]);
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = phy;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ EL3WINDOW(4);
+ data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!suser())
+ return -EPERM;
+ mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
/* This new version of set_rx_mode() supports v1.4 kernels.
The Vortex chip has no documented multicast filter, so the only
multicast setting is to receive all multicast frames. At least
@@ -1145,11 +1923,19 @@ set_rx_mode(struct device *dev)
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
} else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
- } else
+ } else
new_mode = SetRxFilter | RxStation | RxBroadcast;
outw(new_mode, ioaddr + EL3_CMD);
}
+#ifndef NEW_MULTICAST
+/* The old interface to set the Rx mode. */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ set_rx_mode(dev);
+}
+#endif
#ifdef MODULE
void
@@ -1167,12 +1953,15 @@ cleanup_module(void)
root_vortex_dev = next_dev;
}
}
-#endif /* MODULE */
+
+#endif /* MODULE */
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o"
+ * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
* c-indent-level: 4
+ * c-basic-offset: 4
* tab-width: 4
* End:
*/
+
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index 603657a85..f53d758d2 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -181,7 +181,6 @@ if [ "$CONFIG_WAN_ROUTER" != "n" ]; then
bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR
bool ' WANPIPE PPP support' CONFIG_WANPIPE_PPP
fi
- dep_tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25
fi
fi
#
diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c
index fa5ae74e4..dbf0f3beb 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/eth16i.c
@@ -265,7 +265,7 @@ static char *version =
/* Macro to slow down io between EEPROM clock transitions */
-#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0)
+#define eeprom_slow_io() udelay(100) /* FIXME: smaller but right value here */
/* Jumperless Configuration Register (BMPR19) */
#define JUMPERLESS_CONFIG 19
diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in
index 94a86238f..85434e502 100644
--- a/drivers/net/hamradio/Config.in
+++ b/drivers/net/hamradio/Config.in
@@ -26,6 +26,7 @@ if [ "$CONFIG_HAMRADIO" != "n" ] ; then
# tristate 'Serial port 6PACK driver' CONFIG_6PACK
tristate 'BPQ Ethernet driver' CONFIG_BPQETHER
+ tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25
tristate 'Z8530 SCC driver' CONFIG_SCC
if [ "$CONFIG_SCC" != "n" ]; then
bool ' additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
new file mode 100644
index 000000000..33b473d32
--- /dev/null
+++ b/drivers/net/hamradio/dmascc.c
@@ -0,0 +1,1260 @@
+/*
+ * $Id: dmascc.c,v 1.2 1997/12/02 16:49:49 oe1kib Exp $
+ *
+ * Driver for high-speed SCC boards (those with DMA support)
+ * Copyright (C) 1997 Klaus Kudielka
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/dmascc.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/sockios.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <net/ax25.h>
+#include <stdio.h>
+#include "z8530.h"
+
+
+/* Number of buffers per channel */
+
+#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */
+#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */
+#define BUF_SIZE 2016
+
+
+/* Cards supported */
+
+#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 1843200, 3686400 }
+#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 3686400, 7372800 }
+#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \
+ 0, 4, 6144000, 6144000 }
+
+#define HARDWARE { HW_PI, HW_PI2, HW_TWIN }
+
+#define TYPE_PI 0
+#define TYPE_PI2 1
+#define TYPE_TWIN 2
+#define NUM_TYPES 3
+
+#define MAX_NUM_DEVS 32
+
+
+/* SCC chips supported */
+
+#define Z8530 0
+#define Z85C30 1
+#define Z85230 2
+
+#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" }
+
+
+/* I/O registers */
+
+/* 8530 registers relative to card base */
+#define SCCB_CMD 0x00
+#define SCCB_DATA 0x01
+#define SCCA_CMD 0x02
+#define SCCA_DATA 0x03
+
+/* 8254 registers relative to card base */
+#define TMR_CNT0 0x00
+#define TMR_CNT1 0x01
+#define TMR_CNT2 0x02
+#define TMR_CTRL 0x03
+
+/* Additional PI/PI2 registers relative to card base */
+#define PI_DREQ_MASK 0x04
+
+/* Additional PackeTwin registers relative to card base */
+#define TWIN_INT_REG 0x08
+#define TWIN_CLR_TMR1 0x09
+#define TWIN_CLR_TMR2 0x0a
+#define TWIN_SPARE_1 0x0b
+#define TWIN_DMA_CFG 0x08
+#define TWIN_SERIAL_CFG 0x09
+#define TWIN_DMA_CLR_FF 0x0a
+#define TWIN_SPARE_2 0x0b
+
+
+/* PackeTwin I/O register values */
+
+/* INT_REG */
+#define TWIN_SCC_MSK 0x01
+#define TWIN_TMR1_MSK 0x02
+#define TWIN_TMR2_MSK 0x04
+#define TWIN_INT_MSK 0x07
+
+/* SERIAL_CFG */
+#define TWIN_DTRA_ON 0x01
+#define TWIN_DTRB_ON 0x02
+#define TWIN_EXTCLKA 0x04
+#define TWIN_EXTCLKB 0x08
+#define TWIN_LOOPA_ON 0x10
+#define TWIN_LOOPB_ON 0x20
+#define TWIN_EI 0x80
+
+/* DMA_CFG */
+#define TWIN_DMA_HDX_T1 0x08
+#define TWIN_DMA_HDX_R1 0x0a
+#define TWIN_DMA_HDX_T3 0x14
+#define TWIN_DMA_HDX_R3 0x16
+#define TWIN_DMA_FDX_T3R1 0x1b
+#define TWIN_DMA_FDX_T1R3 0x1d
+
+
+/* Status values */
+
+/* tx_state */
+#define TX_IDLE 0
+#define TX_OFF 1
+#define TX_TXDELAY 2
+#define TX_ACTIVE 3
+#define TX_SQDELAY 4
+
+
+/* Data types */
+
+struct scc_hardware {
+ char *name;
+ int io_region;
+ int io_delta;
+ int io_size;
+ int num_devs;
+ int scc_offset;
+ int tmr_offset;
+ int tmr_hz;
+ int pclk_hz;
+};
+
+struct scc_priv {
+ char name[10];
+ struct enet_statistics stats;
+ struct scc_info *info;
+ int channel;
+ int cmd, data, tmr;
+ struct scc_param param;
+ char rx_buf[NUM_RX_BUF][BUF_SIZE];
+ int rx_len[NUM_RX_BUF];
+ int rx_ptr;
+ struct tq_struct rx_task;
+ int rx_head, rx_tail, rx_count;
+ int rx_over;
+ char tx_buf[NUM_TX_BUF][BUF_SIZE];
+ int tx_len[NUM_TX_BUF];
+ int tx_ptr;
+ int tx_head, tx_tail, tx_count;
+ int tx_sem, tx_state;
+ unsigned long tx_start;
+ int status;
+};
+
+struct scc_info {
+ int type;
+ int chip;
+ int open;
+ int scc_base;
+ int tmr_base;
+ int twin_serial_cfg;
+ struct device dev[2];
+ struct scc_priv priv[2];
+ struct scc_info *next;
+};
+
+
+/* Function declarations */
+
+int dmascc_init(void) __init;
+static int setup_adapter(int io, int h, int n) __init;
+
+static inline void write_scc(int ctl, int reg, int val);
+static inline int read_scc(int ctl, int reg);
+static int scc_open(struct device *dev);
+static int scc_close(struct device *dev);
+static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int scc_send_packet(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *scc_get_stats(struct device *dev);
+static int scc_set_mac_address(struct device *dev, void *sa);
+static void scc_isr(int irq, void *dev_id, struct pt_regs * regs);
+static inline void z8530_isr(struct scc_info *info);
+static void rx_isr(struct device *dev);
+static void special_condition(struct device *dev, int rc);
+static void rx_bh(void *arg);
+static void tx_isr(struct device *dev);
+static void es_isr(struct device *dev);
+static void tm_isr(struct device *dev);
+static inline void delay(struct device *dev, int t);
+static inline unsigned char random(void);
+
+
+/* Initialization variables */
+
+static int io[MAX_NUM_DEVS] __initdata = { 0, };
+/* Beware! hw[] is also used in cleanup_module(). If __initdata also applies
+ to modules, we may not declare hw[] as __initdata */
+static struct scc_hardware hw[NUM_TYPES] __initdata = HARDWARE;
+static char ax25_broadcast[7] __initdata =
+ { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1 };
+static char ax25_test[7] __initdata =
+ { 'L'<<1, 'I'<<1, 'N'<<1, 'U'<<1, 'X'<<1, ' '<<1, '1'<<1 };
+
+
+/* Global variables */
+
+static struct scc_info *first = NULL;
+static unsigned long rand;
+
+
+
+/* Module functions */
+
+#ifdef MODULE
+
+
+MODULE_AUTHOR("Klaus Kudielka <oe1kib@oe1xtu.ampr.org>");
+MODULE_DESCRIPTION("Driver for high-speed SCC boards");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
+
+
+int init_module(void)
+{
+ return dmascc_init();
+}
+
+
+void cleanup_module(void)
+{
+ int i;
+ struct scc_info *info;
+
+ while (first) {
+ info = first;
+
+ /* Unregister devices */
+ for (i = 0; i < 2; i++) {
+ if (info->dev[i].name)
+ unregister_netdev(&info->dev[i]);
+ }
+
+ /* Reset board */
+ if (info->type == TYPE_TWIN)
+ outb_p(0, info->dev[0].base_addr + TWIN_SERIAL_CFG);
+ write_scc(info->priv[0].cmd, R9, FHWRES);
+ release_region(info->dev[0].base_addr,
+ hw[info->type].io_size);
+
+ /* Free memory */
+ first = info->next;
+ kfree_s(info, sizeof(struct scc_info));
+ }
+}
+
+
+#else
+
+
+__initfunc(void dmascc_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++)
+ io[i] = ints[i+1];
+}
+
+
+#endif
+
+
+/* Initialization functions */
+
+__initfunc(int dmascc_init(void))
+{
+ int h, i, j, n, base[MAX_NUM_DEVS], tcmd, t0, t1, status;
+ unsigned long time, start[MAX_NUM_DEVS], stop[MAX_NUM_DEVS];
+
+ /* Initialize random number generator */
+ rand = jiffies;
+
+ /* Cards found = 0 */
+ n = 0;
+
+ /* Run autodetection for each card type */
+ for (h = 0; h < NUM_TYPES; h++) {
+
+ if (io[0]) {
+ /* User-specified I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++) base[i] = 0;
+ for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) {
+ j = (io[i] - hw[h].io_region) / hw[h].io_delta;
+ if (j >= 0 &&
+ j < hw[h].num_devs &&
+ hw[h].io_region + j * hw[h].io_delta == io[i])
+ base[j] = io[i];
+ }
+ } else {
+ /* Default I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++)
+ base[i] = hw[h].io_region + i * hw[h].io_delta;
+ }
+
+ /* Check valid I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i] && check_region(base[i], hw[h].io_size))
+ base[i] = 0;
+
+ /* Start timers */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ tcmd = base[i] + hw[h].tmr_offset + TMR_CTRL;
+ t0 = base[i] + hw[h].tmr_offset + TMR_CNT0;
+ t1 = base[i] + hw[h].tmr_offset + TMR_CNT1;
+ /* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */
+ outb_p(0x36, tcmd);
+ outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0);
+ outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0);
+ /* Timer 1: LSB+MSB, Mode 0, HZ/10 */
+ outb_p(0x70, tcmd);
+ outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1);
+ outb_p((TMR_0_HZ/HZ*10) >> 8, t1);
+ /* Timer 2: LSB+MSB, Mode 0 */
+ outb_p(0xb0, tcmd);
+ }
+
+ /* Initialize start values in case we miss the null count bit */
+ time = jiffies;
+ for (i = 0; i < hw[h].num_devs; i++) start[i] = time;
+
+ /* Timing loop */
+ while (jiffies - time < 12) {
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ /* Read back Timer 1: Status */
+ outb_p(0xE4, base[i] + hw[h].tmr_offset + TMR_CTRL);
+ status = inb_p(base[i] + hw[h].tmr_offset + TMR_CNT1);
+ if ((status & 0x3F) != 0x30) base[i] = 0;
+ if (status & 0x40) start[i] = jiffies;
+ if (~status & 0x80) stop[i] = jiffies;
+ }
+ }
+
+ /* Evaluate measurements */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ time = stop[i] - start[i];
+ if (time < 9 || time > 11)
+ /* The time expired doesn't match */
+ base[i] = 0;
+ else {
+ /* Ok, we have found an adapter */
+ if (setup_adapter(base[i], h, n) == 0)
+ n++;
+ }
+ }
+
+ } /* NUM_TYPES */
+
+ /* If any adapter was successfully initialized, return ok */
+ if (n) return 0;
+
+ /* If no adapter found, return error */
+ printk("dmascc: no adapters found\n");
+ return -EIO;
+}
+
+
+__initfunc(int setup_adapter(int io, int h, int n))
+{
+ int i, irq, chip;
+ struct scc_info *info;
+ struct device *dev;
+ struct scc_priv *priv;
+ unsigned long time;
+ unsigned int irqs;
+ int tmr = io + hw[h].tmr_offset;
+ int scc = io + hw[h].scc_offset;
+ int cmd = scc + SCCA_CMD;
+ char *chipnames[] = CHIPNAMES;
+
+ /* Reset 8530 */
+ write_scc(cmd, R9, FHWRES | MIE | NV);
+
+ /* Determine type of chip */
+ write_scc(cmd, R15, 1);
+ if (!read_scc(cmd, R15)) {
+ /* WR7' not present. This is an ordinary Z8530 SCC. */
+ chip = Z8530;
+ } else {
+ /* Put one character in TX FIFO */
+ write_scc(cmd, R8, 0);
+ if (read_scc(cmd, R0) & Tx_BUF_EMP) {
+ /* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */
+ chip = Z85230;
+ } else {
+ /* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */
+ chip = Z85C30;
+ }
+ }
+ write_scc(cmd, R15, 0);
+
+ /* Start IRQ auto-detection */
+ sti();
+ irqs = probe_irq_on();
+
+ /* Enable interrupts */
+ switch (h) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ outb_p(0, io + PI_DREQ_MASK);
+ write_scc(cmd, R15, CTSIE);
+ write_scc(cmd, R0, RES_EXT_INT);
+ write_scc(cmd, R1, EXT_INT_ENAB);
+ break;
+ case TYPE_TWIN:
+ outb_p(0, io + TWIN_DMA_CFG);
+ inb_p(io + TWIN_CLR_TMR1);
+ inb_p(io + TWIN_CLR_TMR2);
+ outb_p(TWIN_EI, io + TWIN_SERIAL_CFG);
+ break;
+ }
+
+ /* Start timer */
+ outb_p(1, tmr + TMR_CNT1);
+ outb_p(0, tmr + TMR_CNT1);
+ /* Wait and detect IRQ */
+ time = jiffies; while (jiffies - time < 2 + HZ / TMR_0_HZ);
+ irq = probe_irq_off(irqs);
+
+ /* Clear pending interrupt, disable interrupts */
+ switch (h) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ write_scc(cmd, R1, 0);
+ write_scc(cmd, R15, 0);
+ write_scc(cmd, R0, RES_EXT_INT);
+ break;
+ case TYPE_TWIN:
+ inb_p(io + TWIN_CLR_TMR1);
+ outb_p(0, io + TWIN_SERIAL_CFG);
+ break;
+ }
+
+ if (irq <= 0) {
+ printk("dmascc: could not find irq of %s at %#3x (irq=%d)\n",
+ hw[h].name, io, irq);
+ return -1;
+ }
+
+ /* Allocate memory */
+ info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA);
+ if (!info) {
+ printk("dmascc: could not allocate memory for %s at %#3x\n",
+ hw[h].name, io);
+ return -1;
+ }
+
+ /* Set up data structures */
+ memset(info, 0, sizeof(struct scc_info));
+ info->type = h;
+ info->chip = chip;
+ info->scc_base = io + hw[h].scc_offset;
+ info->tmr_base = io + hw[h].tmr_offset;
+ info->twin_serial_cfg = 0;
+ for (i = 0; i < 2; i++) {
+ dev = &info->dev[i];
+ priv = &info->priv[i];
+ sprintf(priv->name, "dmascc%i", 2*n+i);
+ priv->info = info;
+ priv->channel = i;
+ priv->cmd = info->scc_base + (i ? SCCB_CMD : SCCA_CMD);
+ priv->data = info->scc_base + (i ? SCCB_DATA : SCCA_DATA);
+ priv->tmr = info->tmr_base + (i ? TMR_CNT2 : TMR_CNT1);
+ priv->param.pclk_hz = hw[h].pclk_hz;
+ priv->param.brg_tc = -1;
+ priv->param.clocks = TCTRxCP | RCRTxCP;
+ priv->param.txdelay = TMR_0_HZ * 10 / 1000;
+ priv->param.txtime = HZ * 3;
+ priv->param.sqdelay = TMR_0_HZ * 1 / 1000;
+ priv->param.slottime = TMR_0_HZ * 10 / 1000;
+ priv->param.waittime = TMR_0_HZ * 100 / 1000;
+ priv->param.persist = 32;
+ priv->rx_task.routine = rx_bh;
+ priv->rx_task.data = dev;
+ dev->priv = priv;
+ dev->name = priv->name;
+ dev->base_addr = io;
+ dev->irq = irq;
+ dev->open = scc_open;
+ dev->stop = scc_close;
+ dev->do_ioctl = scc_ioctl;
+ dev->hard_start_xmit = scc_send_packet;
+ dev->get_stats = scc_get_stats;
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+ dev->set_mac_address = scc_set_mac_address;
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = 73;
+ dev->mtu = 1500;
+ dev->addr_len = 7;
+ dev->tx_queue_len = 64;
+ memcpy(dev->broadcast, ax25_broadcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+ dev->flags = 0;
+ dev_init_buffers(dev);
+ if (register_netdev(dev)) {
+ printk("dmascc: could not register %s\n", dev->name);
+ dev->name = NULL;
+ }
+ }
+
+ request_region(io, hw[h].io_size, "dmascc");
+
+ info->next = first;
+ first = info;
+ printk("dmascc: found %s (%s) at %#3x, irq %d\n", hw[h].name,
+ chipnames[chip], io, irq);
+ return 0;
+}
+
+
+/* Driver functions */
+
+static inline void write_scc(int ctl, int reg, int val)
+{
+ outb_p(reg, ctl);
+ outb_p(val, ctl);
+}
+
+
+static inline int read_scc(int ctl, int reg)
+{
+ outb_p(reg, ctl);
+ return inb_p(ctl);
+}
+
+
+static int scc_open(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int io = dev->base_addr;
+ int cmd = priv->cmd;
+
+ /* Request IRQ if not already used by other channel */
+ if (!info->open) {
+ if (request_irq(dev->irq, scc_isr, SA_INTERRUPT, "dmascc", info))
+ return -EAGAIN;
+ }
+
+ /* Request DMA if required */
+ if (dev->dma && request_dma(dev->dma, "dmascc")) {
+ if (!info->open) free_irq(dev->irq, info);
+ return -EAGAIN;
+ }
+
+ /* Initialize local variables */
+ dev->tbusy = 0;
+ priv->rx_ptr = 0;
+ priv->rx_over = 0;
+ priv->rx_head = priv->rx_tail = priv->rx_count = 0;
+ priv->tx_state = TX_IDLE;
+ priv->tx_head = priv->tx_tail = priv->tx_count = 0;
+ priv->tx_ptr = 0;
+ priv->tx_sem = 0;
+
+ /* Reset channel */
+ write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+ /* X1 clock, SDLC mode */
+ write_scc(cmd, R4, SDLC | X1CLK);
+ /* DMA */
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+ /* 8 bit RX char, RX disable */
+ write_scc(cmd, R3, Rx8);
+ /* 8 bit TX char, TX disable */
+ write_scc(cmd, R5, Tx8);
+ /* SDLC address field */
+ write_scc(cmd, R6, 0);
+ /* SDLC flag */
+ write_scc(cmd, R7, FLAG);
+ switch (info->chip) {
+ case Z85C30:
+ /* Select WR7' */
+ write_scc(cmd, R15, 1);
+ /* Auto EOM reset */
+ write_scc(cmd, R7, 0x02);
+ write_scc(cmd, R15, 0);
+ break;
+ case Z85230:
+ /* Select WR7' */
+ write_scc(cmd, R15, 1);
+ /* RX FIFO half full (interrupt only), Auto EOM reset,
+ TX FIFO empty (DMA only) */
+ write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a);
+ write_scc(cmd, R15, 0);
+ break;
+ }
+ /* Preset CRC, NRZ(I) encoding */
+ write_scc(cmd, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ));
+
+ /* Configure baud rate generator */
+ if (priv->param.brg_tc >= 0) {
+ /* Program BR generator */
+ write_scc(cmd, R12, priv->param.brg_tc & 0xFF);
+ write_scc(cmd, R13, (priv->param.brg_tc>>8) & 0xFF);
+ /* BRG source = SYS CLK; enable BRG; DTR REQ function (required by
+ PackeTwin, not connected on the PI2); set DPLL source to BRG */
+ write_scc(cmd, R14, SSBR | DTRREQ | BRSRC | BRENABL);
+ /* Enable DPLL */
+ write_scc(cmd, R14, SEARCH | DTRREQ | BRSRC | BRENABL);
+ } else {
+ /* Disable BR generator */
+ write_scc(cmd, R14, DTRREQ | BRSRC);
+ }
+
+ /* Configure clocks */
+ if (info->type == TYPE_TWIN) {
+ /* Disable external TX clock receiver */
+ outb_p((info->twin_serial_cfg &=
+ ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+ io + TWIN_SERIAL_CFG);
+ }
+ write_scc(cmd, R11, priv->param.clocks);
+ if ((info->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) {
+ /* Enable external TX clock receiver */
+ outb_p((info->twin_serial_cfg |=
+ (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+ io + TWIN_SERIAL_CFG);
+ }
+
+ /* Configure PackeTwin */
+ if (info->type == TYPE_TWIN) {
+ /* Assert DTR, enable interrupts */
+ outb_p((info->twin_serial_cfg |= TWIN_EI |
+ (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)),
+ io + TWIN_SERIAL_CFG);
+ }
+
+ /* Read current status */
+ priv->status = read_scc(cmd, R0);
+ /* Enable SYNC, DCD, and CTS interrupts */
+ write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
+
+ /* Configure PI2 DMA */
+ if (info->type <= TYPE_PI2) outb_p(1, io + PI_DREQ_MASK);
+
+ dev->start = 1;
+ info->open++;
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int scc_close(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int io = dev->base_addr;
+ int cmd = priv->cmd;
+
+ dev->start = 0;
+ info->open--;
+ MOD_DEC_USE_COUNT;
+
+ if (info->type == TYPE_TWIN)
+ /* Drop DTR */
+ outb_p((info->twin_serial_cfg &=
+ (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)),
+ io + TWIN_SERIAL_CFG);
+
+ /* Reset channel, free DMA */
+ write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+ if (dev->dma) {
+ if (info->type == TYPE_TWIN) outb_p(0, io + TWIN_DMA_CFG);
+ free_dma(dev->dma);
+ }
+
+ if (!info->open) {
+ if (info->type <= TYPE_PI2) outb_p(0, io + PI_DREQ_MASK);
+ free_irq(dev->irq, info);
+ }
+ return 0;
+}
+
+
+static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int rc;
+ struct scc_priv *priv = dev->priv;
+
+ switch (cmd) {
+ case SIOCGSCCPARAM:
+ rc = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct scc_param));
+ if (rc) return rc;
+ copy_to_user(ifr->ifr_data, &priv->param, sizeof(struct scc_param));
+ return 0;
+ case SIOCSSCCPARAM:
+ if (!suser()) return -EPERM;
+ rc = verify_area(VERIFY_READ, ifr->ifr_data, sizeof(struct scc_param));
+ if (rc) return rc;
+ if (dev->start) return -EAGAIN;
+ copy_from_user(&priv->param, ifr->ifr_data, sizeof(struct scc_param));
+ dev->dma = priv->param.dma;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int scc_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+ unsigned long flags;
+ int i;
+
+ /* Block a timer-based transmit from overlapping */
+ if (test_and_set_bit(0, (void *) &priv->tx_sem) != 0) {
+ atomic_inc((void *) &priv->stats.tx_dropped);
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ /* Return with an error if we cannot accept more data */
+ if (dev->tbusy) {
+ priv->tx_sem = 0;
+ return -1;
+ }
+
+ /* Transfer data to DMA buffer */
+ i = priv->tx_head;
+ memcpy(priv->tx_buf[i], skb->data+1, skb->len-1);
+ priv->tx_len[i] = skb->len-1;
+
+ save_flags(flags);
+ cli();
+
+ /* Set the busy flag if we just filled up the last buffer */
+ priv->tx_head = (i + 1) % NUM_TX_BUF;
+ priv->tx_count++;
+ if (priv->tx_count == NUM_TX_BUF) dev->tbusy = 1;
+
+ /* Set new TX state */
+ if (priv->tx_state == TX_IDLE) {
+ /* Assert RTS, start timer */
+ priv->tx_state = TX_TXDELAY;
+ if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ }
+
+ restore_flags(flags);
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ priv->tx_sem = 0;
+ return 0;
+}
+
+
+static struct enet_statistics *scc_get_stats(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+
+ return &priv->stats;
+}
+
+
+static int scc_set_mac_address(struct device *dev, void *sa)
+{
+ memcpy(dev->dev_addr, ((struct sockaddr *)sa)->sa_data, dev->addr_len);
+ return 0;
+}
+
+
+static void scc_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct scc_info *info = dev_id;
+ int is, io = info->dev[0].base_addr;
+
+ /* We're a fast IRQ handler and are called with interrupts disabled */
+
+ /* IRQ sharing doesn't make sense due to ISA's edge-triggered
+ interrupts, hence it is safe to return if we have found and
+ processed a single device. */
+
+ /* Interrupt processing: We loop until we know that the IRQ line is
+ low. If another positive edge occurs afterwards during the ISR,
+ another interrupt will be triggered by the interrupt controller
+ as soon as the IRQ level is enabled again (see asm/irq.h). */
+
+ switch (info->type) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ outb_p(0, io + PI_DREQ_MASK);
+ z8530_isr(info);
+ outb_p(1, io + PI_DREQ_MASK);
+ return;
+ case TYPE_TWIN:
+ while ((is = ~inb_p(io + TWIN_INT_REG)) &
+ TWIN_INT_MSK) {
+ if (is & TWIN_SCC_MSK) {
+ z8530_isr(info);
+ } else if (is & TWIN_TMR1_MSK) {
+ inb_p(io + TWIN_CLR_TMR1);
+ tm_isr(&info->dev[0]);
+ } else {
+ inb_p(io + TWIN_CLR_TMR2);
+ tm_isr(&info->dev[1]);
+ }
+ }
+ /* No interrupts pending from the PackeTwin */
+ return;
+ }
+}
+
+
+static inline void z8530_isr(struct scc_info *info)
+{
+ int is, a_cmd;
+
+ a_cmd = info->scc_base + SCCA_CMD;
+
+ while ((is = read_scc(a_cmd, R3))) {
+ if (is & CHARxIP) {
+ rx_isr(&info->dev[0]);
+ } else if (is & CHATxIP) {
+ tx_isr(&info->dev[0]);
+ } else if (is & CHAEXT) {
+ es_isr(&info->dev[0]);
+ } else if (is & CHBRxIP) {
+ rx_isr(&info->dev[1]);
+ } else if (is & CHBTxIP) {
+ tx_isr(&info->dev[1]);
+ } else {
+ es_isr(&info->dev[1]);
+ }
+ }
+ /* Ok, no interrupts pending from this 8530. The INT line should
+ be inactive now. */
+}
+
+
+static void rx_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ int cmd = priv->cmd;
+
+ if (dev->dma) {
+ /* Check special condition and perform error reset. See 2.4.7.5. */
+ special_condition(dev, read_scc(cmd, R1));
+ write_scc(cmd, R0, ERR_RES);
+ } else {
+ /* Check special condition for each character. Error reset not necessary.
+ Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */
+ int rc;
+ while (read_scc(cmd, R0) & Rx_CH_AV) {
+ rc = read_scc(cmd, R1);
+ if (priv->rx_ptr < BUF_SIZE)
+ priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8);
+ else {
+ priv->rx_over = 2;
+ read_scc(cmd, R8);
+ }
+ special_condition(dev, rc);
+ }
+ }
+}
+
+
+static void special_condition(struct device *dev, int rc)
+{
+ struct scc_priv *priv = dev->priv;
+ int cb, cmd = priv->cmd;
+
+ /* See Figure 2-15. Only overrun and EOF need to be checked. */
+
+ if (rc & Rx_OVR) {
+ /* Receiver overrun */
+ priv->rx_over = 1;
+ if (!dev->dma) write_scc(cmd, R0, ERR_RES);
+ } else if (rc & END_FR) {
+ /* End of frame. Get byte count */
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ cb = BUF_SIZE - get_dma_residue(dev->dma) - 2;
+ } else {
+ cb = priv->rx_ptr - 2;
+ }
+ if (priv->rx_over) {
+ /* We had an overrun */
+ priv->stats.rx_errors++;
+ if (priv->rx_over == 2) priv->stats.rx_length_errors++;
+ else priv->stats.rx_fifo_errors++;
+ priv->rx_over = 0;
+ } else if (rc & CRC_ERR) {
+ /* Count invalid CRC only if packet length >= minimum */
+ if (cb >= 8) {
+ priv->stats.rx_errors++;
+ priv->stats.rx_crc_errors++;
+ }
+ } else {
+ if (cb >= 8) {
+ /* Put good frame in FIFO */
+ priv->rx_len[priv->rx_head] = cb;
+ priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF;
+ priv->rx_count++;
+ if (priv->rx_count == NUM_RX_BUF) {
+ /* Disable receiver if FIFO full */
+ write_scc(cmd, R3, Rx8);
+ priv->stats.rx_errors++;
+ priv->stats.rx_over_errors++;
+ }
+ /* Mark bottom half handler */
+ queue_task(&priv->rx_task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ }
+ /* Get ready for new frame */
+ if (dev->dma) {
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ } else {
+ priv->rx_ptr = 0;
+ }
+ }
+}
+
+
+static void rx_bh(void *arg)
+{
+ struct device *dev = arg;
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+ int i = priv->rx_tail;
+ int cb;
+ unsigned long flags;
+ struct sk_buff *skb;
+ unsigned char *data;
+
+ save_flags(flags);
+ cli();
+
+ while (priv->rx_count) {
+ restore_flags(flags);
+ cb = priv->rx_len[i];
+ /* Allocate buffer */
+ skb = dev_alloc_skb(cb+1);
+ if (skb == NULL) {
+ /* Drop packet */
+ priv->stats.rx_dropped++;
+ } else {
+ /* Fill buffer */
+ data = skb_put(skb, cb+1);
+ data[0] = 0;
+ memcpy(&data[1], priv->rx_buf[i], cb);
+ skb->dev = dev;
+ skb->protocol = ntohs(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ priv->stats.rx_packets++;
+ }
+ save_flags(flags);
+ cli();
+ /* Enable receiver if RX buffers have been unavailable */
+ if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) {
+ if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
+ write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
+ if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
+ }
+ /* Move tail */
+ priv->rx_tail = i = (i + 1) % NUM_RX_BUF;
+ priv->rx_count--;
+ }
+
+ restore_flags(flags);
+}
+
+
+static void tx_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ int cmd = priv->cmd;
+ int i = priv->tx_tail, p = priv->tx_ptr;
+
+ /* Suspend TX interrupts if we don't want to send anything.
+ See Figure 2-22. */
+ if (p == priv->tx_len[i]) {
+ write_scc(cmd, R0, RES_Tx_P);
+ return;
+ }
+
+ /* Write characters */
+ while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) {
+ write_scc(cmd, R8, priv->tx_buf[i][p++]);
+ }
+ priv->tx_ptr = p;
+
+}
+
+
+static void es_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int i, cmd = priv->cmd;
+ int st, dst, res;
+
+ /* Read status and reset interrupt bit */
+ st = read_scc(cmd, R0);
+ write_scc(cmd, R0, RES_EXT_INT);
+ dst = priv->status ^ st;
+ priv->status = st;
+
+ /* Since the EOM latch is reset automatically, we assume that
+ it has been zero if and only if we are in the TX_ACTIVE state.
+ Otherwise we follow 2.4.9.6. */
+
+ /* Transmit underrun */
+ if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) {
+ /* Get remaining bytes */
+ i = priv->tx_tail;
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ res = get_dma_residue(dev->dma);
+ } else {
+ res = priv->tx_len[i] - priv->tx_ptr;
+ if (res) write_scc(cmd, R0, RES_Tx_P);
+ priv->tx_ptr = 0;
+ }
+ /* Remove frame from FIFO */
+ priv->tx_tail = (i + 1) % NUM_TX_BUF;
+ priv->tx_count--;
+ dev->tbusy = 0;
+ /* Check if another frame is available and we are allowed to transmit */
+ if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) {
+ if (dev->dma) {
+ set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
+ set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
+ enable_dma(dev->dma);
+ } else {
+ /* If we have an ESCC, we are allowed to write data bytes
+ immediately. Otherwise we have to wait for the next
+ TX interrupt. See Figure 2-22. */
+ if (info->chip == Z85230) {
+ tx_isr(dev);
+ }
+ }
+ } else {
+ /* No frame available. Disable interrupts. */
+ priv->tx_state = TX_SQDELAY;
+ delay(dev, priv->param.sqdelay);
+ write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+ }
+ /* Update packet statistics */
+ if (res) {
+ priv->stats.tx_errors++;
+ priv->stats.tx_fifo_errors++;
+ } else {
+ priv->stats.tx_packets++;
+ }
+ /* Inform upper layers */
+ mark_bh(NET_BH);
+ }
+
+ /* DCD transition */
+ if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) {
+ /* Transmitter state change */
+ priv->tx_state = TX_OFF;
+ /* Enable or disable receiver */
+ if (st & DCD) {
+ if (dev->dma) {
+ /* Program DMA controller */
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_READ);
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ /* Configure PackeTwin DMA */
+ if (info->type == TYPE_TWIN) {
+ outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3,
+ dev->base_addr + TWIN_DMA_CFG);
+ }
+ /* Sp. cond. intr. only, ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx |
+ WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB);
+ } else {
+ /* Intr. on all Rx characters and Sp. cond., ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT |
+ WT_FN_RDYFN);
+ }
+ if (priv->rx_count < NUM_RX_BUF) {
+ /* Enable receiver */
+ write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
+ }
+ } else {
+ /* Disable DMA */
+ if (dev->dma) disable_dma(dev->dma);
+ /* Disable receiver */
+ write_scc(cmd, R3, Rx8);
+ /* DMA disable, RX int disable, Ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+ /* Transmitter state change */
+ if (random() > priv->param.persist)
+ delay(dev, priv->param.slottime);
+ else {
+ if (priv->tx_count) {
+ priv->tx_state = TX_TXDELAY;
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ } else {
+ priv->tx_state = TX_IDLE;
+ }
+ }
+ }
+ }
+
+ /* CTS transition */
+ if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) {
+ /* Timer has expired */
+ tm_isr(dev);
+ }
+
+ /* /SYNC/HUNT transition */
+ if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) {
+ /* Reset current frame and clear RX FIFO */
+ while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8);
+ priv->rx_over = 0;
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ } else {
+ priv->rx_ptr = 0;
+ }
+ }
+}
+
+
+static void tm_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+
+ switch (priv->tx_state) {
+ case TX_OFF:
+ if (~priv->status & DCD) {
+ if (random() > priv->param.persist) delay(dev, priv->param.slottime);
+ else {
+ if (priv->tx_count) {
+ priv->tx_state = TX_TXDELAY;
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ } else {
+ priv->tx_state = TX_IDLE;
+ }
+ }
+ }
+ break;
+ case TX_TXDELAY:
+ priv->tx_state = TX_ACTIVE;
+ if (dev->dma) {
+ /* Program DMA controller */
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_WRITE);
+ set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
+ set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
+ enable_dma(dev->dma);
+ /* Configure PackeTwin DMA */
+ if (info->type == TYPE_TWIN) {
+ outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3,
+ dev->base_addr + TWIN_DMA_CFG);
+ }
+ /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin
+ is used for TX DMA requests, but we enable the WAIT/DMA request
+ pin, anyway */
+ write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB);
+ } else {
+ write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB);
+ tx_isr(dev);
+ }
+ if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L);
+ break;
+ case TX_SQDELAY:
+ /* Disable transmitter */
+ write_scc(cmd, R5, TxCRC_ENAB | Tx8);
+ /* Transmitter state change: Switch to TX_OFF and wait at least
+ 1 slottime. */
+ priv->tx_state = TX_OFF;
+ if (~priv->status & DCD) delay(dev, priv->param.waittime);
+ }
+}
+
+
+static inline void delay(struct device *dev, int t)
+{
+ struct scc_priv *priv = dev->priv;
+ int tmr = priv->tmr;
+
+ outb_p(t & 0xFF, tmr);
+ outb_p((t >> 8) & 0xFF, tmr);
+}
+
+
+static inline unsigned char random(void)
+{
+ /* See "Numerical Recipes in C", second edition, p. 284 */
+ rand = rand * 1664525L + 1013904223L;
+ return (unsigned char) (rand >> 24);
+}
+
+
diff --git a/drivers/net/ipddp.c b/drivers/net/ipddp.c
index fdca59467..fa3072a6c 100644
--- a/drivers/net/ipddp.c
+++ b/drivers/net/ipddp.c
@@ -1,5 +1,3 @@
-#warning "Needs new networking merges before it will work"
-#if 0
/*
* ipddp.c: IP-over-DDP driver for Linux
*
@@ -309,4 +307,3 @@ void cleanup_module(void)
}
#endif /* MODULE */
-#endif
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c39083b24..7b9a16829 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -154,8 +154,8 @@ struct pci_dev_info dev_info[] = {
DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"),
DEVICE( DPT, DPT, "SmartCache/Raid"),
DEVICE( OPTI, OPTI_92C178, "92C178"),
- DEVICE( OPTI, OPTI_82C557, "82C557"),
- DEVICE( OPTI, OPTI_82C558, "82C558"),
+ DEVICE( OPTI, OPTI_82C557, "82C557 Viper-M"),
+ DEVICE( OPTI, OPTI_82C558, "82C558 Viper-M ISA+IDE"),
DEVICE( OPTI, OPTI_82C621, "82C621"),
DEVICE( OPTI, OPTI_82C700, "82C700"),
DEVICE( OPTI, OPTI_82C701, "82C701 FireStar Plus"),
@@ -394,15 +394,15 @@ struct pci_dev_info dev_info[] = {
DEVICE( INTEL, INTEL_82865, "82865"),
DEVICE( INTEL, INTEL_82557, "82557"),
DEVICE( INTEL, INTEL_82437, "82437"),
- DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"),
- DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"),
+ DEVICE( INTEL, INTEL_82371FB_0,"82371FB PIIX ISA"),
+ DEVICE( INTEL, INTEL_82371FB_1,"82371FB PIIX IDE"),
DEVICE( INTEL, INTEL_82371MX, "430MX - 82371MX MPIIX"),
DEVICE( INTEL, INTEL_82437MX, "430MX - 82437MX MTSC"),
DEVICE( INTEL, INTEL_82441, "82441FX Natoma"),
DEVICE( INTEL, INTEL_82439, "82439HX Triton II"),
- DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"),
- DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"),
- DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
+ DEVICE( INTEL, INTEL_82371SB_0,"82371SB PIIX3 ISA"),
+ DEVICE( INTEL, INTEL_82371SB_1,"82371SB PIIX3 IDE"),
+ DEVICE( INTEL, INTEL_82371SB_2,"82371SB PIIX3 USB"),
DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"),
DEVICE( INTEL, INTEL_82439TX, "82439TX"),
DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4 ISA"),
@@ -555,11 +555,11 @@ const char *pci_strclass (unsigned int class)
case PCI_CLASS_NOT_DEFINED: return "Non-VGA device";
case PCI_CLASS_NOT_DEFINED_VGA: return "VGA compatible device";
- case PCI_CLASS_STORAGE_SCSI: return "SCSI storage controller";
- case PCI_CLASS_STORAGE_IDE: return "IDE interface";
+ case PCI_CLASS_STORAGE_SCSI: return "SCSI bus controller";
+ case PCI_CLASS_STORAGE_IDE: return "IDE controller";
case PCI_CLASS_STORAGE_FLOPPY: return "Floppy disk controller";
case PCI_CLASS_STORAGE_IPI: return "IPI bus controller";
- case PCI_CLASS_STORAGE_RAID: return "RAID bus controller";
+ case PCI_CLASS_STORAGE_RAID: return "RAID controller";
case PCI_CLASS_STORAGE_OTHER: return "Unknown mass storage controller";
case PCI_CLASS_NETWORK_ETHERNET: return "Ethernet controller";
@@ -909,7 +909,7 @@ static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
if (len + 40 > size) {
return -1;
}
- len += sprintf(buf + len, "IRQ %x. ", dev->irq);
+ len += sprintf(buf + len, "IRQ %d. ", dev->irq);
}
if (dev->master) {
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index efd6568f1..ff3264da3 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/scsi/ide-scsi.c Version 0.2 - ALPHA Jan 26, 1997
+ * linux/drivers/scsi/ide-scsi.c Version 0.4 Dec 7, 1997
*
* Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
*/
@@ -15,9 +15,13 @@
* of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
* to Janos Farkas for pointing this out.
* Avoid using bitfields in structures for m68k.
- * Added Scather/Gather and DMA support.
+ * Added Scatter/Gather and DMA support.
+ * Ver 0.4 Dec 7 97 Add support for ATAPI PD/CD drives.
+ * Use variable timeout for each command.
*/
+#define IDESCSI_VERSION "0.4"
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -49,11 +53,12 @@ typedef struct idescsi_pc_s {
struct request *rq; /* The corresponding request */
byte *buffer; /* Data buffer */
byte *current_position; /* Pointer into the above buffer */
- struct scatterlist *sg; /* Scather gather table */
+ struct scatterlist *sg; /* Scatter gather table */
int b_count; /* Bytes transferred from current entry */
Scsi_Cmnd *scsi_cmd; /* SCSI command */
void (*done)(Scsi_Cmnd *); /* Scsi completion routine */
unsigned int flags; /* Status/Action flags */
+ unsigned long timeout; /* Command timeout */
} idescsi_pc_t;
/*
@@ -149,7 +154,7 @@ static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
int i;
if (drive->media == ide_cdrom) {
- if (c[0] == READ_6) {
+ if (c[0] == READ_6 || c[0] == WRITE_6) {
c[8] = c[4]; c[5] = c[3]; c[4] = c[2];
c[3] = c[1] & 0x1f; c[2] = 0; c[1] &= 0xe0;
c[0] = READ_10;
@@ -226,6 +231,11 @@ static void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
scsi->pc = NULL;
}
+static inline unsigned long get_timeout(idescsi_pc_t *pc)
+{
+ return IDE_MAX(WAIT_CMD, pc->timeout - jiffies);
+}
+
/*
* Our interrupt handler.
*/
@@ -247,7 +257,7 @@ static void idescsi_pc_intr (ide_drive_t *drive)
printk ("ide-scsi: %s: DMA complete\n", drive->name);
#endif /* IDESCSI_DEBUG_LOG */
pc->actually_transferred=pc->request_transfer;
- (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+ (void) (HWIF(drive)->dmaproc(ide_dma_end, drive));
}
status = GET_STAT(); /* Clear the interrupt */
@@ -276,7 +286,7 @@ static void idescsi_pc_intr (ide_drive_t *drive)
if (temp > pc->buffer_size) {
printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
idescsi_discard_data (drive,bcount);
- ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc));
return;
}
#if IDESCSI_DEBUG_LOG
@@ -298,12 +308,13 @@ static void idescsi_pc_intr (ide_drive_t *drive)
pc->actually_transferred+=bcount; /* Update the current position */
pc->current_position+=bcount;
- ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD); /* And set the interrupt handler again */
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* And set the interrupt handler again */
}
static void idescsi_transfer_pc (ide_drive_t *drive)
{
idescsi_scsi_t *scsi = drive->driver_data;
+ idescsi_pc_t *pc = scsi->pc;
byte ireason;
if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
@@ -316,8 +327,8 @@ static void idescsi_transfer_pc (ide_drive_t *drive)
ide_do_reset (drive);
return;
}
- ide_set_handler (drive, &idescsi_pc_intr, WAIT_CMD); /* Set the interrupt routine */
- atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */
+ ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* Set the interrupt routine */
+ atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */
}
/*
@@ -349,7 +360,7 @@ static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
}
if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
- ide_set_handler (drive, &idescsi_transfer_pc, WAIT_CMD);
+ ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc));
OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */
} else {
OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
@@ -426,6 +437,8 @@ static ide_module_t idescsi_module = {
* IDE subdriver functions, registered with ide.c
*/
static ide_driver_t idescsi_driver = {
+ "ide-scsi", /* name */
+ IDESCSI_VERSION, /* version */
ide_scsi, /* media */
0, /* busy */
1, /* supports_dma */
@@ -439,7 +452,8 @@ static ide_driver_t idescsi_driver = {
NULL, /* media_change */
NULL, /* pre_reset */
NULL, /* capacity */
- NULL /* special */
+ NULL, /* special */
+ NULL /* proc */
};
static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
@@ -555,7 +569,7 @@ static inline struct buffer_head *idescsi_dma_bh (ide_drive_t *drive, idescsi_pc
int segments = pc->scsi_cmd->use_sg;
struct scatterlist *sg = pc->scsi_cmd->request_buffer;
- if (!drive->using_dma || pc->request_transfer % 1024)
+ if (!drive->using_dma || !pc->request_transfer || pc->request_transfer % 1024)
return NULL;
if (idescsi_set_direction(pc))
return NULL;
@@ -619,6 +633,7 @@ int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
pc->scsi_cmd = cmd;
pc->done = done;
+ pc->timeout = jiffies + cmd->timeout_per_command;
idescsi_transform_pc1 (drive, pc);
ide_init_drive_cmd (rq);
@@ -645,6 +660,18 @@ int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
return SCSI_RESET_PUNT;
}
+int idescsi_bios (Disk *disk, kdev_t dev, int *parm)
+{
+ ide_drive_t *drive = idescsi_drives[disk->device->id];
+
+ if (drive->cyl && drive->head && drive->sect) {
+ parm[0] = drive->head;
+ parm[1] = drive->sect;
+ parm[2] = drive->cyl;
+ }
+ return 0;
+}
+
#ifdef MODULE
Scsi_Host_Template idescsi_template = IDESCSI;
diff --git a/drivers/scsi/ide-scsi.h b/drivers/scsi/ide-scsi.h
index 44b524e73..2ccbc3c03 100644
--- a/drivers/scsi/ide-scsi.h
+++ b/drivers/scsi/ide-scsi.h
@@ -13,6 +13,7 @@ extern const char *idescsi_info (struct Scsi_Host *host);
extern int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
extern int idescsi_abort (Scsi_Cmnd *cmd);
extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
+extern int idescsi_bios (Disk *disk, kdev_t dev, int *parm);
#define IDESCSI \
{ NULL, /* next */ \
@@ -28,7 +29,7 @@ extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
idescsi_abort, /* abort */ \
idescsi_reset, /* reset */ \
NULL, /* slave_attach */ \
- NULL, /* bios_param */ \
+ idescsi_bios, /* bios_param */ \
10, /* can_queue */ \
-1, /* this_id */ \
256, /* sg_tablesize */ \
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index b70522280..1d7ed45b4 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -580,6 +580,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
STp->buffer = st_buffers[i];
(STp->buffer)->in_use = 1;
(STp->buffer)->writing = 0;
+ (STp->buffer)->last_result_fatal = 0;
flags = filp->f_flags;
STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
diff --git a/drivers/sgi/Makefile b/drivers/sgi/Makefile
index 40cb89eaa..e2f0b7b7e 100644
--- a/drivers/sgi/Makefile
+++ b/drivers/sgi/Makefile
@@ -20,3 +20,25 @@ SUB_DIRS += char
L_OBJS += char/sgichar.o
include $(TOPDIR)/Rules.make
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) char
+
+L_OBJS :=
+L_TARGET := sgi.a
+
+# Character devices for SGI machines.
+#
+SUB_DIRS += char
+L_OBJS += char/sgichar.o
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/sgi/char/Makefile b/drivers/sgi/char/Makefile
index 7032c700c..3ccaafd9e 100644
--- a/drivers/sgi/char/Makefile
+++ b/drivers/sgi/char/Makefile
@@ -16,3 +16,21 @@ ifeq ($(CONFIG_SGI_SERIAL),y)
endif
include $(TOPDIR)/Rules.make
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := sgichar.o
+O_OBJS := graphics.o streamable.o newport.o cons_newport.o sgicons.o \
+ vga_font.o rrm.o shmiq.o usema.o
+
+ifeq ($(CONFIG_SGI_SERIAL),y)
+ O_OBJS += sgiserial.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/sgi/char/cons_newport.c b/drivers/sgi/char/cons_newport.c
index 91212c0ae..30aa1f3b7 100644
--- a/drivers/sgi/char/cons_newport.c
+++ b/drivers/sgi/char/cons_newport.c
@@ -3,7 +3,618 @@
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*
- * $Id: cons_newport.c,v 1.5 1997/09/12 01:32:50 ralf Exp $
+ * $Id: cons_newport.c,v 1.1 1997/12/02 02:28:23 ralf Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/sgialib.h>
+#include <asm/ptrace.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
+
+#include "gconsole.h"
+#include "newport.h"
+#include "graphics.h" /* Just for now */
+#include <asm/gfx.h>
+#include <asm/ng1.h>
+
+#if 0
+#include "linux_logo.h"
+#endif
+
+#define BMASK(c) (c << 24)
+
+#define RENDER(regs, cp) do { \
+(regs)->go.zpattern = BMASK((cp)[0x0]); (regs)->go.zpattern = BMASK((cp)[0x1]); \
+(regs)->go.zpattern = BMASK((cp)[0x2]); (regs)->go.zpattern = BMASK((cp)[0x3]); \
+(regs)->go.zpattern = BMASK((cp)[0x4]); (regs)->go.zpattern = BMASK((cp)[0x5]); \
+(regs)->go.zpattern = BMASK((cp)[0x6]); (regs)->go.zpattern = BMASK((cp)[0x7]); \
+(regs)->go.zpattern = BMASK((cp)[0x8]); (regs)->go.zpattern = BMASK((cp)[0x9]); \
+(regs)->go.zpattern = BMASK((cp)[0xa]); (regs)->go.zpattern = BMASK((cp)[0xb]); \
+(regs)->go.zpattern = BMASK((cp)[0xc]); (regs)->go.zpattern = BMASK((cp)[0xd]); \
+(regs)->go.zpattern = BMASK((cp)[0xe]); (regs)->go.zpattern = BMASK((cp)[0xf]); \
+} while(0)
+
+#define REVERSE_RENDER(regs, cp) do { \
+(regs)->go.zpattern = BMASK((~(cp)[0x0])); (regs)->go.zpattern = BMASK((~(cp)[0x1])); \
+(regs)->go.zpattern = BMASK((~(cp)[0x2])); (regs)->go.zpattern = BMASK((~(cp)[0x3])); \
+(regs)->go.zpattern = BMASK((~(cp)[0x4])); (regs)->go.zpattern = BMASK((~(cp)[0x5])); \
+(regs)->go.zpattern = BMASK((~(cp)[0x6])); (regs)->go.zpattern = BMASK((~(cp)[0x7])); \
+(regs)->go.zpattern = BMASK((~(cp)[0x8])); (regs)->go.zpattern = BMASK((~(cp)[0x9])); \
+(regs)->go.zpattern = BMASK((~(cp)[0xa])); (regs)->go.zpattern = BMASK((~(cp)[0xb])); \
+(regs)->go.zpattern = BMASK((~(cp)[0xc])); (regs)->go.zpattern = BMASK((~(cp)[0xd])); \
+(regs)->go.zpattern = BMASK((~(cp)[0xe])); (regs)->go.zpattern = BMASK((~(cp)[0xf])); \
+} while(0)
+
+extern int default_red[16], default_grn[16], default_blu[16];
+extern unsigned char video_type;
+
+static int cursor_pos = -1;
+struct newport_regs *npregs;
+
+#define TESTVAL 0xdeadbeef
+#define XSTI_TO_FXSTART(val) (((val) & 0xffff) << 11)
+
+static inline void
+newport_disable_video(void)
+{
+ unsigned short treg;
+
+ treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
+ newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg & ~(VC2_CTRL_EVIDEO)));
+}
+
+static inline void
+newport_enable_video(void)
+{
+ unsigned short treg;
+
+ treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
+ newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg | VC2_CTRL_EVIDEO));
+}
+
+static inline void
+newport_disable_cursor(void)
+{
+ unsigned short treg;
+
+ treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
+ newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg & ~(VC2_CTRL_ECDISP)));
+}
+
+#if 0
+static inline void
+newport_enable_cursor(void)
+{
+ unsigned short treg;
+
+ treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
+ newport_vc2_set(npregs, VC2_IREG_CONTROL, (treg | VC2_CTRL_ECDISP));
+}
+#endif
+
+static inline void
+newport_init_cmap(void)
+{
+ unsigned short i;
+
+ for(i = 0; i < 16; i++) {
+ newport_bfwait();
+ newport_cmap_setaddr(npregs, color_table[i]);
+ newport_cmap_setrgb(npregs,
+ default_red[i],
+ default_grn[i],
+ default_blu[i]);
+ }
+}
+
+#if 0
+static inline void
+newport_init_cursor(void)
+{
+ unsigned char cursor[256];
+ unsigned short *cookie;
+ int i;
+
+ for(i = 0; i < 256; i++)
+ cursor[i] = 0x0;
+ for(i = 211; i < 256; i+=4) {
+ cursor[i] = 0xff;
+#if 0
+ cursor[(i + 128) << 2] = 0xff;
+ cursor[((i + 128) << 2) + 1] = 0xff;
+#endif
+ }
+
+ /* Load the SRAM on the VC2 for this new GLYPH. */
+ cookie = (unsigned short *) cursor;
+ newport_vc2_set(npregs, VC2_IREG_RADDR, VC2_CGLYPH_ADDR);
+ npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
+ NPORT_DMODE_W2 | VC2_PROTOCOL);
+ for(i = 0; i < 128; i++) {
+ newport_bfwait();
+ npregs->set.dcbdata0.hwords.s1 = *cookie++;
+ }
+
+ /* Place the cursor at origin. */
+ newport_vc2_set(npregs, VC2_IREG_CURSX, 0);
+ newport_vc2_set(npregs, VC2_IREG_CURSY, 0);
+ newport_enable_cursor();
+}
+#endif
+
+static inline void
+newport_clear_screen(void)
+{
+ newport_wait();
+ npregs->set.wrmask = 0xffffffff;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX |
+ NPORT_DMODE0_STOPY);
+ npregs->set.colori = 0;
+ npregs->set.xystarti = 0;
+ npregs->go.xyendi = (((1280 + 63) << 16)|(1024));
+ newport_bfwait();
+}
+
+static inline void
+newport_render_version(void)
+{
+#if 0
+ unsigned short *ush;
+ int currcons = 0;
+ char *p;
+
+ ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20;
+ for (p = "SGI/Linux version " UTS_RELEASE; *p; p++, ush++) {
+ *ush = (attr << 8) + *p;
+ newport_blitc (*ush, (unsigned long) ush);
+ }
+#endif
+}
+
+#if 0
+static inline void
+newport_render_logo(void)
+{
+ int i, xpos, ypos;
+ unsigned char *bmap;
+
+ xpos = 8;
+ ypos = 18;
+
+ newport_wait();
+ npregs->set.colori = 9;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
+ NPORT_DMODE0_L32);
+
+ for(i = 0; i < 80; i+=8) {
+ /* Set coordinates for bitmap operation. */
+ npregs->set.xystarti = ((xpos + i) << 16) | ypos;
+ npregs->set.xyendi = (((xpos + i) + 7) << 16);
+ newport_wait();
+
+ bmap = linux_logo + (i * 80);
+ RENDER(npregs, bmap); bmap += 0x10;
+ RENDER(npregs, bmap); bmap += 0x10;
+ RENDER(npregs, bmap); bmap += 0x10;
+ RENDER(npregs, bmap); bmap += 0x10;
+ RENDER(npregs, bmap);
+ }
+ prom_getchar();
+ prom_imode();
+}
+#endif
+
+static inline void
+newport_render_background(int xpos, int ypos, int ci)
+{
+ newport_wait();
+ npregs->set.wrmask = 0xffffffff;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX |
+ NPORT_DMODE0_STOPY);
+ npregs->set.colori = ci;
+ npregs->set.xystarti = (xpos << 16) | ypos;
+ npregs->go.xyendi = ((xpos + 7) << 16) | (ypos + 15);
+}
+
+void
+newport_set_origin(unsigned short offset)
+{
+ /* maybe this works... */
+ __origin = offset;
+}
+
+void
+newport_hide_cursor(void)
+{
+ int xpos, ypos, idx;
+ unsigned long flags;
+
+ if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+ save_and_cli(flags);
+
+ idx = cursor_pos;
+ if(idx == -1) {
+ restore_flags(flags);
+ return;
+ }
+ xpos = 8 + ((idx % video_num_columns) << 3);
+ ypos = 18 + ((idx / video_num_columns) << 4);
+ newport_render_background(xpos, ypos, 0);
+ restore_flags(flags);
+}
+
+void
+newport_set_cursor(int currcons)
+{
+ int xpos, ypos, idx, oldpos;
+ unsigned short *sp, *osp, cattr;
+ unsigned long flags;
+ unsigned char *p;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ if (__real_origin != __origin)
+ __set_origin(__real_origin);
+
+ save_and_cli(flags);
+
+ idx = (pos - video_mem_base) >> 1;
+ sp = (unsigned short *) pos;
+ oldpos = cursor_pos;
+ cursor_pos = idx;
+ if(!deccm) {
+ hide_cursor();
+ restore_flags(flags);
+ return;
+ }
+ xpos = 8 + ((idx % video_num_columns) << 3);
+ ypos = 18 + ((idx / video_num_columns) << 4);
+ if(oldpos != -1) {
+ int oxpos, oypos;
+
+ /* Restore old location. */
+ osp = (unsigned short *) ((oldpos << 1) + video_mem_base);
+ oxpos = 8 + ((oldpos % video_num_columns) << 3);
+ oypos = 18 + ((oldpos / video_num_columns) << 4);
+ cattr = *osp;
+ newport_render_background(oxpos, oypos, (cattr & 0xf000) >> 12);
+ p = &vga_font[(cattr & 0xff) << 4];
+ newport_wait();
+ npregs->set.colori = (cattr & 0x0f00) >> 8;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
+ NPORT_DMODE0_L32);
+ npregs->set.xystarti = (oxpos << 16) | oypos;
+ npregs->set.xyendi = ((oxpos + 7) << 16);
+ newport_wait();
+ RENDER(npregs, p);
+ }
+ cattr = *sp;
+ newport_render_background(xpos, ypos, (cattr & 0xf000) >> 12);
+ p = &vga_font[(cattr & 0xff) << 4];
+ newport_wait();
+ npregs->set.colori = (cattr & 0x0f00) >> 8;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
+ NPORT_DMODE0_L32);
+ npregs->set.xystarti = (xpos << 16) | ypos;
+ npregs->set.xyendi = ((xpos + 7) << 16);
+ newport_wait();
+ REVERSE_RENDER(npregs, p);
+ restore_flags (flags);
+ return;
+}
+
+void
+newport_get_scrmem(int currcons)
+{
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
+ origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+ scr_end = video_mem_end = video_mem_start + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+}
+
+void
+newport_set_scrmem(int currcons, long offset)
+{
+ if (video_mem_term - video_mem_base < offset + video_screen_size)
+ offset = 0;
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
+ video_mem_start = video_mem_base;
+ video_mem_end = video_mem_term;
+ origin = video_mem_base + offset;
+ scr_end = origin + video_screen_size;
+ pos = origin + y*video_size_row + (x<<1);
+ has_wrapped = 0;
+}
+
+int
+newport_set_get_cmap(unsigned char * arg, int set)
+{
+ unsigned short ent;
+ int i;
+
+ i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, 16*3);
+ if (i)
+ return i;
+
+ for (i=0; i<16; i++) {
+ if (set) {
+ __get_user(default_red[i], arg++);
+ __get_user(default_grn[i], arg++);
+ __get_user(default_blu[i], arg++);
+ } else {
+ __put_user (default_red[i], arg++);
+ __put_user (default_grn[i], arg++);
+ __put_user (default_blu[i], arg++);
+ }
+ }
+ if (set) {
+ for (i=0; i<MAX_NR_CONSOLES; i++) {
+ if (vc_cons_allocated(i)) {
+ int j, k;
+ for (j = k = 0; j<16; j++) {
+ vc_cons[i].d->vc_palette[k++] =
+ default_red[j];
+ vc_cons[i].d->vc_palette[k++] =
+ default_grn[j];
+ vc_cons[i].d->vc_palette[k++] =
+ default_blu[j];
+ }
+ }
+ }
+ if(console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return 0;
+ for(ent = 0; ent < 16; ent++) {
+ newport_bfwait();
+ newport_cmap_setaddr(npregs, ent);
+ newport_cmap_setrgb(npregs,
+ default_red[ent],
+ default_grn[ent],
+ default_blu[ent]);
+ }
+ }
+
+ return 0;
+}
+
+void
+newport_blitc(unsigned short charattr, unsigned long addr)
+{
+ int idx, xpos, ypos;
+ unsigned char *p;
+
+ idx = (addr - (video_mem_base + (__origin<<1))) >> 1;
+ xpos = 8 + ((idx % video_num_columns) << 3);
+ ypos = 18 + ((idx / video_num_columns) << 4);
+
+ p = &vga_font[(charattr & 0xff) << 4];
+ charattr = (charattr >> 8) & 0xff;
+
+ newport_render_background(xpos, ypos, (charattr & 0xf0) >> 4);
+
+ /* Set the color and drawing mode. */
+ newport_wait();
+ npregs->set.colori = charattr & 0xf;
+ npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
+ NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
+ NPORT_DMODE0_L32);
+
+ /* Set coordinates for bitmap operation. */
+ npregs->set.xystarti = (xpos << 16) | ypos;
+ npregs->set.xyendi = ((xpos + 7) << 16);
+ newport_wait();
+
+ /* Go, baby, go... */
+ RENDER(npregs, p);
+}
+
+void
+newport_memsetw(void * s, unsigned short c, unsigned int count)
+{
+ unsigned short * addr = (unsigned short *) s;
+
+ count /= 2;
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ }
+ if ((unsigned long) addr + count > video_mem_term ||
+ (unsigned long) addr < video_mem_base) {
+ if ((unsigned long) addr + count <= video_mem_term ||
+ (unsigned long) addr > video_mem_base) {
+ while (count) {
+ count--;
+ *addr++ = c;
+ }
+ return;
+ } else {
+ while (count) {
+ count--;
+ scr_writew(c, addr++);
+ }
+ }
+ } else {
+ while (count) {
+ count--;
+ if (*addr != c) {
+ newport_blitc(c, (unsigned long)addr);
+ *addr++ = c;
+ } else
+ addr++;
+ }
+ }
+}
+
+void
+newport_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+ memcpy(to, from, count);
+ return;
+ }
+ if ((unsigned long) to + count > video_mem_term ||
+ (unsigned long) to < video_mem_base) {
+ if ((unsigned long) to + count <= video_mem_term ||
+ (unsigned long) to > video_mem_base)
+ memcpy(to, from, count);
+ else {
+ count /= 2;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(from++), to++);
+ }
+ }
+ } else {
+ count /= 2;
+ while (count) {
+ count--;
+ if (*to != *from) {
+ newport_blitc(*from, (unsigned long)to);
+ *to++ = *from++;
+ } else {
+ from++;
+ to++;
+ }
+ }
+ }
+}
+
+struct console_ops newport_console = {
+ newport_set_origin,
+ newport_hide_cursor,
+ newport_set_cursor,
+ newport_get_scrmem,
+ newport_set_scrmem,
+ newport_set_get_cmap,
+ newport_blitc,
+ newport_memsetw,
+ newport_memcpyw
+};
+
+/* Currently hard-coded values that are the same as those found on my system */
+struct ng1_info newport_board_info = {
+ { "NG1", "" /* what is the label? */, 1280, 1024, sizeof (struct ng1_info) },
+ 6, /* boardrev */
+ 1, /* rex3rev */
+ 0, /* vc2rev */
+ 2, /* monitor type */
+ 0, /* videoinstalled */
+ 3, /* mcrev */
+ 24, /* bitplanes */
+ 0, /* xmap9rev */
+ 2, /* cmaprev */
+ { 256, 1280, 1024, 76}, /* ng1_vof_info */
+ 13, /* paneltype */
+ 0
+};
+
+void
+newport_reset (void)
+{
+ newport_wait();
+ newport_enable_video();
+
+ /* Init the cursor disappear. */
+ newport_wait();
+#if 0
+ newport_init_cursor();
+#else
+ newport_disable_cursor();
+#endif
+
+ newport_init_cmap();
+
+ /* Clear the screen. */
+ newport_clear_screen();
+}
+
+/* right now the newport does not do anything at all */
+struct graphics_ops newport_graphic_ops = {
+ 0, /* owner */
+ 0, /* current user */
+ (void *) &newport_board_info, /* board info */
+ sizeof (struct ng1_info), /* size of our data structure */
+ 0, 0, /* g_regs, g_regs_size */
+ newport_save, newport_restore, /* g_save_context, g_restore_context */
+ newport_reset, newport_ioctl /* g_reset_console, g_ioctl */
+};
+
+struct graphics_ops *
+newport_probe (int slot, const char **name)
+{
+ struct newport_regs *p;
+
+ npregs = (struct newport_regs *) (KSEG1 + 0x1f0f0000);
+
+ p = npregs;
+ p->cset.config = NPORT_CFG_GD0;
+
+ if(newport_wait()) {
+ prom_printf("whoops, timeout, no NEWPORT there?");
+ return 0;
+ }
+
+ p->set.xstarti = TESTVAL; if(p->set._xstart.i != XSTI_TO_FXSTART(TESTVAL)) {
+ prom_printf("newport_probe: read back wrong value ;-(\n");
+ return 0;
+ }
+
+ if (slot == 0){
+ register_gconsole (&newport_console);
+ video_type = VIDEO_TYPE_SGI;
+ can_do_color = 1;
+ *name = "NEWPORT";
+ }
+
+ newport_reset ();
+ newport_render_version();
+#if 0
+ newport_render_logo();
+#endif
+ newport_graphic_ops.g_regs = 0x1f0f0000;
+ newport_graphic_ops.g_regs_size = sizeof (struct newport_regs);
+ return &newport_graphic_ops;
+}
+/*
+ * cons_newport.c: Newport graphics console code for the SGI.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: cons_newport.c,v 1.6 1997/09/21 23:00:01 miguel Exp $
*/
#include <linux/kernel.h>
diff --git a/drivers/sgi/char/gconsole.h b/drivers/sgi/char/gconsole.h
index 7bc9794bb..89fd84f2b 100644
--- a/drivers/sgi/char/gconsole.h
+++ b/drivers/sgi/char/gconsole.h
@@ -31,3 +31,36 @@ extern unsigned char vga_font[];
extern void disable_gconsole (void);
extern void enable_gconsole (void);
+/*
+ * This is a temporary measure, we should eventually migrate to
+ * Gert's generic graphic console code.
+ */
+
+#define cmapsz 8192
+#define CHAR_HEIGHT 16
+
+struct console_ops {
+ void (*set_origin)(unsigned short offset);
+ void (*hide_cursor)(void);
+ void (*set_cursor)(int currcons);
+ void (*get_scrmem)(int currcons);
+ void (*set_scrmem)(int currcons, long offset);
+ int (*set_get_cmap)(unsigned char *arg, int set);
+ void (*blitc)(unsigned short charattr, unsigned long addr);
+ void (*memsetw)(void *s, unsigned short c, unsigned int count);
+ void (*memcpyw)(unsigned short *to, unsigned short *from, unsigned int count);
+};
+
+void register_gconsole (struct console_ops *);
+
+/* This points to the system console */
+extern struct console_ops *gconsole;
+
+extern void gfx_init (const char **name);
+
+extern void __set_origin (unsigned short offset);
+extern void hide_cursor (void);
+extern unsigned char vga_font[];
+
+extern void disable_gconsole (void);
+extern void enable_gconsole (void);
diff --git a/drivers/sgi/char/graphics.c b/drivers/sgi/char/graphics.c
index b7e69b243..9d55e4867 100644
--- a/drivers/sgi/char/graphics.c
+++ b/drivers/sgi/char/graphics.c
@@ -325,3 +325,330 @@ gfx_init (const char **name)
}
+/*
+ * gfx.c: support for SGI's /dev/graphics, /dev/opengl
+ *
+ * Author: Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * On IRIX, /dev/graphics is [10, 146]
+ * /dev/opengl is [10, 147]
+ *
+ * From a mail with Mark J. Kilgard, /dev/opengl and /dev/graphics are
+ * the same thing, the use of /dev/graphics seems deprecated though.
+ *
+ * The reason that the original SGI programmer had to use only one
+ * device for all the graphic cards on the system will remain a
+ * mistery for the rest of our lives. Why some ioctls take a board
+ * number and some others not? Mistery. Why do they map the hardware
+ * registers into the user address space with an ioctl instead of
+ * mmap? Mistery too. Why they did not use the standard way of
+ * making ioctl constants and instead sticked a random constant?
+ * Mistery too.
+ *
+ * We implement those misterious things, and tried not to think about
+ * the reasons behind them.
+ */
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <asm/uaccess.h>
+#include "gconsole.h"
+#include "graphics.h"
+#include <asm/gfx.h>
+#include <asm/rrm.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+/* The boards */
+#include "newport.h"
+
+#ifdef PRODUCTION_DRIVER
+#define enable_gconsole()
+#define disable_gconsole()
+#endif
+
+static struct graphics_ops cards [MAXCARDS];
+static int boards;
+
+#define GRAPHICS_CARD(inode) 0
+
+int
+sgi_graphics_open (struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int
+sgi_graphics_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ unsigned int board;
+ unsigned int devnum = GRAPHICS_CARD (inode->i_rdev);
+ int i;
+
+ if ((cmd >= RRM_BASE) && (cmd <= RRM_CMD_LIMIT))
+ return rrm_command (cmd-RRM_BASE, (void *) arg);
+
+ switch (cmd){
+ case GFX_GETNUM_BOARDS:
+ return boards;
+
+ case GFX_GETBOARD_INFO: {
+ struct gfx_getboardinfo_args *bia = (void *) arg;
+ void *dest_buf;
+ int max_len;
+
+ i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct gfx_getboardinfo_args));
+ if (i) return i;
+
+ __get_user_ret (board, &bia->board, -EFAULT);
+ __get_user_ret (dest_buf, &bia->buf, -EFAULT);
+ __get_user_ret (max_len, &bia->len, -EFAULT);
+
+ if (board >= boards)
+ return -EINVAL;
+ if (max_len < sizeof (struct gfx_getboardinfo_args))
+ return -EINVAL;
+ if (max_len > cards [board].g_board_info_len)
+ max_len = cards [boards].g_board_info_len;
+ i = verify_area (VERIFY_WRITE, dest_buf, max_len);
+ if (i) return i;
+ if (copy_to_user (dest_buf, cards [board].g_board_info, max_len))
+ return -EFAULT;
+ return max_len;
+ }
+
+ case GFX_ATTACH_BOARD: {
+ struct gfx_attach_board_args *att = (void *) arg;
+ void *vaddr;
+ int r;
+
+ i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct gfx_attach_board_args));
+ if (i) return i;
+
+ __get_user_ret (board, &att->board, -EFAULT);
+ __get_user_ret (vaddr, &att->vaddr, -EFAULT);
+
+ /* Ok for now we are assuming /dev/graphicsN -> head N even
+ * if the ioctl api suggests that this is not quite the case.
+ *
+ * Otherwise we fail, we use this assumption in the mmap code
+ * below to find our board information.
+ */
+ if (board != devnum){
+ printk ("Parameter board does not match the current board\n");
+ return -EINVAL;
+ }
+
+ if (board >= boards)
+ return -EINVAL;
+
+ /* If it is the first opening it, then make it the board owner */
+ if (!cards [board].g_owner)
+ cards [board].g_owner = current;
+
+ /*
+ * Ok, we now call mmap on this file, which will end up calling
+ * sgi_graphics_mmap
+ */
+ disable_gconsole ();
+ r = do_mmap (file, (unsigned long)vaddr, cards [board].g_regs_size,
+ PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE, 0);
+ if (r)
+ return r;
+
+ }
+
+ /* Strange, the real mapping seems to be done at GFX_ATTACH_BOARD,
+ * GFX_MAPALL is not even used by IRIX X server
+ */
+ case GFX_MAPALL:
+ return 0;
+
+ case GFX_LABEL:
+ return 0;
+
+ /* Version check
+ * for my IRIX 6.2 X server, this is what the kernel returns
+ */
+ case 1:
+ return 3;
+
+ /* Xsgi does not use this one, I assume minor is the board being queried */
+ case GFX_IS_MANAGED:
+ if (devnum > boards)
+ return -EINVAL;
+ return (cards [devnum].g_owner != 0);
+
+ default:
+ if (cards [devnum].g_ioctl)
+ return (*cards [devnum].g_ioctl)(devnum, cmd, arg);
+
+ }
+ return -EINVAL;
+}
+
+int
+sgi_graphics_close (struct inode *inode, struct file *file)
+{
+ int board = GRAPHICS_CARD (inode->i_rdev);
+
+ /* Tell the rendering manager that one client is going away */
+ rrm_close (inode, file);
+
+ /* Was this file handle from the board owner?, clear it */
+ if (current == cards [board].g_owner){
+ cards [board].g_owner = 0;
+ (*cards [board].g_reset_console)();
+ enable_gconsole ();
+ }
+ return 0;
+}
+
+/*
+ * This is the core of the direct rendering engine.
+ */
+
+unsigned long
+sgi_graphics_nopage (struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+ unsigned long page;
+ int board = GRAPHICS_CARD (vma->vm_dentry->d_inode->i_rdev);
+
+#ifdef DEBUG_GRAPHICS
+ printk ("Got a page fault for board %d address=%lx guser=%lx\n", board, address,
+ cards [board].g_user);
+#endif
+
+ /* 1. figure out if another process has this mapped,
+ * and revoke the mapping in that case.
+ */
+ if (cards [board].g_user && cards [board].g_user != current){
+ /* FIXME: save graphics context here, dump it to rendering node? */
+ remove_mapping (cards [board].g_user, vma->vm_start, vma->vm_end);
+ }
+ cards [board].g_user = current;
+#if DEBUG_GRAPHICS
+ printk ("Registers: 0x%lx\n", cards [board].g_regs);
+ printk ("vm_start: 0x%lx\n", vma->vm_start);
+ printk ("address: 0x%lx\n", address);
+ printk ("diff: 0x%lx\n", (address - vma->vm_start));
+
+ printk ("page/pfn: 0x%lx\n", page);
+ printk ("TLB entry: %lx\n", pte_val (mk_pte (page + PAGE_OFFSET, PAGE_USERIO)));
+#endif
+
+ /* 2. Map this into the current process address space */
+ page = ((cards [board].g_regs) + (address - vma->vm_start));
+ return page + PAGE_OFFSET;
+}
+
+/*
+ * We convert a GFX ioctl for mapping hardware registers, in a nice sys_mmap
+ * call, which takes care of everything that must be taken care of.
+ *
+ */
+
+static struct vm_operations_struct graphics_mmap = {
+ NULL, /* no special mmap-open */
+ NULL, /* no special mmap-close */
+ NULL, /* no special mmap-unmap */
+ NULL, /* no special mmap-protect */
+ NULL, /* no special mmap-sync */
+ NULL, /* no special mmap-advise */
+ sgi_graphics_nopage, /* our magic no-page fault handler */
+ NULL, /* no special mmap-wppage */
+ NULL, /* no special mmap-swapout */
+ NULL /* no special mmap-swapin */
+};
+
+int
+sgi_graphics_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
+{
+ uint size;
+
+ size = vma->vm_end - vma->vm_start;
+ if (vma->vm_offset & ~PAGE_MASK)
+ return -ENXIO;
+
+ /* 1. Set our special graphic virtualizer */
+ vma->vm_ops = &graphics_mmap;
+
+ /* 2. Set the special tlb permission bits */
+ vma->vm_page_prot = PAGE_USERIO;
+
+ /* final setup */
+ vma->vm_dentry = dget (file->f_dentry);
+ return 0;
+}
+
+/* Do any post card-detection setup on graphics_ops */
+static void
+graphics_ops_post_init (int slot)
+{
+ /* There is no owner for the card initially */
+ cards [slot].g_owner = (struct task_struct *) 0;
+ cards [slot].g_user = (struct task_struct *) 0;
+}
+
+struct file_operations sgi_graphics_fops = {
+ NULL, /* llseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ sgi_graphics_ioctl, /* ioctl */
+ sgi_graphics_mmap, /* mmap */
+ sgi_graphics_open, /* open */
+ sgi_graphics_close, /* release */
+ NULL, /* fsync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+/* /dev/graphics */
+static struct miscdevice dev_graphics = {
+ SGI_GRAPHICS_MINOR, "sgi-graphics", &sgi_graphics_fops
+};
+
+/* /dev/opengl */
+static struct miscdevice dev_opengl = {
+ SGI_OPENGL_MINOR, "sgi-opengl", &sgi_graphics_fops
+};
+
+/* This is called later from the misc-init routine */
+void
+gfx_register (void)
+{
+ misc_register (&dev_graphics);
+ misc_register (&dev_opengl);
+}
+
+void
+gfx_init (const char **name)
+{
+ struct console_ops *console;
+ struct graphics_ops *g;
+
+ printk ("GFX INIT: ");
+ shmiq_init ();
+ usema_init ();
+
+ if ((g = newport_probe (boards, name)) != 0){
+ cards [boards] = *g;
+ graphics_ops_post_init (boards);
+ boards++;
+ console = 0;
+ }
+ /* Add more graphic drivers here */
+ /* Keep passing console around */
+
+ if (boards > MAXCARDS){
+ printk ("Too many cards found on the system\n");
+ prom_halt ();
+ }
+}
+
+
diff --git a/drivers/sgi/char/graphics.h b/drivers/sgi/char/graphics.h
index d5a8d2979..c8ad7e318 100644
--- a/drivers/sgi/char/graphics.h
+++ b/drivers/sgi/char/graphics.h
@@ -26,3 +26,31 @@ struct graphics_ops {
void shmiq_init (void);
void streamable_init (void);
void usema_init (void);
+#define MAXCARDS 4
+
+struct graphics_ops {
+ /* SGIism: Board owner, gets the shmiq requests from the kernel */
+ struct task_struct *g_owner;
+
+ /* Last process that got the graphics registers mapped */
+ struct task_struct *g_user;
+
+ /* Board info */
+ void *g_board_info;
+ int g_board_info_len;
+
+ /* These point to hardware registers that should be mapped with
+ * GFX_ATTACH_BOARD and the size of the information pointed to
+ */
+ unsigned long g_regs;
+ int g_regs_size;
+
+ void (*g_save_context)(void *);
+ void (*g_restore_context)(void *);
+ void (*g_reset_console)(void);
+ int (*g_ioctl)(int device, int cmd, unsigned long arg);
+};
+
+void shmiq_init (void);
+void streamable_init (void);
+void usema_init (void);
diff --git a/drivers/sgi/char/linux_logo.h b/drivers/sgi/char/linux_logo.h
index ca041ddde..62a635ee3 100644
--- a/drivers/sgi/char/linux_logo.h
+++ b/drivers/sgi/char/linux_logo.h
@@ -907,3 +907,912 @@ unsigned char linux_logo[] = {
0xCF, 0xCE, 0xCE, 0x57, 0x63, 0xCC, 0xCD, 0x57,
};
+/* This is a linux logo to be displayed on boot.
+ *
+ * You can put anything here, but:
+ * LINUX_LOGO_COLORS has to be less than 224
+ * image size has to be 80x80
+ * values have to start from0x20
+ * (i.e. RGB(linux_logo_red[0],
+ * linux_logo_green[0],
+ * linux_logo_blue[0]) is color0x20)
+ */
+
+#define LINUX_LOGO_COLORS 221
+
+unsigned char linux_logo_red[] = {
+ 0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xE7, 0xE5, 0xE3,
+ 0xCA, 0xD4, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xE5,
+ 0xF1, 0xED, 0xEE, 0xE6, 0xC6, 0xDA, 0xDD, 0xE5,
+ 0xD9, 0xC6, 0xE3, 0xD0, 0xC6, 0xBA, 0xB0, 0xB6,
+ 0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xB0, 0xAD,
+ 0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA0, 0x9D,
+ 0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x99, 0x9A, 0x99,
+ 0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+ 0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0x0D, 0x03,
+ 0x66, 0x44, 0x24, 0x08, 0xD6, 0xE6, 0xE9, 0xE6,
+ 0xE7, 0xCA, 0xDC, 0xDB, 0xD5, 0xD0, 0xC9, 0xE2,
+ 0xD5, 0xC6, 0xC4, 0xB3, 0xB2, 0xB9, 0xA9, 0x9A,
+ 0xB2, 0x9D, 0xE8, 0xEC, 0xF5, 0xF5, 0xF4, 0xF4,
+ 0xEC, 0xEE, 0xF0, 0xF5, 0xE0, 0xD6, 0xC5, 0xC2,
+ 0xD9, 0xD5, 0xD8, 0xD6, 0xF6, 0xF4, 0xED, 0xEC,
+ 0xEB, 0xF1, 0xF6, 0xF5, 0xF5, 0xEE, 0xEF, 0xEC,
+ 0xE7, 0xE3, 0xE6, 0xD6, 0xDD, 0xC3, 0xD6, 0xD7,
+ 0xCD, 0xCA, 0xC3, 0xAC, 0x95, 0x99, 0xB7, 0xA3,
+ 0x8B, 0x88, 0x95, 0x8A, 0x94, 0xD2, 0xCC, 0xC4,
+ 0xA8, 0x8E, 0x8F, 0xAE, 0xB8, 0xAC, 0xB6, 0xB4,
+ 0xAD, 0xA5, 0xA0, 0x9B, 0x8B, 0xA3, 0x94, 0x87,
+ 0x85, 0x89, 0x53, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+ 0x76, 0x71, 0x73, 0x6E, 0x6B, 0x67, 0x65, 0x62,
+ 0x4B, 0x5B, 0x5F, 0x53, 0x56, 0x52, 0x4F, 0x46,
+ 0x42, 0x0F, 0x75, 0x78, 0x7D, 0x72, 0x5F, 0x6E,
+ 0x7A, 0x75, 0x6A, 0x58, 0x48, 0x4F, 0x00, 0x2B,
+ 0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x3B, 0x11,
+ 0x1D, 0x14, 0x06, 0x02, 0x00
+};
+
+unsigned char linux_logo_green[] = {
+ 0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xE7, 0xE5, 0xE3,
+ 0xCA, 0xD4, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xD3,
+ 0xDA, 0xD4, 0xD7, 0xCC, 0xC1, 0xCC, 0xCB, 0xC9,
+ 0xC5, 0xBC, 0xBC, 0xBB, 0xB7, 0xA5, 0xB0, 0xB6,
+ 0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xAD, 0xAD,
+ 0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA0, 0x95,
+ 0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x99, 0x9A, 0x99,
+ 0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+ 0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0x08, 0x02,
+ 0x53, 0x2E, 0x19, 0x06, 0xC6, 0xC8, 0xCF, 0xBD,
+ 0xB3, 0xB6, 0xB4, 0xAB, 0xA5, 0xA3, 0x9B, 0xB6,
+ 0xA7, 0x99, 0x92, 0xA4, 0x9E, 0x9D, 0x98, 0x8C,
+ 0x8A, 0x86, 0xCD, 0xCC, 0xC9, 0xD7, 0xCA, 0xC4,
+ 0xCA, 0xC3, 0xC7, 0xC3, 0xC8, 0xB4, 0x91, 0x8E,
+ 0x8A, 0x82, 0x87, 0x85, 0xBD, 0xBF, 0xB6, 0xBC,
+ 0xAE, 0xB7, 0xBC, 0xB8, 0xBF, 0xB6, 0xBC, 0xB5,
+ 0xAB, 0xA6, 0xAD, 0xB2, 0xA5, 0x87, 0x9C, 0x96,
+ 0x95, 0x8E, 0x87, 0x8F, 0x86, 0x86, 0x8E, 0x80,
+ 0x7A, 0x70, 0x7B, 0x78, 0x78, 0x7F, 0x77, 0x6F,
+ 0x70, 0x76, 0x59, 0x77, 0x68, 0x64, 0x7B, 0x7C,
+ 0x75, 0x6D, 0x77, 0x69, 0x65, 0x5F, 0x5B, 0x54,
+ 0x4F, 0x5B, 0x39, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+ 0x76, 0x71, 0x73, 0x6E, 0x6B, 0x67, 0x65, 0x62,
+ 0x4B, 0x5B, 0x5F, 0x53, 0x56, 0x52, 0x4F, 0x46,
+ 0x42, 0x0B, 0x69, 0x66, 0x64, 0x57, 0x4A, 0x4E,
+ 0x55, 0x4B, 0x46, 0x3B, 0x30, 0x33, 0x00, 0x2B,
+ 0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x29, 0x0D,
+ 0x1D, 0x14, 0x06, 0x02, 0x00
+};
+
+unsigned char linux_logo_blue[] = {
+ 0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xEE, 0xE5, 0xDE,
+ 0xD7, 0xD3, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xB5,
+ 0xB0, 0xA6, 0xAC, 0x9B, 0xB5, 0xB5, 0xAE, 0x84,
+ 0x90, 0xA9, 0x81, 0x8D, 0x96, 0x86, 0xB0, 0xB6,
+ 0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xA7, 0xAD,
+ 0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA5, 0x87,
+ 0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x9A, 0x9A, 0x99,
+ 0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+ 0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0xC8, 0xD7,
+ 0x9B, 0x8E, 0x8C, 0xB2, 0x77, 0x77, 0x4E, 0x77,
+ 0x69, 0x71, 0x78, 0x6B, 0x65, 0x66, 0x64, 0x59,
+ 0x5C, 0x5A, 0x48, 0x72, 0x7B, 0x6B, 0x67, 0x6E,
+ 0x42, 0x5B, 0x29, 0x36, 0x25, 0x10, 0x17, 0x14,
+ 0x19, 0x16, 0x13, 0x0E, 0x08, 0x2E, 0x2E, 0x3D,
+ 0x24, 0x24, 0x24, 0x24, 0x13, 0x12, 0x14, 0x14,
+ 0x0E, 0x08, 0x0D, 0x0F, 0x08, 0x0D, 0x0E, 0x08,
+ 0x08, 0x0C, 0x06, 0x06, 0x07, 0x16, 0x07, 0x0E,
+ 0x08, 0x0A, 0x07, 0x0D, 0x2D, 0x3E, 0x09, 0x4E,
+ 0x68, 0x52, 0x56, 0x58, 0x4B, 0x22, 0x20, 0x20,
+ 0x27, 0x39, 0x28, 0x19, 0x1E, 0x1E, 0x08, 0x06,
+ 0x07, 0x09, 0x08, 0x08, 0x05, 0x1D, 0x1F, 0x17,
+ 0x18, 0x06, 0x79, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+ 0x76, 0x71, 0x73, 0x6E, 0x6B, 0x68, 0x65, 0x62,
+ 0x4B, 0x5B, 0x5F, 0x55, 0x56, 0x52, 0x4F, 0x46,
+ 0x42, 0x5A, 0x14, 0x23, 0x3D, 0x2B, 0x21, 0x14,
+ 0x06, 0x04, 0x03, 0x07, 0x09, 0x13, 0x2A, 0x3A,
+ 0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x07, 0x09,
+ 0x1D, 0x14, 0x06, 0x02, 0x00
+};
+
+unsigned char linux_logo[] = {
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x57,
+ 0x58, 0x58, 0x59, 0x5C, 0x5D, 0x5F, 0x60, 0x61,
+ 0x62, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x60, 0x5E, 0x5E,
+ 0x5E, 0x5D, 0x5D, 0x5C, 0x5D, 0x5B, 0x58, 0x58,
+ 0x58, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x58,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x57,
+ 0x54, 0x56, 0x57, 0x67, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x67, 0x4C,
+ 0x4A, 0x49, 0x4A, 0x49, 0x4A, 0x49, 0x49, 0x4A,
+ 0x4A, 0x4B, 0x4B, 0x4B, 0x4C, 0x50, 0x51, 0x52,
+ 0x54, 0x54, 0x56, 0x57, 0x57, 0x57, 0x57, 0x58,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x58, 0x56, 0x56, 0x53,
+ 0x52, 0x53, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0xFB, 0xFB,
+ 0x4B, 0x4B, 0x4B, 0x4A, 0x49, 0x4A, 0x4A, 0x49,
+ 0x49, 0x49, 0x48, 0x49, 0x49, 0x4A, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x52, 0x54, 0x56, 0x55, 0x57, 0x58,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
+ 0x50, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xF4, 0xFB,
+ 0xFC, 0x67, 0x53, 0x50, 0x4D, 0x4C, 0x4C, 0x4C,
+ 0x4B, 0x4A, 0x4A, 0x48, 0x49, 0x48, 0x48, 0x49,
+ 0x49, 0x49, 0x4B, 0x4C, 0x50, 0x52, 0x53, 0x56,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x55, 0x54, 0x53, 0x51, 0x51, 0x50, 0x4C, 0x4D,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0xD2, 0xD7, 0xF5,
+ 0xFC, 0xFC, 0x5D, 0x5D, 0x5C, 0x5C, 0x59, 0x58,
+ 0x58, 0x56, 0x52, 0x4C, 0x4B, 0x4A, 0x4A, 0x48,
+ 0x48, 0x48, 0x48, 0x48, 0x49, 0x4B, 0x4D, 0x51,
+ 0x54, 0x56, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x55, 0x54,
+ 0x53, 0x52, 0x51, 0x4D, 0x4D, 0x4D, 0x50, 0x50,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0x64, 0xD9, 0xF5,
+ 0xF9, 0xFC, 0xFC, 0x64, 0x63, 0x62, 0x61, 0x61,
+ 0x61, 0x60, 0x5E, 0x5B, 0x5A, 0x54, 0x52, 0x4C,
+ 0x4B, 0x49, 0x49, 0x47, 0x47, 0x48, 0x49, 0x4B,
+ 0x4C, 0x51, 0x53, 0x56, 0x57, 0x58, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x57, 0x57, 0x55, 0x53, 0x53,
+ 0x51, 0x50, 0x50, 0x50, 0x50, 0x50, 0x53, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0xF5, 0xF9, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0x64, 0x64, 0x64, 0x64, 0x64,
+ 0x64, 0x64, 0x64, 0x63, 0x61, 0x61, 0x5E, 0x59,
+ 0x55, 0x52, 0x4C, 0x4A, 0x49, 0x47, 0x48, 0x48,
+ 0x49, 0x4B, 0x4D, 0x51, 0x54, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x58, 0x55, 0x54, 0x54, 0x52, 0x51,
+ 0x51, 0x51, 0x51, 0x51, 0x53, 0x54, 0x59, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xF7, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0x60, 0x60, 0x60, 0x61,
+ 0x62, 0x63, 0x64, 0x64, 0x65, 0x65, 0x64, 0x63,
+ 0x61, 0x5E, 0x59, 0x56, 0x4D, 0x4B, 0x48, 0x48,
+ 0x48, 0x48, 0x49, 0x4B, 0x50, 0x53, 0x56, 0x56,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x56, 0x54, 0x53, 0x52, 0x51, 0x51,
+ 0x51, 0x52, 0x53, 0x55, 0x59, 0x5D, 0x5E, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0x4C, 0x4E, 0x51, 0x52,
+ 0x57, 0x5A, 0x5E, 0x60, 0x61, 0x63, 0x65, 0xCB,
+ 0x64, 0x64, 0x63, 0x60, 0x5C, 0x57, 0x50, 0x4B,
+ 0x48, 0x47, 0x47, 0x47, 0x4A, 0x4C, 0x52, 0x53,
+ 0x54, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x55, 0x54, 0x53, 0x53, 0x51, 0x52, 0x52, 0x53,
+ 0x53, 0x57, 0x5A, 0x5D, 0x5E, 0x5E, 0x60, 0xFC,
+ 0xFC, 0xFC, 0xFB, 0xF9, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFA, 0xF9, 0xF5, 0xFB, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0x45, 0x3F, 0x3F,
+ 0x45, 0x48, 0x4B, 0x4D, 0x54, 0x5A, 0x5E, 0x61,
+ 0x63, 0xCB, 0xCB, 0x65, 0x64, 0x62, 0x5E, 0x57,
+ 0x50, 0x4B, 0x48, 0x47, 0x47, 0x48, 0x4B, 0x4D,
+ 0x51, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55,
+ 0x54, 0x54, 0x53, 0x53, 0x52, 0x53, 0x54, 0x57,
+ 0x59, 0x5C, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0xFC,
+ 0xFC, 0xFA, 0xFC, 0xFA, 0xE0, 0xFC, 0xFC, 0xFC,
+ 0xFB, 0xFB, 0xFB, 0xDF, 0xD8, 0xF9, 0xE0, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0x4C, 0x4A, 0x48,
+ 0x48, 0x3E, 0x44, 0x43, 0x3F, 0x47, 0x4B, 0x52,
+ 0x5A, 0x5E, 0x62, 0x64, 0xCB, 0xCB, 0x64, 0x61,
+ 0x5E, 0x57, 0x4D, 0x49, 0x47, 0x47, 0x48, 0x4A,
+ 0x4C, 0x52, 0x54, 0x56, 0x57, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55,
+ 0x54, 0x53, 0x53, 0x54, 0x54, 0x55, 0x58, 0x5B,
+ 0x5C, 0x5D, 0x5E, 0x5D, 0x5D, 0x5B, 0x58, 0xFC,
+ 0xFC, 0xD8, 0x4C, 0x60, 0xFC, 0xF5, 0xFC, 0xFC,
+ 0xFC, 0xF7, 0x5F, 0x48, 0x48, 0x2C, 0xF8, 0xF9,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x4B, 0x4A, 0x49,
+ 0x49, 0x49, 0x49, 0x47, 0x3E, 0x44, 0x42, 0x3F,
+ 0x3E, 0x4B, 0x54, 0x5C, 0x61, 0x64, 0xCB, 0xCB,
+ 0x64, 0x61, 0x5D, 0x53, 0x4B, 0x49, 0x47, 0x47,
+ 0x49, 0x4B, 0x50, 0x53, 0x56, 0x57, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0x55, 0x55, 0x54,
+ 0x53, 0x53, 0x54, 0x56, 0x58, 0x5A, 0x5B, 0x5D,
+ 0x5D, 0x5D, 0x5C, 0x5A, 0x54, 0x52, 0x4C, 0xFC,
+ 0xF7, 0x4E, 0x2D, 0x29, 0x4E, 0xFC, 0xFC, 0xFC,
+ 0xFB, 0x5F, 0x26, 0x24, 0x20, 0x2E, 0x65, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x45, 0x3F, 0x45,
+ 0x3E, 0x47, 0x47, 0x47, 0x47, 0x47, 0x3E, 0x44,
+ 0x43, 0x40, 0x44, 0x49, 0x51, 0x5C, 0x62, 0x64,
+ 0xCB, 0xCB, 0x63, 0x60, 0x58, 0x50, 0x49, 0x48,
+ 0x48, 0x48, 0x4A, 0x4D, 0x53, 0x54, 0x57, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54, 0x54,
+ 0x54, 0x54, 0x55, 0x57, 0x59, 0x5B, 0x5C, 0x5D,
+ 0x5C, 0x5A, 0x54, 0x51, 0x4C, 0x4C, 0x54, 0xFC,
+ 0xF9, 0x23, 0xDB, 0x2D, 0x23, 0xFA, 0xFB, 0xFA,
+ 0xF5, 0x27, 0x21, 0xD9, 0xF8, 0x20, 0x21, 0xFB,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x5D, 0x58, 0x55,
+ 0x50, 0x48, 0x45, 0x43, 0x44, 0x44, 0x45, 0x45,
+ 0x3E, 0x3F, 0x43, 0x41, 0x3F, 0x48, 0x52, 0x5D,
+ 0x63, 0x65, 0xCB, 0x65, 0x61, 0x5D, 0x52, 0x4B,
+ 0x48, 0x47, 0x47, 0x49, 0x4C, 0x51, 0x54, 0x57,
+ 0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54, 0x54,
+ 0x54, 0x58, 0x5A, 0x59, 0x5B, 0x5B, 0x5B, 0x5A,
+ 0x55, 0x52, 0x4D, 0x4D, 0x55, 0x5B, 0x5D, 0xFC,
+ 0xF1, 0xF9, 0xFC, 0xD4, 0x21, 0xCC, 0xF7, 0xF8,
+ 0xF2, 0x21, 0xD9, 0xFC, 0xF2, 0xFB, 0x21, 0x45,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0xD1, 0xD0, 0xCD,
+ 0xCC, 0x63, 0x5E, 0x58, 0x50, 0x47, 0x43, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x40, 0x41, 0x3F, 0x4A,
+ 0x56, 0x5E, 0x64, 0xCB, 0x65, 0x63, 0x5E, 0x56,
+ 0x4C, 0x48, 0x47, 0x47, 0x49, 0x4C, 0x51, 0x54,
+ 0x58, 0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54,
+ 0x57, 0x5A, 0x5A, 0x5C, 0x5B, 0x5A, 0x58, 0x54,
+ 0x51, 0x4C, 0x55, 0x5D, 0x5D, 0x5B, 0x54, 0xFC,
+ 0xF0, 0xF9, 0xFC, 0x65, 0x45, 0xCD, 0xFB, 0xFB,
+ 0xF8, 0x26, 0xFB, 0xFC, 0xFC, 0xFC, 0x21, 0x27,
+ 0xFB, 0xFC, 0xFC, 0xFC, 0xFB, 0xD7, 0x35, 0x34,
+ 0x2F, 0x35, 0x36, 0x2F, 0x2F, 0x36, 0x2F, 0x2F,
+ 0x36, 0x36, 0x35, 0x35, 0x43, 0x42, 0x41, 0x2E,
+ 0x45, 0x4C, 0x5B, 0x62, 0x65, 0xCC, 0x64, 0x60,
+ 0x58, 0x4D, 0x49, 0x47, 0x47, 0x49, 0x4C, 0x51,
+ 0x58, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55, 0x57,
+ 0x58, 0x5A, 0x5A, 0x5B, 0x5A, 0x55, 0x54, 0x51,
+ 0x53, 0x5C, 0x5D, 0x5D, 0x54, 0x4B, 0x4D, 0xFC,
+ 0xFC, 0x44, 0xFC, 0xFB, 0x7B, 0xAB, 0xA8, 0xAE,
+ 0xAB, 0x7F, 0xFC, 0xFC, 0xFB, 0xFB, 0x22, 0x2A,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0x36, 0x2F, 0x30, 0x30,
+ 0x32, 0x30, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x40, 0x41,
+ 0x2E, 0x40, 0x48, 0x56, 0x5F, 0x64, 0xCC, 0x65,
+ 0x61, 0x59, 0x50, 0x49, 0x47, 0x47, 0x49, 0x4C,
+ 0x5A, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58,
+ 0x5A, 0x5A, 0x5A, 0x58, 0x55, 0x52, 0x51, 0x5A,
+ 0x5D, 0x5D, 0x57, 0x4C, 0x51, 0x54, 0x5D, 0xFC,
+ 0xFC, 0x2A, 0xFC, 0xC9, 0xAA, 0x8B, 0x8A, 0x8C,
+ 0xAB, 0x8C, 0x8C, 0xFB, 0xFB, 0x23, 0x20, 0xF1,
+ 0xFC, 0xFC, 0xFC, 0x3B, 0x33, 0x33, 0x32, 0x32,
+ 0x31, 0x32, 0x30, 0x32, 0x32, 0x32, 0x32, 0x30,
+ 0x31, 0x31, 0x31, 0x32, 0x33, 0x33, 0x3C, 0x41,
+ 0x41, 0x2E, 0x2D, 0x45, 0x4D, 0x5D, 0x63, 0xCC,
+ 0x65, 0x62, 0x5D, 0x51, 0x49, 0x47, 0x47, 0x4A,
+ 0x59, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58,
+ 0x5A, 0x5A, 0x58, 0x55, 0x53, 0x53, 0x5C, 0x5E,
+ 0x59, 0x51, 0x4E, 0x54, 0x59, 0x5E, 0x62, 0xFC,
+ 0xFC, 0xDB, 0xAA, 0xA1, 0x95, 0x9C, 0x8C, 0x88,
+ 0x82, 0x83, 0x83, 0x8C, 0x88, 0xAE, 0xB9, 0xFB,
+ 0xFC, 0xFC, 0xFC, 0x3C, 0x3B, 0x72, 0x38, 0x33,
+ 0x33, 0x33, 0x31, 0x33, 0x31, 0x31, 0x31, 0x31,
+ 0x33, 0x33, 0x38, 0x33, 0x72, 0x3B, 0x44, 0x2E,
+ 0x41, 0x2E, 0x2E, 0x2D, 0x43, 0x4B, 0x5B, 0x63,
+ 0xCB, 0xCC, 0x63, 0x5D, 0x51, 0x49, 0x47, 0x49,
+ 0x5C, 0x58, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58,
+ 0x58, 0x58, 0x57, 0x53, 0x58, 0x5D, 0x5E, 0x55,
+ 0x51, 0x53, 0x58, 0x5E, 0x60, 0x63, 0x64, 0xFC,
+ 0xFC, 0xC0, 0xA6, 0x9D, 0x8B, 0x9C, 0x8C, 0x8C,
+ 0x6E, 0x83, 0x88, 0x8C, 0x8C, 0x8C, 0x83, 0xE8,
+ 0xFB, 0xFC, 0xFC, 0xFC, 0x33, 0x70, 0x70, 0x6F,
+ 0x6F, 0x6F, 0x6F, 0x3A, 0x6F, 0x6D, 0x6F, 0x6F,
+ 0x70, 0x6F, 0x6F, 0x70, 0x6F, 0x32, 0x5A, 0x48,
+ 0x41, 0x2D, 0x2D, 0x2D, 0x2C, 0x41, 0x49, 0x5A,
+ 0x62, 0xCB, 0xCB, 0x63, 0x5D, 0x50, 0x49, 0x4A,
+ 0x5C, 0x58, 0x58, 0x57, 0x55, 0x57, 0x57, 0x57,
+ 0x57, 0x55, 0x56, 0x59, 0x5E, 0x5C, 0x52, 0x53,
+ 0x55, 0x5B, 0x5E, 0x61, 0x63, 0x64, 0x63, 0xFC,
+ 0xE8, 0xBF, 0xA4, 0x99, 0x9C, 0x8C, 0x88, 0x88,
+ 0x6E, 0x88, 0x8C, 0x8C, 0x8C, 0xC2, 0xA6, 0xC4,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0x36, 0x3A, 0x6F, 0x70,
+ 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x70, 0x70, 0x70, 0x37, 0x32, 0xCD, 0x5E,
+ 0x4C, 0x43, 0x2C, 0x2D, 0x2D, 0x2C, 0x2E, 0x47,
+ 0x57, 0x61, 0x65, 0xCC, 0x63, 0x5C, 0x50, 0x4D,
+ 0x5C, 0x5A, 0x57, 0x55, 0x55, 0x55, 0x58, 0x58,
+ 0x55, 0x54, 0x5B, 0x5E, 0x5D, 0x53, 0x53, 0x55,
+ 0x5D, 0x5E, 0x61, 0x61, 0x61, 0x61, 0x5E, 0xFC,
+ 0xEA, 0xBE, 0xA4, 0x9B, 0x8B, 0x85, 0x8C, 0x6E,
+ 0x8C, 0x8C, 0x8C, 0xA3, 0xAA, 0xA4, 0xA4, 0xE9,
+ 0xFB, 0xFC, 0xFC, 0xFC, 0x36, 0x6D, 0x70, 0x73,
+ 0x70, 0x70, 0x70, 0x73, 0x73, 0x73, 0x73, 0x70,
+ 0x70, 0x70, 0x73, 0x70, 0x37, 0x38, 0xD1, 0xCF,
+ 0x61, 0x4D, 0x44, 0x2C, 0x2D, 0x2E, 0x2C, 0x2E,
+ 0x3E, 0x56, 0x61, 0xCB, 0xCC, 0x62, 0x5B, 0x57,
+ 0x59, 0x58, 0x55, 0x54, 0x54, 0x55, 0x58, 0x58,
+ 0x58, 0x5B, 0x5E, 0x5B, 0x53, 0x55, 0x55, 0x5C,
+ 0x5E, 0x61, 0x61, 0x60, 0x5D, 0x5A, 0x4E, 0xFC,
+ 0xFC, 0xEA, 0xAA, 0x9C, 0x8A, 0x85, 0x82, 0x8C,
+ 0x8C, 0xA8, 0xEB, 0xA8, 0xA4, 0xA4, 0xAA, 0xFC,
+ 0xFC, 0xFC, 0x64, 0xFB, 0x39, 0x31, 0x72, 0x78,
+ 0x73, 0x78, 0x73, 0x74, 0x74, 0x74, 0x74, 0x73,
+ 0x78, 0x70, 0x73, 0x73, 0x33, 0xCC, 0xD2, 0xD1,
+ 0xCE, 0x62, 0x53, 0x3F, 0x2D, 0x2D, 0x41, 0x2C,
+ 0x2E, 0x3E, 0x56, 0x62, 0xCB, 0xCB, 0x61, 0x5D,
+ 0x54, 0x54, 0x54, 0x54, 0x56, 0x58, 0x58, 0x58,
+ 0x5C, 0x5E, 0x5A, 0x55, 0x58, 0x58, 0x5B, 0x5E,
+ 0x61, 0x5E, 0x5D, 0x5A, 0x52, 0x55, 0xCD, 0xFC,
+ 0xFC, 0x34, 0xC9, 0xE8, 0xA8, 0xAE, 0xC2, 0xE8,
+ 0xC3, 0xA6, 0xA7, 0xA6, 0xAA, 0x78, 0x2E, 0x42,
+ 0xFC, 0xFC, 0xD2, 0x64, 0xF8, 0x31, 0x72, 0x73,
+ 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x74, 0x73,
+ 0x73, 0x73, 0x73, 0x72, 0x33, 0x5C, 0x64, 0xD2,
+ 0xD1, 0xCF, 0x63, 0x54, 0x3F, 0x2C, 0x41, 0x41,
+ 0x2C, 0x2E, 0x47, 0x58, 0x63, 0xCB, 0xCB, 0x62,
+ 0x52, 0x53, 0x53, 0x56, 0x58, 0x58, 0x5A, 0x5B,
+ 0x5E, 0x5A, 0x57, 0x58, 0x58, 0x58, 0x60, 0x60,
+ 0x5D, 0x5A, 0x55, 0x4E, 0x64, 0xD2, 0xD1, 0xFC,
+ 0xFC, 0x41, 0x3E, 0xC1, 0xC0, 0xA3, 0xA6, 0xA7,
+ 0xA7, 0xA9, 0xAA, 0xB8, 0x2E, 0x3F, 0x2C, 0x41,
+ 0xFC, 0xFC, 0xF7, 0xCE, 0xCD, 0x36, 0x72, 0x73,
+ 0x74, 0x75, 0x78, 0x75, 0x75, 0x75, 0x74, 0x74,
+ 0x74, 0x74, 0x78, 0x72, 0x6D, 0x49, 0x59, 0xCB,
+ 0xD1, 0xD1, 0xD2, 0xCB, 0x56, 0x3F, 0x2C, 0x41,
+ 0x40, 0x2D, 0x2E, 0x49, 0x5B, 0x64, 0xCC, 0x64,
+ 0x51, 0x53, 0x53, 0x55, 0x58, 0x59, 0x5B, 0x5E,
+ 0x59, 0x58, 0x58, 0x58, 0x55, 0x60, 0x60, 0x5C,
+ 0x5A, 0x53, 0x5B, 0xD0, 0xD3, 0xD3, 0xD3, 0xFB,
+ 0xFC, 0x40, 0x41, 0x45, 0xC4, 0xC0, 0xBE, 0xBE,
+ 0xC1, 0xC0, 0x3C, 0x47, 0x2E, 0x21, 0x22, 0x20,
+ 0x65, 0xFC, 0xFC, 0xFC, 0xFC, 0x6D, 0x72, 0x75,
+ 0x78, 0x76, 0x75, 0x79, 0x76, 0x76, 0x76, 0x76,
+ 0x75, 0x75, 0x75, 0x72, 0x6D, 0x2E, 0x48, 0x5D,
+ 0xCE, 0xD1, 0xD4, 0xD3, 0xCB, 0x56, 0x43, 0x2C,
+ 0x42, 0x43, 0x2E, 0x2E, 0x4A, 0x5D, 0x64, 0x64,
+ 0x50, 0x52, 0x56, 0x58, 0x5C, 0x5D, 0x5E, 0x5D,
+ 0x5A, 0x58, 0x58, 0x55, 0x61, 0x60, 0x58, 0x58,
+ 0x4E, 0x61, 0xD1, 0xD4, 0xD4, 0xD1, 0xEE, 0xFC,
+ 0xFC, 0x2B, 0x29, 0x2E, 0x3F, 0xB0, 0xAD, 0x81,
+ 0x46, 0x2D, 0x46, 0x2C, 0x24, 0x22, 0x22, 0x23,
+ 0x25, 0xFC, 0xFC, 0xFC, 0xFC, 0x6E, 0x73, 0x76,
+ 0x76, 0x79, 0x79, 0x79, 0x76, 0x76, 0x79, 0x76,
+ 0x79, 0x79, 0x79, 0x74, 0x3F, 0x41, 0x2C, 0x48,
+ 0x5F, 0xCF, 0xD5, 0xD7, 0xD6, 0xCD, 0x57, 0x40,
+ 0x2E, 0x3F, 0x44, 0x2E, 0x41, 0x4C, 0x60, 0x61,
+ 0x51, 0x53, 0x58, 0x5C, 0x5D, 0x5E, 0x5D, 0x5C,
+ 0x58, 0x57, 0x54, 0x5F, 0x5E, 0x55, 0x55, 0x52,
+ 0x64, 0xD4, 0xD5, 0xD4, 0xD1, 0x5D, 0xFA, 0xFB,
+ 0xF4, 0x21, 0x24, 0x41, 0x40, 0x44, 0x2E, 0x2E,
+ 0x42, 0x41, 0x2A, 0x24, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0xD9, 0xFC, 0xFC, 0xFC, 0xFC, 0xE5, 0xB8,
+ 0x8F, 0x8F, 0x7A, 0x8F, 0x7A, 0x8F, 0x7A, 0x8F,
+ 0x8F, 0x8F, 0xB8, 0xE5, 0x3F, 0x3E, 0x43, 0x2C,
+ 0x48, 0x61, 0xD1, 0xD7, 0xD9, 0xD7, 0xD0, 0x57,
+ 0x41, 0x2E, 0x3E, 0x44, 0x2D, 0x40, 0x52, 0x5D,
+ 0x53, 0x55, 0x59, 0x5D, 0x5E, 0x5E, 0x5D, 0x5A,
+ 0x57, 0x53, 0x5E, 0x5E, 0x54, 0x53, 0x54, 0x65,
+ 0xD5, 0xD6, 0xD4, 0xCE, 0x53, 0xFB, 0xF9, 0xFC,
+ 0x24, 0x22, 0x23, 0x23, 0x41, 0x42, 0x2E, 0x40,
+ 0x2B, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x23, 0xFC, 0xFC, 0xFC, 0xFC, 0xE7, 0xBD,
+ 0xB5, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0xB5, 0xC6, 0xEB, 0x2D, 0x47, 0x4A, 0x47,
+ 0x2C, 0x3E, 0x61, 0xD4, 0xDC, 0xDC, 0xDA, 0xCF,
+ 0x54, 0x41, 0x41, 0x3E, 0x45, 0x2C, 0x3F, 0x4A,
+ 0x58, 0x5A, 0x5C, 0x5F, 0x60, 0x5E, 0x5D, 0x57,
+ 0x51, 0x5D, 0x5D, 0x51, 0x53, 0x53, 0xCB, 0xD5,
+ 0xD6, 0xD5, 0x63, 0x55, 0xFC, 0xFC, 0xFC, 0x2C,
+ 0x23, 0x22, 0x23, 0x22, 0x20, 0x2D, 0x2C, 0x26,
+ 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x21, 0xF0, 0xFC, 0xFC, 0xFC, 0xE2, 0xC6,
+ 0xB5, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0xC7, 0xE3, 0x3E, 0x2E, 0x49, 0x52,
+ 0x4C, 0x41, 0x44, 0x62, 0xD6, 0xDE, 0xDE, 0xD9,
+ 0xD0, 0x51, 0x2E, 0x40, 0x47, 0x44, 0x2C, 0x42,
+ 0x5D, 0x5D, 0x5F, 0x60, 0x60, 0x5D, 0x57, 0x51,
+ 0x58, 0x5D, 0x4E, 0x52, 0x55, 0x64, 0xD5, 0xD6,
+ 0xD4, 0x61, 0x59, 0x6B, 0xFC, 0xFC, 0xFC, 0x21,
+ 0x23, 0x22, 0x23, 0x22, 0x23, 0x21, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x21, 0x24, 0xFC, 0xFC, 0xFC, 0xE2, 0xC7,
+ 0xB5, 0x90, 0x93, 0x93, 0x93, 0x90, 0x93, 0x93,
+ 0x90, 0xB5, 0xC8, 0xE4, 0x5F, 0x45, 0x2E, 0x4D,
+ 0x57, 0x57, 0x44, 0x43, 0x63, 0xDA, 0xDF, 0xDF,
+ 0xD9, 0xCE, 0x4C, 0x2C, 0x3F, 0x3E, 0x40, 0x40,
+ 0x60, 0x5E, 0x61, 0x61, 0x5E, 0x5B, 0x53, 0x52,
+ 0x5C, 0x52, 0x52, 0x55, 0x61, 0xD4, 0xD5, 0xD1,
+ 0x5E, 0x5B, 0x5C, 0xFB, 0xFC, 0xFC, 0x2A, 0x21,
+ 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0xFB, 0xFC, 0xFC, 0xB3, 0xC8,
+ 0xB5, 0x90, 0x92, 0xB5, 0x93, 0x93, 0xB5, 0x93,
+ 0x92, 0xB5, 0xC8, 0xB9, 0xD0, 0x5E, 0x44, 0x40,
+ 0x52, 0x58, 0x57, 0x48, 0x40, 0x63, 0xD9, 0xE0,
+ 0xE0, 0xD9, 0xCB, 0x49, 0x2D, 0x3F, 0x45, 0x3F,
+ 0x63, 0x61, 0x62, 0x60, 0x5E, 0x55, 0x4D, 0x59,
+ 0x53, 0x4E, 0x54, 0x5D, 0xD2, 0xD4, 0xD2, 0x5E,
+ 0x5C, 0x5D, 0xFC, 0xFC, 0xFC, 0xF8, 0x29, 0x23,
+ 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x22, 0x22, 0x23, 0x23, 0x23, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0xF0, 0xFC, 0xFC, 0xB3, 0xC7,
+ 0xB5, 0x93, 0xB5, 0x93, 0x93, 0x91, 0x93, 0x93,
+ 0x91, 0xB5, 0xC7, 0xAD, 0xD6, 0xD2, 0x5E, 0x3F,
+ 0x3F, 0x57, 0x57, 0x58, 0x4A, 0x41, 0x64, 0xDC,
+ 0xF1, 0xDF, 0xDA, 0x61, 0x45, 0x2E, 0x43, 0x47,
+ 0xCB, 0x63, 0x62, 0x5F, 0x58, 0x51, 0x53, 0x54,
+ 0x4C, 0x52, 0x5C, 0xCD, 0xD3, 0xD2, 0x60, 0x5D,
+ 0x5D, 0xFB, 0xFC, 0xFC, 0xFC, 0xDB, 0x49, 0x24,
+ 0x21, 0x23, 0x23, 0x22, 0x26, 0x26, 0x2A, 0x24,
+ 0x22, 0x23, 0x22, 0x21, 0x24, 0x26, 0x26, 0x2A,
+ 0x29, 0x2B, 0x24, 0x25, 0xFC, 0xFC, 0xB3, 0xC5,
+ 0x91, 0x91, 0x92, 0x91, 0x92, 0x92, 0x93, 0x93,
+ 0x91, 0x93, 0xC6, 0xAD, 0xDC, 0xD9, 0xD4, 0x60,
+ 0x43, 0x45, 0x58, 0x58, 0x57, 0x4B, 0x43, 0xCC,
+ 0xDD, 0xF1, 0xD8, 0xD5, 0x5D, 0x43, 0x41, 0x47,
+ 0xCD, 0x63, 0x62, 0x5D, 0x54, 0x4C, 0x55, 0x4B,
+ 0x51, 0x58, 0x62, 0xD0, 0xD0, 0x62, 0x5D, 0x5D,
+ 0x67, 0xFC, 0xFC, 0xFC, 0xFC, 0x58, 0x4E, 0x28,
+ 0x2A, 0x20, 0x23, 0x22, 0x23, 0x2A, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x23, 0x25, 0x2A, 0x2E, 0x2D,
+ 0x2E, 0x2E, 0x2E, 0x23, 0xFA, 0xFC, 0xB2, 0xBD,
+ 0xB5, 0x90, 0x91, 0x93, 0x92, 0x90, 0x91, 0x93,
+ 0x92, 0x91, 0xBD, 0xAD, 0xDE, 0xE0, 0xD8, 0xD7,
+ 0x61, 0x40, 0x48, 0x58, 0x58, 0x58, 0x48, 0x44,
+ 0xCF, 0xDE, 0xE0, 0xDD, 0xD0, 0x52, 0x41, 0x45,
+ 0xCD, 0x63, 0x61, 0x58, 0x4D, 0x51, 0x4C, 0x4B,
+ 0x54, 0x5D, 0xCC, 0xCE, 0x63, 0x61, 0x5D, 0x5D,
+ 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0x4B, 0x27, 0x21,
+ 0x22, 0x22, 0x23, 0x22, 0x22, 0x24, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x20,
+ 0x27, 0x2B, 0x41, 0x2B, 0x23, 0xFC, 0xB2, 0xB6,
+ 0x93, 0x90, 0x92, 0xB5, 0x92, 0x90, 0xB5, 0x90,
+ 0x92, 0x93, 0xBC, 0xAD, 0xDC, 0xF1, 0xF3, 0xF0,
+ 0xD9, 0x61, 0x41, 0x4A, 0x58, 0x57, 0x57, 0x44,
+ 0x49, 0xD2, 0xDD, 0xD8, 0xDA, 0x63, 0x4A, 0x45,
+ 0xCC, 0x63, 0x5E, 0x52, 0x4B, 0x4C, 0x49, 0x51,
+ 0x5C, 0x61, 0xCD, 0x65, 0x63, 0x5E, 0x4E, 0xCF,
+ 0xFB, 0xFB, 0xF0, 0xFC, 0xD2, 0x2A, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x26, 0x41, 0x27, 0xF9, 0x81, 0xB7,
+ 0xB5, 0x91, 0x92, 0xB5, 0x91, 0xB5, 0x93, 0xB5,
+ 0x93, 0xB6, 0xB7, 0xB9, 0xCB, 0xD8, 0xF3, 0xF2,
+ 0xF2, 0xDB, 0x61, 0x2D, 0x51, 0x58, 0x57, 0x58,
+ 0x41, 0x51, 0xD4, 0xDB, 0xDC, 0xD1, 0x5B, 0x4C,
+ 0xCB, 0x62, 0x59, 0x4C, 0x4A, 0x49, 0x4B, 0x55,
+ 0x60, 0x64, 0xCC, 0x64, 0x5E, 0x55, 0x60, 0xE1,
+ 0xFB, 0xF8, 0xFC, 0xFC, 0x21, 0x22, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x21, 0x24, 0x2D, 0x21, 0xB4, 0xBB,
+ 0xB6, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6,
+ 0xB6, 0xB6, 0xBB, 0xB9, 0x45, 0xCB, 0xDF, 0xF3,
+ 0xF3, 0xF3, 0xDB, 0x5E, 0x2C, 0x51, 0x58, 0x58,
+ 0x52, 0x2D, 0x5C, 0xD4, 0xD9, 0xD5, 0x63, 0x58,
+ 0x64, 0x60, 0x53, 0x49, 0x4A, 0x49, 0x52, 0x5C,
+ 0x63, 0xCD, 0xCD, 0x63, 0x5C, 0x4E, 0x65, 0xFC,
+ 0xFC, 0xF5, 0xFC, 0xD2, 0x23, 0x22, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x21, 0x22, 0x25, 0x29, 0xB3, 0xC7,
+ 0xB5, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB5, 0xC7, 0xAD, 0x57, 0x3F, 0xCB, 0xF0,
+ 0xF3, 0xF3, 0xF2, 0xD9, 0x58, 0x41, 0x4C, 0x58,
+ 0x57, 0x47, 0x42, 0x62, 0xD4, 0xD4, 0xCC, 0x60,
+ 0x63, 0x5D, 0x50, 0x47, 0x48, 0x4B, 0x58, 0x60,
+ 0xCC, 0xCE, 0xCD, 0x60, 0x53, 0x5C, 0x62, 0xFB,
+ 0xF9, 0xFC, 0xFC, 0x21, 0x23, 0x22, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x23, 0x23, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x23, 0x22, 0x23, 0x81, 0xC7,
+ 0xB7, 0xB7, 0xBC, 0xB7, 0xBC, 0xBC, 0xBC, 0xB7,
+ 0xB7, 0xB7, 0xC8, 0x80, 0x58, 0x57, 0x40, 0xCE,
+ 0xF3, 0xF2, 0xF2, 0xF0, 0xD5, 0x4C, 0x3F, 0x4B,
+ 0x52, 0x50, 0x2D, 0x4B, 0x64, 0xD2, 0xCC, 0x61,
+ 0x60, 0x58, 0x4A, 0x47, 0x47, 0x4C, 0x59, 0x64,
+ 0xD0, 0xD0, 0x64, 0x59, 0x49, 0x5D, 0xFB, 0xFC,
+ 0xD9, 0xFC, 0xD6, 0x23, 0x22, 0x22, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x23, 0x21, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x23, 0x22, 0x23, 0xB4, 0xC8,
+ 0xBD, 0xB7, 0xBD, 0xBC, 0xBD, 0xC5, 0xBC, 0xC5,
+ 0xBC, 0xBD, 0xC7, 0xAC, 0x58, 0x57, 0x58, 0x2C,
+ 0xD1, 0xF0, 0xF3, 0xF3, 0xE0, 0xCD, 0x45, 0x3E,
+ 0x48, 0x4B, 0x3F, 0x41, 0x56, 0x64, 0x65, 0x62,
+ 0x5D, 0x52, 0x47, 0x48, 0x48, 0x53, 0x60, 0xCC,
+ 0xD2, 0xD0, 0x63, 0x52, 0x4E, 0x53, 0xFB, 0xFB,
+ 0xFC, 0xFC, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x23, 0x20, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0xB4, 0xC7,
+ 0xC5, 0xBC, 0xC5, 0xBD, 0xC5, 0xC5, 0xBD, 0xC5,
+ 0xBC, 0xC6, 0xC7, 0xB9, 0x58, 0x57, 0x58, 0x57,
+ 0x2D, 0xD4, 0xF1, 0xF2, 0xF0, 0xD9, 0x5D, 0x47,
+ 0x48, 0x3F, 0x42, 0x2C, 0x48, 0x5C, 0x5F, 0x60,
+ 0x58, 0x50, 0x47, 0x4A, 0x49, 0x55, 0x63, 0xD0,
+ 0xD2, 0xCD, 0x5D, 0x49, 0x4E, 0xE1, 0xFC, 0xF0,
+ 0xFC, 0xF8, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x20, 0x21, 0x21, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x22,
+ 0x23, 0x22, 0x23, 0x23, 0x23, 0x22, 0xC4, 0xC8,
+ 0xBD, 0xBD, 0xC6, 0xBD, 0xC6, 0xC6, 0xC5, 0xC6,
+ 0xBD, 0xC6, 0xC7, 0xE4, 0x54, 0x57, 0x58, 0x57,
+ 0x57, 0x43, 0xD7, 0xE0, 0xF1, 0xD8, 0xCD, 0x4B,
+ 0x4A, 0x47, 0x42, 0x2C, 0x3F, 0x4D, 0x58, 0x5C,
+ 0x52, 0x4B, 0x48, 0x4B, 0x4A, 0x58, 0xCB, 0xD3,
+ 0xD2, 0xCD, 0x58, 0x47, 0x4A, 0xFC, 0xFC, 0xFB,
+ 0xFC, 0x2B, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x26, 0x21, 0x21, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0xE5, 0xC8,
+ 0xBA, 0xC5, 0xC6, 0xC6, 0xC6, 0xC7, 0xC6, 0xC7,
+ 0xC5, 0xC6, 0xC8, 0xE5, 0x2E, 0x54, 0x58, 0x57,
+ 0x57, 0x4C, 0x4D, 0xDA, 0xD8, 0xD8, 0xD4, 0x5C,
+ 0x4B, 0x4B, 0x3F, 0x42, 0x44, 0x4A, 0x51, 0x58,
+ 0x4B, 0x48, 0x4B, 0x51, 0x4D, 0x5F, 0xD0, 0xD1,
+ 0xD0, 0x64, 0x51, 0x44, 0x6B, 0xFC, 0xFB, 0xFC,
+ 0xFC, 0x21, 0x23, 0x22, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x22, 0x23, 0x26, 0x21, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0xE5, 0xED,
+ 0xE7, 0xBA, 0xC8, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7,
+ 0xC7, 0xE5, 0xED, 0xE6, 0x61, 0x41, 0x52, 0x58,
+ 0x58, 0x57, 0x45, 0x5E, 0xD7, 0xDD, 0xD5, 0x60,
+ 0x4B, 0x4C, 0x48, 0x4D, 0x4D, 0x50, 0x4D, 0x56,
+ 0x4A, 0x3E, 0x53, 0x53, 0x52, 0x63, 0xD3, 0xD0,
+ 0xCE, 0x60, 0x4A, 0x45, 0xFC, 0xFC, 0xF7, 0xFC,
+ 0xFC, 0x21, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x21, 0x2A, 0x20, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x22, 0x21, 0x23, 0xEB, 0xF6,
+ 0xF6, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED,
+ 0xF6, 0xF6, 0xF6, 0xE6, 0xDB, 0x58, 0x45, 0x4B,
+ 0x58, 0x57, 0x4D, 0x4B, 0x64, 0xD4, 0xD0, 0x5C,
+ 0x48, 0x51, 0x4C, 0x5D, 0x5E, 0x5C, 0x56, 0x59,
+ 0x3E, 0x4A, 0x58, 0x54, 0x52, 0x65, 0xD3, 0xD0,
+ 0xCF, 0x5D, 0x48, 0xFC, 0xFC, 0xFC, 0xFA, 0xFC,
+ 0xFC, 0x21, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x21, 0x2A, 0x21, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x22, 0x21, 0x4F, 0xE6, 0xC6,
+ 0xC6, 0xBD, 0xC6, 0xBD, 0xBD, 0xBD, 0xBD, 0xC6,
+ 0xC5, 0xBA, 0xC7, 0xE6, 0xF2, 0xD4, 0x49, 0x4B,
+ 0x3E, 0x4D, 0x52, 0x3E, 0x52, 0x63, 0x64, 0x56,
+ 0x48, 0x54, 0x4D, 0x61, 0xCC, 0xCC, 0x60, 0x60,
+ 0x47, 0x4D, 0x5C, 0x53, 0x58, 0xCF, 0xD1, 0xCF,
+ 0xD0, 0x59, 0x45, 0xFC, 0xFC, 0xFC, 0xEF, 0xF9,
+ 0xFC, 0x21, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+ 0x23, 0x22, 0x23, 0x2A, 0x21, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x22, 0x23, 0x4F, 0xE4, 0xB9,
+ 0xAF, 0x80, 0x80, 0x8E, 0x8E, 0x8E, 0x8E, 0x8F,
+ 0x80, 0xB4, 0xB9, 0xE4, 0x7F, 0xDE, 0x61, 0x52,
+ 0x54, 0x48, 0x3F, 0x43, 0x4D, 0x56, 0x59, 0x4B,
+ 0x3E, 0x58, 0x53, 0x61, 0xD3, 0xD4, 0xCF, 0xCD,
+ 0x4C, 0x58, 0x5F, 0x53, 0x5E, 0xD3, 0xD0, 0xCE,
+ 0xCE, 0x52, 0x3F, 0xFC, 0xFC, 0xFC, 0xF7, 0x65,
+ 0xFA, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+ 0x23, 0x22, 0x21, 0x2A, 0x23, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x23, 0x22, 0x23, 0x22, 0x21, 0xB1, 0xE4, 0xE6,
+ 0x7C, 0xB1, 0x7C, 0xB1, 0xB2, 0xB2, 0xB3, 0x3D,
+ 0xB3, 0x3C, 0xE5, 0xB3, 0xB0, 0xF1, 0xD0, 0x58,
+ 0x5D, 0x4D, 0x40, 0x41, 0x48, 0x51, 0x4C, 0x3F,
+ 0x3F, 0x4D, 0x5A, 0x5A, 0xD5, 0xD9, 0xD7, 0xD4,
+ 0x57, 0x5E, 0x61, 0x4C, 0x63, 0xD4, 0xCF, 0xCE,
+ 0xCB, 0x4D, 0x4A, 0xFC, 0xFC, 0xFC, 0xFC, 0xF0,
+ 0xFB, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+ 0x23, 0x22, 0x23, 0x2A, 0x21, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x22, 0x23, 0x22, 0x23, 0x23, 0xB1, 0x81, 0x7D,
+ 0x39, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x7C, 0xB2, 0xB0, 0xDF, 0xD2, 0x57,
+ 0x60, 0x59, 0x5B, 0x59, 0x52, 0x4C, 0x4A, 0x40,
+ 0x42, 0x4A, 0x53, 0x4D, 0xD2, 0xDE, 0xDE, 0xD9,
+ 0x5E, 0x5E, 0x60, 0x4A, 0xCD, 0xD1, 0xCF, 0xCE,
+ 0x63, 0x49, 0x5C, 0xFB, 0xE8, 0x89, 0x9F, 0xFC,
+ 0xD6, 0x21, 0x21, 0x23, 0x22, 0x22, 0x23, 0x22,
+ 0x23, 0x22, 0x21, 0x2A, 0x22, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x7F, 0xB9,
+ 0x71, 0x6C, 0x38, 0x38, 0x33, 0x33, 0x33, 0x38,
+ 0x38, 0x71, 0xAD, 0xE4, 0xD3, 0xDA, 0xCC, 0x52,
+ 0x63, 0x60, 0xCE, 0xD4, 0xCF, 0x60, 0x4C, 0x40,
+ 0x3F, 0x45, 0x4B, 0x5A, 0xCB, 0xD8, 0xDE, 0xDC,
+ 0x5E, 0x5E, 0x5F, 0x4C, 0xD2, 0xD2, 0xCF, 0xCF,
+ 0x61, 0x45, 0x5E, 0xA7, 0x9D, 0x95, 0x8B, 0x99,
+ 0xFC, 0x41, 0x21, 0x23, 0x23, 0x22, 0x23, 0x22,
+ 0x23, 0x22, 0x23, 0x2A, 0x23, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x77, 0x77, 0xF6,
+ 0xFC, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D,
+ 0x7D, 0xFC, 0x47, 0x64, 0xD0, 0xD0, 0x5D, 0x4B,
+ 0x62, 0xCC, 0xD1, 0xDE, 0xDE, 0xD4, 0x5E, 0x43,
+ 0x3F, 0x3E, 0x48, 0x53, 0x58, 0xDB, 0xD8, 0xDC,
+ 0x5E, 0x5E, 0x5E, 0x53, 0xD4, 0xD2, 0xD0, 0xD0,
+ 0x5E, 0x49, 0xA7, 0xA6, 0x89, 0x95, 0x8B, 0x9C,
+ 0x9C, 0xFB, 0xD4, 0x22, 0x22, 0x22, 0x22, 0x23,
+ 0x22, 0x23, 0x23, 0x2A, 0x22, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x23, 0x22, 0x23, 0x23, 0x98, 0x8C, 0x8C, 0x88,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8,
+ 0xE9, 0x9C, 0x48, 0x5C, 0xD0, 0xCB, 0x48, 0x49,
+ 0x5B, 0xCB, 0xCD, 0xE0, 0xF1, 0xDD, 0xD0, 0x4A,
+ 0x41, 0x47, 0x45, 0x4C, 0x48, 0xD7, 0xDE, 0xDC,
+ 0x5E, 0x5E, 0x5A, 0x58, 0xD1, 0xD0, 0xD0, 0xD2,
+ 0x5C, 0x55, 0xA7, 0xA6, 0x87, 0x86, 0x89, 0x94,
+ 0x9C, 0xA9, 0xFC, 0xF4, 0x22, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x22, 0x2A, 0x21, 0x23, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x22, 0x23, 0x22, 0x23, 0xA4, 0x89, 0x8C, 0xAA,
+ 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF7,
+ 0x85, 0x88, 0x8D, 0x59, 0x64, 0x63, 0x47, 0x3E,
+ 0x4C, 0x60, 0x61, 0xE0, 0xF0, 0xDF, 0xD9, 0x5D,
+ 0x2E, 0x3E, 0x3E, 0x47, 0x4D, 0xCD, 0xDE, 0xDC,
+ 0x5D, 0x5C, 0x51, 0x5D, 0xD1, 0xD2, 0xD2, 0xD4,
+ 0x5A, 0xBE, 0xA7, 0x98, 0x8A, 0x8A, 0xA0, 0x8B,
+ 0x86, 0x86, 0xF7, 0xFC, 0xF7, 0x26, 0x23, 0x23,
+ 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x22, 0x21, 0x21, 0x21, 0xA1, 0x98, 0x9F, 0xBF,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xA7,
+ 0x8C, 0x86, 0x8D, 0x59, 0x5E, 0x5D, 0x3F, 0x3E,
+ 0x47, 0x53, 0x63, 0xD9, 0xF0, 0xF1, 0xDE, 0xD0,
+ 0x43, 0x3E, 0x47, 0x45, 0x4A, 0x5B, 0xDC, 0xDA,
+ 0x5D, 0x59, 0x49, 0x5F, 0xD1, 0xD2, 0xD3, 0xB9,
+ 0xA5, 0xA7, 0x98, 0x9B, 0x96, 0x9D, 0x89, 0x89,
+ 0x8B, 0x9C, 0x9D, 0xFC, 0xFC, 0xFC, 0x26, 0x22,
+ 0x23, 0x23, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x22, 0x22, 0x29, 0x2D, 0x99, 0x99, 0xA2, 0xAA,
+ 0xC4, 0xFB, 0xFC, 0xFC, 0xFC, 0xF6, 0xBF, 0xA2,
+ 0x9C, 0x9C, 0x8E, 0xDC, 0xCD, 0x51, 0x41, 0x3E,
+ 0x45, 0x49, 0x58, 0xCD, 0xE0, 0xE0, 0xD8, 0xDA,
+ 0x4C, 0x4A, 0x45, 0x45, 0x48, 0x47, 0xDA, 0xDA,
+ 0x5C, 0x58, 0x44, 0x69, 0xA9, 0x98, 0xA4, 0xA6,
+ 0xA1, 0xA4, 0x99, 0x9E, 0x9D, 0x8B, 0x8A, 0x97,
+ 0x87, 0x9A, 0x8A, 0xC2, 0xFC, 0xFC, 0xFC, 0x4D,
+ 0x21, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x22,
+ 0x21, 0x22, 0x2D, 0x34, 0xA4, 0xA2, 0xA2, 0xA9,
+ 0xBF, 0xC0, 0xC3, 0xC1, 0xC0, 0xBE, 0xA6, 0x9D,
+ 0x99, 0x87, 0xA2, 0xF1, 0xDC, 0x64, 0x42, 0x45,
+ 0x47, 0x3E, 0x49, 0x4C, 0xDD, 0xDF, 0xD8, 0xDB,
+ 0x5E, 0x4C, 0x48, 0x45, 0x45, 0x41, 0xD1, 0xD6,
+ 0x5A, 0x55, 0x3F, 0xA7, 0xA1, 0x98, 0x9F, 0x99,
+ 0x9F, 0x9D, 0x9A, 0x95, 0x8B, 0x97, 0x89, 0x8A,
+ 0x88, 0x94, 0x9C, 0x8C, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xF4, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+ 0x23, 0x23, 0x2C, 0x2C, 0xA8, 0xA2, 0xA4, 0xA4,
+ 0xA9, 0xAA, 0xAA, 0xAA, 0xA9, 0xA6, 0x98, 0x9C,
+ 0x8B, 0x88, 0x98, 0x8D, 0xD8, 0xD6, 0x4E, 0x47,
+ 0x47, 0x49, 0x47, 0x3F, 0xDA, 0xDD, 0xDE, 0xDD,
+ 0xCC, 0x4A, 0x4B, 0x3E, 0x45, 0x43, 0x61, 0xD4,
+ 0x56, 0x51, 0x44, 0xA4, 0x9B, 0x8B, 0x9C, 0x9A,
+ 0xA0, 0xA2, 0x98, 0x98, 0x8B, 0x8B, 0x98, 0x98,
+ 0x84, 0x8B, 0x94, 0x8A, 0xA4, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xF2, 0x21, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x22, 0x2C, 0x2D, 0xC0, 0xA4, 0xA2, 0xA4,
+ 0xA4, 0xA6, 0xA6, 0xA6, 0xA4, 0xA2, 0x9F, 0x89,
+ 0x8B, 0x9C, 0x9C, 0x8B, 0x68, 0xDB, 0x5F, 0x4B,
+ 0x3E, 0x49, 0x4B, 0x3E, 0xCC, 0xDA, 0xDC, 0xDD,
+ 0xD3, 0x49, 0x52, 0x48, 0x45, 0x45, 0x53, 0xD0,
+ 0x51, 0x4A, 0x44, 0xA4, 0x9B, 0x8B, 0x9C, 0xA0,
+ 0x9B, 0x86, 0x89, 0x98, 0x89, 0x8A, 0x96, 0x8A,
+ 0x9C, 0x89, 0x89, 0x9C, 0x8C, 0xF6, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0x21, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x23,
+ 0x22, 0x21, 0x2B, 0x34, 0xC0, 0xA8, 0xA4, 0xA2,
+ 0xA2, 0x98, 0xA1, 0xA0, 0x98, 0x9F, 0x95, 0x8A,
+ 0x94, 0xA1, 0x8A, 0x84, 0x9B, 0x68, 0xCC, 0x49,
+ 0x4A, 0x47, 0x4C, 0x4B, 0x51, 0xD3, 0xDA, 0xDC,
+ 0xD5, 0x56, 0x56, 0x4A, 0x3E, 0x45, 0x48, 0x63,
+ 0x4A, 0x47, 0x3E, 0xA7, 0x98, 0x9D, 0x9E, 0x8B,
+ 0x95, 0x9B, 0x89, 0x86, 0x9B, 0x8B, 0x89, 0x84,
+ 0x9A, 0xA1, 0x95, 0x9A, 0x8C, 0xA4, 0xFC, 0xFC,
+ 0xFC, 0xFA, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x23,
+ 0x21, 0x23, 0x2C, 0xF6, 0xBF, 0xA9, 0xA2, 0x99,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9B, 0x87, 0x8B,
+ 0x9C, 0x86, 0x9C, 0x8A, 0x87, 0x87, 0x89, 0x51,
+ 0x54, 0x47, 0x4B, 0x50, 0x4B, 0xCF, 0xD6, 0xDC,
+ 0xD5, 0x60, 0x54, 0x52, 0x48, 0x45, 0x40, 0x5A,
+ 0x45, 0x43, 0x47, 0xA7, 0x98, 0x9B, 0x95, 0x95,
+ 0x9A, 0x87, 0x98, 0x98, 0x8A, 0x86, 0x87, 0x9E,
+ 0x9B, 0x95, 0x9D, 0x9D, 0x99, 0x85, 0xA6, 0xFA,
+ 0xF2, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x22,
+ 0x21, 0x24, 0xFB, 0xF7, 0xBF, 0xA6, 0xA2, 0x99,
+ 0x97, 0x89, 0x86, 0x89, 0x9C, 0x96, 0x9E, 0x94,
+ 0x89, 0x99, 0x98, 0x89, 0x9E, 0x9B, 0x89, 0x8B,
+ 0x58, 0x4B, 0x4A, 0x52, 0x48, 0xCC, 0xD3, 0xDA,
+ 0xD3, 0x65, 0x4C, 0x58, 0x49, 0x3E, 0x2E, 0x4D,
+ 0x40, 0x41, 0x45, 0xA9, 0xA1, 0x9B, 0x9E, 0x9C,
+ 0x95, 0x8A, 0x94, 0x89, 0x96, 0x87, 0x9C, 0x9A,
+ 0x84, 0x9D, 0x9C, 0x9E, 0x9A, 0x9C, 0x9D, 0xBB,
+ 0x23, 0x23, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x23, 0x23,
+ 0x24, 0xFC, 0xFC, 0xF6, 0xBF, 0xA6, 0x9F, 0x99,
+ 0x89, 0x95, 0x87, 0x94, 0x9D, 0x9E, 0x97, 0x9E,
+ 0x95, 0x9B, 0x89, 0x95, 0x95, 0x9B, 0x89, 0x87,
+ 0x5D, 0x56, 0x3E, 0x51, 0x3E, 0x60, 0xCF, 0xD3,
+ 0xD2, 0xCD, 0x5C, 0x49, 0x4B, 0x3E, 0x2C, 0x48,
+ 0x3E, 0x43, 0x3E, 0xA9, 0xA1, 0x9B, 0x97, 0x94,
+ 0x95, 0x9A, 0x9C, 0x87, 0x87, 0x9B, 0x9C, 0x95,
+ 0x9D, 0x89, 0x9A, 0x89, 0x9E, 0x9E, 0x8C, 0xA6,
+ 0x20, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x20, 0x40,
+ 0xFC, 0xFC, 0xFC, 0xEC, 0xBE, 0xA4, 0x9F, 0x99,
+ 0x95, 0x9F, 0xA0, 0x88, 0x9D, 0x8B, 0x97, 0x95,
+ 0x87, 0x95, 0x96, 0x95, 0x97, 0x94, 0x94, 0x98,
+ 0xD3, 0x4C, 0x47, 0x4D, 0x42, 0x4C, 0x60, 0xCC,
+ 0xCE, 0xD0, 0x65, 0x4B, 0x47, 0x44, 0x2B, 0x45,
+ 0x4B, 0x47, 0x49, 0xA7, 0xA1, 0x9A, 0x97, 0x89,
+ 0x95, 0x97, 0x97, 0x9E, 0x89, 0x95, 0x89, 0x9C,
+ 0x87, 0x95, 0x97, 0x99, 0x95, 0x99, 0x9F, 0xA4,
+ 0xC4, 0x21, 0x21, 0x23, 0x21, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x21, 0x20, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xEA, 0xAA, 0xA6, 0xA2, 0x99,
+ 0x8B, 0x9A, 0x95, 0x9E, 0x9E, 0x9A, 0x94, 0x87,
+ 0x94, 0x94, 0x89, 0x94, 0x9B, 0x9B, 0xA7, 0xDC,
+ 0xDB, 0x65, 0x2E, 0x3E, 0x43, 0x44, 0x49, 0x58,
+ 0x63, 0xD3, 0xD3, 0x5E, 0x42, 0x42, 0x2D, 0x40,
+ 0x54, 0x4C, 0x4A, 0xA7, 0xA0, 0x99, 0x9B, 0x94,
+ 0xA0, 0x8A, 0x9B, 0x9D, 0x87, 0x95, 0x94, 0x8B,
+ 0x8A, 0x98, 0x9C, 0x8A, 0x9B, 0x99, 0xA2, 0xA6,
+ 0xBF, 0xEC, 0x2A, 0x20, 0x21, 0x23, 0x21, 0x20,
+ 0x20, 0x20, 0x20, 0x4C, 0xF9, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xEB, 0xAA, 0xA4, 0x9F, 0x9C,
+ 0x8B, 0x9B, 0x88, 0x84, 0x9E, 0x9D, 0x96, 0x94,
+ 0x94, 0x9A, 0x9B, 0x9B, 0xA4, 0xD5, 0xCD, 0xDE,
+ 0xF1, 0xDA, 0x4C, 0x2D, 0x41, 0x2B, 0x42, 0x4C,
+ 0x5E, 0xD4, 0xD7, 0xCD, 0x49, 0x2E, 0x2E, 0x41,
+ 0x5E, 0x57, 0xA7, 0xA6, 0xA7, 0xA4, 0xA2, 0x98,
+ 0x9D, 0x9C, 0xA1, 0x99, 0x9D, 0x88, 0x8B, 0x9C,
+ 0x8A, 0x9C, 0x9C, 0x94, 0x9C, 0x89, 0xA0, 0xA6,
+ 0xAA, 0xEB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFB, 0xE9, 0xAA, 0xA6, 0xA2, 0x8B,
+ 0x8B, 0x8A, 0x86, 0x9B, 0x9C, 0x98, 0xA0, 0x9B,
+ 0x9B, 0x84, 0xA7, 0xB4, 0x61, 0xD1, 0xD2, 0xE0,
+ 0xF1, 0xDC, 0x61, 0x2D, 0x2E, 0x3F, 0x56, 0x62,
+ 0x5D, 0xD4, 0xD9, 0xD3, 0x54, 0x41, 0x41, 0x44,
+ 0xCB, 0x60, 0x52, 0xA9, 0xA9, 0xA9, 0xA7, 0xA6,
+ 0xA6, 0xA4, 0xA4, 0xA2, 0xA2, 0x9D, 0x95, 0x89,
+ 0x9C, 0x8A, 0x9E, 0x9C, 0x8A, 0x9E, 0xA0, 0xA8,
+ 0xC0, 0xE9, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xE9, 0xAA, 0xA6, 0xA0, 0x99,
+ 0x9C, 0x8B, 0x9A, 0x84, 0x9B, 0x9B, 0x98, 0x98,
+ 0xA9, 0xB9, 0x49, 0x57, 0xCB, 0xD4, 0xD3, 0xF1,
+ 0xD8, 0xDA, 0xCE, 0x3F, 0x41, 0x4B, 0x5D, 0xCB,
+ 0x5E, 0xD6, 0xDB, 0xD6, 0x5D, 0x43, 0x3F, 0x49,
+ 0xD1, 0xCC, 0x4F, 0xDD, 0xC3, 0xBB, 0xBF, 0xAA,
+ 0xAA, 0xA9, 0xAA, 0xA8, 0xA8, 0xA6, 0xA6, 0xA2,
+ 0x9C, 0x9F, 0x9B, 0x9A, 0x9D, 0xA2, 0xA8, 0xAA,
+ 0xC1, 0xEA, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xEA, 0xC0, 0xAA, 0xA6, 0xA2,
+ 0xA2, 0x99, 0xA0, 0xA0, 0xA4, 0xA7, 0xA9, 0xC0,
+ 0x67, 0x49, 0x54, 0x60, 0xD0, 0xD4, 0xCC, 0xDF,
+ 0xD9, 0xD5, 0xD2, 0x3E, 0x47, 0x56, 0x60, 0xCD,
+ 0x5D, 0xD9, 0xD9, 0xD6, 0x61, 0x3F, 0x47, 0x52,
+ 0xD6, 0xD3, 0x62, 0x4D, 0x40, 0x4A, 0x57, 0xCA,
+ 0xC3, 0xC1, 0xC1, 0xC0, 0xBF, 0xBF, 0xAA, 0xAA,
+ 0xA6, 0xA4, 0xA4, 0xA4, 0xA6, 0xA8, 0xBE, 0xC1,
+ 0xC9, 0xEB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+ 0xFC, 0xFC, 0xFC, 0xEB, 0xC3, 0xC0, 0xAA, 0xA8,
+ 0xA6, 0xA6, 0xA6, 0xA9, 0xAA, 0xC0, 0xE8, 0xD0,
+ 0xD2, 0x4C, 0x5E, 0x64, 0xD0, 0xD1, 0x5F, 0xD9,
+ 0xD5, 0xD1, 0xD0, 0x48, 0x52, 0x5C, 0x64, 0xCD,
+ 0x5C, 0xDC, 0xD7, 0xD5, 0x62, 0x3F, 0x4C, 0x53,
+ 0xDA, 0xD7, 0xCE, 0x56, 0x40, 0x4B, 0x52, 0x56,
+ 0xCE, 0xDF, 0x6A, 0xEB, 0xE9, 0xC9, 0xC3, 0xC0,
+ 0xC0, 0xBF, 0xBE, 0xAA, 0xBF, 0xC0, 0xC3, 0xC9,
+ 0xEA, 0xF6, 0xEE, 0x58, 0x57, 0x5E, 0xD6, 0xD0,
+ 0xD2, 0x61, 0xCB, 0xD6, 0xD6, 0xD4, 0xDF, 0xF3,
+ 0xF2, 0xDD, 0xD7, 0xEB, 0xC9, 0xC1, 0xC0, 0xBF,
+ 0xAA, 0xAA, 0xAA, 0xBE, 0xC3, 0xF0, 0xD2, 0xD2,
+ 0xD2, 0x51, 0x62, 0xCC, 0xD0, 0xCC, 0x61, 0xD3,
+ 0xCF, 0xCE, 0xD2, 0x48, 0x5A, 0x61, 0xCC, 0xCE,
+ 0x5F, 0xD9, 0xD5, 0xD1, 0x63, 0x44, 0x56, 0x56,
+ 0xDC, 0xD9, 0xD4, 0x5E, 0x42, 0x4A, 0x4C, 0x57,
+ 0x5D, 0xD8, 0xE0, 0xD8, 0xDC, 0xCB, 0x66, 0xEC,
+ 0xE8, 0xC3, 0xC3, 0xC3, 0xC3, 0xC9, 0xE8, 0xEA,
+ 0xF6, 0x50, 0x3E, 0x58, 0x57, 0x5A, 0xD6, 0xD4,
+ 0xCC, 0x4B, 0x53, 0x5C, 0x64, 0xD1, 0xDF, 0xF3,
+ 0xF1, 0xDE, 0xD9, 0xF6, 0xEB, 0xC9, 0xC1, 0xC1,
+ 0xC0, 0xC0, 0xC1, 0xC9, 0xF0, 0xD6, 0xCD, 0xD6,
+ 0xD3, 0x53, 0xCB, 0xCF, 0xCD, 0x5F, 0x5F, 0xCE,
+ 0xCF, 0xCD, 0xD0, 0x47, 0x5F, 0xCB, 0xCE, 0xCD,
+ 0x63, 0xD6, 0xD3, 0xD1, 0x63, 0x3F, 0x58, 0x58,
+ 0xDB, 0xDC, 0xDA, 0x65, 0x3E, 0x49, 0x49, 0x4D,
+ 0x49, 0xDC, 0xDF, 0xE0, 0xDE, 0xD5, 0x47, 0x47,
+ 0x46, 0x6B, 0xEB, 0xEA, 0xE9, 0xEA, 0xEB, 0xF6,
+ 0xD0, 0x57, 0x57, 0x47, 0x47, 0x5B, 0xD4, 0xD4,
+ 0xCD, 0x44, 0x3E, 0x4B, 0x50, 0x4B, 0x51, 0xD5,
+ 0xDB, 0xD8, 0xDE, 0x4B, 0xF6, 0xF6, 0xEA, 0xE9,
+ 0xE8, 0xEA, 0xEB, 0x67, 0x5E, 0xCC, 0xD6, 0xDC,
+ 0xD5, 0x58, 0xCE, 0xCE, 0x62, 0x50, 0xCC, 0xD3,
+ 0xD2, 0xCD, 0xCD, 0x4B, 0x64, 0xCE, 0xCE, 0x64,
+ 0xCC, 0xD3, 0xD2, 0xD2, 0x61, 0x47, 0x5D, 0x5C,
+ 0xDD, 0xDD, 0xD9, 0xD1, 0x4C, 0x47, 0x49, 0x4A,
+ 0x4B, 0xD1, 0xD8, 0xE0, 0xDF, 0xDD, 0x5D, 0x4A,
+ 0x48, 0x52, 0x51, 0x3F, 0xF6, 0xEC, 0xE0, 0xE0,
+ 0xD3, 0x5E, 0x5F, 0x50, 0x4B, 0x50, 0xCB, 0xCE,
+ 0x64, 0x45, 0x4C, 0x57, 0x57, 0x58, 0x52, 0xD6,
+ 0xD3, 0xDE, 0xDF, 0xD1, 0x3E, 0x4B, 0xF6, 0xF6,
+ 0xEC, 0x66, 0x53, 0x43, 0x56, 0xD1, 0xD9, 0xDE,
+ 0xD4, 0x5E, 0xCE, 0xCC, 0x5B, 0x2C, 0xD4, 0xD5,
+ 0xD2, 0xD0, 0x63, 0x5D, 0xCD, 0xD0, 0xCD, 0x5E,
+ 0xD0, 0xCF, 0xCE, 0xD2, 0x5E, 0x50, 0x60, 0x5D,
+ 0xDE, 0xDD, 0xDC, 0xD7, 0x5D, 0x45, 0x47, 0x3E,
+ 0x4B, 0x5E, 0xDE, 0xDF, 0xE0, 0xD8, 0xCF, 0x3E,
+ 0x45, 0x51, 0x58, 0x42, 0xCB, 0xDA, 0xDE, 0xD8,
+ 0xD2, 0x61, 0xCC, 0xCF, 0xD6, 0xDA, 0xDA, 0xD5,
+ 0xD0, 0x50, 0x44, 0x57, 0x57, 0x58, 0x45, 0xD1,
+ 0xD1, 0xD7, 0xDF, 0xDF, 0xD7, 0xCF, 0x64, 0x60,
+ 0xCE, 0xCE, 0xCE, 0x63, 0xCF, 0xDA, 0xDE, 0xD9,
+ 0xCF, 0x63, 0xCD, 0x63, 0x4D, 0x4B, 0xD6, 0xD5,
+ 0xCE, 0xD3, 0x60, 0xCB, 0xD0, 0xD0, 0x65, 0x47,
+ 0xD0, 0xCC, 0xCC, 0xD1, 0x59, 0x5D, 0x63, 0x5E,
+ 0xDD, 0xDD, 0xDE, 0xDC, 0xCB, 0x40, 0x48, 0x45,
+ 0x3E, 0x3E, 0xD9, 0xDF, 0xE0, 0xDF, 0xDA, 0x51,
+ 0x4C, 0x48, 0x56, 0x4C, 0x5B, 0xD2, 0xDA, 0xDB,
+ 0xCB, 0x5F, 0xD0, 0xCC, 0xDC, 0xF0, 0xF3, 0xE0,
+ 0xDD, 0xCC, 0x41, 0x50, 0x57, 0x57, 0x4B, 0x5D,
+ 0xD3, 0xD1, 0xDE, 0xDF, 0xDE, 0xD7, 0xD0, 0xD0,
+ 0xD5, 0xD6, 0xD6, 0xCE, 0xD7, 0xDC, 0xDA, 0xD5,
+ 0x60, 0x63, 0x64, 0x5E, 0x47, 0x61, 0xD5, 0xD2,
+ 0xCF, 0xD0, 0x59, 0xCD, 0xD1, 0xCF, 0x61, 0x4D,
+ 0xCC, 0xCE, 0xCD, 0xD0, 0x52, 0x61, 0x64, 0x60,
+ 0xDA, 0xDE, 0xDE, 0xDD, 0xD1, 0x4B, 0x4A, 0x45,
+ 0x3E, 0x41, 0xCD, 0xDE, 0xE0, 0xF1, 0xDE, 0x63,
+ 0x4A, 0x4A, 0x4A, 0x4B, 0x50, 0xCB, 0xD4, 0xD7,
+ 0x5E, 0x54, 0x62, 0xD3, 0xD4, 0xF0, 0xF3, 0xF3,
+ 0xF2, 0xDE, 0x61, 0x40, 0x49, 0x56, 0x4D, 0x3E,
+ 0x4B, 0xCE, 0xD9, 0xD8, 0xD9, 0xD5, 0xCF, 0xD2,
+ 0xD6, 0xD6, 0xD1, 0xD1, 0xD7, 0xD5, 0xCF, 0xD0,
+ 0x54, 0x64, 0x63, 0x56, 0x2C, 0xCB, 0xD1, 0xCC,
+ 0xD3, 0xCD, 0x54, 0xCF, 0xD1, 0xCE, 0x5E, 0x5C,
+ 0xCE, 0xCE, 0xCE, 0xCB, 0x4B, 0x63, 0xCC, 0x61,
+ 0xD4, 0xDC, 0xDE, 0xDE, 0xDA, 0x5D, 0x45, 0x45,
+ 0x48, 0x3F, 0x52, 0xD9, 0xD8, 0xDF, 0xDF, 0xD2,
+ 0x52, 0x4B, 0x3E, 0x2E, 0x47, 0x60, 0xCF, 0xD3,
+ 0x59, 0x48, 0x50, 0x5E, 0xCC, 0xDE, 0xF2, 0xF2,
+ 0xF3, 0xF3, 0xDD, 0x5D, 0x3E, 0x48, 0x47, 0x47,
+ 0x58, 0xD1, 0xDA, 0xDA, 0xD5, 0xD1, 0xCD, 0xD2,
+ 0xD3, 0xCF, 0xD3, 0xD1, 0xCD, 0xD3, 0xD2, 0x5E,
+ 0x52, 0x64, 0x60, 0x4B, 0x45, 0x61, 0xCD, 0xD3,
+ 0xD3, 0x64, 0x61, 0xD0, 0xD0, 0x64, 0x45, 0x63,
+ 0xD0, 0xCE, 0xD0, 0x60, 0x56, 0xCB, 0xCC, 0x62,
+ 0xCE, 0xDA, 0xDE, 0xD8, 0xDD, 0xCC, 0x45, 0x49,
+ 0x3E, 0x47, 0x42, 0xD1, 0xDC, 0xD8, 0xD8, 0xD3,
+ 0x5D, 0x4C, 0x49, 0x3F, 0x47, 0x59, 0xCD, 0xCF,
+ 0x59, 0x2E, 0x48, 0x47, 0x52, 0x63, 0xF0, 0xF2,
+ 0xF3, 0xF3, 0xF2, 0xDA, 0x52, 0x4B, 0x52, 0x58,
+ 0x5E, 0x63, 0xD0, 0xD0, 0xD0, 0xCF, 0xCE, 0xCE,
+ 0xCF, 0x65, 0x61, 0xD6, 0xD6, 0xD6, 0xCB, 0x4B,
+ 0x61, 0x62, 0x5D, 0x43, 0x4B, 0x61, 0xD0, 0xD4,
+ 0xD1, 0x61, 0xCE, 0xD2, 0xCD, 0x5E, 0x4A, 0xCE,
+ 0xD0, 0xCC, 0xD0, 0x59, 0x61, 0xCC, 0xCC, 0x62,
+ 0xD1, 0xD5, 0xDE, 0xD8, 0xDD, 0xCF, 0x4B, 0x4A,
+ 0x45, 0x3E, 0x2D, 0xCB, 0xDC, 0xDE, 0xD8, 0xD5,
+ 0x60, 0x54, 0x51, 0x4C, 0x4D, 0x5C, 0xCC, 0xCE,
+ 0x5A, 0x2C, 0x50, 0x53, 0x3E, 0x59, 0xD8, 0xF3,
+ 0xF2, 0xF3, 0xF3, 0xE0, 0x5E, 0x4A, 0x4C, 0x53,
+ 0x5E, 0x63, 0xCC, 0xCC, 0xCC, 0xCD, 0xCF, 0xD3,
+ 0x62, 0x53, 0xD6, 0xD6, 0xD6, 0xD6, 0x5B, 0x48,
+ 0x64, 0x63, 0x59, 0x44, 0x57, 0x63, 0xD2, 0xD3,
+ 0xD0, 0x5E, 0xD0, 0xD1, 0xCB, 0x58, 0x4C, 0xCF,
+ 0xCF, 0xCE, 0xCE, 0x57, 0x63, 0xCC, 0xCD, 0x57,
+};
+
diff --git a/drivers/sgi/char/newport.c b/drivers/sgi/char/newport.c
index 756aec709..e617ddd99 100644
--- a/drivers/sgi/char/newport.c
+++ b/drivers/sgi/char/newport.c
@@ -215,3 +215,220 @@ newport_ioctl (int card, int cmd, unsigned long arg)
}
return -EINVAL;
}
+/*
+ * newport.c: context switching the newport graphics card and
+ * newport graphics support.
+ *
+ * Author: Miguel de Icaza
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/types.h>
+#include <asm/gfx.h>
+#include <asm/ng1.h>
+#include <asm/uaccess.h>
+#include "newport.h"
+
+/* Kernel routines for supporting graphics context switching */
+
+void newport_save (void *y)
+{
+ newport_ctx *x = y;
+ newport_wait ();
+
+#define LOAD(val) x->val = npregs->set.val;
+#define LOADI(val) x->val = npregs->set.val.i;
+#define LOADC(val) x->val = npregs->cset.val;
+
+ LOAD(drawmode1);
+ LOAD(drawmode0);
+ LOAD(lsmode);
+ LOAD(lspattern);
+ LOAD(lspatsave);
+ LOAD(zpattern);
+ LOAD(colorback);
+ LOAD(colorvram);
+ LOAD(alpharef);
+ LOAD(smask0x);
+ LOAD(smask0y);
+ LOADI(_xstart);
+ LOADI(_ystart);
+ LOADI(_xend);
+ LOADI(_yend);
+ LOAD(xsave);
+ LOAD(xymove);
+ LOADI(bresd);
+ LOADI(bress1);
+ LOAD(bresoctinc1);
+ LOAD(bresrndinc2);
+ LOAD(brese1);
+ LOAD(bress2);
+ LOAD(aweight0);
+ LOAD(aweight1);
+ LOADI(colorred);
+ LOADI(coloralpha);
+ LOADI(colorgrn);
+ LOADI(colorblue);
+ LOADI(slopered);
+ LOADI(slopealpha);
+ LOADI(slopegrn);
+ LOADI(slopeblue);
+ LOAD(wrmask);
+ LOAD(hostrw0);
+ LOAD(hostrw1);
+
+ /* configregs */
+
+ LOADC(smask1x);
+ LOADC(smask1y);
+ LOADC(smask2x);
+ LOADC(smask2y);
+ LOADC(smask3x);
+ LOADC(smask3y);
+ LOADC(smask4x);
+ LOADC(smask4y);
+ LOADC(topscan);
+ LOADC(xywin);
+ LOADC(clipmode);
+ LOADC(config);
+
+ /* Mhm, maybe I am missing something, but it seems that
+ * saving/restoring the DCB is only a matter of saving these
+ * registers
+ */
+
+ newport_bfwait ();
+ LOAD (dcbmode);
+ newport_bfwait ();
+ x->dcbdata0 = npregs->set.dcbdata0.all;
+ newport_bfwait ();
+ LOAD(dcbdata1);
+}
+
+/*
+ * Importat things to keep in mind when restoring the newport context:
+ *
+ * 1. slopered register is stored as a 2's complete (s12.11);
+ * needs to be converted to a signed magnitude (s(8)12.11).
+ *
+ * 2. xsave should be stored after xstart.
+ *
+ * 3. None of the registers should be written with the GO address.
+ * (read the docs for more details on this).
+ */
+void newport_restore (void *y)
+{
+ newport_ctx *x = y;
+#define STORE(val) npregs->set.val = x->val
+#define STOREI(val) npregs->set.val.i = x->val
+#define STOREC(val) npregs->cset.val = x->val
+ newport_wait ();
+
+ STORE(drawmode1);
+ STORE(drawmode0);
+ STORE(lsmode);
+ STORE(lspattern);
+ STORE(lspatsave);
+ STORE(zpattern);
+ STORE(colorback);
+ STORE(colorvram);
+ STORE(alpharef);
+ STORE(smask0x);
+ STORE(smask0y);
+ STOREI(_xstart);
+ STOREI(_ystart);
+ STOREI(_xend);
+ STOREI(_yend);
+ STORE(xsave);
+ STORE(xymove);
+ STOREI(bresd);
+ STOREI(bress1);
+ STORE(bresoctinc1);
+ STORE(bresrndinc2);
+ STORE(brese1);
+ STORE(bress2);
+ STORE(aweight0);
+ STORE(aweight1);
+ STOREI(colorred);
+ STOREI(coloralpha);
+ STOREI(colorgrn);
+ STOREI(colorblue);
+ STOREI(slopered);
+ STOREI(slopealpha);
+ STOREI(slopegrn);
+ STOREI(slopeblue);
+ STORE(wrmask);
+ STORE(hostrw0);
+ STORE(hostrw1);
+
+ /* configregs */
+
+ STOREC(smask1x);
+ STOREC(smask1y);
+ STOREC(smask2x);
+ STOREC(smask2y);
+ STOREC(smask3x);
+ STOREC(smask3y);
+ STOREC(smask4x);
+ STOREC(smask4y);
+ STOREC(topscan);
+ STOREC(xywin);
+ STOREC(clipmode);
+ STOREC(config);
+
+ /* FIXME: restore dcb thingies */
+}
+
+int
+newport_ioctl (int card, int cmd, unsigned long arg)
+{
+ switch (cmd){
+ case NG1_SETDISPLAYMODE: {
+ int i;
+ struct ng1_setdisplaymode_args request;
+
+ if (copy_from_user (&request, (void *) arg, sizeof (request)))
+ return -EFAULT;
+
+ newport_wait ();
+ newport_bfwait ();
+ npregs->set.dcbmode = DCB_XMAP0 | XM9_CRS_FIFO_AVAIL |
+ DCB_DATAWIDTH_1 | R_DCB_XMAP9_PROTOCOL;
+ xmap9FIFOWait (npregs);
+
+ /* FIXME: timing is wrong, just be extracted from
+ * the per-board timing table. I still have to figure
+ * out where this comes from
+ *
+ * This is used to select the protocol used to talk to
+ * the xmap9. For now I am using 60, selecting the
+ * WSLOW_DCB_XMAP9_PROTOCOL.
+ *
+ * Robert Tray comments on this issue:
+ *
+ * cfreq refers to the frequency of the monitor
+ * (ie. the refresh rate). Our monitors run typically
+ * between 60 Hz and 76 Hz. But it will be as low as
+ * 50 Hz if you're displaying NTSC/PAL and as high as
+ * 120 Hz if you are runining in stereo mode. You
+ * might want to try the WSLOW values.
+ */
+ xmap9SetModeReg (npregs, request.wid, request.mode, 60);
+ return 0;
+ }
+ case NG1_SET_CURSOR_HOTSPOT: {
+ struct ng1_set_cursor_hotspot request;
+
+ if (copy_from_user (&request, (void *) arg, sizeof (request)))
+ return -EFAULT;
+ /* FIXME: make request.xhot, request.yhot the hot spot */
+ return 0;
+ }
+
+ case NG1_SETGAMMARAMP0:
+ /* FIXME: load the gamma ramps :-) */
+ return 0;
+
+ }
+ return -EINVAL;
+}
diff --git a/drivers/sgi/char/newport.h b/drivers/sgi/char/newport.h
index 2f9f044cb..6ad987045 100644
--- a/drivers/sgi/char/newport.h
+++ b/drivers/sgi/char/newport.h
@@ -1,4 +1,589 @@
-/* $Id: newport.h,v 1.4 1997/08/28 01:45:17 miguel Exp $
+/* $Id: newport.h,v 1.2 1996/06/10 16:38:34 dm Exp $
+ * newport.h: Defines and register layout for NEWPORT graphics
+ * hardware.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#ifndef _SGI_NEWPORT_H
+#define _SGI_NEWPORT_H
+
+
+typedef volatile unsigned long npireg_t;
+
+union npfloat {
+ volatile float f;
+ npireg_t i;
+};
+
+typedef union npfloat npfreg_t;
+
+union np_dcb {
+ npireg_t all;
+ struct { volatile unsigned short s0, s1; } hwords;
+ struct { volatile unsigned char b0, b1, b2, b3; } bytes;
+};
+
+struct newport_rexregs {
+ npireg_t drawmode1; /* GL extra mode bits */
+
+#define DM1_PLANES 0x00000007
+#define DM1_NOPLANES 0x00000000
+#define DM1_RGBPLANES 0x00000001
+#define DM1_RGBAPLANES 0x00000002
+#define DM1_OLAYPLANES 0x00000004
+#define DM1_PUPPLANES 0x00000005
+#define DM1_CIDPLANES 0x00000006
+
+#define NPORT_DMODE1_DDMASK 0x00000018
+#define NPORT_DMODE1_DD4 0x00000000
+#define NPORT_DMODE1_DD8 0x00000008
+#define NPORT_DMODE1_DD12 0x00000010
+#define NPORT_DMODE1_DD24 0x00000018
+#define NPORT_DMODE1_DSRC 0x00000020
+#define NPORT_DMODE1_YFLIP 0x00000040
+#define NPORT_DMODE1_RWPCKD 0x00000080
+#define NPORT_DMODE1_HDMASK 0x00000300
+#define NPORT_DMODE1_HD4 0x00000000
+#define NPORT_DMODE1_HD8 0x00000100
+#define NPORT_DMODE1_HD12 0x00000200
+#define NPORT_DMODE1_HD32 0x00000300
+#define NPORT_DMODE1_RWDBL 0x00000400
+#define NPORT_DMODE1_ESWAP 0x00000800 /* Endian swap */
+#define NPORT_DMODE1_CCMASK 0x00007000
+#define NPORT_DMODE1_CCLT 0x00001000
+#define NPORT_DMODE1_CCEQ 0x00002000
+#define NPORT_DMODE1_CCGT 0x00004000
+#define NPORT_DMODE1_RGBMD 0x00008000
+#define NPORT_DMODE1_DENAB 0x00010000 /* Dither enable */
+#define NPORT_DMODE1_FCLR 0x00020000 /* Fast clear */
+#define NPORT_DMODE1_BENAB 0x00040000 /* Blend enable */
+#define NPORT_DMODE1_SFMASK 0x00380000
+#define NPORT_DMODE1_SF0 0x00000000
+#define NPORT_DMODE1_SF1 0x00080000
+#define NPORT_DMODE1_SFDC 0x00100000
+#define NPORT_DMODE1_SFMDC 0x00180000
+#define NPORT_DMODE1_SFSA 0x00200000
+#define NPORT_DMODE1_SFMSA 0x00280000
+#define NPORT_DMODE1_DFMASK 0x01c00000
+#define NPORT_DMODE1_DF0 0x00000000
+#define NPORT_DMODE1_DF1 0x00400000
+#define NPORT_DMODE1_DFSC 0x00800000
+#define NPORT_DMODE1_DFMSC 0x00c00000
+#define NPORT_DMODE1_DFSA 0x01000000
+#define NPORT_DMODE1_DFMSA 0x01400000
+#define NPORT_DMODE1_BBENAB 0x02000000 /* Back blend enable */
+#define NPORT_DMODE1_PFENAB 0x04000000 /* Pre-fetch enable */
+#define NPORT_DMODE1_ABLEND 0x08000000 /* Alpha blend */
+#define NPORT_DMODE1_LOMASK 0xf0000000
+#define NPORT_DMODE1_LOZERO 0x00000000
+#define NPORT_DMODE1_LOAND 0x10000000
+#define NPORT_DMODE1_LOANDR 0x20000000
+#define NPORT_DMODE1_LOSRC 0x30000000
+#define NPORT_DMODE1_LOANDI 0x40000000
+#define NPORT_DMODE1_LODST 0x50000000
+#define NPORT_DMODE1_LOXOR 0x60000000
+#define NPORT_DMODE1_LOOR 0x70000000
+#define NPORT_DMODE1_LONOR 0x80000000
+#define NPORT_DMODE1_LOXNOR 0x90000000
+#define NPORT_DMODE1_LONDST 0xa0000000
+#define NPORT_DMODE1_LOORR 0xb0000000
+#define NPORT_DMODE1_LONSRC 0xc0000000
+#define NPORT_DMODE1_LOORI 0xd0000000
+#define NPORT_DMODE1_LONAND 0xe0000000
+#define NPORT_DMODE1_LOONE 0xf0000000
+
+ npireg_t drawmode0; /* REX command register */
+
+ /* These bits define the graphics opcode being performed. */
+#define NPORT_DMODE0_OPMASK 0x00000003 /* Opcode mask */
+#define NPORT_DMODE0_NOP 0x00000000 /* No operation */
+#define NPORT_DMODE0_RD 0x00000001 /* Read operation */
+#define NPORT_DMODE0_DRAW 0x00000002 /* Draw operation */
+#define NPORT_DMODE0_S2S 0x00000003 /* Screen to screen operation */
+
+ /* The following decide what addressing mode(s) are to be used */
+#define NPORT_DMODE0_AMMASK 0x0000001c /* Address mode mask */
+#define NPORT_DMODE0_SPAN 0x00000000 /* Spanning address mode */
+#define NPORT_DMODE0_BLOCK 0x00000004 /* Block address mode */
+#define NPORT_DMODE0_ILINE 0x00000008 /* Iline address mode */
+#define NPORT_DMODE0_FLINE 0x0000000c /* Fline address mode */
+#define NPORT_DMODE0_ALINE 0x00000010 /* Aline address mode */
+#define NPORT_DMODE0_TLINE 0x00000014 /* Tline address mode */
+#define NPORT_DMODE0_BLINE 0x00000018 /* Bline address mode */
+
+ /* And now some misc. operation control bits. */
+#define NPORT_DMODE0_DOSETUP 0x00000020
+#define NPORT_DMODE0_CHOST 0x00000040
+#define NPORT_DMODE0_AHOST 0x00000080
+#define NPORT_DMODE0_STOPX 0x00000100
+#define NPORT_DMODE0_STOPY 0x00000200
+#define NPORT_DMODE0_SK1ST 0x00000400
+#define NPORT_DMODE0_SKLST 0x00000800
+#define NPORT_DMODE0_ZPENAB 0x00001000
+#define NPORT_DMODE0_LISPENAB 0x00002000
+#define NPORT_DMODE0_LISLST 0x00004000
+#define NPORT_DMODE0_L32 0x00008000
+#define NPORT_DMODE0_ZOPQ 0x00010000
+#define NPORT_DMODE0_LISOPQ 0x00020000
+#define NPORT_DMODE0_SHADE 0x00040000
+#define NPORT_DMODE0_LRONLY 0x00080000
+#define NPORT_DMODE0_XYOFF 0x00100000
+#define NPORT_DMODE0_CLAMP 0x00200000
+#define NPORT_DMODE0_ENDPF 0x00400000
+#define NPORT_DMODE0_YSTR 0x00800000
+
+ npireg_t lsmode; /* Mode for line stipple ops */
+ npireg_t lspattern; /* Pattern for line stipple ops */
+ npireg_t lspatsave; /* Backup save pattern */
+ npireg_t zpattern; /* Pixel zpattern */
+ npireg_t colorback; /* Background color */
+ npireg_t colorvram; /* Clear color for fast vram */
+ npireg_t alpharef; /* Reference value for afunctions */
+ unsigned long pad0;
+ npireg_t smask0x; /* Window GL relative screen mask 0 */
+ npireg_t smask0y; /* Window GL relative screen mask 0 */
+ npireg_t _setup;
+ npireg_t _stepz;
+ npireg_t _lsrestore;
+ npireg_t _lssave;
+
+ unsigned long _pad1[0x30];
+
+ /* Iterators, full state for context switch */
+ npfreg_t _xstart; /* X-start point (current) */
+ npfreg_t _ystart; /* Y-start point (current) */
+ npfreg_t _xend; /* x-end point */
+ npfreg_t _yend; /* y-end point */
+ npireg_t xsave; /* copy of xstart integer value for BLOCk addressing MODE */
+ npireg_t xymove; /* x.y offset from xstart, ystart for relative operations */
+ npfreg_t bresd;
+ npfreg_t bress1;;
+ npireg_t bresoctinc1;
+ volatile int bresrndinc2;
+ npireg_t brese1;
+ npireg_t bress2;
+ npireg_t aweight0;
+ npireg_t aweight1;
+ npfreg_t xstartf;
+ npfreg_t ystartf;
+ npfreg_t xendf;
+ npfreg_t yendf;
+ npireg_t xstarti;
+ npfreg_t xendf1;
+ npireg_t xystarti;
+ npireg_t xyendi;
+ npireg_t xstartendi;
+
+ unsigned long _unused2[0x29];
+
+ npfreg_t colorred;
+ npfreg_t coloralpha;
+ npfreg_t colorgrn;
+ npfreg_t colorblue;
+ npfreg_t slopered;
+ npfreg_t slopealpha;
+ npfreg_t slopegrn;
+ npfreg_t slopeblue;
+ npireg_t wrmask;
+ npireg_t colori;
+ npfreg_t colorx;
+ npfreg_t slopered1;
+ npireg_t hostrw0;
+ npireg_t hostrw1;
+ npireg_t dcbmode;
+#define NPORT_DMODE_WMASK 0x00000003
+#define NPORT_DMODE_W4 0x00000000
+#define NPORT_DMODE_W1 0x00000001
+#define NPORT_DMODE_W2 0x00000002
+#define NPORT_DMODE_W3 0x00000003
+#define NPORT_DMODE_EDPACK 0x00000004
+#define NPORT_DMODE_ECINC 0x00000008
+#define NPORT_DMODE_CMASK 0x00000070
+#define NPORT_DMODE_AMASK 0x00000780
+#define NPORT_DMODE_AVC2 0x00000000
+#define NPORT_DMODE_ACMALL 0x00000080
+#define NPORT_DMODE_ACM0 0x00000100
+#define NPORT_DMODE_ACM1 0x00000180
+#define NPORT_DMODE_AXMALL 0x00000200
+#define NPORT_DMODE_AXM0 0x00000280
+#define NPORT_DMODE_AXM1 0x00000300
+#define NPORT_DMODE_ABT 0x00000380
+#define NPORT_DMODE_AVCC1 0x00000400
+#define NPORT_DMODE_AVAB1 0x00000480
+#define NPORT_DMODE_ALG3V0 0x00000500
+#define NPORT_DMODE_A1562 0x00000580
+#define NPORT_DMODE_ESACK 0x00000800
+#define NPORT_DMODE_EASACK 0x00001000
+#define NPORT_DMODE_CWMASK 0x0003e000
+#define NPORT_DMODE_CHMASK 0x007c0000
+#define NPORT_DMODE_CSMASK 0x0f800000
+#define NPORT_DMODE_SENDIAN 0x10000000
+
+ unsigned long _unused3;
+
+ union np_dcb dcbdata0;
+ npireg_t dcbdata1;
+};
+
+struct newport_cregs {
+ npireg_t smask1x;
+ npireg_t smask1y;
+ npireg_t smask2x;
+ npireg_t smask2y;
+ npireg_t smask3x;
+ npireg_t smask3y;
+ npireg_t smask4x;
+ npireg_t smask4y;
+ npireg_t topscan;
+ npireg_t xywin;
+ npireg_t clipmode;
+#define NPORT_CMODE_SM0 0x00000001
+#define NPORT_CMODE_SM1 0x00000002
+#define NPORT_CMODE_SM2 0x00000004
+#define NPORT_CMODE_SM3 0x00000008
+#define NPORT_CMODE_SM4 0x00000010
+#define NPORT_CMODE_CMSK 0x00001e00
+
+ unsigned long _unused0;
+ unsigned long config;
+#define NPORT_CFG_G32MD 0x00000001
+#define NPORT_CFG_BWIDTH 0x00000002
+#define NPORT_CFG_ERCVR 0x00000004
+#define NPORT_CFG_BDMSK 0x00000078
+#define NPORT_CFG_GDMSK 0x00000f80
+#define NPORT_CFG_GD0 0x00000080
+#define NPORT_CFG_GD1 0x00000100
+#define NPORT_CFG_GD2 0x00000200
+#define NPORT_CFG_GD3 0x00000400
+#define NPORT_CFG_GD4 0x00000800
+#define NPORT_CFG_GFAINT 0x00001000
+#define NPORT_CFG_TOMSK 0x0000e000
+#define NPORT_CFG_VRMSK 0x00070000
+#define NPORT_CFG_FBTYP 0x00080000
+
+ npireg_t _unused1;
+ npireg_t stat;
+#define NPORT_STAT_VERS 0x00000007
+#define NPORT_STAT_GBUSY 0x00000008
+#define NPORT_STAT_BBUSY 0x00000010
+#define NPORT_STAT_VRINT 0x00000020
+#define NPORT_STAT_VIDINT 0x00000040
+#define NPORT_STAT_GLMSK 0x00001f80
+#define NPORT_STAT_BLMSK 0x0007e000
+#define NPORT_STAT_BFIRQ 0x00080000
+#define NPORT_STAT_GFIRQ 0x00100000
+
+ npireg_t ustat;
+ npireg_t dreset;
+};
+
+struct newport_regs {
+ struct newport_rexregs set;
+ unsigned long _unused0[0x16e];
+ struct newport_rexregs go;
+ unsigned long _unused1[0x22e];
+ struct newport_cregs cset;
+ unsigned long _unused2[0x1ef];
+ struct newport_cregs cgo;
+};
+extern struct newport_regs *npregs;
+
+
+typedef struct {
+ unsigned int drawmode1;
+ unsigned int drawmode0;
+ unsigned int lsmode;
+ unsigned int lspattern;
+ unsigned int lspatsave;
+ unsigned int zpattern;
+ unsigned int colorback;
+ unsigned int colorvram;
+ unsigned int alpharef;
+ unsigned int smask0x;
+ unsigned int smask0y;
+ unsigned int _xstart;
+ unsigned int _ystart;
+ unsigned int _xend;
+ unsigned int _yend;
+ unsigned int xsave;
+ unsigned int xymove;
+ unsigned int bresd;
+ unsigned int bress1;
+ unsigned int bresoctinc1;
+ unsigned int bresrndinc2;
+ unsigned int brese1;
+ unsigned int bress2;
+
+ unsigned int aweight0;
+ unsigned int aweight1;
+ unsigned int colorred;
+ unsigned int coloralpha;
+ unsigned int colorgrn;
+ unsigned int colorblue;
+ unsigned int slopered;
+ unsigned int slopealpha;
+ unsigned int slopegrn;
+ unsigned int slopeblue;
+ unsigned int wrmask;
+ unsigned int hostrw0;
+ unsigned int hostrw1;
+
+ /* configregs */
+
+ unsigned int smask1x;
+ unsigned int smask1y;
+ unsigned int smask2x;
+ unsigned int smask2y;
+ unsigned int smask3x;
+ unsigned int smask3y;
+ unsigned int smask4x;
+ unsigned int smask4y;
+ unsigned int topscan;
+ unsigned int xywin;
+ unsigned int clipmode;
+ unsigned int config;
+
+ /* dcb registers */
+ unsigned int dcbmode;
+ unsigned int dcbdata0;
+ unsigned int dcbdata1;
+} newport_ctx;
+
+/* Reading/writing VC2 registers. */
+#define VC2_REGADDR_INDEX 0x00000000
+#define VC2_REGADDR_IREG 0x00000010
+#define VC2_REGADDR_RAM 0x00000030
+#define VC2_PROTOCOL (NPORT_DMODE_EASACK | 0x00800000 | 0x00040000)
+
+#define VC2_VLINET_ADDR 0x000
+#define VC2_VFRAMET_ADDR 0x400
+#define VC2_CGLYPH_ADDR 0x500
+
+/* Now the Indexed registers of the VC2. */
+#define VC2_IREG_VENTRY 0x00
+#define VC2_IREG_CENTRY 0x01
+#define VC2_IREG_CURSX 0x02
+#define VC2_IREG_CURSY 0x03
+#define VC2_IREG_CCURSX 0x04
+#define VC2_IREG_DENTRY 0x05
+#define VC2_IREG_SLEN 0x06
+#define VC2_IREG_RADDR 0x07
+#define VC2_IREG_VFPTR 0x08
+#define VC2_IREG_VLSPTR 0x09
+#define VC2_IREG_VLIR 0x0a
+#define VC2_IREG_VLCTR 0x0b
+#define VC2_IREG_CTPTR 0x0c
+#define VC2_IREG_WCURSY 0x0d
+#define VC2_IREG_DFPTR 0x0e
+#define VC2_IREG_DLTPTR 0x0f
+#define VC2_IREG_CONTROL 0x10
+#define VC2_IREG_CONFIG 0x20
+
+extern inline void newport_vc2_set(struct newport_regs *regs, unsigned char vc2ireg,
+ unsigned short val)
+{
+ regs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_INDEX | NPORT_DMODE_W3 |
+ NPORT_DMODE_ECINC | VC2_PROTOCOL);
+ regs->set.dcbdata0.all = (vc2ireg << 24) | (val << 8);
+}
+
+extern inline unsigned short newport_vc2_get(struct newport_regs *regs,
+ unsigned char vc2ireg)
+{
+ regs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_INDEX | NPORT_DMODE_W1 |
+ NPORT_DMODE_ECINC | VC2_PROTOCOL);
+ regs->set.dcbdata0.bytes.b3 = vc2ireg;
+ regs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_IREG | NPORT_DMODE_W2 |
+ NPORT_DMODE_ECINC | VC2_PROTOCOL);
+ return regs->set.dcbdata0.hwords.s1;
+}
+
+/* VC2 Control register bits */
+#define VC2_CTRL_EVIRQ 0x0001
+#define VC2_CTRL_EDISP 0x0002
+#define VC2_CTRL_EVIDEO 0x0004
+#define VC2_CTRL_EDIDS 0x0008
+#define VC2_CTRL_ECURS 0x0010
+#define VC2_CTRL_EGSYNC 0x0020
+#define VC2_CTRL_EILACE 0x0040
+#define VC2_CTRL_ECDISP 0x0080
+#define VC2_CTRL_ECCURS 0x0100
+#define VC2_CTRL_ECG64 0x0200
+#define VC2_CTRL_GLSEL 0x0400
+
+/* Controlling the color map on NEWPORT. */
+#define NCMAP_REGADDR_AREG 0x00000000
+#define NCMAP_REGADDR_ALO 0x00000000
+#define NCMAP_REGADDR_AHI 0x00000010
+#define NCMAP_REGADDR_PBUF 0x00000020
+#define NCMAP_REGADDR_CREG 0x00000030
+#define NCMAP_REGADDR_SREG 0x00000040
+#define NCMAP_REGADDR_RREG 0x00000060
+#define NCMAP_PROTOCOL (0x00008000 | 0x00040000 | 0x00800000)
+
+static inline void newport_cmap_setaddr(struct newport_regs *regs,
+ unsigned short addr)
+{
+ regs->set.dcbmode = (NPORT_DMODE_ACMALL | NCMAP_PROTOCOL |
+ NPORT_DMODE_SENDIAN | NPORT_DMODE_ECINC |
+ NCMAP_REGADDR_AREG | NPORT_DMODE_W2);
+ regs->set.dcbdata0.hwords.s1 = addr;
+ regs->set.dcbmode = (NPORT_DMODE_ACMALL | NCMAP_PROTOCOL |
+ NCMAP_REGADDR_PBUF | NPORT_DMODE_W3);
+}
+
+static inline void newport_cmap_setrgb(struct newport_regs *regs,
+ unsigned char red,
+ unsigned char green,
+ unsigned char blue)
+{
+ regs->set.dcbdata0.all =
+ (red << 24) |
+ (green << 16) |
+ (blue << 8);
+}
+
+/* Miscellaneous NEWPORT routines. */
+#define BUSY_TIMEOUT 100000
+static inline int newport_wait(void)
+{
+ int i = 0;
+
+ while(i < BUSY_TIMEOUT)
+ if(!(npregs->cset.stat & NPORT_STAT_GBUSY))
+ break;
+ if(i == BUSY_TIMEOUT)
+ return 1;
+ return 0;
+}
+
+static inline int newport_bfwait(void)
+{
+ int i = 0;
+
+ while(i < BUSY_TIMEOUT)
+ if(!(npregs->cset.stat & NPORT_STAT_BBUSY))
+ break;
+ if(i == BUSY_TIMEOUT)
+ return 1;
+ return 0;
+}
+
+/* newport.c and cons_newport.c routines */
+extern struct graphics_ops *newport_probe (int, const char **);
+
+void newport_save (void *);
+void newport_restore (void *);
+void newport_reset (void);
+int newport_ioctl (int card, int cmd, unsigned long arg);
+
+/*
+ * DCBMODE register defines:
+ */
+
+/* Widht of the data being transfered for each DCBDATA[01] word */
+#define DCB_DATAWIDTH_4 0x0
+#define DCB_DATAWIDTH_1 0x1
+#define DCB_DATAWIDTH_2 0x2
+#define DCB_DATAWIDTH_3 0x3
+
+/* If set, all of DCBDATA will be moved, otherwise only DATAWIDTH bytes */
+#define DCB_ENDATAPACK (1 << 2)
+
+/* Enables DCBCRS auto increment after each DCB transfer */
+#define DCB_ENCRSINC (1 << 3)
+
+/* shift for accessing the control register select address (DBCCRS, 3 bits) */
+#define DCB_CRS_SHIFT 4
+
+/* DCBADDR (4 bits): display bus slave address */
+#define DCB_ADDR_SHIFT 7
+#define DCB_VC2 (0 << DCB_ADDR_SHIFT)
+#define DCB_CMAP_ALL (1 << DCB_ADDR_SHIFT)
+#define DCB_CMAP0 (2 << DCB_ADDR_SHIFT)
+#define DCB_CMAP1 (3 << DCB_ADDR_SHIFT)
+#define DCB_XMAP_ALL (4 << DCB_ADDR_SHIFT)
+#define DCB_XMAP0 (5 << DCB_ADDR_SHIFT)
+#define DCB_XMAP1 (6 << DCB_ADDR_SHIFT)
+#define DCB_BT445 (7 << DCB_ADDR_SHIFT)
+#define DCB_VCC1 (8 << DCB_ADDR_SHIFT)
+#define DCB_VAB1 (9 << DCB_ADDR_SHIFT)
+#define DCB_LG3_BDVERS0 (10 << DCB_ADDR_SHIFT)
+#define DCB_LG3_ICS1562 (11 << DCB_ADDR_SHIFT)
+#define DCB_RESERVED (15 << DCB_ADDR_SHIFT)
+
+/* DCB protocol ack types */
+#define DCB_ENSYNCACK (1 << 11)
+#define DCB_ENASYNCACK (1 << 12)
+
+#define DCB_CSWIDTH_SHIFT 13
+#define DCB_CSHOLD_SHIFT 18
+#define DCB_CSSETUP_SHIFT 23
+
+/* XMAP9 specific defines */
+/* XMAP9 -- registers as seen on the DCBMODE register*/
+# define XM9_CRS_CONFIG (0 << DCB_CRS_SHIFT)
+# define XM9_PUPMODE (1 << 0)
+# define XM9_ODD_PIXEL (1 << 1)
+# define XM9_8_BITPLANES (1 << 2)
+# define XM9_SLOW_DCB (1 << 3)
+# define XM9_VIDEO_RGBMAP_MASK (3 << 4)
+# define XM9_EXPRESS_VIDEO (1 << 6)
+# define XM9_VIDEO_OPTION (1 << 7)
+# define XM9_CRS_REVISION (1 << DCB_CRS_SHIFT)
+# define XM9_CRS_FIFO_AVAIL (2 << DCB_CRS_SHIFT)
+# define XM9_FIFO_0_AVAIL 0
+# define XM9_FIFO_1_AVAIL 1
+# define XM9_FIFO_2_AVAIL 3
+# define XM9_FIFO_3_AVAIL 2
+# define XM9_FIFO_FULL XM9_FIFO_0_AVAIL
+# define XM9_FIFO_EMPTY XM9_FIFO_3_AVAIL
+# define XM9_CRS_CURS_CMAP_MSB (3 << DCB_CRS_SHIFT)
+# define XM9_CRS_PUP_CMAP_MSB (4 << DCB_CRS_SHIFT)
+# define XM9_CRS_MODE_REG_DATA (5 << DCB_CRS_SHIFT)
+# define XM9_CRS_MODE_REG_INDEX (7 << DCB_CRS_SHIFT)
+
+
+#define DCB_CYCLES(setup,hold,width) \
+ ((hold << DCB_CSHOLD_SHIFT) | \
+ (setup << DCB_CSSETUP_SHIFT)| \
+ (width << DCB_CSWIDTH_SHIFT))
+
+#define W_DCB_XMAP9_PROTOCOL DCB_CYCLES (2, 1, 0)
+#define WSLOW_DCB_XMAP9_PROTOCOL DCB_CYCLES (5, 5, 0)
+#define WAYSLOW_DCB_XMAP9_PROTOCOL DCB_CYCLES (12, 12, 0)
+#define R_DCB_XMAP9_PROTOCOL DCB_CYCLES (2, 1, 3)
+
+static inline void
+xmap9FIFOWait (struct newport_regs *rex)
+{
+ rex->set.dcbmode = DCB_XMAP0 | XM9_CRS_FIFO_AVAIL |
+ DCB_DATAWIDTH_1 | R_DCB_XMAP9_PROTOCOL;
+ newport_bfwait ();
+
+ while ((rex->set.dcbdata0.bytes.b3 & 3) != XM9_FIFO_EMPTY)
+ ;
+}
+
+static inline void
+xmap9SetModeReg (struct newport_regs *rex, unsigned int modereg, unsigned int data24, int cfreq)
+{
+ if (cfreq > 119)
+ rex->set.dcbmode = DCB_XMAP_ALL | XM9_CRS_MODE_REG_DATA |
+ DCB_DATAWIDTH_4 | W_DCB_XMAP9_PROTOCOL;
+ else if (cfreq > 59)
+ rex->set.dcbmode = DCB_XMAP_ALL | XM9_CRS_MODE_REG_DATA |
+ DCB_DATAWIDTH_4 | WSLOW_DCB_XMAP9_PROTOCOL;
+ else
+ rex->set.dcbmode = DCB_XMAP_ALL | XM9_CRS_MODE_REG_DATA |
+ DCB_DATAWIDTH_4 | WAYSLOW_DCB_XMAP9_PROTOCOL;
+ rex->set.dcbdata0.all = ((modereg) << 24) | (data24 & 0xffffff);
+}
+
+#endif /* !(_SGI_NEWPORT_H) */
+
+/* $Id: newport.h,v 1.5 1997/09/21 23:06:05 miguel Exp $
* newport.h: Defines and register layout for NEWPORT graphics
* hardware.
*
diff --git a/drivers/sgi/char/rrm.c b/drivers/sgi/char/rrm.c
index b13c79588..507299354 100644
--- a/drivers/sgi/char/rrm.c
+++ b/drivers/sgi/char/rrm.c
@@ -67,3 +67,72 @@ rrm_close (struct inode *inode, struct file *file)
return 0;
}
+/*
+ * Linux Rendering Resource Manager
+ *
+ * Implements the SGI-compatible rendering resource manager.
+ * This takes care of implementing the virtualized video hardware
+ * access required for OpenGL direct rendering.
+ *
+ * Author: Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * Fixes:
+ */
+#include <asm/uaccess.h>
+#include <asm/rrm.h>
+
+int
+rrm_open_rn (int rnid, void *arg)
+{
+ return 0;
+}
+
+int
+rrm_close_rn (int rnid, void *arg)
+{
+ return 0;
+}
+
+int
+rrm_bind_proc_to_rn (int rnid, void *arg)
+{
+ return 0;
+}
+
+typedef int (*rrm_function )(void *arg);
+
+struct {
+ int (*r_fn)(int rnid, void *arg);
+ int arg_size;
+} rrm_functions [] = {
+ { rrm_open_rn, sizeof (struct RRM_OpenRN) },
+ { rrm_close_rn, sizeof (struct RRM_CloseRN) },
+ { rrm_bind_proc_to_rn, sizeof (struct RRM_BindProcToRN) }
+};
+
+#define RRM_FUNCTIONS (sizeof (rrm_functions)/sizeof (rrm_functions [0]))
+
+/* cmd is a number in the range [0..RRM_CMD_LIMIT-RRM_BASE] */
+int
+rrm_command (unsigned int cmd, void *arg)
+{
+ int i, rnid;
+
+ if (cmd > RRM_FUNCTIONS){
+ printk ("Called unimplemented rrm ioctl: %d\n", cmd + RRM_BASE);
+ return -EINVAL;
+ }
+ i = verify_area (VERIFY_READ, arg, rrm_functions [cmd].arg_size);
+ if (i) return i;
+
+ __get_user_ret (rnid, (int *) arg, -EFAULT);
+ return (*(rrm_functions [cmd].r_fn))(rnid, arg);
+}
+
+int
+rrm_close (struct inode *inode, struct file *file)
+{
+ /* This routine is invoked when the device is closed */
+ return 0;
+}
+
diff --git a/drivers/sgi/char/sgicons.c b/drivers/sgi/char/sgicons.c
index 746ac93b4..069b8dbe1 100644
--- a/drivers/sgi/char/sgicons.c
+++ b/drivers/sgi/char/sgicons.c
@@ -159,6 +159,189 @@ void set_vesa_blanking(const unsigned long arg) { }
void vesa_powerdown(void) { }
void set_palette (void) { }
+extern unsigned long video_mem_base, video_screen_size, video_mem_term;
+
+__initfunc(unsigned long con_type_init(unsigned long start_mem, const char **name))
+{
+ extern int serial_console;
+
+ if (serial_console)
+ *name = "NONE";
+ else {
+ gfx_init (name);
+ printk("Video screen size is %08lx at %08lx\n",
+ video_screen_size, start_mem);
+ video_mem_base = start_mem;
+ start_mem += (video_screen_size * 2);
+ video_mem_term = start_mem;
+ }
+ return start_mem;
+}
+
+__initfunc(void con_type_init_finish(void))
+{
+}
+/*
+ * sgicons.c: Setting up and registering console I/O on the SGI.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * This implement a virtual console interface.
+ *
+ * This should be replaced with Gert's all-singing all-dancing
+ * graphics console code in the future
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include "gconsole.h"
+
+/* To make psaux code cleaner */
+int aux_device_present = 0xaa;
+
+/* This is the system graphics console (the first adapter found) */
+struct console_ops *gconsole = 0;
+struct console_ops *real_gconsole = 0;
+
+void
+enable_gconsole (void)
+{
+ if (!gconsole)
+ gconsole = real_gconsole;
+}
+
+void
+disable_gconsole (void)
+{
+ if (gconsole){
+ real_gconsole = gconsole;
+ gconsole = 0;
+ }
+}
+
+void
+register_gconsole (struct console_ops *gc)
+{
+ if (gconsole)
+ return;
+ gconsole = gc;
+}
+
+void
+__set_origin (unsigned short offset)
+{
+ if (gconsole)
+ (*gconsole->set_origin)(offset);
+}
+
+void
+hide_cursor (void)
+{
+
+ if (gconsole)
+ (*gconsole->hide_cursor)();
+}
+
+void
+set_cursor (int currcons)
+{
+ if (gconsole)
+ (*gconsole->set_cursor)(currcons);
+}
+
+void
+get_scrmem (int currcons)
+{
+ if (gconsole)
+ (*gconsole->get_scrmem)(currcons);
+}
+
+void
+set_scrmem (int currcons, long offset)
+{
+ if (gconsole)
+ (*gconsole->set_scrmem)(currcons, offset);
+}
+
+int
+set_get_cmap (unsigned char *arg, int set)
+{
+ if (gconsole)
+ return (*gconsole->set_get_cmap)(arg, set);
+ return 0;
+}
+
+void
+blitc (unsigned short charattr, unsigned long addr)
+{
+ if (gconsole)
+ (*gconsole->blitc)(charattr, addr);
+}
+
+void
+memsetw (void *s, unsigned short c, unsigned int count)
+{
+ if (gconsole)
+ (*gconsole->memsetw)(s, c, count);
+}
+
+void
+memcpyw (unsigned short *to, unsigned short *from, unsigned int count)
+{
+ if (gconsole)
+ (*gconsole->memcpyw)(to, from, count);
+}
+
+int
+con_adjust_height (unsigned long fontheight)
+{
+ return -EINVAL;
+}
+
+int
+set_get_font (char *arg, int set, int ch512)
+{
+ int error, i, line;
+
+ if (!arg)
+ return -EINVAL;
+ error = verify_area (set ? VERIFY_READ : VERIFY_WRITE, (void *) arg,
+ ch512 ? 2* cmapsz : cmapsz);
+ if (error)
+ return error;
+
+ /* download the current font */
+ if (!set) {
+ memset (arg, 0, cmapsz);
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++)
+ __put_user (vga_font [i], arg+(i*32+line));
+ }
+ return 0;
+ }
+
+ /* set the font */
+ for (i = 0; i < 256; i++) {
+ for (line = 0; line < CHAR_HEIGHT; line++) {
+ __get_user(vga_font [i*CHAR_HEIGHT + line],
+ arg + (i * 32 + line));
+ }
+ }
+ return 0;
+}
+
+/*
+ * dummy routines for the VESA blanking code, which is VGA only,
+ * so we don't have to carry that stuff around for the Sparc... */
+void vesa_blank(void) { }
+void vesa_unblank(void) { }
+void set_vesa_blanking(const unsigned long arg) { }
+void vesa_powerdown(void) { }
+void set_palette (void) { }
+
__initfunc(int con_is_present())
{
return 1;
diff --git a/drivers/sgi/char/sgiserial.c b/drivers/sgi/char/sgiserial.c
index c8d4d7e54..b6a3effc6 100644
--- a/drivers/sgi/char/sgiserial.c
+++ b/drivers/sgi/char/sgiserial.c
@@ -1601,6 +1601,2025 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
!(info->flags & ZILOG_CLOSING) && do_clocal)
break;
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sgi_serial *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ /* The zilog lines for the mouse/keyboard must be
+ * opened using their respective drivers.
+ */
+ if ((line < 0) || (line >= NUM_CHANNELS))
+ return -ENODEV;
+ info = zs_soft + line;
+ /* Is the kgdb running over this line? */
+ if (info->kgdb_channel)
+ return -ENODEV;
+ if (serial_paranoia_check(info, tty->device, "rs_open"))
+ return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->count);
+#endif
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+ printk("SGI Zilog8530 serial driver version 1.00\n");
+}
+
+/* Return layout for the requested zs chip number. */
+static inline struct sgi_zslayout *get_zs(int chip)
+{
+ extern struct hpc3_miscregs *hpc3mregs;
+
+ if(chip > 0) {
+ prom_printf("Wheee, bogus zs chip number requested.\n");
+ prom_getchar();
+ romvec->imode();
+ }
+ return (struct sgi_zslayout *) (&hpc3mregs->ser1cmd);
+
+}
+
+extern void register_console(void (*proc)(const char *));
+
+static inline void
+rs_cons_check(struct sgi_serial *ss, int channel)
+{
+ int i, o, io;
+ static consout_registered = 0;
+ static msg_printed = 0;
+
+ i = o = io = 0;
+
+ /* Is this one of the serial console lines? */
+ if((zs_cons_chanout != channel) &&
+ (zs_cons_chanin != channel))
+ return;
+ zs_conschan = ss->zs_channel;
+ zs_consinfo = ss;
+
+ /* Register the console output putchar, if necessary */
+ if((zs_cons_chanout == channel)) {
+ o = 1;
+ /* double whee.. */
+ if(!consout_registered) {
+ register_console(zs_console_print);
+ consout_registered = 1;
+ }
+ }
+
+ /* If this is console input, we handle the break received
+ * status interrupt on this line to mean prom_halt().
+ */
+ if(zs_cons_chanin == channel) {
+ ss->break_abort = 1;
+ i = 1;
+ }
+ if(o && i)
+ io = 1;
+ if(ss->zs_baud != 9562) { /* Don't ask... */
+ prom_printf("BAD console baud rate %d\n", ss->zs_baud);
+ prom_getchar();
+ prom_imode();
+ panic("Console baud rate weirdness");
+ }
+
+ /* Set flag variable for this port so that it cannot be
+ * opened for other uses by accident.
+ */
+ ss->is_cons = 1;
+
+ if(io) {
+ if(!msg_printed) {
+ printk("zs%d: console I/O\n", ((channel>>1)&1));
+ msg_printed = 1;
+ }
+ } else {
+ printk("zs%d: console %s\n", ((channel>>1)&1),
+ (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
+ }
+}
+
+volatile int test_done;
+
+/* rs_init inits the driver */
+int rs_init(void)
+{
+ int chip, channel, i, flags;
+ struct sgi_serial *info;
+
+ /* Setup base handler, and timer table. */
+ init_bh(SERIAL_BH, do_serial_bh);
+ timer_table[RS_TIMER].fn = rs_timer;
+ timer_table[RS_TIMER].expires = 0;
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+ /* SGI: Not all of this is exactly right for us. */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NUM_CHANNELS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_open;
+ serial_driver.close = rs_close;
+ serial_driver.write = rs_write;
+ serial_driver.flush_chars = rs_flush_chars;
+ serial_driver.write_room = rs_write_room;
+ serial_driver.chars_in_buffer = rs_chars_in_buffer;
+ serial_driver.flush_buffer = rs_flush_buffer;
+ serial_driver.ioctl = rs_ioctl;
+ serial_driver.throttle = rs_throttle;
+ serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.set_termios = rs_set_termios;
+ serial_driver.stop = rs_stop;
+ serial_driver.start = rs_start;
+ serial_driver.hangup = rs_hangup;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ save_flags(flags); cli();
+
+ /* Set up our interrupt linked list */
+ zs_chain = &zs_soft[0];
+ zs_soft[0].zs_next = &zs_soft[1];
+ zs_soft[1].zs_next = 0;
+
+ for(chip = 0; chip < NUM_SERIAL; chip++) {
+ /* If we are doing kgdb over one of the channels on
+ * chip zero, kgdb_channel will be set to 1 by the
+ * rs_kgdb_hook() routine below.
+ */
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ zs_soft[(chip*2)].kgdb_channel = 0;
+ zs_soft[(chip*2)+1].kgdb_channel = 0;
+ }
+ /* First, set up channel A on this chip. */
+ channel = chip * 2;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+ zs_soft[channel].cons_mouse = 0;
+ /* If not keyboard/mouse and is console serial
+ * line, then enable receiver interrupts.
+ */
+ if(zs_soft[channel].is_cons) {
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9, (NV | MIE));
+ write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ));
+ write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE));
+ write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB));
+ }
+ /* If this is the kgdb line, enable interrupts because we
+ * now want to receive the 'control-c' character from the
+ * client attached to us asynchronously.
+ */
+ if(zs_soft[channel].kgdb_channel)
+ kgdb_chaninit(&zs_soft[channel], 1,
+ zs_soft[channel].zs_baud);
+
+ /* Now, channel B */
+ channel++;
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+ zs_soft[channel].cons_keyb = 0;
+ /* If console serial line, then enable receiver interrupts. */
+ if(zs_soft[channel].is_cons) {
+ write_zsreg(zs_soft[channel].zs_channel, R1,
+ (EXT_INT_ENAB | INT_ALL_Rx));
+ write_zsreg(zs_soft[channel].zs_channel, R9,
+ (NV | MIE));
+ write_zsreg(zs_soft[channel].zs_channel, R10,
+ (NRZ));
+ write_zsreg(zs_soft[channel].zs_channel, R3,
+ (Rx8|RxENABLE));
+ write_zsreg(zs_soft[channel].zs_channel, R5,
+ (Tx8 | TxENAB | RTS | DTR));
+ }
+ }
+
+ for(info=zs_chain, i=0; info; info = info->zs_next, i++)
+ {
+ info->magic = SERIAL_MAGIC;
+ info->port = (int) info->zs_channel;
+ info->line = i;
+ info->tty = 0;
+ info->irq = zilog_irq;
+ info->custom_divisor = 16;
+ info->close_delay = 50;
+ info->closing_wait = 3000;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios =callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ printk("tty%02d at 0x%04x (irq = %d)", info->line,
+ info->port, info->irq);
+ printk(" is a Zilog8530\n");
+ }
+
+ if (request_irq(zilog_irq, rs_interrupt, (SA_INTERRUPT),
+ "Zilog8530", zs_chain))
+ panic("Unable to attach zs intr\n");
+ restore_flags(flags);
+
+ return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+/* SGI: Unused at this time, just here to make things link. */
+int register_serial(struct serial_struct *req)
+{
+ return -1;
+}
+
+void unregister_serial(int line)
+{
+ return;
+}
+
+/* Hooks for running a serial console. con_init() calls this if the
+ * console is being run over one of the ttya/ttyb serial ports.
+ * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
+ * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
+ * are addressed backwards, channel B is first, then channel A.
+ */
+void
+rs_cons_hook(int chip, int out, int line)
+{
+ int channel;
+
+ if(chip)
+ panic("rs_cons_hook called with chip not zero");
+ if(line != 1 && line != 2)
+ panic("rs_cons_hook called with line not ttya or ttyb");
+ channel = line - 1;
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[channel].zs_channel = zs_channels[channel];
+ zs_soft[channel].change_needed = 0;
+ zs_soft[channel].clk_divisor = 16;
+ zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+ if(out)
+ zs_cons_chanout = ((chip * 2) + channel);
+ else
+ zs_cons_chanin = ((chip * 2) + channel);
+ rs_cons_check(&zs_soft[channel], channel);
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line. The 'tty_num' argument is 0 for /dev/ttyd2 and 1 for
+ * /dev/ttyd1 (yes they are backwards on purpose) which is determined
+ * in setup_arch() from the boot command line flags.
+ */
+void
+rs_kgdb_hook(int tty_num)
+{
+ int chip = 0;
+
+ if(!zs_chips[chip]) {
+ zs_chips[chip] = get_zs(chip);
+ /* Two channels per chip */
+ zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+ zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+ }
+ zs_soft[tty_num].zs_channel = zs_channels[tty_num];
+ zs_kgdbchan = zs_soft[tty_num].zs_channel;
+ zs_soft[tty_num].change_needed = 0;
+ zs_soft[tty_num].clk_divisor = 16;
+ zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]);
+ zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */
+ zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+
+ /* Turn on transmitter/receiver at 8-bits/char */
+ kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+ ZS_CLEARERR(zs_kgdbchan);
+ udelay(5);
+ ZS_CLEARFIFO(zs_kgdbchan);
+}
+/* sgiserial.c: Serial port driver for SGI machines.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sgialib.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/sgihpc.h>
+#include <asm/sgint23.h>
+#include <asm/uaccess.h>
+
+#include "sgiserial.h"
+
+#define NUM_SERIAL 1 /* One chip on board. */
+#define NUM_CHANNELS (NUM_SERIAL * 2)
+
+extern struct wait_queue * keypress_wait;
+
+struct sgi_zslayout *zs_chips[NUM_SERIAL] = { 0, };
+struct sgi_zschannel *zs_channels[NUM_CHANNELS] = { 0, 0, };
+struct sgi_zschannel *zs_conschan;
+struct sgi_zschannel *zs_kgdbchan;
+int zs_nodes[NUM_SERIAL] = { 0, };
+
+struct sgi_serial zs_soft[NUM_CHANNELS];
+struct sgi_serial *zs_chain; /* IRQ servicing chain */
+static int zilog_irq = 21;
+
+struct tty_struct zs_ttys[NUM_CHANNELS];
+/** struct tty_struct *zs_constty; **/
+
+/* Console hooks... */
+static int zs_cons_chanout = 0;
+static int zs_cons_chanin = 0;
+struct sgi_serial *zs_consinfo = 0;
+
+static unsigned char kgdb_regs[16] = {
+ 0, 0, 0, /* write 0, 1, 2 */
+ (Rx8 | RxENABLE), /* write 3 */
+ (X16CLK | SB1 | PAR_EVEN), /* write 4 */
+ (Tx8 | TxENAB), /* write 5 */
+ 0, 0, 0, /* write 6, 7, 8 */
+ (NV), /* write 9 */
+ (NRZ), /* write 10 */
+ (TCBR | RCBR), /* write 11 */
+ 0, 0, /* BRG time constant, write 12 + 13 */
+ (BRENABL), /* write 14 */
+ (DCDIE) /* write 15 */
+};
+
+#define ZS_CLOCK 3672000 /* Zilog input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+static void change_speed(struct sgi_serial *info);
+
+static struct tty_struct *serial_table[NUM_CHANNELS];
+static struct termios *serial_termios[NUM_CHANNELS];
+static struct termios *serial_termios_locked[NUM_CHANNELS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sgi_serial *info,
+ dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: null sun_serial for (%d, %d) in %s\n";
+
+ if (!info) {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 0 };
+
+/*
+ * Reading and writing Zilog8530 registers. The delays are to make this
+ * driver work on the Sun4 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ */
+static inline unsigned char read_zsreg(struct sgi_zschannel *channel, unsigned char reg)
+{
+ unsigned char retval;
+ volatile unsigned char junk;
+
+ udelay(2);
+ channel->control = reg;
+ junk = ioc_icontrol->istat0;
+ udelay(1);
+ retval = channel->control;
+ return retval;
+}
+
+static inline void write_zsreg(struct sgi_zschannel *channel, unsigned char reg, unsigned char value)
+{
+ volatile unsigned char junk;
+
+ udelay(2);
+ channel->control = reg;
+ junk = ioc_icontrol->istat0;
+ udelay(1);
+ channel->control = value;
+ junk = ioc_icontrol->istat0;
+ return;
+}
+
+static inline void load_zsregs(struct sgi_zschannel *channel, unsigned char *regs)
+{
+ ZS_CLEARERR(channel);
+ ZS_CLEARFIFO(channel);
+ /* Load 'em up */
+ write_zsreg(channel, R4, regs[R4]);
+ write_zsreg(channel, R10, regs[R10]);
+ write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
+ write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+ write_zsreg(channel, R1, regs[R1]);
+ write_zsreg(channel, R9, regs[R9]);
+ write_zsreg(channel, R11, regs[R11]);
+ write_zsreg(channel, R12, regs[R12]);
+ write_zsreg(channel, R13, regs[R13]);
+ write_zsreg(channel, R14, regs[R14]);
+ write_zsreg(channel, R15, regs[R15]);
+ write_zsreg(channel, R3, regs[R3]);
+ write_zsreg(channel, R5, regs[R5]);
+ return;
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct sgi_serial *ss, int set)
+{
+ if(set) {
+ ss->curregs[5] |= (RTS | DTR);
+ ss->pendregs[5] = ss->curregs[5];
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ } else {
+ ss->curregs[5] &= ~(RTS | DTR);
+ ss->pendregs[5] = ss->curregs[5];
+ write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+ }
+ return;
+}
+
+static inline void kgdb_chaninit(struct sgi_serial *ss, int intson, int bps)
+{
+ int brg;
+
+ if(intson) {
+ kgdb_regs[R1] = INT_ALL_Rx;
+ kgdb_regs[R9] |= MIE;
+ } else {
+ kgdb_regs[R1] = 0;
+ kgdb_regs[R9] &= ~MIE;
+ }
+ brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+ kgdb_regs[R12] = (brg & 255);
+ kgdb_regs[R13] = ((brg >> 8) & 255);
+ load_zsregs(ss->zs_channel, kgdb_regs);
+}
+
+/* Utility routines for the Zilog */
+static inline int get_zsbaud(struct sgi_serial *ss)
+{
+ struct sgi_zschannel *channel = ss->zs_channel;
+ int brg;
+
+ /* The baud rate is split up between two 8-bit registers in
+ * what is termed 'BRG time constant' format in my docs for
+ * the chip, it is a function of the clk rate the chip is
+ * receiving which happens to be constant.
+ */
+ brg = ((read_zsreg(channel, 13)&0xff) << 8);
+ brg |= (read_zsreg(channel, 12)&0xff);
+ return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->curregs[5] & TxENAB) {
+ info->curregs[5] &= ~TxENAB;
+ info->pendregs[5] &= ~TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_start"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+ info->curregs[5] |= TxENAB;
+ info->pendregs[5] = info->curregs[5];
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+}
+
+/* Drop into either the boot monitor or kadb upon receiving a break
+ * from keyboard/console input.
+ */
+static void batten_down_hatches(void)
+{
+ prom_imode();
+#if 0
+ /* If we are doing kadb, we call the debugger
+ * else we just drop into the boot monitor.
+ * Note that we must flush the user windows
+ * first before giving up control.
+ */
+ printk("\n");
+ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
+ (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
+ sp_enter_debugger();
+ else
+ prom_halt();
+
+ /* XXX We want to notify the keyboard driver that all
+ * XXX keys are in the up state or else weird things
+ * XXX happen...
+ */
+#endif
+ return;
+}
+
+/* On receive, this clears errors and the receiver interrupts */
+static inline void rs_recv_clear(struct sgi_zschannel *zsc)
+{
+ volatile unsigned char junk;
+
+ udelay(2);
+ zsc->control = ERR_RES;
+ junk = ioc_icontrol->istat0;
+ udelay(2);
+ zsc->control = RES_H_IUS;
+ junk = ioc_icontrol->istat0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct sgi_serial *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern void set_async_breakpoint(unsigned int epc);
+#endif
+
+static _INLINE_ void receive_chars(struct sgi_serial *info, struct pt_regs *regs)
+{
+ struct tty_struct *tty = info->tty;
+ volatile unsigned char junk;
+ unsigned char ch, stat;
+
+ udelay(2);
+ ch = info->zs_channel->data;
+ junk = ioc_icontrol->istat0;
+ udelay(2);
+ stat = read_zsreg(info->zs_channel, R1);
+
+ /* If this is the console keyboard, we need to handle
+ * L1-A's here.
+ */
+ if(info->is_cons) {
+ if(ch==0) { /* whee, break received */
+ batten_down_hatches();
+ rs_recv_clear(info->zs_channel);
+ return;
+ } else if (ch == 1) {
+ show_state();
+ return;
+ } else if (ch == 2) {
+ show_buffers();
+ return;
+ }
+ /* It is a 'keyboard interrupt' ;-) */
+ wake_up(&keypress_wait);
+ }
+ /* Look for kgdb 'stop' character, consult the gdb documentation
+ * for remote target debugging and arch/sparc/kernel/sparc-stub.c
+ * to see how all this works.
+ */
+#ifdef CONFIG_REMOTE_DEBUG
+ if((info->kgdb_channel) && (ch =='\003')) {
+ set_async_breakpoint(read_32bit_cp0_register(CP0_EPC));
+ goto clear_and_exit;
+ }
+#endif
+ if(!tty)
+ goto clear_and_exit;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ queue_task(&tty->flip.tqueue, &tq_timer);
+ tty->flip.count++;
+ if(stat & PAR_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ else if(stat & Rx_OVR)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ else if(stat & CRC_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+ else
+ *tty->flip.flag_buf_ptr++ = 0; /* XXX */
+ *tty->flip.char_buf_ptr++ = ch;
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+
+clear_and_exit:
+ rs_recv_clear(info->zs_channel);
+ return;
+}
+
+static _INLINE_ void transmit_chars(struct sgi_serial *info)
+{
+ volatile unsigned char junk;
+
+ /* P3: In theory we have to test readiness here because a
+ * serial console can clog the chip through rs_put_char().
+ * David did not do this. I think he relies on 3-chars FIFO in 8530.
+ * Let's watch for lost _output_ characters. XXX
+ */
+
+ /* SGI ADDENDUM: On most SGI machines, the Zilog does possess
+ * a 16 or 17 byte fifo, so no worries. -dm
+ */
+
+ if (info->x_char) {
+ /* Send next char */
+ udelay(2);
+ info->zs_channel->data = info->x_char;
+ junk = ioc_icontrol->istat0;
+
+ info->x_char = 0;
+ goto clear_and_return;
+ }
+
+ if((info->xmit_cnt <= 0) || info->tty->stopped) {
+ /* That's peculiar... */
+ udelay(2);
+ info->zs_channel->control = RES_Tx_P;
+ junk = ioc_icontrol->istat0;
+ goto clear_and_return;
+ }
+
+ /* Send char */
+ udelay(2);
+ info->zs_channel->data = info->xmit_buf[info->xmit_tail++];
+ junk = ioc_icontrol->istat0;
+
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+ if(info->xmit_cnt <= 0) {
+ udelay(2);
+ info->zs_channel->control = RES_Tx_P;
+ junk = ioc_icontrol->istat0;
+ goto clear_and_return;
+ }
+
+clear_and_return:
+ /* Clear interrupt */
+ udelay(2);
+ info->zs_channel->control = RES_H_IUS;
+ junk = ioc_icontrol->istat0;
+ return;
+}
+
+static _INLINE_ void status_handle(struct sgi_serial *info)
+{
+ volatile unsigned char junk;
+ unsigned char status;
+
+ /* Get status from Read Register 0 */
+ udelay(2);
+ status = info->zs_channel->control;
+ junk = ioc_icontrol->istat0;
+ /* Clear status condition... */
+ udelay(2);
+ info->zs_channel->control = RES_EXT_INT;
+ junk = ioc_icontrol->istat0;
+ /* Clear the interrupt */
+ udelay(2);
+ info->zs_channel->control = RES_H_IUS;
+ junk = ioc_icontrol->istat0;
+
+#if 0
+ if(status & DCD) {
+ if((info->tty->termios->c_cflag & CRTSCTS) &&
+ ((info->curregs[3] & AUTO_ENAB)==0)) {
+ info->curregs[3] |= AUTO_ENAB;
+ info->pendregs[3] |= AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ } else {
+ if((info->curregs[3] & AUTO_ENAB)) {
+ info->curregs[3] &= ~AUTO_ENAB;
+ info->pendregs[3] &= ~AUTO_ENAB;
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ }
+ }
+#endif
+ /* Whee, if this is console input and this is a
+ * 'break asserted' status change interrupt, call
+ * the boot prom.
+ */
+ if((status & BRK_ABRT) && info->break_abort)
+ batten_down_hatches();
+
+ /* XXX Whee, put in a buffer somewhere, the status information
+ * XXX whee whee whee... Where does the information go...
+ */
+ return;
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct sgi_serial * info = (struct sgi_serial *) dev_id;
+ unsigned char zs_intreg;
+
+ zs_intreg = read_zsreg(info->zs_channel, 3);
+
+ /* NOTE: The read register 3, which holds the irq status,
+ * does so for both channels on each chip. Although
+ * the status value itself must be read from the A
+ * channel and is only valid when read from channel A.
+ * Yes... broken hardware...
+ */
+#define CHAN_A_IRQMASK (CHARxIP | CHATxIP | CHAEXT)
+#define CHAN_B_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
+
+ /* *** Chip 1 *** */
+ /* Channel A -- /dev/ttya, could be the console */
+ if(zs_intreg & CHAN_A_IRQMASK) {
+ if (zs_intreg & CHARxIP)
+ receive_chars(info, regs);
+ if (zs_intreg & CHATxIP)
+ transmit_chars(info);
+ if (zs_intreg & CHAEXT)
+ status_handle(info);
+ }
+
+ info=info->zs_next;
+
+ /* Channel B -- /dev/ttyb, could be the console */
+ if(zs_intreg & CHAN_B_IRQMASK) {
+ if (zs_intreg & CHBRxIP)
+ receive_chars(info, regs);
+ if (zs_intreg & CHBTxIP)
+ transmit_chars(info);
+ if (zs_intreg & CHBEXT)
+ status_handle(info);
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct sgi_serial *info = (struct sgi_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct sgi_serial *info = (struct sgi_serial *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+
+/*
+ * This subroutine is called when the RS_TIMER goes off. It is used
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
+ */
+
+static void rs_timer(void)
+{
+ printk("rs_timer called\n");
+ prom_halt();
+ return;
+}
+
+static int startup(struct sgi_serial * info)
+{
+ volatile unsigned char junk;
+ unsigned long flags;
+
+ if (info->flags & ZILOG_INITIALIZED)
+ return 0;
+
+ if (!info->xmit_buf) {
+ info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+ if (!info->xmit_buf)
+ return -ENOMEM;
+ }
+
+ save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+#endif
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ ZS_CLEARFIFO(info->zs_channel);
+ info->xmit_fifo_size = 1;
+
+ /*
+ * Clear the interrupt registers.
+ */
+ udelay(2);
+ info->zs_channel->control = ERR_RES;
+ junk = ioc_icontrol->istat0;
+ udelay(2);
+ info->zs_channel->control = RES_H_IUS;
+ junk = ioc_icontrol->istat0;
+
+ /*
+ * Now, initialize the Zilog
+ */
+ zs_rtsdtr(info, 1);
+
+ /*
+ * Finally, enable sequencing and interrupts
+ */
+ info->curregs[1] |= (info->curregs[1] & ~0x18) | (EXT_INT_ENAB|INT_ALL_Rx);
+ info->pendregs[1] = info->curregs[1];
+ info->curregs[3] |= (RxENABLE | Rx8);
+ info->pendregs[3] = info->curregs[3];
+ /* We enable Tx interrupts as needed. */
+ info->curregs[5] |= (TxENAB | Tx8);
+ info->pendregs[5] = info->curregs[5];
+ info->curregs[9] |= (NV | MIE);
+ info->pendregs[9] = info->curregs[9];
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ udelay(2);
+ info->zs_channel->control = ERR_RES;
+ junk = ioc_icontrol->istat0;
+ udelay(2);
+ info->zs_channel->control = RES_H_IUS;
+ junk = ioc_icontrol->istat0;
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * Set up serial timers...
+ */
+#if 0 /* Works well and stops the machine. */
+ timer_table[RS_TIMER].expires = jiffies + 2;
+ timer_active |= 1 << RS_TIMER;
+#endif
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ info->flags |= ZILOG_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct sgi_serial * info)
+{
+ unsigned long flags;
+
+ if (!(info->flags & ZILOG_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ZILOG_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sgi_serial *info)
+{
+ unsigned short port;
+ unsigned cflag;
+ int i;
+ int brg;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+ if (!(port = info->port))
+ return;
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ /* XXX CBAUDEX is not obeyed.
+ * It is impossible at a 32bits SPARC.
+ * But we have to report this to user ... someday.
+ */
+ i = B9600;
+ }
+ if (i == 0) {
+ /* XXX B0, hangup the line. */
+ do_serial_hangup(info);
+ } else if (baud_table[i]) {
+ info->zs_baud = baud_table[i];
+ info->clk_divisor = 16;
+
+ info->curregs[4] = X16CLK;
+ info->curregs[11] = TCBR | RCBR;
+ brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+ info->curregs[12] = (brg & 255);
+ info->curregs[13] = ((brg >> 8) & 255);
+ info->curregs[14] = BRENABL;
+ }
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5:
+ info->curregs[3] &= ~(0xc0);
+ info->curregs[3] |= Rx5;
+ info->pendregs[3] = info->curregs[3];
+ info->curregs[5] &= ~(0xe0);
+ info->curregs[5] |= Tx5;
+ info->pendregs[5] = info->curregs[5];
+ break;
+ case CS6:
+ info->curregs[3] &= ~(0xc0);
+ info->curregs[3] |= Rx6;
+ info->pendregs[3] = info->curregs[3];
+ info->curregs[5] &= ~(0xe0);
+ info->curregs[5] |= Tx6;
+ info->pendregs[5] = info->curregs[5];
+ break;
+ case CS7:
+ info->curregs[3] &= ~(0xc0);
+ info->curregs[3] |= Rx7;
+ info->pendregs[3] = info->curregs[3];
+ info->curregs[5] &= ~(0xe0);
+ info->curregs[5] |= Tx7;
+ info->pendregs[5] = info->curregs[5];
+ break;
+ case CS8:
+ default: /* defaults to 8 bits */
+ info->curregs[3] &= ~(0xc0);
+ info->curregs[3] |= Rx8;
+ info->pendregs[3] = info->curregs[3];
+ info->curregs[5] &= ~(0xe0);
+ info->curregs[5] |= Tx8;
+ info->pendregs[5] = info->curregs[5];
+ break;
+ }
+ info->curregs[4] &= ~(0x0c);
+ if (cflag & CSTOPB) {
+ info->curregs[4] |= SB2;
+ } else {
+ info->curregs[4] |= SB1;
+ }
+ info->pendregs[4] = info->curregs[4];
+ if (cflag & PARENB) {
+ info->curregs[4] |= PAR_ENA;
+ info->pendregs[4] |= PAR_ENA;
+ } else {
+ info->curregs[4] &= ~PAR_ENA;
+ info->pendregs[4] &= ~PAR_ENA;
+ }
+ if (!(cflag & PARODD)) {
+ info->curregs[4] |= PAR_EVEN;
+ info->pendregs[4] |= PAR_EVEN;
+ } else {
+ info->curregs[4] &= ~PAR_EVEN;
+ info->pendregs[4] &= ~PAR_EVEN;
+ }
+
+ /* Load up the new values */
+ load_zsregs(info->zs_channel, info->curregs);
+
+ return;
+}
+
+/* This is for console output over ttya/ttyb */
+static void rs_put_char(char ch)
+{
+ struct sgi_zschannel *chan = zs_conschan;
+ volatile unsigned char junk;
+ int flags, loops = 0;
+
+ save_flags(flags); cli();
+ while(((junk = chan->control) & Tx_BUF_EMP)==0 && loops < 10000) {
+ loops++;
+ udelay(2);
+ }
+
+ udelay(2);
+ chan->data = ch;
+ junk = ioc_icontrol->istat0;
+ restore_flags(flags);
+}
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+ struct sgi_zschannel *chan = zs_kgdbchan;
+ volatile unsigned char junk;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ udelay(2);
+ while((chan->control & Tx_BUF_EMP)==0)
+ udelay(2);
+
+ udelay(2);
+ chan->data = kgdb_char;
+ junk = ioc_icontrol->istat0;
+ restore_flags(flags);
+}
+
+char getDebugChar(void)
+{
+ struct sgi_zschannel *chan = zs_kgdbchan;
+ unsigned char junk;
+
+ while((chan->control & Rx_CH_AV)==0)
+ udelay(2);
+
+ junk = ioc_icontrol->istat0;
+ udelay(2);
+ return chan->data;
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void rs_fair_output(void)
+{
+ int left; /* Output no more than that */
+ unsigned long flags;
+ struct sgi_serial *info = zs_consinfo;
+ volatile unsigned char junk;
+ char c;
+
+ if (info == 0) return;
+ if (info->xmit_buf == 0) return;
+
+ save_flags(flags); cli();
+ left = info->xmit_cnt;
+ while (left != 0) {
+ c = info->xmit_buf[info->xmit_tail];
+ info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ restore_flags(flags);
+
+ rs_put_char(c);
+
+ save_flags(flags); cli();
+ left = MIN(info->xmit_cnt, left-1);
+ }
+
+ /* Last character is being transmitted now (hopefully). */
+ udelay(2);
+ zs_conschan->control = RES_Tx_P;
+ junk = ioc_icontrol->istat0;
+
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * zs_console_print is registered for printk.
+ */
+static void zs_console_print(const char *p)
+{
+ char c;
+
+ while((c=*(p++)) != 0) {
+ if(c == '\n')
+ rs_put_char('\r');
+ rs_put_char(c);
+ }
+
+ /* Comment this if you want to have a strict interrupt-driven output */
+ rs_fair_output();
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ /* Enable transmitter */
+ save_flags(flags); cli();
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ info->pendregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+ /*
+ * Send a first (bootstrapping) character. A best solution is
+ * to call transmit_chars() here which handles output in a
+ * generic way. Current transmit_chars() not only transmits,
+ * but resets interrupts also what we do not desire here.
+ * XXX Discuss with David.
+ */
+ if (info->zs_channel->control & Tx_BUF_EMP) {
+ volatile unsigned char junk;
+
+ /* Send char */
+ udelay(2);
+ info->zs_channel->data = info->xmit_buf[info->xmit_tail++];
+ junk = ioc_icontrol->istat0;
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt--;
+ }
+ restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, total = 0;
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf)
+ return 0;
+
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ down(&tmp_buf_sem);
+ copy_from_user(tmp_buf, buf, c);
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ up(&tmp_buf_sem);
+ } else
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ !(info->curregs[5] & TxENAB)) {
+ /* Enable transmitter */
+ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ info->curregs[5] |= TxENAB;
+ info->pendregs[5] |= TxENAB;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ }
+ restore_flags(flags);
+ return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+ return;
+ cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ sti();
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+
+ /* Turn off RTS line */
+ cli();
+ info->curregs[5] &= ~RTS;
+ info->pendregs[5] &= ~RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+
+ /* Assert RTS line */
+ cli();
+ info->curregs[5] |= RTS;
+ info->pendregs[5] |= RTS;
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sgi_serial * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = info->port;
+ tmp.irq = info->irq;
+ tmp.flags = info->flags;
+ tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
+ tmp.custom_divisor = info->custom_divisor;
+ return copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+}
+
+static int set_serial_info(struct sgi_serial * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct sgi_serial old_info;
+ int retval = 0;
+
+ if (!new_info)
+ return -EFAULT;
+ copy_from_user(&new_serial,new_info,sizeof(new_serial));
+ old_info = *info;
+
+ if (!suser()) {
+ if ((new_serial.baud_base != info->baud_base) ||
+ (new_serial.type != info->type) ||
+ (new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ~ZILOG_USR_MASK) !=
+ (info->flags & ~ZILOG_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+ (new_serial.flags & ZILOG_USR_MASK));
+ info->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->baud_base = new_serial.baud_base;
+ info->flags = ((info->flags & ~ZILOG_FLAGS) |
+ (new_serial.flags & ZILOG_FLAGS));
+ info->type = new_serial.type;
+ info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+ retval = startup(info);
+ return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct sgi_serial * info, unsigned int *value)
+{
+ volatile unsigned char junk;
+ unsigned char status;
+
+ cli();
+ udelay(2);
+ status = info->zs_channel->control;
+ junk = ioc_icontrol->istat0;
+ sti();
+ return put_user(status,value);
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct sgi_serial * info, int duration)
+{
+ if (!info->port)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+ cli();
+ write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
+ schedule();
+ write_zsreg(info->zs_channel, 5, info->curregs[5]);
+ sti();
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct sgi_serial * info = (struct sgi_serial *)tty->driver_data;
+ int retval;
+
+ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ send_break(info, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ return 0;
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
+ if (error)
+ return error;
+ put_user(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
+ return 0;
+ case TIOCSSOFTCAR:
+ error = get_user(arg, (unsigned long *)arg);
+ if (error)
+ return error;
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCGSERIAL:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_struct));
+ if (error)
+ return error;
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERGETLSR: /* Get line status register */
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ else
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct sgi_serial));
+ if (error)
+ return error;
+ copy_to_user((struct sun_serial *) arg,
+ info, sizeof(struct sgi_serial));
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+
+ change_speed(info);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ rs_start(tty);
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * ZILOG structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sgi_serial * info = (struct sgi_serial *)tty->driver_data;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+ return;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("rs_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk("rs_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ZILOG_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ /** if (!info->iscons) ... **/
+ info->curregs[3] &= ~RxENABLE;
+ info->pendregs[3] = info->curregs[3];
+ write_zsreg(info->zs_channel, 3, info->curregs[3]);
+ info->curregs[1] &= ~(0x18);
+ info->pendregs[1] = info->curregs[1];
+ write_zsreg(info->zs_channel, 1, info->curregs[1]);
+ ZS_CLEARFIFO(info->zs_channel);
+
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (tty->ldisc.num != ldiscs[N_TTY].num) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ tty->ldisc = ldiscs[N_TTY];
+ tty->termios->c_line = N_TTY;
+ if (tty->ldisc.open)
+ (tty->ldisc.open)(tty);
+ }
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+ ZILOG_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ restore_flags(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+ struct sgi_serial * info = (struct sgi_serial *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+ return;
+
+ rs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sgi_serial *info)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval;
+ int do_clocal = 0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ZILOG_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ZILOG_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ (info->flags & ZILOG_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ZILOG_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ZILOG_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ZILOG_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ info->line, info->count);
+#endif
+ info->count--;
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
+ zs_rtsdtr(info, 1);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ZILOG_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+ !(info->flags & ZILOG_CLOSING) && do_clocal)
+ break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
diff --git a/drivers/sgi/char/sgiserial.h b/drivers/sgi/char/sgiserial.h
index 35f18cab7..9a6c589d5 100644
--- a/drivers/sgi/char/sgiserial.h
+++ b/drivers/sgi/char/sgiserial.h
@@ -442,3 +442,447 @@ extern inline void ZS_CLEARFIFO(struct sgi_zschannel *channel)
#endif
#endif /* !(_SPARC_SERIAL_H) */
+/* sgiserial.h: Definitions for the SGI Zilog85C30 serial driver.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+#ifndef _SGI_SERIAL_H
+#define _SGI_SERIAL_H
+
+/* Just one channel */
+struct sgi_zschannel {
+#ifdef __MIPSEB__
+ volatile unsigned char unused0[3];
+ volatile unsigned char control;
+ volatile unsigned char unused1[3];
+ volatile unsigned char data;
+#else /* __MIPSEL__ */
+ volatile unsigned char control;
+ volatile unsigned char unused0[3];
+ volatile unsigned char data;
+ volatile unsigned char unused1[3];
+#endif
+};
+
+/* The address space layout for each zs chip. Yes they are
+ * backwards.
+ */
+struct sgi_zslayout {
+ struct sgi_zschannel channelB;
+ struct sgi_zschannel channelA;
+};
+
+#define NUM_ZSREGS 16
+
+struct serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ int reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output. 65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF 0
+#define ZILOG_CLOSING_WAIT_NONE 65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK 0x0030
+#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct sgi_serial {
+ struct sgi_serial *zs_next; /* For IRQ servicing chain */
+ struct sgi_zschannel *zs_channel; /* Channel registers */
+ unsigned char read_reg_zero;
+
+ char soft_carrier; /* Use soft carrier on this channel */
+ char cons_keyb; /* Channel runs the keyboard */
+ char cons_mouse; /* Channel runs the mouse */
+ char break_abort; /* Is serial console in, so process brk/abrt */
+ char kgdb_channel; /* Kgdb is running on this channel */
+ char is_cons; /* Is this our console. */
+
+ /* We need to know the current clock divisor
+ * to read the bps rate the chip has currently
+ * loaded.
+ */
+ unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
+ int zs_baud;
+
+ /* Current write register values */
+ unsigned char curregs[NUM_ZSREGS];
+
+ /* Values we need to set next opportunity */
+ unsigned char pendregs[NUM_ZSREGS];
+
+ char change_needed;
+
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags; /* defined in tty.h */
+ int type; /* UART type */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENABLE 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+
+#define PAR_ENA 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENABL 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC_HUNT 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define CRC_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc inlines */
+extern inline void ZS_CLEARERR(struct sgi_zschannel *channel)
+{
+ volatile unsigned char junk;
+
+ udelay(2);
+ channel->control = ERR_RES;
+ junk = ioc_icontrol->istat0;
+}
+
+extern inline void ZS_CLEARFIFO(struct sgi_zschannel *channel)
+{
+ volatile unsigned char junk;
+
+ udelay(2);
+ junk = channel->data;
+ udelay(2); junk = ioc_icontrol->istat0;
+ junk = channel->data;
+ udelay(2); junk = ioc_icontrol->istat0;
+ junk = channel->data;
+ udelay(2); junk = ioc_icontrol->istat0;
+}
+
+#if 0
+
+#define ZS_CLEARERR(channel) (channel->control = ERR_RES)
+#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); \
+ garbage = channel->data; \
+ udelay(2); } while(0)
+
+#endif
+
+#endif /* !(_SPARC_SERIAL_H) */
diff --git a/drivers/sgi/char/shmiq.c b/drivers/sgi/char/shmiq.c
index 37a8b9bf9..623f34abe 100644
--- a/drivers/sgi/char/shmiq.c
+++ b/drivers/sgi/char/shmiq.c
@@ -47,6 +47,466 @@
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/major.h>
+
+#include <asm/smp_lock.h>
+#include <asm/shmiq.h>
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+#include <asm/poll.h>
+#include "graphics.h"
+
+/* we are not really getting any more than a few files in the shmiq */
+#define MAX_SHMIQ_DEVS 10
+
+/*
+ * One per X server running, not going to get very big.
+ * Even if we have this we now assume just 1 /dev/qcntl can be
+ * active, I need to find how this works on multi-headed machines.
+ */
+#define MAX_SHMI_QUEUES 4
+
+static struct {
+ int used;
+ struct file *filp;
+ struct shmiqsetcpos cpos;
+} shmiq_pushed_devices [MAX_SHMIQ_DEVS];
+
+/* /dev/qcntlN attached memory regions, location and size of the event queue */
+static struct {
+ int opened; /* if this device has been opened */
+ void *shmiq_vaddr; /* mapping in kernel-land */
+ int tail; /* our copy of the shmiq->tail */
+ int events;
+ int mapped;
+
+ struct wait_queue *proc_list;
+ struct fasync_struct *fasync;
+} shmiqs [MAX_SHMI_QUEUES];
+
+void
+shmiq_push_event (struct shmqevent *e)
+{
+ struct sharedMemoryInputQueue *s;
+ int device = 0; /* FIXME: here is the assumption /dev/shmiq == /dev/qcntl0 */
+ int tail_next;
+
+ if (!shmiqs [device].mapped)
+ return;
+ s = shmiqs [device].shmiq_vaddr;
+
+ s->flags = 0;
+ if (s->tail != shmiqs [device].tail){
+ s->flags |= SHMIQ_CORRUPTED;
+ return;
+ }
+ tail_next = (s->tail + 1) % (shmiqs [device].events);
+
+ if (tail_next == s->head){
+ s->flags |= SHMIQ_OVERFLOW;
+ return;
+ }
+
+ e->un.time = jiffies;
+ s->events [s->tail] = *e;
+ printk ("KERNEL: dev=%d which=%d type=%d flags=%d\n",
+ e->data.device, e->data.which, e->data.type, e->data.flags);
+ s->tail = tail_next;
+ shmiqs [device].tail = tail_next;
+ if (shmiqs [device].fasync)
+ kill_fasync (shmiqs [device].fasync, SIGIO);
+ wake_up_interruptible (&shmiqs [device].proc_list);
+}
+
+static int
+shmiq_manage_file (struct file *filp)
+{
+ int i;
+
+ if (!filp->f_op || !filp->f_op->ioctl)
+ return -ENOSR;
+
+ for (i = 0; i < MAX_SHMIQ_DEVS; i++){
+ if (shmiq_pushed_devices [i].used)
+ continue;
+ if ((*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_ON, i) != 0)
+ return -ENOSR;
+ shmiq_pushed_devices [i].used = 1;
+ shmiq_pushed_devices [i].filp = filp;
+ shmiq_pushed_devices [i].cpos.x = 0;
+ shmiq_pushed_devices [i].cpos.y = 0;
+ return i;
+ }
+ return -ENOSR;
+}
+
+static int
+shmiq_forget_file (unsigned long fdes)
+{
+ struct file *filp;
+
+ if (fdes > MAX_SHMIQ_DEVS)
+ return -EINVAL;
+
+ if (!shmiq_pushed_devices [fdes].used)
+ return -EINVAL;
+
+ filp = shmiq_pushed_devices [fdes].filp;
+ if (filp){
+ (*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_OFF, 0);
+ shmiq_pushed_devices [fdes].filp = 0;
+ fput (filp);
+ }
+ shmiq_pushed_devices [fdes].used = 0;
+
+ return 0;
+}
+
+static int
+shmiq_sioc (int device, int cmd, struct strioctl *s)
+{
+ switch (cmd){
+ case QIOCGETINDX:
+ /*
+ * Ok, we just return the index they are providing us
+ */
+ printk ("QIOCGETINDX: returning %d\n", *(int *)s->ic_dp);
+ return 0;
+
+ case QIOCIISTR: {
+ struct muxioctl *mux = (struct muxioctl *) s->ic_dp;
+
+ printk ("Double indirect ioctl: [%d, %x\n", mux->index, mux->realcmd);
+ return -EINVAL;
+ }
+
+ case QIOCSETCPOS: {
+ if (copy_from_user (&shmiq_pushed_devices [device].cpos, s->ic_dp,
+ sizeof (struct shmiqsetcpos)))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ printk ("Unknown I_STR request for shmiq device: 0x%x\n", cmd);
+ return -EINVAL;
+}
+
+static int
+shmiq_ioctl (struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg)
+{
+ struct file *file;
+ struct strioctl sioc;
+ int v;
+
+ switch (cmd){
+ /*
+ * They are giving us the file descriptor for one
+ * of their streams devices
+ */
+
+ case I_LINK:
+ file = fget (arg);
+ if (!file)
+ goto bad_file;
+
+ v = shmiq_manage_file (file);
+ return v;
+
+ /*
+ * Remove a device from our list of managed
+ * stream devices
+ */
+ case I_UNLINK:
+ v = shmiq_forget_file (arg);
+ return v;
+
+ case I_STR:
+ v = get_sioc (&sioc, arg);
+ if (v)
+ return v;
+
+ /* FIXME: This forces device = 0 */
+ return shmiq_sioc (0, sioc.ic_cmd, &sioc);
+ }
+
+ return -EINVAL;
+bad_file:
+ unlock_kernel ();
+ return -EBADF;
+}
+
+extern sys_munmap(unsigned long addr, size_t len);
+
+static int
+qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg, int minor)
+{
+ struct shmiqreq req;
+ struct vm_area_struct *vma;
+ int v;
+
+ switch (cmd){
+ /*
+ * The address space is already mapped as a /dev/zero
+ * mapping. FIXME: check that /dev/zero is what the user
+ * had mapped before :-)
+ */
+ case QIOCATTACH: {
+ unsigned long vaddr;
+ int s;
+
+ v = verify_area (VERIFY_READ, (void *) arg, sizeof (struct shmiqreq));
+ if (v)
+ return v;
+ if (copy_from_user (&req, (void *) arg, sizeof (req)))
+ return -EFAULT;
+ /* Do not allow to attach to another region if it has been already attached */
+ if (shmiqs [minor].mapped){
+ printk ("SHMIQ:The thingie is already mapped\n");
+ return -EINVAL;
+ }
+
+ vaddr = (unsigned long) req.user_vaddr;
+ vma = find_vma (current->mm, vaddr);
+ if (!vma){
+ printk ("SHMIQ: could not find %lx the vma\n", vaddr);
+ return -EINVAL;
+ }
+ s = req.arg * sizeof (struct shmqevent) + sizeof (struct sharedMemoryInputQueue);
+ v = sys_munmap (vaddr, s);
+ do_mmap (filp, vaddr, s, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 0);
+ shmiqs [minor].events = req.arg;
+ shmiqs [minor].mapped = 1;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+unsigned long
+shmiq_nopage (struct vm_area_struct *vma, unsigned long address, int write_access)
+{
+ /* Do not allow for mremap to expand us */
+ return 0;
+}
+
+static struct vm_operations_struct qcntl_mmap = {
+ NULL, /* no special mmap-open */
+ NULL, /* no special mmap-close */
+ NULL, /* no special mmap-unmap */
+ NULL, /* no special mmap-protect */
+ NULL, /* no special mmap-sync */
+ NULL, /* no special mmap-advise */
+ shmiq_nopage, /* our magic no-page fault handler */
+ NULL, /* no special mmap-wppage */
+ NULL, /* no special mmap-swapout */
+ NULL /* no special mmap-swapin */
+};
+
+static int
+shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
+{
+ int minor = MINOR (file->f_dentry->d_inode->i_rdev), error;
+ unsigned int size;
+ unsigned long mem, start;
+
+ /* mmap is only supported on the qcntl devices */
+ if (minor-- == 0)
+ return -EINVAL;
+
+ if (vma->vm_offset != 0)
+ return -EINVAL;
+
+ size = vma->vm_end - vma->vm_start;
+ start = vma->vm_start;
+ mem = (unsigned long) shmiqs [minor].shmiq_vaddr = vmalloc_uncached (size);
+ if (!mem)
+ return -EINVAL;
+
+ /* Prevent the swapper from considering these pages for swap and touching them */
+ vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
+ vma->vm_ops = &qcntl_mmap;
+
+ /* Uncache the pages */
+ vma->vm_page_prot = PAGE_USERIO;
+
+ error = vmap_page_range (vma->vm_start, size, mem);
+
+ shmiqs [minor].tail = 0;
+ /* Init the shared memory input queue */
+ memset (shmiqs [minor].shmiq_vaddr, 0, size);
+
+ return error;
+}
+
+static int
+shmiq_qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int minor = MINOR (inode->i_rdev);
+
+ lock_kernel ();
+
+ if (minor-- == 0)
+ return shmiq_ioctl (inode, filp, cmd, arg);
+
+ return qcntl_ioctl (inode, filp, cmd, arg, minor);
+}
+
+static unsigned int
+shmiq_qcntl_poll (struct file *filp, poll_table *wait)
+{
+ struct sharedMemoryInputQueue *s;
+ int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
+
+ if (minor-- == 0)
+ return 0;
+
+ if (!shmiqs [minor].mapped)
+ return 0;
+
+ poll_wait (&shmiqs [minor].proc_list, wait);
+ s = shmiqs [minor].shmiq_vaddr;
+ if (s->head != s->tail)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int
+shmiq_qcntl_open (struct inode *inode, struct file *filp)
+{
+ int minor = MINOR (inode->i_rdev);
+
+ if (minor == 0)
+ return 0;
+
+ minor--;
+ if (minor > MAX_SHMI_QUEUES)
+ return -EINVAL;
+ if (shmiqs [minor].opened)
+ return -EBUSY;
+
+ lock_kernel ();
+ shmiqs [minor].opened = 1;
+ shmiqs [minor].shmiq_vaddr = 0;
+ unlock_kernel ();
+ return 0;
+}
+
+static int
+shmiq_qcntl_fasync (struct file *file, int on)
+{
+ int retval;
+ int minor = MINOR (file->f_dentry->d_inode->i_rdev);
+
+ retval = fasync_helper (file, on, &shmiqs [minor].fasync);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+static int
+shmiq_qcntl_close (struct inode *inode, struct file *filp)
+{
+ int minor = MINOR (inode->i_rdev);
+ int j;
+
+ if (minor-- == 0){
+ for (j = 0; j < MAX_SHMIQ_DEVS; j++)
+ shmiq_forget_file (j);
+ }
+
+ if (minor > MAX_SHMI_QUEUES)
+ return -EINVAL;
+ if (shmiqs [minor].opened == 0)
+ return -EINVAL;
+
+ lock_kernel ();
+ shmiq_qcntl_fasync (filp, 0);
+ shmiqs [minor].opened = 0;
+ shmiqs [minor].mapped = 0;
+ shmiqs [minor].events = 0;
+ shmiqs [minor].fasync = 0;
+ vfree (shmiqs [minor].shmiq_vaddr);
+ shmiqs [minor].shmiq_vaddr = 0;
+ unlock_kernel ();
+ return 0;
+}
+
+
+static struct
+file_operations shmiq_fops =
+{
+ NULL, /* seek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ shmiq_qcntl_poll, /* poll */
+ shmiq_qcntl_ioctl, /* ioctl */
+ shmiq_qcntl_mmap, /* mmap */
+ shmiq_qcntl_open, /* open */
+ shmiq_qcntl_close, /* close */
+ NULL, /* fsync */
+ shmiq_qcntl_fasync, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+void
+shmiq_init (void)
+{
+ printk ("SHMIQ setup\n");
+ register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops);
+}
+/*
+ * shmiq.c: shared memory input queue driver
+ * written 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * We implement /dev/shmiq, /dev/qcntlN here
+ * this is different from IRIX that has shmiq as a misc
+ * streams device and the and qcntl devices as a major device.
+ *
+ * minor number 0 implements /dev/shmiq,
+ * any other number implements /dev/qcntl${minor-1}
+ *
+ * /dev/shmiq is used by the X server for two things:
+ *
+ * 1. for I_LINK()ing trough ioctl the file handle of a
+ * STREAMS device.
+ *
+ * 2. To send STREAMS-commands to the devices with the
+ * QIO ioctl interface.
+ *
+ * I have not yet figured how to make multiple X servers share
+ * /dev/shmiq for having different servers running. So, for now
+ * I keep a kernel-global array of inodes that are pushed into
+ * /dev/shmiq.
+ *
+ * /dev/qcntlN is used by the X server for two things:
+ *
+ * 1. Issuing the QIOCATTACH for mapping the shared input
+ * queue into the address space of the X server (yeah, yeah,
+ * I did not invent this interface).
+ *
+ * 2. used by select. I bet it is used for checking for events on
+ * the queue.
+ *
+ * Now the problem is that there does not seem anything that
+ * establishes a connection between /dev/shmiq and the qcntlN file. I
+ * need an strace from an X server that runs on a machine with more
+ * than one keyboard. And this is a problem since the file handles
+ * are pushed in /dev/shmiq, while the events should be dispatched to
+ * the /dev/qcntlN device.
+ *
+ * Until then, I just allow for 1 qcntl device.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
diff --git a/drivers/sgi/char/streamable.c b/drivers/sgi/char/streamable.c
index f26aad0e2..02e40cf05 100644
--- a/drivers/sgi/char/streamable.c
+++ b/drivers/sgi/char/streamable.c
@@ -361,3 +361,366 @@ streamable_init (void)
misc_register (&dev_input_keyboard);
misc_register (&dev_input_mouse);
}
+/*
+ * streamable.c: streamable devices. /dev/gfx
+ * (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * Major 10 is the streams clone device. The IRIX Xsgi server just
+ * opens /dev/gfx and closes it inmediately.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <asm/uaccess.h>
+#include <asm/shmiq.h>
+#include <asm/keyboard.h>
+#include "graphics.h"
+
+
+extern struct kbd_struct kbd_table [MAX_NR_CONSOLES];
+
+/* console number where forwarding is enabled */
+int forward_chars;
+
+/* To which shmiq this keyboard is assigned */
+int kbd_assigned_device;
+
+/* previous kbd_mode for the forward_chars terminal */
+int kbd_prev_mode;
+
+/* Fetchs the strioctl information from user space for I_STR ioctls */
+int
+get_sioc (struct strioctl *sioc, unsigned long arg)
+{
+ int v;
+
+ v = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct strioctl));
+ if (v)
+ return v;
+ if (copy_from_user (sioc, (void *) arg, sizeof (struct strioctl)))
+ return -EFAULT;
+
+ v = verify_area (VERIFY_WRITE, (void *) sioc->ic_dp, sioc->ic_len);
+ if (v)
+ return v;
+ return 0;
+}
+
+/* /dev/gfx device */
+static int
+sgi_gfx_open (struct inode *inode, struct file *file)
+{
+ printk ("GFX: Opened by %d\n", current->pid);
+ return 0;
+}
+
+static int
+sgi_gfx_close (struct inode *inode, struct file *file)
+{
+ printk ("GFX: Closed by %d\n", current->pid);
+ return 0;
+}
+
+static int
+sgi_gfx_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ printk ("GFX: ioctl 0x%x %ld called\n", cmd, arg);
+ return 0;
+ return -EINVAL;
+}
+
+struct file_operations sgi_gfx_fops = {
+ NULL, /* llseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ sgi_gfx_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sgi_gfx_open, /* open */
+ sgi_gfx_close, /* release */
+ NULL, /* fsync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+static struct miscdevice dev_gfx = {
+ SGI_GFX_MINOR, "sgi-gfx", &sgi_gfx_fops
+};
+
+/* /dev/input/keyboard streams device */
+static idevDesc sgi_kbd_desc = {
+ "keyboard", /* devName */
+ "KEYBOARD", /* devType */
+ 240, /* nButtons */
+ 0, /* nValuators */
+ 0, /* nLEDs */
+ 0, /* nStrDpys */
+ 0, /* nIntDpys */
+ 0, /* nBells */
+ IDEV_HAS_KEYMAP | IDEV_HAS_PCKBD
+};
+
+static int
+sgi_kbd_sioc (idevInfo *dinfo, int cmd, int size, char *data, int *found)
+{
+ *found = 1;
+
+ switch (cmd){
+
+ case IDEVINITDEVICE:
+ return 0;
+
+ case IDEVGETDEVICEDESC:
+ if (size >= sizeof (idevDesc)){
+ if (copy_to_user (data, &sgi_kbd_desc, sizeof (sgi_kbd_desc)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+
+ case IDEVGETKEYMAPDESC:
+ if (size >= sizeof (idevKeymapDesc)){
+ if (copy_to_user (data, "US", 3))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+ }
+ *found = 0;
+ return -EINVAL;
+}
+
+static int
+sgi_keyb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct strioctl sioc;
+ int f, v;
+
+ /* IRIX calls I_PUSH on the opened device, go figure */
+ if (cmd == I_PUSH)
+ return 0;
+
+ if (cmd == I_STR){
+ v = get_sioc (&sioc, arg);
+ if (v)
+ return v;
+
+ /* Why like this? Because this is a sample piece of code
+ * that can be copied into other drivers and shows how to
+ * call a stock IRIX xxx_wioctl routine
+ *
+ * The NULL is supposed to be a idevInfo, right now we
+ * do not support this in our kernel.
+ */
+ return sgi_kbd_sioc (NULL, sioc.ic_cmd, sioc.ic_len, sioc.ic_dp, &f);
+ }
+
+ if (cmd == SHMIQ_ON){
+ kbd_assigned_device = arg;
+ forward_chars = fg_console + 1;
+ kbd_prev_mode = kbd_table [fg_console].kbdmode;
+
+ kbd_table [fg_console].kbdmode = VC_RAW;
+ } else if (cmd == SHMIQ_OFF && forward_chars){
+ kbd_table [forward_chars-1].kbdmode = kbd_prev_mode;
+ forward_chars = 0;
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+void
+kbd_forward_char (int ch)
+{
+ static struct shmqevent ev;
+
+ ev.data.flags = (ch & 0200) ? 0 : 1;
+ ev.data.which = ch;
+ ev.data.device = kbd_assigned_device + 0x11;
+ shmiq_push_event (&ev);
+}
+
+static int
+sgi_keyb_open (struct inode *inode, struct file *file)
+{
+ /* Nothing, but required by the misc driver */
+ return 0;
+}
+
+struct file_operations sgi_keyb_fops = {
+ NULL, /* llseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ sgi_keyb_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sgi_keyb_open, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+static struct miscdevice dev_input_keyboard = {
+ SGI_STREAMS_KEYBOARD, "streams-keyboard", &sgi_keyb_fops
+};
+
+/* /dev/input/mouse streams device */
+#define MOUSE_VALUATORS 2
+static idevDesc sgi_mouse_desc = {
+ "mouse", /* devName */
+ "MOUSE", /* devType */
+ 3, /* nButtons */
+ MOUSE_VALUATORS, /* nValuators */
+ 0, /* nLEDs */
+ 0, /* nStrDpys */
+ 0, /* nIntDpys */
+ 0, /* nBells */
+ 0 /* flags */
+};
+
+static idevValuatorDesc mouse_default_valuator = {
+ 200, /* hwMinRes */
+ 200, /* hwMaxRes */
+ 0, /* hwMinVal */
+ 65000, /* hwMaxVal */
+ IDEV_EITHER, /* possibleModes */
+ IDEV_ABSOLUTE, /* default mode */
+ 200, /* resolution */
+ 0, /* minVal */
+ 65000 /* maxVal */
+};
+
+static int mouse_opened;
+static idevValuatorDesc mouse_valuators [MOUSE_VALUATORS];
+
+int
+sgi_mouse_open (struct inode *inode, struct file *file)
+{
+ int i;
+
+ if (mouse_opened)
+ return -EBUSY;
+
+ mouse_opened = 1;
+ for (i = 0; i < MOUSE_VALUATORS; i++)
+ mouse_valuators [i] = mouse_default_valuator;
+ return 0;
+}
+
+static int
+sgi_mouse_close (struct inode *inode, struct file *filp)
+{
+ mouse_opened = 0;
+ return 0;
+}
+
+static int
+sgi_mouse_sioc (idevInfo *dinfo, int cmd, int size, char *data, int *found)
+{
+ *found = 1;
+
+ switch (cmd){
+ case IDEVINITDEVICE:
+ return 0;
+
+ case IDEVGETDEVICEDESC:
+ if (size >= sizeof (idevDesc)){
+ if (copy_to_user (data, &sgi_mouse_desc, sizeof (sgi_mouse_desc)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+
+ case IDEVGETVALUATORDESC: {
+ idevGetSetValDesc request, *ureq = (idevGetSetValDesc *) data;
+
+ if (size < sizeof (idevGetSetValDesc))
+ return -EINVAL;
+
+ if (copy_from_user (&request, data, sizeof (request)))
+ return -EFAULT;
+ if (request.valNum >= MOUSE_VALUATORS)
+ return -EINVAL;
+ if (copy_to_user ((void *)&ureq->desc,
+ (void *)&mouse_valuators [request.valNum],
+ sizeof (idevValuatorDesc)))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ *found = 0;
+ return -EINVAL;
+}
+
+static int
+sgi_mouse_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct strioctl sioc;
+ int f, v;
+
+ /* IRIX calls I_PUSH on the opened device, go figure */
+ switch (cmd){
+ case I_PUSH:
+ return 0;
+
+ case I_STR:
+ v = get_sioc (&sioc, arg);
+ if (v)
+ return v;
+
+ /* Why like this? Because this is a sample piece of code
+ * that can be copied into other drivers and shows how to
+ * call a stock IRIX xxx_wioctl routine
+ *
+ * The NULL is supposed to be a idevInfo, right now we
+ * do not support this in our kernel.
+ */
+ return sgi_mouse_sioc (NULL, sioc.ic_cmd, sioc.ic_len, sioc.ic_dp, &f);
+
+ case SHMIQ_ON:
+ case SHMIQ_OFF:
+ return 0;
+ }
+ return 0;
+}
+
+struct file_operations sgi_mouse_fops = {
+ NULL, /* llseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ sgi_mouse_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sgi_mouse_open, /* open */
+ sgi_mouse_close, /* release */
+ NULL, /* fsync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+/* /dev/input/mouse */
+static struct miscdevice dev_input_mouse = {
+ SGI_STREAMS_KEYBOARD, "streams-mouse", &sgi_mouse_fops
+};
+
+void
+streamable_init (void)
+{
+ printk ("streamable misc devices registered (keyb:%d, gfx:%d)\n",
+ SGI_STREAMS_KEYBOARD, SGI_GFX_MINOR);
+
+ misc_register (&dev_gfx);
+ misc_register (&dev_input_keyboard);
+ misc_register (&dev_input_mouse);
+}
diff --git a/drivers/sgi/char/usema.c b/drivers/sgi/char/usema.c
index dfef772eb..262f5724f 100644
--- a/drivers/sgi/char/usema.c
+++ b/drivers/sgi/char/usema.c
@@ -27,6 +27,197 @@
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/dcache.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <asm/smp_lock.h>
+#include <asm/usioctl.h>
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+
+struct irix_usema {
+ struct file *filp;
+ struct wait_queue *proc_list;
+};
+
+static int
+sgi_usema_attach (usattach_t * attach, struct irix_usema *usema)
+{
+ int newfd;
+ newfd = get_unused_fd();
+ if (newfd < 0)
+ return newfd;
+
+ current->files->fd [newfd] = usema->filp;
+ usema->filp->f_count++;
+ /* Is that it? */
+ printk("UIOCATTACHSEMA: new usema fd is %d", newfd);
+ return newfd;
+}
+
+static int
+sgi_usemaclone_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct irix_usema *usema = file->private_data;
+ int retval;
+
+ printk("[%s:%d] wants ioctl 0x%xd (arg 0x%lx)",
+ current->comm, current->pid, cmd, arg);
+
+ switch(cmd) {
+ case UIOCATTACHSEMA: {
+ /* They pass us information about the semaphore to
+ which they wish to be attached, and we create&return
+ a new fd corresponding to the appropriate semaphore.
+ */
+ usattach_t *attach = (usattach_t *)arg;
+ retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
+ if (retval) {
+ printk("[%s:%d] sgi_usema_ioctl(UIOCATTACHSEMA): "
+ "verify_area failure",
+ current->comm, current->pid);
+ return retval;
+ }
+ if (usema == 0)
+ return -EINVAL;
+
+ printk("UIOCATTACHSEMA: attaching usema %p to process %d\n", usema, current->pid);
+ /* XXX what is attach->us_handle for? */
+ return sgi_usema_attach(attach, usema);
+ break;
+ }
+ case UIOCABLOCK: /* XXX make `async' */
+ case UIOCNOIBLOCK: /* XXX maybe? */
+ case UIOCBLOCK: {
+ /* Block this process on the semaphore */
+ usattach_t *attach = (usattach_t *)arg;
+
+ retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
+ if (retval) {
+ printk("[%s:%d] sgi_usema_ioctl(UIOC*BLOCK): "
+ "verify_area failure",
+ current->comm, current->pid);
+ return retval;
+ }
+ printk("UIOC*BLOCK: putting process %d to sleep on usema %p",
+ current->pid, usema);
+ if (cmd == UIOCNOIBLOCK)
+ interruptible_sleep_on(&usema->proc_list);
+ else
+ sleep_on(&usema->proc_list);
+ return 0;
+ }
+ case UIOCAUNBLOCK: /* XXX make `async' */
+ case UIOCUNBLOCK: {
+ /* Wake up all process waiting on this semaphore */
+ usattach_t *attach = (usattach_t *)arg;
+
+ retval = verify_area(VERIFY_READ, attach, sizeof(usattach_t));
+ if (retval) {
+ printk("[%s:%d] sgi_usema_ioctl(UIOC*BLOCK): "
+ "verify_area failure",
+ current->comm, current->pid);
+ return retval;
+ }
+
+ printk("[%s:%d] releasing usema %p",
+ current->comm, current->pid, usema);
+ wake_up(&usema->proc_list);
+ return 0;
+ }
+ }
+ return -ENOSYS;
+}
+
+static unsigned int
+sgi_usemaclone_poll(struct file *filp, poll_table *wait)
+{
+ struct irix_usema *usema = filp->private_data;
+
+ printk("[%s:%d] wants to poll usema %p", current->comm, current->pid, usema);
+
+ return 0;
+}
+
+static int
+sgi_usemaclone_open(struct inode *inode, struct file *filp)
+{
+ struct irix_usema *usema;
+
+ usema = kmalloc (sizeof (struct irix_usema), GFP_KERNEL);
+ if (!usema)
+ return -ENOMEM;
+
+ usema->filp = filp;
+ usema->proc_list = NULL;
+ filp->private_data = usema;
+ return 0;
+}
+
+static int
+sgi_usemaclone_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+struct file_operations sgi_usemaclone_fops = {
+ NULL, /* llseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ sgi_usemaclone_poll, /* poll */
+ sgi_usemaclone_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sgi_usemaclone_open, /* open */
+ sgi_usemaclone_release, /* release */
+ NULL, /* fsync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+static struct miscdevice dev_usemaclone = {
+ SGI_USEMACLONE, "usemaclone", &sgi_usemaclone_fops
+};
+
+void
+usema_init(void)
+{
+ printk("usemaclone misc device registered (minor: %d)\n", SGI_USEMACLONE);
+ misc_register(&dev_usemaclone);
+}
+/*
+ * usema.c: software semaphore driver (see IRIX's usema(7M))
+ * written 1997 Mike Shaver (shaver@neon.ingenia.ca)
+ * 1997 Miguel de Icaza (miguel@kernel.org)
+ *
+ * This file contains the implementation of /dev/usemaclone,
+ * the devices used by IRIX's us* semaphore routines.
+ *
+ * /dev/usemaclone is used to create a new semaphore device, and then
+ * the semaphore is manipulated via ioctls.
+ *
+ * At least for the zero-contention case, lock set and unset as well
+ * as semaphore P and V are done in userland, which makes things a
+ * little bit better. I suspect that the ioctls are used to register
+ * the process as blocking, etc.
+ *
+ * Much inspiration and structure stolen from Miguel's shmiq work.
+ *
+ * For more information:
+ * usema(7m), usinit(3p), usnewsema(3p)
+ * /usr/include/sys/usioctl.h
+ *
+*/
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/major.h>
#include <linux/poll.h>
#include <linux/string.h>
#include <linux/dcache.h>
diff --git a/drivers/sgi/char/vga_font.c b/drivers/sgi/char/vga_font.c
index 2ab67797c..2c7def4f6 100644
--- a/drivers/sgi/char/vga_font.c
+++ b/drivers/sgi/char/vga_font.c
@@ -344,3 +344,349 @@ unsigned char vga_font[cmapsz] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
+#include "gconsole.h"
+
+unsigned char vga_font[cmapsz] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd,
+0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff,
+0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe,
+0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
+0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e,
+0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30,
+0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63,
+0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8,
+0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e,
+0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
+0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6,
+0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
+0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
+0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18,
+0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
+0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
+0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
+0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
+0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
+0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
+0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
+0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7,
+0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
+0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c,
+0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c,
+0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18,
+0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06,
+0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb,
+0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60,
+0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30,
+0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18,
+0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
+0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06,
+0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6,
+0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
+0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
+0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c,
+0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18,
+0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66,
+0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06,
+0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
+0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44,
+0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
+0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8,
+0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66,
+0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66,
+0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60,
+0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18,
+0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
+0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/sound/lowlevel/awe_wave.c b/drivers/sound/lowlevel/awe_wave.c
index 8ac02d490..a9ac8850e 100644
--- a/drivers/sound/lowlevel/awe_wave.c
+++ b/drivers/sound/lowlevel/awe_wave.c
@@ -564,7 +564,7 @@ int attach_awe(void)
synth_devs[my_dev] = &awe_operations;
#ifdef CONFIG_AWE32_MIXER
- if ((my_mixerdev=sound_alloc_mixerdev())==-1) {
+ if ((my_mixerdev=sound_alloc_mixerdev())!=-1) {
mixer_devs[my_mixerdev] = &awe_mixer_operations;
}
#endif