VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 26
+SUBLEVEL = 27
ARCH = i386
tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A n
tristate 'Standard Mitsumi [no XA/Multisession] CDROM support' CONFIG_MCD n
tristate 'Experimental Mitsumi [XA/MultiSession] support' CONFIG_MCDX n
- tristate 'Matsushita/Panasonic CDROM support' CONFIG_SBPCD n
+ tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD n
if [ "$CONFIG_SBPCD" = "y" ]; then
- bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
+ bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2 n
if [ "$CONFIG_SBPCD2" = "y" ]; then
- bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
+ bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3 n
if [ "$CONFIG_SBPCD3" = "y" ]; then
- bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
+ bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4 n
fi
fi
fi
comment 'character devices'
bool 'Cyclades async mux support' CONFIG_CYCLADES n
+bool 'Stallion multiport serial support' CONFIG_STALDRV n
+if [ "$CONFIG_STALDRV" = "y" ]; then
+ tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION n
+ tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION n
+fi
tristate 'Parallel printer support' CONFIG_PRINTER n
tristate 'Logitech busmouse support' CONFIG_BUSMOUSE n
tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
-This README belongs to release 3.7 or newer of the SoundBlaster Pro
+This README belongs to release 3.9 or newer of the SoundBlaster Pro
(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
CD-ROM driver for Linux.
-The driver is able to drive the whole family of "traditional" IDE-style (that
+Sbpcd really, really is NOT for ANY IDE/ATAPI drive!
+Not even if you have an "original" SoundBlaster card with an IDE interface!
+So, you better have a look into README.ide if your port address is 0x1F0,
+0x170, 0x1E8, 0x168 or similar.
+I get tons of mails from IDE/ATAPI drive users - I really can't continue
+any more to answer them all. So, if your drive/interface information sheets
+mention "IDE" (primary, secondary, tertiary, quaternary) and the DOS driver
+invoking line within your CONFIG.SYS is using an address below 0x230:
+DON'T ROB MY LAST NERVE - jumper your interface to address 0x170 and IRQ 15
+(that is the "secondary IDE" configuration), set your drive to "master" and
+use ide-cd as your driver. If you do not have a second IDE hard disk, use the
+LILO commands
+ hdb=noprobe hdc=cdrom
+and get lucky.
+To make it fully clear to you: if you mail me about IDE/ATAPI drive problems,
+my answer is above, and I simply will discard your mail, hoping to stop the
+flood and to find time to lead my 12-years old son towards happy computing.
+
+The driver is able to drive the whole family of "traditional" AT-style (that
is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita,
Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
+CR-574 is an IDE/ATAPI drive.
The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It is supported now - with help by Serge Robyns.
+Matsushita command set. It is supported - with help by Serge Robyns.
There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
with a special controller board. This drive is supported (the interface is
an internal drive, and you can use it as an internal, too - f.e. plug it into
a soundcard).
-CreativeLabs has a new drive "CD-200". Support is under construction.
-Drive detection and playing audio should already work. I need qualified
-feedback about the bugs within the data functions or a drive (I never saw
-a CD200).
-
-The quad-speed TEAC CD-55A drive is supported. The routines may still be
-a little bit buggy, but the data rate already reaches 500 kB/sec if you
-set SBP_BUFFER_FRAMES to 64. The drive is able to deliver 600 kB/sec, so
-this has to get a point of work.
+CreativeLabs has a new drive "CD200" and a similar drive "CD200F". The latter
+is made by Funai and sometimes named "E2550UA". Support is under construction
+ - CD200F should work, CD200 is still giving problems.
+Drive detection and playing audio should work. I need qualified feedback
+about the bugs within the data functions or a drive (I never saw a CD200).
+
+The quad-speed TEAC CD-55A drive is supported, but still does not reach "full
+speed". The data rate already reaches 500 kB/sec if you set SBP_BUFFER_FRAMES
+to 64 (it is not recommended to do that for normal "file access" usage, but it
+can speed up things a lot if you use something like "dd" to read from the
+drive; I use it for verifying self-written CDs this way).
+The drive itself is able to deliver 600 kB/sec, so this has to get a point of
+work; with the normal setup, the performance currently is not even as good as
+double-speed.
This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
-and this driver is in no way usable for any new IDE ATAPI drive.
+and again: this driver is in no way usable for any IDE/ATAPI drive. If you
+think your drive should work and it doesn't: send me the DOS driver for your
+beast (gzipped + uuencoded) and your CONFIG.SYS if you want to ask me for help,
+and include an original log message excerpt, and try to give all information
+a complete idiot needs to understand your hassle already with your first
+mail. And if you want to say "as I have mailed you before", be sure that I
+don't remember your "case" by such remarks; at the moment, I have some
+hundreds open correspondences about Linux CDROM questions (hope to reduce if
+the IDE/ATAPI user questions disappear).
-This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
-SoundFX, ...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
-WDH-7001C, Longshine LCS-6853, older Aztech cards, ...).
-It should work too now with the "configurable" interface "Sequoia S-1000",
-which is found on the Spea Media FX sound card. I still need feedback about
-this, or such a card. Anyways, the procedure "boot DOS and wait until
-CONFIG.SYS is done, then use CTL-ALT-DEL to boot Linux" should make it
-work.
+This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
+SoundFX, Mozart, ...) and with the "no-sound" cards (Panasonic CI-101P,
+LaserMate, WDH-7001C, Longshine LCS-6853, TEAC ...).
+
+It finally works now with the "configurable" interface "Sequoia S-1000", too,
+which is found on the Spea Media FX and Ensonic Soundscape sound cards. You
+have to specify the type "SBPRO 2" and the true CDROM port address with it,
+not the "configuration port" address.
+
+If you have a sound card which needs a "configuration driver" instead of
+jumpers for interface types and addresses (like Mozart cards) - those
+drivers get invoked before the DOS CDROM driver in your CONFIG.SYS, typical
+names are "cdsetup.sys" and "mztinit.sys" -, let the sound driver do the
+CDROM port configuration (the leading comments within
+linux/drivers/sound/mad16.c are just for you!). Hannu Savolainen's mad16.c
+code is able to set up my Mozart card - I simply had to add
+ #define MAD16_CONF 0x06
+ #define MAD16_CDSEL 0x03
+to configure the CDROM interface for type "Panasonic" (LaserMate) and address
+0x340.
The interface type has to get configured in /usr/include/linux/sbpcd.h,
-because the behavior of some sound card interfaces is different.
+because the register layout is different between the "SoundBlaster" and the
+"LaserMate" type.
-With some TEAC drives I have seen interface cards which seem to lack the
-"drive select" lines; always drive 0 gets addressed. To avoid "mirror drives"
-with such interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0.
+I got a report that the TEAC interface card "I/F E117098" is of type
+"SoundBlaster" (i.e. you have to set SBPRO to 1) even with the addresses
+0x300 and above. This is unusual, and it can't get covered by the auto
+probing scheme.
+If auto-probing found the drive, the address is correct. The reported type
+may be wrong. A "mount" will give success only if the interface type is set
+right. Playing audio should work with a wrong set interface type, too.
+
+With some TEAC and some CD200 drives I have seen interface cards which seem
+to lack the "drive select" lines; always drive 0 gets addressed. To avoid
+"mirror drives" (four drives detected where you only have one) with such
+interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0 (if
+possible).
-The driver respects all known drive firmware releases - my old drive is a 2.11,
-but it should work with CR-52x drives <2.01 ... >3.00 and with CR-56x drives
-<0.75 .. 5.00.
Up to 4 drives per interface card, and up to 4 interface cards are supported.
All supported drive families can be mixed, but the CR-521 drives are
At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
convert photo CD image files and Gerd Knorr's viewing utility.
-The transfer rate will reach 150 kB/sec with "old" drives, 300 kB/sec with
-double-speed drives, and about 500 kB/sec with quad speed drives.
+The transfer rate will reach 150 kB/sec with CR-52x drives, 300 kB/sec with
+CR-56x drives, and currently not more than 500 kB/sec (usually less than
+250 kB/sec) with the TEAC quad speed drives.
XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
-This release is part of the standard kernel and consists of
+This release consists of
- this README file
- the driver file linux/drivers/block/sbpcd.c
+- the stub files linux/drivers/block/sbpcd[234].c
- the header file linux/include/linux/sbpcd.h.
1. Setup your hardware parameters. Though the driver does "auto-probing" at a
lot of (not all possible!) addresses, this step is recommended for
- every-day use.
+ every-day use. You should let sbpcd auto-probe once and use the reported
+ address if a drive got found. The reported type may be incorrect; it is
+ correct if you can mount a data CD. There is no choice for you with the
+ type; only one is the right, the others are deadly wrong.
+
a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your
hardware (near the beginning):
a1. Set it up for the appropriate type of interface board.
"Original" CreativeLabs sound cards need "SBPRO 1".
- Most "compatible" sound cards (for example "Highscreen", "SoundFX"
- and "Galaxy") need "SBPRO 0".
+ Most "compatible" sound cards (almost all "non-CreativeLabs" cards)
+ need "SBPRO 0".
The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
All other "no-sound" boards need the "SBPRO 0" setup.
+ Possibly some TEAC "no-sound" boards need the "SBPRO 1" setup.
The Spea Media FX sound card needs "SBPRO 2".
sbpcd.c holds some examples in its auto-probe list.
If you configure "SBPRO" wrong, the playing of audio CDs will work,
but you will not be able to mount a data CD.
a2. Tell the address of your CDROM_PORT (not of the sound port).
- a3. Set DISTRIBUTION to 0.
+ a3. If 4 drives get found, but you have only one, set MAX_DRIVES to 1.
+ a4. Set DISTRIBUTION to 0.
b. Additionally for 2.a1 and 2.a2, the setup may be done during
boot time (via the "kernel command line" or "LILO option"):
sbpcd=0x230,SoundBlaster
or
sbpcd=0x330,SPEA
This is especially useful if you install a fresh distribution.
+ If the second parameter is a number, it gets taken as the type
+ setting; 0 is "LaserMate", 1 is "SoundBlaster".
+ So, for example
+ sbpcd=0x230,1
+ is equivalent to
+ sbpcd=0x230,SoundBlaster
+
2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
CD-ROM support and for ISO9660 FileSystem support. If you do not have a
second, third, or fourth controller installed, do not say "y" to the
secondary Matsushita CD-ROM questions.
- SCSI and/or SCSI CD-ROM support is not needed.
3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
-4. Make the device file(s). The driver uses definitely and exclusive the
- MAJOR 25, so do
+4. Make the device file(s). This step usually already has been done by the
+ MAKEDEV script.
+ The driver uses MAJOR 25, so, if necessary, do
mknod /dev/sbpcd b 25 0 (if you have only one drive)
and/or
mknod /dev/sbpcd0 b 25 0
mknod /dev/sbpcd3 b 25 3
to make the node(s).
- The driver no longer uses the "AT bus style" device numbering; the SCSI
- scheme is used now; that means, the "first found" drive gets MINOR 0
- (regardless to its jumpered ID), the "next found" (at the same cable)
- gets MINOR 1, ...
+ The "first found" drive gets MINOR 0 (regardless to its jumpered ID), the
+ "next found" (at the same cable) gets MINOR 1, ...
For a second interface board, you have to make nodes like
mknod /dev/sbpcd4 b 26 0
You should now be able to do
mkdir /CD
and
- mount -t iso9660 -o ro /dev/sbpcd /CD
+ mount -rt iso9660 /dev/sbpcd /CD
or
- mount -t iso9660 -o ro,block=2048 /dev/sbpcd /CD
-and see the contents of your CD in the /CD directory, and/or hear music with
-"workman -c /dev/sbpcd &".
+ mount -rt iso9660 -o block=2048 /dev/sbpcd /CD
+and see the contents of your CD in the /CD directory.
+To use audio CDs, a mounting is not recommended (and it would fail if the
+first track is not a data track).
Using sbpcd as a "loadable module":
To reduce or increase the amount of kernel messages, edit sbpcd.c and play
with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
+Don't forget to reflect what you do; enabling all DBG_xxx switches at once
+may crash your system.
The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
specify "block=2048" as a mount option. Doing this will disable the direct
one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
do not use block=2048 with those.
-At the beginning of sbpcd.c, you will find some "#define"s (f.e. EJECT and
-JUKEBOX). With that, you can configure the driver for some special things.
+Within sbpcd.h, you will find some "#define"s (f.e. EJECT and JUKEBOX). With
+that, you can configure the driver for some special things.
You can use the appended program "cdtester" to set the auto-eject feature
during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
for you.
-There is a new ioctl CDROMMULTISESSION to obtain with a user program if
+There is an ioctl CDROMMULTISESSION to obtain with a user program if
the CD is an XA disk and - if it is - where the last session starts. The
"cdtester" program illustrates how to call it.
the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
change CDROM_PORT to the address of your CDROM I/O port.
-Most of the "SoundBlaster compatible" cards behave like the no-sound
-interfaces!
+Almost all of the "SoundBlaster compatible" cards behave like the no-sound
+interfaces, i.e. need SBPRO 0!
With "original" SB Pro cards, an initial setting of CD_volume through the
sound cards MIXER register gets done.
rc=ioctl(drive,CDROMPLAYMSF,&msf);
if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
break;
+ case 'V':
+ rc=ioctl(drive,CDROMVOLREAD,&volctrl);
+ if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+ printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
+ break;
+ case 'R':
+ rc=ioctl(drive,CDROMRESET);
+ if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
+ break;
+ case 'B': /* set the driver's (?) read ahead value */
+ printf("enter read-ahead size: ? ");
+ scanf("%d",&i);
+ rc=ioctl(drive,BLKRASET,i);
+ if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
+ break;
#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
case 'd':
printf("Address (min:sec:frm) ");
rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
printf("%d frames granted.\n",rc);
break;
- case 'V':
- rc=ioctl(drive,CDROMVOLREAD,&volctrl);
- if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
- printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
- break;
- case 'R':
- rc=ioctl(drive,CDROMRESET);
- if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
- break;
- case 'B': /* set the driver's (?) read ahead value */
- printf("enter read-ahead size: ? ");
- scanf("%d",&i);
- rc=ioctl(drive,BLKRASET,i);
- if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
- break;
#endif SBP_PRIVATE_IOCTLS
default:
printf("unknown command: \"%s\".\n",command);
}
}
/*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
* detailed bug reports).
* Also for the TEAC CD-55A drive.
* Not for Sanyo drives (but sjcd is there...).
- * Not for Funai drives.
+ * Not for any other Funai drives than E2550UA (="CD200" with "F").
*
- * NOTE: This is release 3.8.
+ * NOTE: This is release 3.9.
*
* VERSION HISTORY
*
*
* 3.8 Elongated max_latency for CR-56x drives.
*
+ * 3.9 Finally fixed the long-known SoundScape/SPEA/Sequoia S-1000 interface
+ * configuration bug.
+ * Now Corey, Heiko, Ken, Leo, Vadim/Eric & Werner are invited to copy
+ * the config_spea() routine into their drivers. ;-)
+ *
+ *
* TODO
*
* disk change detection
- * allow & synchronize multi-activity
+ * synchronize multi-activity
* (data + audio + ioctl + disk change, multiple drives)
* implement "read all subchannel data" (96 bytes per frame)
+ * check if CDROMPLAYMSF can cause a hang
*
* special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
* elaborated speed-up experiments (and the fabulous results!), for
#include "blk.h"
-#define VERSION "v3.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v3.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
/*==========================================================================*/
/*
0x270, 1, /* Soundblaster 16 */
0x670, 0, /* "sound card #9" */
0x690, 0, /* "sound card #9" */
- 0x330, 2, /* SPEA Media FX (default) */
- 0x320, 2, /* SPEA Media FX */
- 0x340, 2, /* SPEA Media FX */
+ 0x338, 2, /* SPEA Media FX, Ensonic SoundScape (default) */
+ 0x328, 2, /* SPEA Media FX */
+ 0x348, 2, /* SPEA Media FX */
0x634, 0, /* some newer sound cards */
0x638, 0, /* some newer sound cards */
0x230, 1, /* some newer sound cards */
*/
0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
0x350, 0, /* Lasermate, CI-101P */
- 0x350, 2, /* SPEA Media FX */
+ 0x358, 2, /* SPEA Media FX */
0x370, 0, /* Lasermate, CI-101P */
0x290, 1, /* Soundblaster 16 */
0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
static const char *str_lm = "LaserMate";
static const char *str_sp = "SPEA";
static const char *str_sp_l = "spea";
+static const char *str_ss = "SoundScape";
+static const char *str_ss_l = "soundscape";
const char *type;
#if !(SBPCD_ISSUE-1)
u_int lba_multi;
int first_session;
int last_session;
+ int track_of_last_session;
u_char audio_state;
u_int pos_audio_start;
u_char UPC_ctl_adr;
u_char UPC_buf[7];
- int CDsize_blk;
int frame_size;
int CDsize_frm;
static void mark_timeout_delay(u_long i)
{
timed_out_delay=1;
+#if 0
msg(DBG_TIM,"delay timer expired.\n");
+#endif
}
/*==========================================================================*/
static void mark_timeout_data(u_long i)
{
timed_out_data=1;
+#if 0
msg(DBG_TIM,"data timer expired.\n");
+#endif
}
/*==========================================================================*/
#if 0
static void mark_timeout_audio(u_long i)
{
timed_out_audio=1;
+#if 0
msg(DBG_TIM,"audio timer expired.\n");
+#endif
}
#endif
/*==========================================================================*/
*/
static INLINE void lba2msf(int lba, u_char *msf)
{
- lba += CD_BLOCK_OFFSET;
+ lba += CD_MSF_OFFSET;
msf[0] = lba / (CD_SECS*CD_FRAMES);
lba %= CD_SECS*CD_FRAMES;
msf[1] = lba / CD_FRAMES;
u_int mm;
msf.c[3] = 0;
- msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
- mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
+ msf.c[2] = (blk + CD_MSF_OFFSET) / (CD_SECS * CD_FRAMES);
+ mm = (blk + CD_MSF_OFFSET) % (CD_SECS * CD_FRAMES);
msf.c[1] = mm / CD_FRAMES;
msf.c[0] = mm % CD_FRAMES;
return (msf.n);
int i;
msf.n=msfx;
- i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
+ i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_MSF_OFFSET;
if (i<0) return (0);
return (i);
}
{
int i;
- i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
+ i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_MSF_OFFSET;
if (i<0) return (0);
return (i);
}
if (!(j&s_not_result_ready)) return (j);
if (fam0L_drive) if (j&s_attention) return (j);
}
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
i = 1;
}
#else
if (!(j&s_not_result_ready)) return (j);
if (fam0L_drive) if (j&s_attention) return (j);
}
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
i = 1;
}
#endif MODULE
if (!(st&s_not_result_ready)) break;
}
if ((j!=0)||(timeout<=jiffies)) break;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
j = 1;
}
if (timeout<=jiffies) break;
if (!(i&s_not_result_ready)) break;
}
if ((j!=0)||(timeout<jiffies)) break;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
j = 1;
}
while (1);
drvcmd[0]=CMDT_READ_ERR;
j=cmd_out_T(); /* !!! recursive here !!! */
--recursion;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
}
while (j<0);
D_S[d].error_state=infobuf[2];
i=GetStatus();
if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
if (!st_caddy_in) break;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
}
while (!st_diskok);
#if 000
{
i=cc_LockDoor(0);
--j;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
}
while ((i<0)&&(j));
if (i<0)
{
i=cc_LockDoor(1);
--j;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
}
while ((i<0)&&(j));
if (j==0)
{
i=cc_LockDoor(1);
--j;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
}
while ((i<0)&&(j));
}
i=cmd_out();
if (i<0) return (i);
i=0;
+ D_S[d].sense_byte=0;
if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
- else if (fam0L_drive) D_S[d].sense_byte=0;
else if (famT_drive)
{
- D_S[d].sense_byte=0;
- if (infobuf[4]==0x01) D_S[d].xa_byte=0x20; /* wrong!!!! */
+ if (infobuf[4]==0x01) D_S[d].xa_byte=0x20;
+ else D_S[d].xa_byte=0;
i=2;
}
D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
{
int i, j;
- if (famL_drive) return (0);
+ if (famL_drive) return (0); /* some firmware lacks this command */
+ if (famT_drive) return (0); /* done with cc_ReadTocDescr() */
D_S[d].diskstate_flags &= ~cd_size_bit;
for (j=3;j>0;j--)
{
response_count=5;
flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
}
- else if (famT_drive)
- {
- response_count=12;
- drvcmd[0]=CMDT_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[6]=CDROM_LEADOUT;
- drvcmd[8]=response_count;
- drvcmd[9]=0x00;
- }
i=cmd_out();
if (i>=0) break;
msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
cc_ReadError();
}
if (j==0) return (i);
- if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_BLOCK_OFFSET;
+ if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_MSF_OFFSET;
else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
- else if (famT_drive)
- {
- D_S[d].CDsize_frm=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
- D_S[d].n_first_track=infobuf[2];
- D_S[d].n_last_track=infobuf[3];
-
- }
D_S[d].diskstate_flags |= cd_size_bit;
msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
return (0);
{
D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+ D_S[d].CDsize_frm=D_S[d].size_blk+1;
D_S[d].n_first_track=infobuf[2];
D_S[d].n_last_track=infobuf[3];
}
D_S[d].diskstate_flags &= ~upc_bit;
#if TEST_UPC
- for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
+ for (block=CD_MSF_OFFSET+1;block<CD_MSF_OFFSET+200;block++)
{
#endif TEST_UPC
clr_cmdbuf();
drvcmd[9]=0x40;
i=cmd_out();
if (i<0) return (i);
- D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+ D_S[d].first_session=infobuf[2];
+ D_S[d].last_session=infobuf[3];
+ D_S[d].track_of_last_session=infobuf[6];
+ if (D_S[d].first_session!=D_S[d].last_session)
+ {
+ D_S[d].f_multisession=1;
+ D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+ }
}
for (i=0;i<response_count;i++)
sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
delay_timer.expires=jiffies+11*HZ/10;
timed_out_delay=0;
add_timer(&delay_timer);
+#if 0
msg(DBG_TIM,"delay timer started (11*HZ/10).\n");
+#endif
do
{
i=inb(CDi_status);
}
while (!timed_out_delay);
del_timer(&delay_timer);
+#if 0
msg(DBG_TIM,"datarate: %04X\n", datarate);
+#endif
if (datarate<65536) datarate=65536;
maxtim16=datarate*16;
maxtim04=datarate*4;
#else
maxtim_data=datarate/300;
#endif LONG_TIMING
- msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n",
- maxtim_8, maxtim_data);
+#if 0
+ msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n", maxtim_8, maxtim_data);
+#endif
}
/*==========================================================================*/
#if 0
else
{
D_S[d].drv_type=drv_100;
- if ((j!=500)||(j!=102)) ask_mail();
+ if ((j!=500)&&(j!=102)) ask_mail();
}
}
else if (fam2_drive)
{
msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
- msg(DBG_INF,"support is not fulfilled yet - audio should work.\n");
- if ((j!=101)&&(j!=35)) ask_mail(); /* only 1.01 and 0.35 known at time */
+ msg(DBG_INF,"CD200 is not fully supported yet - CD200F should work.\n");
+ if ((j!=1)&&(j!=101)&&(j!=35)) ask_mail(); /* unknown version at time */
}
}
msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
if ((fam0L_drive) && (D_S[d].xa_byte==0x20))
{
/* XA disk with old drive */
- cc_ModeSelect(CD_FRAMESIZE_XA);
+ cc_ModeSelect(CD_FRAMESIZE_RAW1);
cc_ModeSense();
}
if (famT_drive) cc_prep_mode_T();
case CDROMREADMODE2: /* not usable at the moment */
msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
- cc_ModeSelect(CD_FRAMESIZE_XA);
+ cc_ModeSelect(CD_FRAMESIZE_RAW1);
cc_ModeSense();
D_S[d].mode=READ_M2;
return (0);
flags_cmd_out |= f_respo3;
cc_ReadStatus();
if (sbp_status() != 0) break;
- sbp_sleep(HZ/100); /* wait a bit, try again */
+ sbp_sleep(1); /* wait a bit, try again */
}
if (status_tries == 0)
{
if (try != 0 || timeout <= jiffies) break;
if (data_retrying == 0) data_waits++;
data_retrying = 1;
- sbp_sleep(HZ/100);
+ sbp_sleep(1);
try = 1;
}
if (try==0)
* or
* sbpcd=0x300,LaserMate
* or
- * sbpcd=0x330,SPEA
+ * sbpcd=0x330,SoundScape
*
- * (upper/lower case sensitive here!!!).
+ * (upper/lower case sensitive here - but all-lowercase is ok!!!).
*
- * the address value has to be the TRUE CDROM PORT ADDRESS -
+ * the address value has to be the CDROM PORT ADDRESS -
* not the soundcard base address.
+ * For the SPEA/SoundScape setup, DO NOT specify the "configuration port"
+ * address, but the address which is really used for the CDROM (usually 8
+ * bytes above).
*
*/
#if (SBPCD_ISSUE-1)
{
setup_done++;
msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
- sbpro_type=0;
+ sbpro_type=0; /* default: "LaserMate" */
+ if (p[0]>1) sbpro_type=p[2];
if (!strcmp(s,str_sb)) sbpro_type=1;
else if (!strcmp(s,str_sb_l)) sbpro_type=1;
else if (!strcmp(s,str_sp)) sbpro_type=2;
else if (!strcmp(s,str_sp_l)) sbpro_type=2;
+ else if (!strcmp(s,str_ss)) sbpro_type=2;
+ else if (!strcmp(s,str_ss_l)) sbpro_type=2;
if (p[0]>0) sbpcd_ioaddr=p[1];
CDo_command=sbpcd_ioaddr;
/*==========================================================================*/
/*
* Sequoia S-1000 CD-ROM Interface Configuration
- * as used within SPEA Media FX card
- * The SPEA soundcard has to get configured for
- * -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi)
- * -> I/O base address (0x320, 0x330, 0x340, 0x350)
+ * as used within SPEA Media FX, Ensonic SoundScape and some Reveal cards
+ * The soundcard has to get jumpered for the interface type "Panasonic"
+ * (not Sony or Mitsumi) and to get soft-configured for
+ * -> configuration port address
+ * -> CDROM port offset (num_ports): has to be 8 here. Possibly this
+ * offset value determines the interface type (none, Panasonic,
+ * Mitsumi, Sony).
+ * The interface uses a configuration port (0x320, 0x330, 0x340, 0x350)
+ * some bytes below the real CDROM address.
+ *
+ * For the Panasonic style (LaserMate) interface and the configuration
+ * port 0x330, we have to use an offset of 8; so, the real CDROM port
+ * address is 0x338.
*/
static int config_spea(void)
{
int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
- /* What is n_ports? Number of addresses or base address offset? */
- int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
- int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */
+ /* base address offset between configuration port and CDROM port */
+ int irq_number=0; /* off:0x00, 2:0x01, 7:0x03, 12:0x05, 15:0x07 */
+ int dma_channel=0; /* off: 0x00, 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68 */
int dack_polarity=0; /* L:0x00, H:0x80 */
int drq_polarity=0x40; /* L:0x00, H:0x40 */
-
int i;
-
-#define SPEA_REG_1 sbpcd_ioaddr+4
-#define SPEA_REG_2 sbpcd_ioaddr+5
+
+#define SPEA_REG_1 sbpcd_ioaddr-0x08+4
+#define SPEA_REG_2 sbpcd_ioaddr-0x08+5
OUT(SPEA_REG_1,0xFF);
i=inb(SPEA_REG_1);
OUT(SPEA_REG_2,i);
sbpro_type = 0; /* acts like a LaserMate interface now */
- msg(DBG_SEQ,"found SPEA interface at %04X.\n", sbpcd_ioaddr);
+ msg(DBG_SEQ,"found SoundScape interface at %04X.\n", sbpcd_ioaddr);
return (0);
}
/*==========================================================================*/
#if DISTRIBUTION
if (!setup_done)
{
- msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, IBM, Longshine, TEAC CD-ROM drives\n");
- msg(DBG_INF,"\n= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
- msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an ethernet card).\n");
+ msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, Longshine, TEAC CD-ROM drives\n");
+ msg(DBG_INF,"= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
+ msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an NE2000 card).\n");
msg(DBG_INF,"If that happens, you have to reboot and use the\n");
msg(DBG_INF,"LILO (kernel) command line feature like:\n");
msg(DBG_INF," LILO boot: ... sbpcd=0x230,SoundBlaster\n");
msg(DBG_INF,"or like:\n");
msg(DBG_INF," LILO boot: ... sbpcd=0x300,LaserMate\n");
msg(DBG_INF,"or like:\n");
- msg(DBG_INF," LILO boot: ... sbpcd=0x330,SPEA\n");
+ msg(DBG_INF," LILO boot: ... sbpcd=0x338,SoundScape\n");
msg(DBG_INF,"with your REAL address.\n");
- msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = = = = = =\n\n");
+ msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = == = = =\n");
}
#endif DISTRIBUTION
sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
L_OBJS += cyclades.o
endif
+ifeq ($(CONFIG_STALLION),y)
+L_OBJS += stallion.o
+else
+ ifeq ($(CONFIG_STALLION),m)
+ M_OBJS += stallion.o
+ endif
+endif
+
+ifeq ($(CONFIG_ISTALLION),y)
+L_OBJS += istallion.o
+else
+ ifeq ($(CONFIG_ISTALLION),m)
+ M_OBJS += istallion.o
+ endif
+endif
+
ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
M = y
L_OBJS += atixlmouse.o
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
+#include <linux/mouse.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
-#include "mouse.h"
-
#define ATIXL_MOUSE_IRQ 5 /* H/W interrupt # set up on ATIXL board */
#define ATIXL_BUSMOUSE 3 /* Minor device # (mknod c 10 3 /dev/bm) */
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/mm.h>
+#include <linux/mouse.h>
#include <asm/io.h>
#include <asm/segment.h>
fasync_mouse,
};
-static struct mouse bus_mous = {
+static struct mouse bus_mouse = {
LOGITECH_BUSMOUSE, "busmouse", &bus_mouse_fops
};
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * istallion.c -- stallion intelligent multiport serial driver.
+ *
+ * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This code is loosely based on the Linux serial driver, written by
+ * Linus Torvalds, Theodore T'so and others.
+ *
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/cdk.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+/*****************************************************************************/
+
+/*
+ * Define different board types. Not all of the following board types
+ * are supported by this driver. But I will use the standard "assigned"
+ * board numbers. Currently supported boards are abbreviated as:
+ * ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
+ * STAL = Stallion.
+ */
+#define BRD_UNKNOWN 0
+#define BRD_STALLION 1
+#define BRD_BRUMBY4 2
+#define BRD_ONBOARD2 3
+#define BRD_ONBOARD 4
+#define BRD_BRUMBY8 5
+#define BRD_BRUMBY16 6
+#define BRD_ONBOARDE 7
+#define BRD_ONBOARD32 9
+#define BRD_ONBOARD2_32 10
+#define BRD_ONBOARDRS 11
+#define BRD_EASYIO 20
+#define BRD_ECH 21
+#define BRD_ECHMC 22
+#define BRD_ECP 23
+#define BRD_ECPE 24
+#define BRD_ECPMC 25
+#define BRD_ECHPCI 26
+
+#define BRD_BRUMBY BRD_BRUMBY4
+
+/*
+ * Define a configuration structure to hold the board configuration.
+ * Need to set this up in the code (for now) with the boards that are
+ * to be configured into the system. This is what needs to be modified
+ * when adding/removing/modifying boards. Each line entry in the
+ * stli_brdconf[] array is a board. Each line contains io/irq/memory
+ * ranges for that board (as well as what type of board it is).
+ * Some examples:
+ * { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+ * This line will configure an EasyConnection 8/64 at io address 2a0,
+ * and shared memory address of cc000. Multiple EasyConnection 8/64
+ * boards can share the same shared memory address space. No interrupt
+ * is required for this board type.
+ * Another example:
+ * { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
+ * This line will configure an ONboard (ISA type) at io address 240,
+ * and shared memory address of d0000. Multiple ONboards can share
+ * the same shared memory address space. No interrupt required.
+ * Another example:
+ * { BRD_BRUMBY4, 0x360, 0, 0xc8000, 0, 0 },
+ * This line will configure a Brumby board (any number of ports!) at
+ * io address 360 and shared memory address of c8000. All Brumby boards
+ * configured into a system must have their own separate io and memory
+ * addresses. No interrupt is required.
+ * Another example:
+ * { BRD_STALLION, 0x330, 0, 0xd0000, 0, 0 },
+ * This line will configure an original Stallion board at io address 330
+ * and shared memory address d0000 (this would only be valid for a "V4.0"
+ * or Rev.O Stallion board). All Stallion boards configured into the
+ * system must have their own separate io and memory addresses. No
+ * interrupt is required.
+ */
+
+typedef struct {
+ int brdtype;
+ int ioaddr1;
+ int ioaddr2;
+ unsigned long memaddr;
+ int irq;
+ int irqtype;
+} stlconf_t;
+
+static stlconf_t stli_brdconf[] = {
+ { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+};
+
+static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t);
+
+/*
+ * Code support is offered for boards to use the above 1Mb memory
+ * ranges for those boards which support this (supported on the ONboard
+ * and ECP-EI hardware). The following switch should be enabled. The only
+ * catch is that the kernel functions required to do this are not
+ * normally exported symbols, so you will have to do some extra work
+ * for this to be used in the loadable module form of the driver.
+ * Unfortunately this doesn't work either if you linke the driver into
+ * the kernel, sincethe memory management code is not set up early
+ * enough (before our initialization routine is run).
+ */
+#define STLI_HIMEMORY 0
+
+#if STLI_HIMEMORY
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Define some important driver characteristics. Device major numbers
+ * allocated as per Linux Device Registery.
+ */
+#ifndef STL_SIOMEMMAJOR
+#define STL_SIOMEMMAJOR 28
+#endif
+#ifndef STL_SERIALMAJOR
+#define STL_SERIALMAJOR 24
+#endif
+#ifndef STL_CALLOUTMAJOR
+#define STL_CALLOUTMAJOR 25
+#endif
+
+#define STL_DRVTYPSERIAL 1
+#define STL_DRVTYPCALLOUT 2
+
+#define STL_MAXBRDS 4
+#define STL_MAXPANELS 4
+#define STL_MAXPORTS 64
+#define STL_MAXCHANS (STL_MAXPORTS + 1)
+#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS)
+
+/*****************************************************************************/
+
+/*
+ * Define our local driver identity first. Set up stuff to deal with
+ * all the local structures required by a serial tty driver.
+ */
+static char *stli_drvname = "Stallion Intelligent Multiport Serial Driver";
+static char *stli_drvversion = "1.0.0";
+static char *stli_serialname = "ttyE";
+static char *stli_calloutname = "cue";
+
+static struct tty_driver stli_serial;
+static struct tty_driver stli_callout;
+static struct tty_struct *stli_ttys[STL_MAXDEVS];
+static struct termios *stli_termios[STL_MAXDEVS];
+static struct termios *stli_termioslocked[STL_MAXDEVS];
+static int stli_refcount;
+
+/*
+ * We will need to allocate a temporary write buffer for chars that
+ * come direct from user space. The problem is that a copy from user
+ * space might cause a page fault (typically on a system that is
+ * swapping!). All ports will share one buffer - since if the system
+ * is already swapping a shared buffer won't make things any worse.
+ */
+static char *stli_tmpwritebuf = (char *) NULL;
+static struct semaphore stli_tmpwritesem = MUTEX;
+
+#define STLI_TXBUFSIZE 4096
+
+/*
+ * Use a fast local buffer for cooked characters. Typically a whole
+ * bunch of cooked characters come in for a port, 1 at a time. So we
+ * save those up into a local buffer, then write out the whole lot
+ * with a large memcpy. Just use 1 buffer for all ports, since its
+ * use it is only need for short periods of time by each port.
+ */
+static char *stli_txcookbuf = (char *) NULL;
+static int stli_txcooksize = 0;
+static int stli_txcookrealsize = 0;
+static struct tty_struct *stli_txcooktty = (struct tty_struct *) NULL;
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially. Basically all it defines is a raw port
+ * at 9600 baud, 8 data bits, no parity, 1 stop bit.
+ */
+static struct termios stli_deftermios = {
+ 0,
+ 0,
+ (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+ 0,
+ 0,
+ INIT_C_CC
+};
+
+/*
+ * Memory allocation vars. These keep track of what memory allocation
+ * we can currently use. They help deal with memory in a consistent
+ * way, whether during init or run-time.
+ */
+static int stli_meminited = 0;
+static long stli_memend;
+
+/*****************************************************************************/
+
+/*
+ * Define a set of structures to hold all the board/panel/port info
+ * for our ports. These will be dynamically allocated as required at
+ * driver initialization time.
+ */
+
+/*
+ * Port and board structures to hold status info about each object.
+ * The board structure contains pointers to structures for each port
+ * connected to it. Panels are not distinguished here, since
+ * communication with the slave board will always be on a per port
+ * basis.
+ */
+typedef struct {
+ int portnr;
+ int panelnr;
+ int brdnr;
+ unsigned long state;
+ int devnr;
+ int flags;
+ int baud_base;
+ int custom_divisor;
+ int close_delay;
+ int closing_wait;
+ int refcount;
+ int openwaitcnt;
+ int rc;
+ int argsize;
+ void *argp;
+ long session;
+ long pgrp;
+ unsigned int rxmarkmsk;
+ struct tty_struct *tty;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *raw_wait;
+ struct tq_struct tqhangup;
+ struct termios normaltermios;
+ struct termios callouttermios;
+ asysigs_t asig;
+ unsigned long addr;
+ unsigned long rxoffset;
+ unsigned long txoffset;
+ unsigned int rxsize;
+ unsigned int txsize;
+ unsigned long sigs;
+ unsigned char reqbit;
+ unsigned char portidx;
+ unsigned char portbit;
+} stliport_t;
+
+/*
+ * Use a structure of function pointers to do board level operations.
+ * These include, enable/disable, paging shared memory, interrupting, etc.
+ */
+typedef struct stlbrd {
+ int brdnr;
+ int brdtype;
+ int state;
+ int nrpanels;
+ int nrports;
+ int nrdevs;
+ unsigned int iobase;
+ void *membase;
+ int memsize;
+ int pagesize;
+ int hostoffset;
+ int slaveoffset;
+ int bitsize;
+ int panels[STL_MAXPANELS];
+ void (*init)(struct stlbrd *brdp);
+ void (*enable)(struct stlbrd *brdp);
+ void (*reenable)(struct stlbrd *brdp);
+ void (*disable)(struct stlbrd *brdp);
+ char *(*getmemptr)(struct stlbrd *brdp, unsigned long offset, int line);
+ void (*intr)(struct stlbrd *brdp);
+ void (*reset)(struct stlbrd *brdp);
+ stliport_t *ports[STL_MAXPORTS];
+} stlibrd_t;
+
+static stlibrd_t *stli_brds;
+
+static int stli_shared = 0;
+
+/*
+ * Per board state flags. Used with the state field of the board struct.
+ * Not really much here... All we need to do is keep track of whether
+ * the board has been detected, and whether it is actully running a slave
+ * or not.
+ */
+#define BST_FOUND 0x1
+#define BST_STARTED 0x2
+
+/*
+ * Define the set of port state flags. These are marked for internal
+ * state purposes only, usually to do with the state of communications
+ * with the slave. Most of them need to be updated atomically, so always
+ * use the bit setting operations (unless protected by cli/sti).
+ */
+#define ST_INITIALIZING 1
+#define ST_OPENING 2
+#define ST_CLOSING 3
+#define ST_CMDING 4
+#define ST_TXBUSY 5
+#define ST_RXING 6
+#define ST_DOFLUSHRX 7
+#define ST_DOFLUSHTX 8
+#define ST_DOSIGS 9
+#define ST_RXSTOP 10
+#define ST_GETSIGS 11
+
+/*
+ * Define an array of board names as printable strings. Handy for
+ * referencing boards when printing trace and stuff.
+ */
+static char *stli_brdnames[] = {
+ "Unknown",
+ "Stallion",
+ "Brumby",
+ "ONboard-MC",
+ "ONboard",
+ "Brumby",
+ "Brumby",
+ "ONboard-EI",
+ (char *) NULL,
+ "ONboard",
+ "ONboard-MC",
+ "ONboard-MC",
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ "EasyIO",
+ "EC8/32-AT",
+ "EC8/32-MC",
+ "EC8/64-AT",
+ "EC8/64-EI",
+ "EC8/64-MC",
+ "EC8/32-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ * Hardware configuration info for ECP boards. These defines apply
+ * to the directly accessable io ports of the ECP. There is a set of
+ * defines for each ECP board type, ISA, EISA and MCA.
+ */
+#define ECP_IOSIZE 4
+#define ECP_MEMSIZE (128 * 1024)
+#define ECP_ATPAGESIZE (4 * 1024)
+#define ECP_EIPAGESIZE (64 * 1024)
+#define ECP_MCPAGESIZE (4 * 1024)
+
+/*
+ * Important defines for the ISA class of ECP board.
+ */
+#define ECP_ATIREG 0
+#define ECP_ATCONFR 1
+#define ECP_ATMEMAR 2
+#define ECP_ATMEMPR 3
+#define ECP_ATSTOP 0x1
+#define ECP_ATINTENAB 0x10
+#define ECP_ATENABLE 0x20
+#define ECP_ATDISABLE 0x00
+#define ECP_ATADDRMASK 0x3f000
+#define ECP_ATADDRSHFT 12
+
+/*
+ * Important defines for the EISA class of ECP board.
+ */
+#define ECP_EIIREG 0
+#define ECP_EIMEMARL 1
+#define ECP_EICONFR 2
+#define ECP_EIMEMARH 3
+#define ECP_EIENABLE 0x1
+#define ECP_EIDISABLE 0x0
+#define ECP_EISTOP 0x4
+#define ECP_EIEDGE 0x00
+#define ECP_EILEVEL 0x80
+#define ECP_EIADDRMASKL 0x00ff0000
+#define ECP_EIADDRSHFTL 16
+#define ECP_EIADDRMASKH 0xff000000
+#define ECP_EIADDRSHFTH 24
+#define ECP_EIBRDENAB 0xc84
+
+/*
+ * Important defines for the Micro-channel class of ECP board.
+ * (It has a lot in common with the ISA boards.)
+ */
+#define ECP_MCIREG 0
+#define ECP_MCCONFR 1
+#define ECP_MCSTOP 0x20
+#define ECP_MCENABLE 0x80
+#define ECP_MCDISABLE 0x00
+
+/*
+ * Hardware configuration info for ONboard and Brumby boards. These
+ * defines apply to the directly accessable io ports of these boards.
+ */
+#define ONB_IOSIZE 16
+#define ONB_MEMSIZE (64 * 1024)
+#define ONB_ATPAGESIZE (64 * 1024)
+#define ONB_MCPAGESIZE (64 * 1024)
+#define ONB_EIMEMSIZE (128 * 1024)
+#define ONB_EIPAGESIZE (64 * 1024)
+
+/*
+ * Important defines for the ISA class of ONboard board.
+ */
+#define ONB_ATIREG 0
+#define ONB_ATMEMAR 1
+#define ONB_ATCONFR 2
+#define ONB_ATSTOP 0x4
+#define ONB_ATENABLE 0x01
+#define ONB_ATDISABLE 0x00
+#define ONB_ATADDRMASK 0xff0000
+#define ONB_ATADDRSHFT 16
+
+#if STLI_HIMEMORY
+#define ONB_HIMEMENAB 0x02
+#else
+#define ONB_HIMEMENAB 0
+#endif
+
+/*
+ * Important defines for the EISA class of ONboard board.
+ */
+#define ONB_EIIREG 0
+#define ONB_EIMEMARL 1
+#define ONB_EICONFR 2
+#define ONB_EIMEMARH 3
+#define ONB_EIENABLE 0x1
+#define ONB_EIDISABLE 0x0
+#define ONB_EISTOP 0x4
+#define ONB_EIEDGE 0x00
+#define ONB_EILEVEL 0x80
+#define ONB_EIADDRMASKL 0x00ff0000
+#define ONB_EIADDRSHFTL 16
+#define ONB_EIADDRMASKH 0xff000000
+#define ONB_EIADDRSHFTH 24
+#define ONB_EIBRDENAB 0xc84
+
+/*
+ * Important defines for the Brumby boards. They are pretty simple,
+ * there is not much that is programmably configurable.
+ */
+#define BBY_IOSIZE 16
+#define BBY_MEMSIZE (64 * 1024)
+#define BBY_PAGESIZE (16 * 1024)
+
+#define BBY_ATIREG 0
+#define BBY_ATCONFR 1
+#define BBY_ATSTOP 0x4
+
+/*
+ * Important defines for the Stallion boards. They are pretty simple,
+ * there is not much that is programmably configurable.
+ */
+#define STAL_IOSIZE 16
+#define STAL_MEMSIZE (64 * 1024)
+#define STAL_PAGESIZE (64 * 1024)
+
+/*
+ * Define the set of status register values for EasyConnection panels.
+ * The signature will return with the status value for each panel. From
+ * this we can determine what is attached to the board - before we have
+ * actually down loaded any code to it.
+ */
+#define ECH_PNLSTATUS 2
+#define ECH_PNL16PORT 0x20
+#define ECH_PNLIDMASK 0x07
+#define ECH_PNLINTRPEND 0x80
+
+/*
+ * Define some macros to do things to the board. Even those these boards
+ * are somewhat related there is often significantly different ways of
+ * doing some operation on it (like enable, paging, reset, etc). So each
+ * board class has a set of functions which do the commonly required
+ * operations. The macros below basically just call these functions,
+ * generally checking for a NULL function - which means that the board
+ * needs nothing done to it to achieve this operation!
+ */
+#define EBRDINIT(brdp) \
+ if (brdp->init != NULL) \
+ (* brdp->init)(brdp)
+
+#define EBRDENABLE(brdp) \
+ if (brdp->enable != NULL) \
+ (* brdp->enable)(brdp);
+
+#define EBRDDISABLE(brdp) \
+ if (brdp->disable != NULL) \
+ (* brdp->disable)(brdp);
+
+#define EBRDINTR(brdp) \
+ if (brdp->intr != NULL) \
+ (* brdp->intr)(brdp);
+
+#define EBRDRESET(brdp) \
+ if (brdp->reset != NULL) \
+ (* brdp->reset)(brdp);
+
+#define EBRDGETMEMPTR(brdp,offset) \
+ (* brdp->getmemptr)(brdp, offset, __LINE__)
+
+/*
+ * Define the maximal baud rate, and he default baud base for ports.
+ */
+#define STL_MAXBAUD 230400
+#define STL_BAUDBASE 115200
+#define STL_CLOSEDELAY 50
+
+/*****************************************************************************/
+
+/*
+ * Define macros to extract a brd or port number from a minor number.
+ */
+#define MKDEV2BRD(min) (((min) & 0xc0) >> 6)
+#define MKDEV2PORT(min) ((min) & 0x3f)
+
+/*
+ * Define a baud rate table that converts termios baud rate selector
+ * into the actual baud rate value. All baud rate calculations are based
+ * on the actual baud rate required.
+ */
+static unsigned int stli_baudrates[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400
+};
+
+/*****************************************************************************/
+
+/*
+ * Define some handy local macros...
+ */
+#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
+
+/*****************************************************************************/
+
+/*
+ * Prototype all functions in this driver!
+ */
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#else
+static void stli_meminit(long base);
+static long stli_memhalt(void);
+#endif
+static void *stli_memalloc(int len);
+
+long stli_init(long kmem_start);
+static int stli_open(struct tty_struct *tty, struct file *filp);
+static void stli_close(struct tty_struct *tty, struct file *filp);
+static int stli_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static void stli_putchar(struct tty_struct *tty, unsigned char ch);
+static void stli_flushchars(struct tty_struct *tty);
+static int stli_writeroom(struct tty_struct *tty);
+static int stli_charsinbuffer(struct tty_struct *tty);
+static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void stli_settermios(struct tty_struct *tty, struct termios *old);
+static void stli_throttle(struct tty_struct *tty);
+static void stli_unthrottle(struct tty_struct *tty);
+static void stli_stop(struct tty_struct *tty);
+static void stli_start(struct tty_struct *tty);
+static void stli_flushbuffer(struct tty_struct *tty);
+static void stli_hangup(struct tty_struct *tty);
+
+static int stli_brdinit(void);
+static int stli_initecp(stlibrd_t *brdp, stlconf_t *confp);
+static int stli_initonb(stlibrd_t *brdp, stlconf_t *confp);
+static int stli_initports(stlibrd_t *brdp);
+static int stli_startbrd(stlibrd_t *brdp);
+static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count);
+static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count);
+static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void stli_poll(unsigned long arg);
+static int stli_hostcmd(stlibrd_t *brdp, int channr);
+static int stli_initopen(stlibrd_t *brdp, stliport_t *portp);
+static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp);
+static void stli_dohangup(void *arg);
+static void stli_delay(int len);
+static int stli_setport(stliport_t *portp);
+static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp);
+static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp);
+static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
+static long stli_mktiocm(unsigned long sigvalue);
+static void stli_read(stlibrd_t *brdp, stliport_t *portp);
+static void stli_getserial(stliport_t *portp, struct serial_struct *sp);
+static int stli_setserial(stliport_t *portp, struct serial_struct *sp);
+
+static void stli_ecpinit(stlibrd_t *brdp);
+static void stli_ecpenable(stlibrd_t *brdp);
+static void stli_ecpdisable(stlibrd_t *brdp);
+static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpreset(stlibrd_t *brdp);
+static void stli_ecpintr(stlibrd_t *brdp);
+static void stli_ecpeiinit(stlibrd_t *brdp);
+static void stli_ecpeienable(stlibrd_t *brdp);
+static void stli_ecpeidisable(stlibrd_t *brdp);
+static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpeireset(stlibrd_t *brdp);
+static void stli_ecpmcenable(stlibrd_t *brdp);
+static void stli_ecpmcdisable(stlibrd_t *brdp);
+static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpmcreset(stlibrd_t *brdp);
+
+static void stli_onbinit(stlibrd_t *brdp);
+static void stli_onbenable(stlibrd_t *brdp);
+static void stli_onbdisable(stlibrd_t *brdp);
+static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_onbreset(stlibrd_t *brdp);
+static void stli_onbeinit(stlibrd_t *brdp);
+static void stli_onbeenable(stlibrd_t *brdp);
+static void stli_onbedisable(stlibrd_t *brdp);
+static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_onbereset(stlibrd_t *brdp);
+static void stli_bbyinit(stlibrd_t *brdp);
+static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_bbyreset(stlibrd_t *brdp);
+static void stli_stalinit(stlibrd_t *brdp);
+static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_stalreset(stlibrd_t *brdp);
+
+#if STLI_HIMEMORY
+static void *stli_mapbrdmem(unsigned long physaddr, unsigned int size);
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Define the driver info for a user level shared memory device. This
+ * device will work sort of like the /dev/kmem device - except that it
+ * will give access to the shared memory on the Stallion intelligent
+ * board. This is also a very useful debugging tool.
+ */
+static struct file_operations stli_fsiomem = {
+ NULL,
+ stli_memread,
+ stli_memwrite,
+ NULL,
+ NULL,
+ stli_memioctl,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*****************************************************************************/
+
+/*
+ * Define a timer_list entry for our poll routine. The slave board
+ * is polled every so often to see if anything needs doing. This is
+ * much cheaper on host cpu than using interrupts. It turns out to
+ * not increase character latency by much either...
+ */
+static struct timer_list stli_timerlist = {
+ NULL, NULL, 0, 0, stli_poll
+};
+
+static int stli_timeron = 0;
+
+/*
+ * This is hack to allow for the kernel changes made to add_timer
+ * in the newer 1.3.X kernels (changed around 1.3.1X).
+ */
+#ifdef LINUX_1_2_X_COMPAT
+#define STLI_TIMEOUT 0
+#else
+#define STLI_TIMEOUT (jiffies + 1)
+#endif
+
+/*****************************************************************************/
+
+#ifdef MODULE
+
+/*
+ * Include kernel version number for modules.
+ */
+char kernel_version[] = UTS_RELEASE;
+
+int init_module()
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("init_module()\n");
+#endif
+
+ save_flags(flags);
+ cli();
+ stli_init(0);
+ restore_flags(flags);
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+void cleanup_module()
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned long flags;
+ int i, j;
+
+#if DEBUG
+ printk("cleanup_module()\n");
+#endif
+
+ printk("Unloading %s: version %s\n", stli_drvname, stli_drvversion);
+
+ save_flags(flags);
+ cli();
+
+/*
+ * Free up all allocated resources used by the ports. This includes
+ * memory and interrupts.
+ */
+ if (stli_timeron) {
+ stli_timeron = 0;
+ del_timer(&stli_timerlist);
+ }
+
+ i = tty_unregister_driver(&stli_serial);
+ j = tty_unregister_driver(&stli_callout);
+ if (i || j) {
+ printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+ restore_flags(flags);
+ return;
+ }
+ if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+ printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+
+ if (stli_tmpwritebuf != (char *) NULL)
+ kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
+ if (stli_txcookbuf != (char *) NULL)
+ kfree_s(stli_txcookbuf, STLI_TXBUFSIZE);
+
+ for (i = 0; (i < stli_nrbrds); i++) {
+ brdp = &stli_brds[i];
+ for (j = 0; (j < STL_MAXPORTS); j++) {
+ portp = brdp->ports[j];
+ if (portp != (stliport_t *) NULL) {
+ if (portp->tty != (struct tty_struct *) NULL)
+ tty_hangup(portp->tty);
+ kfree_s(portp, sizeof(stliport_t));
+ }
+ }
+
+#if STLI_HIMEMORY
+ if (((unsigned long) brdp->membase) >= 0x100000)
+ vfree(brdp->membase);
+#endif
+ if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC))
+ release_region(brdp->iobase, ECP_IOSIZE);
+ else
+ release_region(brdp->iobase, ONB_IOSIZE);
+ }
+ kfree_s(stli_brds, (sizeof(stlibrd_t) * stli_nrbrds));
+
+ restore_flags(flags);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Local memory allocation routines. These are used so we can deal with
+ * memory allocation at init time and during run-time in a consistent
+ * way. Everbody just calls the stli_memalloc routine to allocate
+ * memory and it will do the right thing. There is no common memory
+ * deallocation code - since this is only done is special cases, all of
+ * which are tightly controlled.
+ */
+
+#ifndef MODULE
+
+static void stli_meminit(long base)
+{
+ stli_memend = base;
+ stli_meminited = 1;
+}
+
+static long stli_memhalt()
+{
+ stli_meminited = 0;
+ return(stli_memend);
+}
+
+#endif
+
+static void *stli_memalloc(int len)
+{
+ void *mem;
+
+ if (stli_meminited) {
+ mem = (void *) stli_memend;
+ stli_memend += len;
+ } else {
+ mem = (void *) kmalloc(len, GFP_KERNEL);
+ }
+ return(mem);
+}
+
+/*****************************************************************************/
+
+static int stli_open(struct tty_struct *tty, struct file *filp)
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned int minordev;
+ int brdnr, portnr, rc;
+
+#if DEBUG
+ printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+#endif
+
+ minordev = MINOR(tty->device);
+ brdnr = MKDEV2BRD(minordev);
+ if (brdnr >= stli_nrbrds)
+ return(-ENODEV);
+ if (stli_brds == (stlibrd_t *) NULL)
+ return(-ENODEV);
+ brdp = &stli_brds[brdnr];
+ if ((brdp->state & BST_STARTED) == 0)
+ return(-ENODEV);
+ portnr = MKDEV2PORT(minordev);
+ if ((portnr < 0) || (portnr > brdp->nrports))
+ return(-ENODEV);
+
+ portp = brdp->ports[portnr];
+ if (portp == (stliport_t *) NULL)
+ return(-ENODEV);
+ if (portp->devnr < 1)
+ return(-ENODEV);
+
+/*
+ * Check if this port is in the middle of closing. If so then wait
+ * until it is closed then return error status based on flag settings.
+ * The sleep here does not need interrupt protection since the wakeup
+ * for it is done with the same context.
+ */
+ if (portp->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&portp->close_wait);
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ return(-EAGAIN);
+ return(-ERESTARTSYS);
+ }
+
+/*
+ * On the first open of the device setup the port hardware, and
+ * initialize the per port data structure. Since initializing the port
+ * requires serval commands to the board we will need to wait for any
+ * other open that is already initializing the port.
+ */
+ portp->tty = tty;
+ tty->driver_data = portp;
+ portp->refcount++;
+
+ while (test_bit(ST_INITIALIZING, &portp->state)) {
+ if (current->signal & ~current->blocked)
+ return(-ERESTARTSYS);
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+
+ if ((portp->flags & ASYNC_INITIALIZED) == 0) {
+ set_bit(ST_INITIALIZING, &portp->state);
+ if ((rc = stli_initopen(brdp, portp)) >= 0) {
+ portp->flags |= ASYNC_INITIALIZED;
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ }
+ clear_bit(ST_INITIALIZING, &portp->state);
+ wake_up_interruptible(&portp->open_wait);
+ if (rc < 0)
+ return(rc);
+ }
+
+/*
+ * Check if this port is in the middle of closing. If so then wait
+ * until it is closed then return error status, based on flag settings.
+ * The sleep here does not need interrupt protection since the wakeup
+ * for it is done with the same context.
+ */
+ if (portp->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&portp->close_wait);
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ return(-EAGAIN);
+ return(-ERESTARTSYS);
+ }
+
+/*
+ * Based on type of open being done check if it can overlap with any
+ * previous opens still in effect. If we are a normal serial device
+ * then also we might have to wait for carrier.
+ */
+ if (tty->driver.subtype == STL_DRVTYPCALLOUT) {
+ if (portp->flags & ASYNC_NORMAL_ACTIVE)
+ return(-EBUSY);
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
+ if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
+ (portp->session != current->session))
+ return(-EBUSY);
+ if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
+ (portp->pgrp != current->pgrp))
+ return(-EBUSY);
+ }
+ portp->flags |= ASYNC_CALLOUT_ACTIVE;
+ } else {
+ if (filp->f_flags & O_NONBLOCK) {
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+ return(-EBUSY);
+ } else {
+ if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0)
+ return(rc);
+ }
+ portp->flags |= ASYNC_NORMAL_ACTIVE;
+ }
+
+ if ((portp->refcount == 1) && (portp->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == STL_DRVTYPSERIAL)
+ *tty->termios = portp->normaltermios;
+ else
+ *tty->termios = portp->callouttermios;
+ stli_setport(portp);
+ }
+
+ portp->session = current->session;
+ portp->pgrp = current->pgrp;
+ return(0);
+}
+
+/*****************************************************************************/
+
+static void stli_close(struct tty_struct *tty, struct file *filp)
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
+#endif
+
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+ if (portp->refcount-- > 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ portp->flags |= ASYNC_CLOSING;
+
+ if (portp->flags & ASYNC_NORMAL_ACTIVE)
+ portp->normaltermios = *tty->termios;
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+ portp->callouttermios = *tty->termios;
+
+/*
+ * May want to wait for data to drain before closing. The BUSY flag
+ * keeps track of whether we are still transmitting or not. It is
+ * updated by messages from the slave - indicating when all chars
+ * really have drained.
+ */
+ if (tty == stli_txcooktty)
+ stli_flushchars(tty);
+ tty->closing = 1;
+ if (test_bit(ST_TXBUSY, &portp->state)) {
+ if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, portp->closing_wait);
+ }
+
+ portp->flags &= ~ASYNC_INITIALIZED;
+ brdp = &stli_brds[portp->brdnr];
+ stli_rawclose(brdp, portp, 0, 1);
+ if (tty->termios->c_cflag & HUPCL) {
+ stli_mkasysigs(&portp->asig, 0, 0);
+ stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ }
+ clear_bit(ST_TXBUSY, &portp->state);
+ clear_bit(ST_RXSTOP, &portp->state);
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ if (tty->ldisc.flush_buffer)
+ (tty->ldisc.flush_buffer)(tty);
+ set_bit(ST_DOFLUSHRX, &portp->state);
+ stli_flushbuffer(tty);
+
+ tty->closing = 0;
+ tty->driver_data = (void *) NULL;
+ portp->tty = (struct tty_struct *) NULL;
+
+ if (portp->openwaitcnt) {
+ if (portp->close_delay)
+ stli_delay(portp->close_delay);
+ wake_up_interruptible(&portp->open_wait);
+ }
+
+ portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+ wake_up_interruptible(&portp->close_wait);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Carry out first open operations on a port. This involves a number of
+ * commands to be sent to the slave. We need to open the port, set the
+ * notification events, set the initial port settings, get and set the
+ * initial signal values. We sleep and wait in between each one. But
+ * this still all happens pretty quickly.
+ */
+
+static int stli_initopen(stlibrd_t *brdp, stliport_t *portp)
+{
+ struct tty_struct *tty;
+ asynotify_t nt;
+ asyport_t aport;
+ int rc;
+
+#if DEBUG
+ printk("stli_initopen(brdp=%x,portp=%x)\n", (int) brdp, (int) portp);
+#endif
+
+ if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
+ return(rc);
+
+ memset(&nt, 0, sizeof(asynotify_t));
+ nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
+ nt.signal = SG_DCD;
+ if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0)
+ return(rc);
+
+ tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return(-ENODEV);
+ stli_mkasyport(portp, &aport, tty->termios);
+ if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0)
+ return(rc);
+
+ set_bit(ST_GETSIGS, &portp->state);
+ if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+ return(rc);
+ if (clear_bit(ST_GETSIGS, &portp->state))
+ portp->sigs = stli_mktiocm(portp->asig.sigvalue);
+ stli_mkasysigs(&portp->asig, 1, 1);
+ if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+ return(rc);
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Send an open message to the slave. This will sleep waiting for the
+ * acknowledgement, so must have user context. We need to co-ordinate
+ * with close events here, since we don't want open and close events
+ * to overlap.
+ */
+
+static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
+{
+ volatile cdkhdr_t *hdrp;
+ volatile cdkctrl_t *cp;
+ volatile unsigned char *bits;
+ unsigned long flags;
+ int rc;
+
+#if DEBUG
+ printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+#endif
+
+/*
+ * Send a message to the slave to open this port.
+ */
+ save_flags(flags);
+ cli();
+
+/*
+ * Slave is already closing this port. This can happen if a hangup
+ * occurs on this port. So we must wait until it is complete. The
+ * order of opens and closes may not be preserved across shared
+ * memory, so we must wait until it is complete.
+ */
+ while (test_bit(ST_CLOSING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ restore_flags(flags);
+ return(-ERESTARTSYS);
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+
+/*
+ * Everything is ready now, so write the open message into shared
+ * memory. Once the message is in set the service bits to say that
+ * this port wants service.
+ */
+ EBRDENABLE(brdp);
+ cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ cp->openarg = arg;
+ cp->open = 1;
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp->slavereq |= portp->reqbit;
+ bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+ *bits |= portp->portbit;
+ EBRDDISABLE(brdp);
+
+ if (wait == 0) {
+ restore_flags(flags);
+ return(0);
+ }
+
+/*
+ * Slave is in action, so now we must wait for the open acknowledgment
+ * to come back.
+ */
+ rc = 0;
+ set_bit(ST_OPENING, &portp->state);
+ while (test_bit(ST_OPENING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ rc = -ERESTARTSYS;
+ break;
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+ restore_flags(flags);
+
+ if ((rc == 0) && (portp->rc != 0))
+ rc = -EIO;
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Send a close message to the slave. Normally this will sleep waiting
+ * for the acknowledgement, but if wait parameter is 0 it will not. If
+ * wait is true then must have user context (to sleep).
+ */
+
+static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
+{
+ volatile cdkhdr_t *hdrp;
+ volatile cdkctrl_t *cp;
+ volatile unsigned char *bits;
+ unsigned long flags;
+ int rc;
+
+#if DEBUG
+ printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+#endif
+
+ save_flags(flags);
+ cli();
+
+/*
+ * Slave is already closing this port. This can happen if a hangup
+ * occurs on this port.
+ */
+ if (wait) {
+ while (test_bit(ST_CLOSING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ restore_flags(flags);
+ return(-ERESTARTSYS);
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+ }
+
+/*
+ * Write the close command into shared memory.
+ */
+ EBRDENABLE(brdp);
+ cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ cp->closearg = arg;
+ cp->close = 1;
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp->slavereq |= portp->reqbit;
+ bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+ *bits |= portp->portbit;
+ EBRDDISABLE(brdp);
+
+ set_bit(ST_CLOSING, &portp->state);
+ if (wait == 0) {
+ restore_flags(flags);
+ return(0);
+ }
+
+/*
+ * Slave is in action, so now we must wait for the open acknowledgment
+ * to come back.
+ */
+ rc = 0;
+ while (test_bit(ST_CLOSING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ rc = -ERESTARTSYS;
+ break;
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+ restore_flags(flags);
+
+ if ((rc == 0) && (portp->rc != 0))
+ rc = -EIO;
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Send a command to the slave and wait for the response. This must
+ * have user context (it sleeps). This routine is generic in that it
+ * can send any type of command. Its purpose is to wait for that command
+ * to complete (as opposed to initiating the command then returning).
+ */
+
+static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+#endif
+
+ save_flags(flags);
+ cli();
+ while (test_bit(ST_CMDING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ restore_flags(flags);
+ return(-ERESTARTSYS);
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+
+ stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
+
+ while (test_bit(ST_CMDING, &portp->state)) {
+ if (current->signal & ~current->blocked) {
+ restore_flags(flags);
+ return(-ERESTARTSYS);
+ }
+ interruptible_sleep_on(&portp->raw_wait);
+ }
+ restore_flags(flags);
+
+ if (portp->rc != 0)
+ return(-EIO);
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Send the termios settings for this port to the slave. This sleeps
+ * waiting for the command to complete - so must have user context.
+ */
+
+static int stli_setport(stliport_t *portp)
+{
+ stlibrd_t *brdp;
+ asyport_t aport;
+
+#if DEBUG
+ printk("stli_setport(portp=%x)\n", (int) portp);
+#endif
+
+ if (portp == (stliport_t *) NULL)
+ return(-ENODEV);
+ if (portp->tty == (struct tty_struct *) NULL)
+ return(-ENODEV);
+ if ((portp->brdnr < 0) && (portp->brdnr >= stli_nrbrds))
+ return(-ENODEV);
+ brdp = &stli_brds[portp->brdnr];
+
+ stli_mkasyport(portp, &aport, portp->tty->termios);
+ return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
+}
+
+/*****************************************************************************/
+
+/*
+ * Wait for a specified delay period, this is not a busy-loop. It will
+ * give up the processor while waiting. Unfortunately this has some
+ * rather intimate knowledge of the process management stuff.
+ */
+
+static void stli_delay(int len)
+{
+#if DEBUG
+ printk("stl_delay(len=%d)\n", len);
+#endif
+ if (len > 0) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + len;
+ schedule();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Possibly need to wait for carrier (DCD signal) to come high. Say
+ * maybe because if we are clocal then we don't need to wait...
+ */
+
+static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp)
+{
+ unsigned long flags;
+ int rc;
+
+#if DEBUG
+ printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp);
+#endif
+
+ rc = 0;
+
+ save_flags(flags);
+ cli();
+ portp->openwaitcnt++;
+ if (portp->refcount > 0)
+ portp->refcount--;
+
+ for (;;) {
+ if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) {
+ stli_mkasysigs(&portp->asig, 1, 1);
+ if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+ break;
+ }
+ if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ rc = -EBUSY;
+ else
+ rc = -ERESTARTSYS;
+ break;
+ }
+ if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
+ ((portp->flags & ASYNC_CLOSING) == 0) &&
+ ((portp->tty->termios->c_cflag & CLOCAL) ||
+ (portp->sigs & TIOCM_CD))) {
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ rc = -ERESTARTSYS;
+ break;
+ }
+ interruptible_sleep_on(&portp->open_wait);
+ }
+
+ if (! tty_hung_up_p(filp))
+ portp->refcount++;
+ portp->openwaitcnt--;
+ restore_flags(flags);
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Write routine. Take the data and put it in the shared memory ring
+ * queue. If port is not already sending chars then need to mark the
+ * service bits for this port.
+ */
+
+static int stli_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+ volatile cdkasy_t *ap;
+ volatile cdkhdr_t *hdrp;
+ volatile unsigned char *bits;
+ unsigned char *shbuf, *chbuf;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int len, stlen, head, tail, size;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+#endif
+
+ if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL))
+ return(0);
+ if (tty == stli_txcooktty)
+ stli_flushchars(tty);
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return(0);
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return(0);
+ brdp = &stli_brds[portp->brdnr];
+ chbuf = (unsigned char *) buf;
+
+/*
+ * If copying direct from user space we need to be able to handle page
+ * faults while we are copying. To do this copy as much as we can now
+ * into a kernel buffer. From there we copy it into shared memory. The
+ * big problem is that we do not want shared memory enabled when we are
+ * sleeping (other boards may be serviced while asleep). Something else
+ * to note here is the reading of the tail twice. Since the boards
+ * shared memory can be on an 8-bit bus then we need to be very carefull
+ * reading 16 bit quantities - since both the board (slave) and host
+ * cound be writing and reading at the same time.
+ */
+ if (from_user) {
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ head = (unsigned int) ap->txq.head;
+ tail = (unsigned int) ap->txq.tail;
+ if (tail != ((unsigned int) ap->txq.tail))
+ tail = (unsigned int) ap->txq.tail;
+ len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1);
+ count = MIN(len, count);
+ EBRDDISABLE(brdp);
+
+ down(&stli_tmpwritesem);
+ memcpy_fromfs(stli_tmpwritebuf, chbuf, count);
+ up(&stli_tmpwritesem);
+ chbuf = &stli_tmpwritebuf[0];
+ restore_flags(flags);
+ }
+
+/*
+ * All data is now local, shove as much as possible into shared memory.
+ */
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ head = (unsigned int) ap->txq.head;
+ tail = (unsigned int) ap->txq.tail;
+ if (tail != ((unsigned int) ap->txq.tail))
+ tail = (unsigned int) ap->txq.tail;
+ size = portp->txsize;
+ if (head >= tail) {
+ len = size - (head - tail) - 1;
+ stlen = size - head;
+ } else {
+ len = tail - head - 1;
+ stlen = len;
+ }
+
+ len = MIN(len, count);
+ count = 0;
+ shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
+
+ while (len > 0) {
+ stlen = MIN(len, stlen);
+ memcpy((shbuf + head), chbuf, stlen);
+ chbuf += stlen;
+ len -= stlen;
+ count += stlen;
+ head += stlen;
+ if (head >= size) {
+ head = 0;
+ stlen = tail;
+ }
+ }
+
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ ap->txq.head = head;
+ if (test_bit(ST_TXBUSY, &portp->state)) {
+ if (ap->changed.data & DT_TXEMPTY)
+ ap->changed.data &= ~DT_TXEMPTY;
+ }
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp->slavereq |= portp->reqbit;
+ bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+ *bits |= portp->portbit;
+ set_bit(ST_TXBUSY, &portp->state);
+
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ * Output a single character. We put it into a temporary local buffer
+ * (for speed) then write out that buffer when the flushchars routine
+ * is called. There is a safety catch here so that if some other port
+ * writes chars before the current buffer has been, then we write them
+ * first them do the new ports.
+ */
+
+static void stli_putchar(struct tty_struct *tty, unsigned char ch)
+{
+#if DEBUG
+ printk("stli_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ if (tty != stli_txcooktty) {
+ if (stli_txcooktty != (struct tty_struct *) NULL)
+ stli_flushchars(stli_txcooktty);
+ stli_txcooktty = tty;
+ }
+
+ stli_txcookbuf[stli_txcooksize++] = ch;
+}
+
+/*****************************************************************************/
+
+/*
+ * Transfer characters from the local TX cooking buffer to the board.
+ * We sort of ignore the tty that gets passed in here. We rely on the
+ * info stored with the TX cook buffer to tell us which port to flush
+ * the data on. In any case we clean out the TX cook buffer, for re-use
+ * by someone else.
+ */
+
+static void stli_flushchars(struct tty_struct *tty)
+{
+ volatile cdkhdr_t *hdrp;
+ volatile unsigned char *bits;
+ volatile cdkasy_t *ap;
+ struct tty_struct *cooktty;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int len, stlen, head, tail, size, count, cooksize;
+ unsigned char *buf, *shbuf;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_flushchars(tty=%x)\n", (int) tty);
+#endif
+
+ cooksize = stli_txcooksize;
+ cooktty = stli_txcooktty;
+ stli_txcooksize = 0;
+ stli_txcookrealsize = 0;
+ stli_txcooktty = (struct tty_struct *) NULL;
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ if (cooktty == (struct tty_struct *) NULL)
+ return;
+ if (tty != cooktty)
+ tty = cooktty;
+ if (cooksize == 0)
+ return;
+
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ head = (unsigned int) ap->txq.head;
+ tail = (unsigned int) ap->txq.tail;
+ if (tail != ((unsigned int) ap->txq.tail))
+ tail = (unsigned int) ap->txq.tail;
+ size = portp->txsize;
+ if (head >= tail) {
+ len = size - (head - tail) - 1;
+ stlen = size - head;
+ } else {
+ len = tail - head - 1;
+ stlen = len;
+ }
+
+ len = MIN(len, cooksize);
+ count = 0;
+ shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
+ buf = stli_txcookbuf;
+
+ while (len > 0) {
+ stlen = MIN(len, stlen);
+ memcpy((shbuf + head), buf, stlen);
+ buf += stlen;
+ len -= stlen;
+ count += stlen;
+ head += stlen;
+ if (head >= size) {
+ head = 0;
+ stlen = tail;
+ }
+ }
+
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ ap->txq.head = head;
+
+ if (test_bit(ST_TXBUSY, &portp->state)) {
+ if (ap->changed.data & DT_TXEMPTY)
+ ap->changed.data &= ~DT_TXEMPTY;
+ }
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp->slavereq |= portp->reqbit;
+ bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+ *bits |= portp->portbit;
+ set_bit(ST_TXBUSY, &portp->state);
+
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static int stli_writeroom(struct tty_struct *tty)
+{
+ volatile cdkasyrq_t *rp;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int head, tail, len;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_writeroom(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(0);
+ if (tty == stli_txcooktty) {
+ if (stli_txcookrealsize != 0) {
+ len = stli_txcookrealsize - stli_txcooksize;
+ return(len);
+ }
+ }
+
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return(0);
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return(0);
+ brdp = &stli_brds[portp->brdnr];
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+ head = (unsigned int) rp->head;
+ tail = (unsigned int) rp->tail;
+ if (tail != ((unsigned int) rp->tail))
+ tail = (unsigned int) rp->tail;
+ len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head);
+ len--;
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ if (tty == stli_txcooktty) {
+ stli_txcookrealsize = len;
+ len -= stli_txcooksize;
+ }
+ return(len);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the number of characters in the transmit buffer. Normally we
+ * will return the number of chars in the shared memory ring queue.
+ * We need to kludge around the case where the shared memory buffer is
+ * empty but not all characters have drained yet, for this case just
+ * return that there is 1 character in the buffer!
+ */
+
+static int stli_charsinbuffer(struct tty_struct *tty)
+{
+ volatile cdkasyrq_t *rp;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int head, tail, len;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_charsinbuffer(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(0);
+ if (tty == stli_txcooktty)
+ stli_flushchars(tty);
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return(0);
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return(0);
+ brdp = &stli_brds[portp->brdnr];
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+ head = (unsigned int) rp->head;
+ tail = (unsigned int) rp->tail;
+ if (tail != ((unsigned int) rp->tail))
+ tail = (unsigned int) rp->tail;
+ len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head));
+ if ((len == 0) && test_bit(ST_TXBUSY, &portp->state))
+ len = 1;
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ return(len);
+}
+
+/*****************************************************************************/
+
+/*
+ * Generate the serial struct info.
+ */
+
+static void stli_getserial(stliport_t *portp, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+
+#if DEBUG
+ printk("stli_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+ memset(&sio, 0, sizeof(struct serial_struct));
+ sio.type = PORT_UNKNOWN;
+ sio.line = portp->portnr;
+ sio.port = stli_brdconf[portp->brdnr].ioaddr1;
+ sio.irq = stli_brdconf[portp->brdnr].irq;
+ sio.flags = portp->flags;
+ sio.baud_base = portp->baud_base;
+ sio.close_delay = portp->close_delay;
+ sio.closing_wait = portp->closing_wait;
+ sio.custom_divisor = portp->custom_divisor;
+ sio.xmit_fifo_size = 0;
+ sio.hub6 = 0;
+ memcpy_tofs(sp, &sio, sizeof(struct serial_struct));
+}
+
+/*****************************************************************************/
+
+/*
+ * Set port according to the serial struct info.
+ * At this point we do not do any auto-configure stuff, so we will
+ * just quietly ignore any requests to change irq, etc.
+ */
+
+static int stli_setserial(stliport_t *portp, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+ int rc;
+
+#if DEBUG
+ printk("stli_setserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+ memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
+ if (!suser()) {
+ if ((sio.baud_base != portp->baud_base) ||
+ (sio.close_delay != portp->close_delay) ||
+ ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+ return(-EPERM);
+ }
+
+ portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+ portp->baud_base = sio.baud_base;
+ portp->close_delay = sio.close_delay;
+ portp->closing_wait = sio.closing_wait;
+ portp->custom_divisor = sio.custom_divisor;
+
+ if ((rc = stli_setport(portp)) < 0)
+ return(rc);
+ return(0);
+}
+
+/*****************************************************************************/
+
+static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned long val;
+ int rc;
+
+#if DEBUG
+ printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(-ENODEV);
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return(-ENODEV);
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return(0);
+ brdp = &stli_brds[portp->brdnr];
+
+ rc = 0;
+
+ switch (cmd) {
+ case TCSBRK:
+ if ((rc = tty_check_change(tty)) == 0) {
+ tty_wait_until_sent(tty, 0);
+ if (! arg) {
+ val = 250;
+ rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+ }
+ }
+ break;
+ case TCSBRKP:
+ if ((rc = tty_check_change(tty)) == 0) {
+ tty_wait_until_sent(tty, 0);
+ val = (arg ? (arg * 100) : 250);
+ rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+ }
+ break;
+ case TIOCGSOFTCAR:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
+ put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+ break;
+ case TIOCSSOFTCAR:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+ }
+ break;
+ case TIOCMGET:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+ if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+ return(rc);
+ val = stli_mktiocm(portp->asig.sigvalue);
+ put_fs_long(val, (unsigned long *) arg);
+ }
+ break;
+ case TIOCMBIS:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
+ rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ }
+ break;
+ case TIOCMBIC:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
+ rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ }
+ break;
+ case TIOCMSET:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
+ rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ }
+ break;
+ case TIOCGSERIAL:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+ stli_getserial(portp, (struct serial_struct *) arg);
+ break;
+ case TIOCSSERIAL:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+ rc = stli_setserial(portp, (struct serial_struct *) arg);
+ break;
+ case TIOCSERCONFIG:
+ case TIOCSERGWILD:
+ case TIOCSERSWILD:
+ case TIOCSERGETLSR:
+ case TIOCSERGSTRUCT:
+ case TIOCSERGETMULTI:
+ case TIOCSERSETMULTI:
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * This routine assumes that we have user context and can sleep.
+ * Looks like it is true for the current ttys implementation..!!
+ */
+
+static void stli_settermios(struct tty_struct *tty, struct termios *old)
+{
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ struct termios *tiosp;
+ asyport_t aport;
+
+#if DEBUG
+ printk("stli_settermios(tty=%x,old=%x)\n", (int) tty, (int) old);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ tiosp = tty->termios;
+ if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+ return;
+
+ stli_mkasyport(portp, &aport, tiosp);
+ stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
+ stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
+ stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
+ tty->hw_stopped = 0;
+ if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+ wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ * Attempt to flow control who ever is sending us data. We won't really
+ * do any flow control action here. We can't directly, and even if we
+ * wanted to we would have to send a command to the slave. The slave
+ * knows how to flow control, and will do so when its buffers reach its
+ * internal high water marks. So what we will do is set a local state
+ * bit that will stop us sending any RX data up from the poll routine
+ * (which is the place where RX data from the slave is handled).
+ */
+
+static void stli_throttle(struct tty_struct *tty)
+{
+ stliport_t *portp;
+
+#if DEBUG
+ printk("stli_throttle(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+
+ set_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ * Unflow control the device sending us data... That means that all
+ * we have to do is clear the RXSTOP state bit. The next poll call
+ * will then be able to pass the RX data back up.
+ */
+
+static void stli_unthrottle(struct tty_struct *tty)
+{
+ stliport_t *portp;
+
+#if DEBUG
+ printk("stli_unthrottle(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+
+ clear_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ * Stop the transmitter. Basically to do this we will just turn TX
+ * interrupts off.
+ */
+
+static void stli_stop(struct tty_struct *tty)
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ asyctrl_t actrl;
+
+#if DEBUG
+ printk("stli_stop(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ memset(&actrl, 0, sizeof(asyctrl_t));
+ actrl.txctrl = CT_STOPFLOW;
+#if 0
+ stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+#endif
+}
+
+/*****************************************************************************/
+
+/*
+ * Start the transmitter again. Just turn TX interrupts back on.
+ */
+
+static void stli_start(struct tty_struct *tty)
+{
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ asyctrl_t actrl;
+
+#if DEBUG
+ printk("stli_start(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ memset(&actrl, 0, sizeof(asyctrl_t));
+ actrl.txctrl = CT_STARTFLOW;
+#if 0
+ stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+#endif
+}
+
+/*****************************************************************************/
+
+/*
+ * Scheduler called hang up routine. This is called from the scheduler,
+ * not direct from the driver "poll" routine. We can't call it there
+ * since the real local hangup code will enable/disable the board and
+ * other things that we can't do while handling the poll. Much easier
+ * to deal with it some time later (don't really care when, hangups
+ * aren't that time critical).
+ */
+
+static void stli_dohangup(void *arg)
+{
+ stliport_t *portp;
+
+#if DEBUG
+ printk("stli_dohangup(portp=%x)\n", (int) arg);
+#endif
+
+ portp = (stliport_t *) arg;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if (portp->tty == (struct tty_struct *) NULL)
+ return;
+ tty_hangup(portp->tty);
+}
+
+/*****************************************************************************/
+
+/*
+ * Hangup this port. This is pretty much like closing the port, only
+ * a little more brutal. No waiting for data to drain. Shutdown the
+ * port and maybe drop signals. This is rather tricky really. We want
+ * to close the port as well.
+ */
+
+static void stli_hangup(struct tty_struct *tty)
+{
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_hangup(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ portp->flags &= ~ASYNC_INITIALIZED;
+
+ save_flags(flags);
+ cli();
+ if (! test_bit(ST_CLOSING, &portp->state))
+ stli_rawclose(brdp, portp, 0, 0);
+ if (tty->termios->c_cflag & HUPCL) {
+ stli_mkasysigs(&portp->asig, 0, 0);
+ if (test_bit(ST_CMDING, &portp->state)) {
+ set_bit(ST_DOSIGS, &portp->state);
+ set_bit(ST_DOFLUSHTX, &portp->state);
+ set_bit(ST_DOFLUSHRX, &portp->state);
+ } else {
+ stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0);
+ }
+ }
+ restore_flags(flags);
+
+ clear_bit(ST_TXBUSY, &portp->state);
+ clear_bit(ST_RXSTOP, &portp->state);
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ tty->driver_data = (void *) NULL;
+ portp->tty = (struct tty_struct *) NULL;
+ portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+ portp->refcount = 0;
+ wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ * Flush characters from the lower buffer. We may not have user context
+ * so we cannot sleep waiting for it to complete. Also we need to check
+ * if there is chars for this port in the TX cook buffer, and flush them
+ * as well.
+ */
+
+static void stli_flushbuffer(struct tty_struct *tty)
+{
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned long ftype, flags;
+
+#if DEBUG
+ printk("stli_flushbuffer(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stliport_t *) NULL)
+ return;
+ if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ return;
+ brdp = &stli_brds[portp->brdnr];
+
+ save_flags(flags);
+ cli();
+ if (tty == stli_txcooktty) {
+ stli_txcooktty = (struct tty_struct *) NULL;
+ stli_txcooksize = 0;
+ stli_txcookrealsize = 0;
+ }
+ if (test_bit(ST_CMDING, &portp->state)) {
+ set_bit(ST_DOFLUSHTX, &portp->state);
+ } else {
+ ftype = FLUSHTX;
+ if (test_bit(ST_DOFLUSHRX, &portp->state)) {
+ ftype |= FLUSHRX;
+ clear_bit(ST_DOFLUSHRX, &portp->state);
+ }
+ stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0);
+ }
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ * Generic send command routine. This will send a message to the slave,
+ * of the specified type with the specified argument. Must be very
+ * carefull of data that will be copied out from shared memory -
+ * containing command results. The command completion is all done from
+ * a poll routine that does not have user coontext. Therefore you cannot
+ * copy back directly into user space, or to the kernel stack. This
+ * routine does not sleep, so can be called from anywhere.
+ */
+
+static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+ volatile cdkhdr_t *hdrp;
+ volatile cdkctrl_t *cp;
+ volatile unsigned char *bits;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+#endif
+
+ if (test_bit(ST_CMDING, &portp->state)) {
+ printk("STALLION: command already busy, cmd=%x!\n", (int) cmd);
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ if (size > 0) {
+ memcpy((void *) &(cp->args[0]), arg, size);
+ if (copyback) {
+ portp->argp = arg;
+ portp->argsize = size;
+ }
+ }
+ cp->status = 0;
+ cp->cmd = cmd;
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp->slavereq |= portp->reqbit;
+ bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+ *bits |= portp->portbit;
+ set_bit(ST_CMDING, &portp->state);
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Read data from shared memory. This assumes that the shared memory
+ * is enabled and that interrupts are off. Basically we just empty out
+ * the shared memory buffer into the tty buffer. Must be carefull to
+ * handle the case where we fill up the tty buffer, but still have
+ * more chars to unload.
+ */
+
+static inline void stli_read(stlibrd_t *brdp, stliport_t *portp)
+{
+ volatile cdkasyrq_t *rp;
+ volatile char *shbuf;
+ struct tty_struct *tty;
+ unsigned int head, tail, size;
+ unsigned int len, stlen;
+
+#if DEBUG
+ printk("stli_read(brdp=%x,portp=%d)\n", (int) brdp, (int) portp);
+#endif
+
+ if (test_bit(ST_RXSTOP, &portp->state))
+ return;
+ tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return;
+
+ rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+ head = (unsigned int) rp->head;
+ if (head != ((unsigned int) rp->head))
+ head = (unsigned int) rp->head;
+ tail = (unsigned int) rp->tail;
+ size = portp->rxsize;
+ if (head >= tail) {
+ len = head - tail;
+ stlen = len;
+ } else {
+ len = size - (tail - head);
+ stlen = size - tail;
+ }
+
+ len = MIN(len, (TTY_FLIPBUF_SIZE - tty->flip.count));
+ shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset);
+
+ while (len > 0) {
+ stlen = MIN(len, stlen);
+ memcpy(tty->flip.char_buf_ptr, (char *) (shbuf + tail), stlen);
+ memset(tty->flip.flag_buf_ptr, 0, stlen);
+ tty->flip.char_buf_ptr += stlen;
+ tty->flip.flag_buf_ptr += stlen;
+ tty->flip.count += stlen;
+
+ len -= stlen;
+ tail += stlen;
+ if (tail >= size) {
+ tail = 0;
+ stlen = head;
+ }
+ }
+ rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+ rp->tail = tail;
+
+ if (head != tail)
+ set_bit(ST_RXING, &portp->state);
+
+ tty_schedule_flip(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ * Set up and carry out any delayed commands. There is only a small set
+ * of slave commands that can be done "off-level". So it is not too
+ * difficult to deal with them here.
+ */
+
+static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
+{
+ int cmd;
+
+ if (test_bit(ST_DOSIGS, &portp->state)) {
+ if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state))
+ cmd = A_SETSIGNALSF;
+ else if (test_bit(ST_DOFLUSHTX, &portp->state))
+ cmd = A_SETSIGNALSFTX;
+ else if (test_bit(ST_DOFLUSHRX, &portp->state))
+ cmd = A_SETSIGNALSFRX;
+ else
+ cmd = A_SETSIGNALS;
+ clear_bit(ST_DOFLUSHTX, &portp->state);
+ clear_bit(ST_DOFLUSHRX, &portp->state);
+ clear_bit(ST_DOSIGS, &portp->state);
+ memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t));
+ cp->status = 0;
+ cp->cmd = cmd;
+ set_bit(ST_CMDING, &portp->state);
+ } else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) {
+ cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
+ cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
+ clear_bit(ST_DOFLUSHTX, &portp->state);
+ clear_bit(ST_DOFLUSHRX, &portp->state);
+ memcpy((void *) &(cp->args[0]), (void *) &cmd, sizeof(int));
+ cp->status = 0;
+ cp->cmd = A_FLUSH;
+ set_bit(ST_CMDING, &portp->state);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Host command service checking. This handles commands or messages
+ * coming from the slave to the host. Must have board shared memory
+ * enabled and interrupts off when called. Notice that by servicing the
+ * read data last we don't need to change the shared memory pointer
+ * during processing (which is a slow IO operation).
+ */
+
+static inline int stli_hostcmd(stlibrd_t *brdp, int channr)
+{
+ volatile cdkasy_t *ap;
+ volatile cdkctrl_t *cp;
+ struct tty_struct *tty;
+ asynotify_t nt;
+ stliport_t *portp;
+ unsigned long oldsigs;
+ int rc, donerx;
+
+#if DEBUG
+ printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr);
+#endif
+
+ portp = brdp->ports[(channr - 1)];
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ cp = &ap->ctrl;
+
+/*
+ * Check if we are waiting for an open completion message.
+ */
+ if (test_bit(ST_OPENING, &portp->state)) {
+ rc = (int) cp->openarg;
+ if ((cp->open == 0) && (rc != 0)) {
+ if (rc > 0)
+ rc--;
+ cp->openarg = 0;
+ portp->rc = rc;
+ clear_bit(ST_OPENING, &portp->state);
+ wake_up_interruptible(&portp->raw_wait);
+ }
+ }
+
+/*
+ * Check if we are waiting for a close completion message.
+ */
+ if (test_bit(ST_CLOSING, &portp->state)) {
+ rc = (int) cp->closearg;
+ if ((cp->close == 0) && (rc != 0)) {
+ if (rc > 0)
+ rc--;
+ cp->closearg = 0;
+ portp->rc = rc;
+ clear_bit(ST_CLOSING, &portp->state);
+ wake_up_interruptible(&portp->raw_wait);
+ }
+ }
+
+/*
+ * Check if we are waiting for a command completion message. We may
+ * need to copy out the command results associated with this command.
+ */
+ if (test_bit(ST_CMDING, &portp->state)) {
+ rc = cp->status;
+ if ((cp->cmd == 0) && (rc != 0)) {
+ if (rc > 0)
+ rc--;
+ if (portp->argp != (void *) NULL) {
+ memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize);
+ portp->argp = (void *) NULL;
+ }
+ cp->status = 0;
+ portp->rc = rc;
+ clear_bit(ST_CMDING, &portp->state);
+ stli_dodelaycmd(portp, cp);
+ wake_up_interruptible(&portp->raw_wait);
+ }
+ }
+
+/*
+ * Check for any notification messages ready. This includes lots of
+ * different types of events - RX chars ready, RX break received,
+ * TX data low or empty in the slave, modem signals changed state.
+ */
+ donerx = 0;
+
+ if (ap->notify) {
+ nt = ap->changed;
+ ap->notify = 0;
+ tty = portp->tty;
+
+ if (nt.signal & SG_DCD) {
+ oldsigs = portp->sigs;
+ portp->sigs = stli_mktiocm(nt.sigvalue);
+ clear_bit(ST_GETSIGS, &portp->state);
+ if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+ wake_up_interruptible(&portp->open_wait);
+ if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+ if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (portp->flags & ASYNC_CALLOUT_NOHUP))) {
+ if (tty != (struct tty_struct *) NULL)
+ queue_task_irq_off(&portp->tqhangup, &tq_scheduler);
+ }
+ }
+ }
+
+ if (nt.data & DT_TXEMPTY)
+ clear_bit(ST_TXBUSY, &portp->state);
+ if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
+ if (tty != (struct tty_struct *) NULL) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ }
+
+ if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) {
+ if (tty != (struct tty_struct *) NULL) {
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+ *tty->flip.char_buf_ptr++ = 0;
+#ifndef MODULE
+ if (portp->flags & ASYNC_SAK)
+ do_SAK(tty);
+#endif
+ tty_schedule_flip(tty);
+ }
+ }
+ }
+
+ if (nt.data & DT_RXBUSY) {
+ donerx++;
+ stli_read(brdp, portp);
+ }
+ }
+
+/*
+ * It might seem odd that we are checking for more RX chars here.
+ * But, we need to handle the case where the tty buffer was previously
+ * filled, but we had more characters to pass up. The slave will not
+ * send any more RX notify messages until the RX buffer has been emptied.
+ * But it will leave the service bits on (since the buffer is not empty).
+ * So from here we can try to process more RX chars.
+ */
+ if ((!donerx) && test_bit(ST_RXING, &portp->state)) {
+ clear_bit(ST_RXING, &portp->state);
+ stli_read(brdp, portp);
+ }
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Driver poll routine. This routine polls the boards in use and passes
+ * messages back up to host when neccesary. This is actually very
+ * CPU efficient, since we will always have the kernel poll clock, it
+ * adds only a few cycles when idle (since board service can be
+ * determined very easily), but when loaded generates no interrupts
+ * (with their expensive associated context change).
+ */
+
+static void stli_poll(unsigned long arg)
+{
+ volatile cdkhdr_t *hdrp;
+ unsigned char bits[(STL_MAXCHANS / 8) + 1];
+ unsigned char hostreq, slavereq;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ int bitpos, bitat, bitsize;
+ int brdnr, channr, nrdevs;
+
+ stli_timerlist.expires = STLI_TIMEOUT;
+ add_timer(&stli_timerlist);
+
+/*
+ * Check each board and do any servicing required.
+ */
+ for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
+ brdp = &stli_brds[brdnr];
+ if ((brdp->state & BST_STARTED) == 0)
+ continue;
+
+ EBRDENABLE(brdp);
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hostreq = hdrp->hostreq;
+ slavereq = hdrp->slavereq;
+ bitsize = brdp->bitsize;
+ nrdevs = brdp->nrdevs;
+
+/*
+ * Check if slave wants any service. Basically we try to do as
+ * little work as possible here. There are 2 levels of service
+ * bits. So if there is nothing to do we bail early. We check
+ * 8 service bits at a time in the inner loop, so we can bypass
+ * the lot if none of them want service.
+ */
+ if (hostreq) {
+ memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
+
+ for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+ if (bits[bitpos] == 0)
+ continue;
+ channr = bitpos * 8;
+ for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+ if (bits[bitpos] & bitat) {
+ stli_hostcmd(brdp, channr);
+ }
+ }
+ }
+ }
+
+/*
+ * Check if any of the out-standing host commands have completed.
+ * It is a bit unfortunate that we need to check stuff that we
+ * initiated! This ain't pretty, but it needs to be fast.
+ */
+ if (slavereq) {
+ slavereq = 0;
+ hostreq = 0;
+ memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize);
+
+ for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+ if (bits[bitpos] == 0)
+ continue;
+ channr = bitpos * 8;
+ for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+ if (bits[bitpos] & bitat) {
+ portp = brdp->ports[(channr - 1)];
+ if (test_bit(ST_OPENING, &portp->state) ||
+ test_bit(ST_CLOSING, &portp->state) ||
+ test_bit(ST_CMDING, &portp->state) ||
+ test_bit(ST_TXBUSY, &portp->state)) {
+ slavereq |= portp->reqbit;
+ } else {
+ bits[bitpos] &= ~bitat;
+ hostreq++;
+ }
+ }
+ }
+ }
+ hdrp->slavereq = slavereq;
+ if (hostreq)
+ memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize);
+ }
+
+ EBRDDISABLE(brdp);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Translate the termios settings into the port setting structure of
+ * the slave.
+ */
+
+static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
+{
+#if DEBUG
+ printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp);
+#endif
+
+ memset(pp, 0, sizeof(asyport_t));
+
+/*
+ * Start of by setting the baud, char size, parity and stop bit info.
+ */
+ pp->baudout = tiosp->c_cflag & CBAUD;
+ if (pp->baudout & CBAUDEX) {
+ pp->baudout &= ~CBAUDEX;
+ if ((pp->baudout < 1) || (pp->baudout > 2))
+ tiosp->c_cflag &= ~CBAUDEX;
+ else
+ pp->baudout += 15;
+ }
+ pp->baudout = stli_baudrates[pp->baudout];
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ pp->baudout = 57600;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ pp->baudout = 115200;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ pp->baudout = (portp->baud_base / portp->custom_divisor);
+ }
+ if (pp->baudout > STL_MAXBAUD)
+ pp->baudout = STL_MAXBAUD;
+ pp->baudin = pp->baudout;
+
+ switch (tiosp->c_cflag & CSIZE) {
+ case CS5:
+ pp->csize = 5;
+ break;
+ case CS6:
+ pp->csize = 6;
+ break;
+ case CS7:
+ pp->csize = 7;
+ break;
+ default:
+ pp->csize = 8;
+ break;
+ }
+
+ if (tiosp->c_cflag & CSTOPB)
+ pp->stopbs = PT_STOP2;
+ else
+ pp->stopbs = PT_STOP1;
+
+ if (tiosp->c_cflag & PARENB) {
+ if (tiosp->c_cflag & PARODD)
+ pp->parity = PT_ODDPARITY;
+ else
+ pp->parity = PT_EVENPARITY;
+ } else {
+ pp->parity = PT_NOPARITY;
+ }
+
+/*
+ * Set up any flow control options enabled.
+ */
+ if (tiosp->c_iflag & IXON) {
+ pp->flow |= F_IXON;
+ if (tiosp->c_iflag & IXANY)
+ pp->flow |= F_IXANY;
+ }
+ if (tiosp->c_cflag & CRTSCTS)
+ pp->flow |= (F_RTSFLOW | F_CTSFLOW);
+
+ pp->startin = tiosp->c_cc[VSTART];
+ pp->stopin = tiosp->c_cc[VSTOP];
+ pp->startout = tiosp->c_cc[VSTART];
+ pp->stopout = tiosp->c_cc[VSTOP];
+
+/*
+ * Set up the RX char marking mask with those RX error types we must
+ * catch. We can get the slave to help us out a little here, it will
+ * ignore parity errors and breaks for us, and mark parity errors in
+ * the data stream.
+ */
+ if (tiosp->c_iflag & IGNPAR)
+ pp->iflag |= FI_IGNRXERRS;
+ if (tiosp->c_iflag & IGNBRK)
+ pp->iflag |= FI_IGNBREAK;
+
+ portp->rxmarkmsk = 0;
+ if (tiosp->c_iflag & (INPCK | PARMRK))
+ pp->iflag |= FI_1MARKRXERRS;
+ if (tiosp->c_iflag & BRKINT)
+ portp->rxmarkmsk |= BRKINT;
+}
+
+/*****************************************************************************/
+
+/*
+ * Construct a slave signals structure for setting the DTR and RTS
+ * signals as specified.
+ */
+
+static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
+{
+#if DEBUG
+ printk("stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n", (int) sp, dtr, rts);
+#endif
+
+ memset(sp, 0, sizeof(asysigs_t));
+ if (dtr >= 0) {
+ sp->signal |= SG_DTR;
+ sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0);
+ }
+ if (rts >= 0) {
+ sp->signal |= SG_RTS;
+ sp->sigvalue |= ((rts > 0) ? SG_RTS : 0);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Convert the signals returned from the slave into a local TIOCM type
+ * signals value. We keep them localy in TIOCM format.
+ */
+
+static long stli_mktiocm(unsigned long sigvalue)
+{
+ long tiocm;
+
+#if DEBUG
+ printk("stli_mktiocm(sigvalue=%x)\n", (int) sigvalue);
+#endif
+
+ tiocm = 0;
+ tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
+ tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
+ tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
+ tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0);
+ tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0);
+ tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0);
+ return(tiocm);
+}
+
+/*****************************************************************************/
+
+/*
+ * All panels and ports actually attached have been worked out. All
+ * we need to do here is set up the appropriate per port data structures.
+ */
+
+static int stli_initports(stlibrd_t *brdp)
+{
+ stliport_t *portp;
+ int i, panelnr, panelport;
+
+#if DEBUG
+ printk("stli_initports(brdp=%x)\n", (int) brdp);
+#endif
+
+ for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
+ portp = (stliport_t *) stli_memalloc(sizeof(stliport_t));
+ if (portp == (stliport_t *) NULL) {
+ printk("STALLION: failed to allocate port structure\n");
+ continue;
+ }
+
+ memset(portp, 0, sizeof(stliport_t));
+ portp->portnr = i;
+ portp->brdnr = brdp->brdnr;
+ portp->panelnr = panelnr;
+ portp->baud_base = STL_BAUDBASE;
+ portp->close_delay = STL_CLOSEDELAY;
+ portp->closing_wait = 30 * HZ;
+ portp->tqhangup.routine = stli_dohangup;
+ portp->tqhangup.data = portp;
+ portp->normaltermios = stli_deftermios;
+ portp->callouttermios = stli_deftermios;
+ panelport++;
+ if (panelport >= brdp->panels[panelnr]) {
+ panelport = 0;
+ panelnr++;
+ }
+ brdp->ports[i] = portp;
+ }
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * All the following routines are board specific hardware operations.
+ */
+
+static void stli_ecpinit(stlibrd_t *brdp)
+{
+ unsigned long memconf;
+
+#if DEBUG
+ printk("stli_ecpinit(brdp=%d)\n", (int) brdp);
+#endif
+
+ outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+ udelay(10);
+ outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+ udelay(100);
+
+ memconf = (((unsigned long) brdp->membase) & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
+ outb(memconf, (brdp->iobase + ECP_ATMEMAR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpenable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_ecpenable(brdp=%x)\n", (int) brdp);
+#endif
+ outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpdisable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_ecpdisable(brdp=%x)\n", (int) brdp);
+#endif
+ outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+ unsigned char val;
+
+#if DEBUG
+ printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ val = 0;
+ } else {
+ ptr = brdp->membase + (offset % ECP_ATPAGESIZE);
+ val = (unsigned char) (offset / ECP_ATPAGESIZE);
+ }
+ outb(val, (brdp->iobase + ECP_ATMEMPR));
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpreset(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_ecpreset(brdp=%x)\n", (int) brdp);
+#endif
+
+ outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+ udelay(10);
+ outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+ udelay(500);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpintr(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_ecpintr(brdp=%x)\n", (int) brdp);
+#endif
+ outb(0x1, brdp->iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following set of functions act on ECP EISA boards.
+ */
+
+static void stli_ecpeiinit(stlibrd_t *brdp)
+{
+ unsigned long memconf;
+
+#if DEBUG
+ printk("stli_ecpeiinit(brdp=%x)\n", (int) brdp);
+#endif
+
+ outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
+ outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+ udelay(10);
+ outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+ udelay(500);
+
+ memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
+ outb(memconf, (brdp->iobase + ECP_EIMEMARL));
+ memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
+ outb(memconf, (brdp->iobase + ECP_EIMEMARH));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeienable(stlibrd_t *brdp)
+{
+ outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeidisable(stlibrd_t *brdp)
+{
+ outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+ unsigned char val;
+
+#if DEBUG
+ printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ val = 0;
+ } else {
+ ptr = brdp->membase + (offset % ECP_EIPAGESIZE);
+ if (offset < ECP_EIPAGESIZE)
+ val = ECP_EIENABLE;
+ else
+ val = ECP_EIENABLE | 0x40;
+ }
+ outb(val, (brdp->iobase + ECP_EICONFR));
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeireset(stlibrd_t *brdp)
+{
+ outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+ udelay(10);
+ outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+ udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following set of functions act on ECP MCA boards.
+ */
+
+static void stli_ecpmcenable(stlibrd_t *brdp)
+{
+ outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcdisable(stlibrd_t *brdp)
+{
+ outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+ unsigned char val;
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ val = 0;
+ } else {
+ ptr = brdp->membase + (offset % ECP_MCPAGESIZE);
+ val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE;
+ }
+ outb(val, (brdp->iobase + ECP_MCCONFR));
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcreset(stlibrd_t *brdp)
+{
+ outb(ECP_MCSTOP, (brdp->iobase + ECP_MCCONFR));
+ udelay(10);
+ outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+ udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following routines act on ONboards.
+ */
+
+static void stli_onbinit(stlibrd_t *brdp)
+{
+ unsigned long memconf;
+ int i;
+
+#if DEBUG
+ printk("stli_onbinit(brdp=%d)\n", (int) brdp);
+#endif
+
+ outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+ udelay(10);
+ outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+
+ memconf = (((unsigned long) brdp->membase) & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
+ outb(memconf, (brdp->iobase + ONB_ATMEMAR));
+ outb(0x1, brdp->iobase);
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+static void stli_onbenable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_onbenable(brdp=%x)\n", (int) brdp);
+#endif
+ outb((ONB_ATENABLE | ONB_HIMEMENAB), (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbdisable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_onbdisable(brdp=%x)\n", (int) brdp);
+#endif
+ outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+
+#if DEBUG
+ printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ } else {
+ ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
+ }
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbreset(stlibrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stli_onbreset(brdp=%x)\n", (int) brdp);
+#endif
+
+ outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+ udelay(10);
+ outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following routines act on ONboard EISA.
+ */
+
+static void stli_onbeinit(stlibrd_t *brdp)
+{
+ unsigned long memconf;
+ int i;
+
+#if DEBUG
+ printk("stli_onbeinit(brdp=%d)\n", (int) brdp);
+#endif
+
+ outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
+ outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+ udelay(10);
+ outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+
+ memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
+ outb(memconf, (brdp->iobase + ONB_EIMEMARL));
+ memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
+ outb(memconf, (brdp->iobase + ONB_EIMEMARH));
+ outb(0x1, brdp->iobase);
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+static void stli_onbeenable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_onbeenable(brdp=%x)\n", (int) brdp);
+#endif
+ outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbedisable(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_onbedisable(brdp=%x)\n", (int) brdp);
+#endif
+ outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+ unsigned char val;
+
+#if DEBUG
+ printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ val = 0;
+ } else {
+ ptr = brdp->membase + (offset % ONB_EIPAGESIZE);
+ if (offset < ONB_EIPAGESIZE)
+ val = ONB_EIENABLE;
+ else
+ val = ONB_EIENABLE | 0x40;
+ }
+ outb(val, (brdp->iobase + ONB_EICONFR));
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbereset(stlibrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stli_onbereset(brdp=%x)\n", (int) brdp);
+#endif
+
+ outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+ udelay(10);
+ outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following routines act on Brumby boards.
+ */
+
+static void stli_bbyinit(stlibrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stli_bbyinit(brdp=%d)\n", (int) brdp);
+#endif
+
+ outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+ udelay(10);
+ outb(0, (brdp->iobase + BBY_ATCONFR));
+ for (i = 0; (i < 500); i++)
+ udelay(1000);
+ outb(0x1, brdp->iobase);
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+ unsigned char val;
+
+#if DEBUG
+ printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ val = 0;
+ } else {
+ ptr = brdp->membase + (offset % BBY_PAGESIZE);
+ val = (unsigned char) (offset / BBY_PAGESIZE);
+ }
+ outb(val, (brdp->iobase + BBY_ATCONFR));
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_bbyreset(stlibrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stli_bbyreset(brdp=%x)\n", (int) brdp);
+#endif
+
+ outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+ udelay(10);
+ outb(0, (brdp->iobase + BBY_ATCONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ * The following routines act on original old Stallion boards.
+ */
+
+static void stli_stalinit(stlibrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stli_stalinit(brdp=%d)\n", (int) brdp);
+#endif
+
+ outb(0x1, brdp->iobase);
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
+{
+ void *ptr;
+
+#if DEBUG
+ printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+#endif
+
+ if (offset > brdp->memsize) {
+ printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+ ptr = 0;
+ } else {
+ ptr = brdp->membase + (offset % STAL_PAGESIZE);
+ }
+ return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_stalreset(stlibrd_t *brdp)
+{
+ volatile unsigned long *vecp;
+ int i;
+
+#if DEBUG
+ printk("stli_stalreset(brdp=%x)\n", (int) brdp);
+#endif
+
+ vecp = (volatile unsigned long *) (brdp->membase + 0x30);
+ *vecp = 0xffff0000;
+ outb(0, brdp->iobase);
+ for (i = 0; (i < 500); i++)
+ udelay(1000);
+}
+
+/*****************************************************************************/
+
+#if STLI_HIMEMORY
+
+#define PAGE_IOMEM __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_PCD)
+
+/*
+ * To support shared memory addresses outside of the lower 1 Mb region
+ * we will need to pull some tricks with memory management to map the
+ * higher range into kernel virtual address space... Radical stuff...
+ */
+
+static void *stli_mapbrdmem(unsigned long physaddr, unsigned int size)
+{
+ void *virtaddr;
+ int rc;
+
+#if DEBUG
+ printk("stli_mapbrdmem(physaddr=%x,size=%x)\n", (int) physaddr, size);
+#endif
+
+ if ((virtaddr = vmalloc(size)) == (char *) NULL) {
+ printk("STALLION: failed to allocate virtual address space, size=%x\n", size);
+ return((void *) NULL);
+ }
+ if ((rc = remap_page_range((TASK_SIZE + ((unsigned long) virtaddr)), physaddr, size, PAGE_IOMEM))) {
+ printk("STALLION: failed to map phyiscal address=%x, errno=%d\n", (int) physaddr, rc);
+ return((void *) NULL);
+ }
+ return(virtaddr);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Try to find an ECP board and initialize it. This handles only ECP
+ * board types.
+ */
+
+static int stli_initecp(stlibrd_t *brdp, stlconf_t *confp)
+{
+ cdkecpsig_t sig;
+ cdkecpsig_t *sigsp;
+ unsigned int status, nxtid;
+ int panelnr;
+
+#if DEBUG
+ printk("stli_initecp(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+#endif
+
+/*
+ * Based on the specific board type setup the common vars to access
+ * and enable shared memory. Set all board specific information now
+ * as well.
+ */
+ switch (brdp->brdtype) {
+ case BRD_ECP:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = ECP_MEMSIZE;
+ brdp->pagesize = ECP_ATPAGESIZE;
+ brdp->init = stli_ecpinit;
+ brdp->enable = stli_ecpenable;
+ brdp->reenable = stli_ecpenable;
+ brdp->disable = stli_ecpdisable;
+ brdp->getmemptr = stli_ecpgetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_ecpreset;
+ break;
+
+ case BRD_ECPE:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = ECP_MEMSIZE;
+ brdp->pagesize = ECP_EIPAGESIZE;
+ brdp->init = stli_ecpeiinit;
+ brdp->enable = stli_ecpeienable;
+ brdp->reenable = stli_ecpeienable;
+ brdp->disable = stli_ecpeidisable;
+ brdp->getmemptr = stli_ecpeigetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_ecpeireset;
+ break;
+
+ case BRD_ECPMC:
+ brdp->memsize = ECP_MEMSIZE;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->pagesize = ECP_MCPAGESIZE;
+ brdp->iobase = confp->ioaddr1;
+ brdp->init = NULL;
+ brdp->enable = stli_ecpmcenable;
+ brdp->reenable = stli_ecpmcenable;
+ brdp->disable = stli_ecpmcdisable;
+ brdp->getmemptr = stli_ecpmcgetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_ecpmcreset;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+
+/*
+ * The per-board operations structure is all setup, so now lets go
+ * and get the board operational. Firstly initialize board configuration
+ * registers. Then if we are using the higher 1Mb support then set up
+ * the memory mapping info so we can get at the boards shared memory.
+ */
+ EBRDINIT(brdp);
+
+#if STLI_HIMEMORY
+ if (confp->memaddr > 0x100000) {
+ brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+ if (brdp->membase == (void *) NULL)
+ return(-ENOMEM);
+ }
+#endif
+
+/*
+ * Now that all specific code is set up, enable the shared memory and
+ * look for the a signature area that will tell us exactly what board
+ * this is, and what is connected to it.
+ */
+ EBRDENABLE(brdp);
+ sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+ memcpy(&sig, sigsp, sizeof(cdkecpsig_t));
+ EBRDDISABLE(brdp);
+
+#if 0
+ printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
+ __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0],
+ (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3],
+ (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6],
+ (int) sig.panelid[7]);
+#endif
+
+ if (sig.magic != ECP_MAGIC)
+ return(-ENODEV);
+
+/*
+ * Scan through the signature looking at the panels connected to the
+ * board. Calculate the total number of ports as we go.
+ */
+ for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) {
+ status = sig.panelid[nxtid];
+ if ((status & ECH_PNLIDMASK) != nxtid)
+ break;
+ if (status & ECH_PNL16PORT) {
+ brdp->panels[panelnr] = 16;
+ brdp->nrports += 16;
+ nxtid += 2;
+ } else {
+ brdp->panels[panelnr] = 8;
+ brdp->nrports += 8;
+ nxtid++;
+ }
+ brdp->nrpanels++;
+ }
+
+ request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)");
+ brdp->state |= BST_FOUND;
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Try to find an ONboard, Brumby or Stallion board and initialize it.
+ * This handles only these board types.
+ */
+
+static int stli_initonb(stlibrd_t *brdp, stlconf_t *confp)
+{
+ cdkonbsig_t sig;
+ cdkonbsig_t *sigsp;
+ int i;
+
+#if DEBUG
+ printk("stli_initonb(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+#endif
+
+/*
+ * Based on the specific board type setup the common vars to access
+ * and enable shared memory. Set all board specific information now
+ * as well.
+ */
+ switch (brdp->brdtype) {
+ case BRD_ONBOARD:
+ case BRD_ONBOARD32:
+ case BRD_ONBOARD2:
+ case BRD_ONBOARD2_32:
+ case BRD_ONBOARDRS:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = ONB_MEMSIZE;
+ brdp->pagesize = ONB_ATPAGESIZE;
+ brdp->init = stli_onbinit;
+ brdp->enable = stli_onbenable;
+ brdp->reenable = stli_onbenable;
+ brdp->disable = stli_onbdisable;
+ brdp->getmemptr = stli_onbgetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_onbreset;
+ break;
+
+ case BRD_ONBOARDE:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = ONB_EIMEMSIZE;
+ brdp->pagesize = ONB_EIPAGESIZE;
+ brdp->init = stli_onbeinit;
+ brdp->enable = stli_onbeenable;
+ brdp->reenable = stli_onbeenable;
+ brdp->disable = stli_onbedisable;
+ brdp->getmemptr = stli_onbegetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_onbereset;
+ break;
+
+ case BRD_BRUMBY4:
+ case BRD_BRUMBY8:
+ case BRD_BRUMBY16:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = BBY_MEMSIZE;
+ brdp->pagesize = BBY_PAGESIZE;
+ brdp->init = stli_bbyinit;
+ brdp->enable = NULL;
+ brdp->reenable = NULL;
+ brdp->disable = NULL;
+ brdp->getmemptr = stli_bbygetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_bbyreset;
+ break;
+
+ case BRD_STALLION:
+ brdp->iobase = confp->ioaddr1;
+ brdp->membase = (void *) confp->memaddr;
+ brdp->memsize = STAL_MEMSIZE;
+ brdp->pagesize = STAL_PAGESIZE;
+ brdp->init = stli_stalinit;
+ brdp->enable = NULL;
+ brdp->reenable = NULL;
+ brdp->disable = NULL;
+ brdp->getmemptr = stli_stalgetmemptr;
+ brdp->intr = stli_ecpintr;
+ brdp->reset = stli_stalreset;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+
+/*
+ * The per-board operations structure is all setup, so now lets go
+ * and get the board operational. Firstly initialize board configuration
+ * registers. Then if we are using the higher 1Mb support then set up
+ * the memory mapping info so we can get at the boards shared memory.
+ */
+ EBRDINIT(brdp);
+
+#if STLI_HIMEMORY
+ if (confp->memaddr > 0x100000) {
+ brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+ if (brdp->membase == (void *) NULL)
+ return(-ENOMEM);
+ }
+#endif
+
+/*
+ * Now that all specific code is set up, enable the shared memory and
+ * look for the a signature area that will tell us exactly what board
+ * this is, and how many ports.
+ */
+ EBRDENABLE(brdp);
+ sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+ memcpy(&sig, sigsp, sizeof(cdkonbsig_t));
+ EBRDDISABLE(brdp);
+
+#if 0
+ printk("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n",
+ __FILE__, __LINE__, sig.magic0, sig.magic1, sig.magic2,
+ sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2);
+#endif
+
+ if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
+ (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
+ return(-ENODEV);
+
+/*
+ * Scan through the signature alive mask and calculate how many ports
+ * there are on this board.
+ */
+ brdp->nrpanels = 1;
+ if (sig.amask1) {
+ brdp->nrports = 32;
+ } else {
+ for (i = 0; (i < 16); i++) {
+ if (((sig.amask0 << i) & 0x8000) == 0)
+ break;
+ }
+ brdp->nrports = i;
+ }
+
+ request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)");
+ brdp->state |= BST_FOUND;
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Start up a running board. This routine is only called after the
+ * code has been down loaded to the board and is operational. It will
+ * read in the memory map, and get the show on the road...
+ */
+
+static int stli_startbrd(stlibrd_t *brdp)
+{
+ volatile cdkhdr_t *hdrp;
+ volatile cdkmem_t *memp;
+ volatile cdkasy_t *ap;
+ unsigned long flags;
+ stliport_t *portp;
+ int portnr, nrdevs, i, rc;
+
+#if DEBUG
+ printk("stli_startbrd(brdp=%x)\n", (int) brdp);
+#endif
+
+ rc = 0;
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ nrdevs = hdrp->nrdevs;
+
+#if 0
+ printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n",
+ __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification,
+ hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp,
+ (int) hdrp->slavep);
+#endif
+
+ if (nrdevs < (brdp->nrports + 1)) {
+ printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs);
+ brdp->nrports = nrdevs - 1;
+ }
+ brdp->nrdevs = nrdevs;
+ brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
+ brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
+ brdp->bitsize = (nrdevs + 7) / 8;
+ memp = (volatile cdkmem_t *) hdrp->memp;
+ if (((unsigned long) memp) > brdp->memsize) {
+ printk("STALLION: corrupted shared memory region?\n");
+ rc = -EIO;
+ goto stli_donestartup;
+ }
+ memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp, (unsigned long) memp);
+ if (memp->dtype != TYP_ASYNCTRL) {
+ printk("STALLION: no slave control device found\n");
+ goto stli_donestartup;
+ }
+ memp++;
+
+/*
+ * Cycle through memory allocation of each port. We are guaranteed to
+ * have all ports inside the first page of slave window, so no need to
+ * change pages while reading memory map.
+ */
+ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
+ if (memp->dtype != TYP_ASYNC)
+ break;
+ portp = brdp->ports[portnr];
+ if (portp == (stliport_t *) NULL)
+ break;
+ portp->devnr = i;
+ portp->addr = memp->offset;
+ portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs));
+ portp->portidx = (unsigned char) (i / 8);
+ portp->portbit = (unsigned char) (0x1 << (i % 8));
+ }
+
+/*
+ * For each port setup a local copy of the RX and TX buffer offsets
+ * and sizes. We do this separate from the above, because we need to
+ * move the shared memory page...
+ */
+ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
+ portp = brdp->ports[portnr];
+ if (portp == (stliport_t *) NULL)
+ break;
+ if (portp->addr == 0)
+ break;
+ ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ if (ap != (volatile cdkasy_t *) NULL) {
+ portp->rxsize = ap->rxq.size;
+ portp->txsize = ap->txq.size;
+ portp->rxoffset = ap->rxq.offset;
+ portp->txoffset = ap->txq.offset;
+ }
+ }
+
+stli_donestartup:
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ if (rc == 0)
+ brdp->state |= BST_STARTED;
+
+ if (! stli_timeron) {
+ stli_timeron++;
+ stli_timerlist.expires = STLI_TIMEOUT;
+ add_timer(&stli_timerlist);
+ }
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Scan through all the boards in the configuration and see what we
+ * can find.
+ */
+
+static int stli_brdinit()
+{
+ stlibrd_t *brdp;
+ stlconf_t *confp;
+ int i, j;
+
+#if DEBUG
+ printk("stli_brdinit()\n");
+#endif
+
+ if (stli_nrbrds > STL_MAXBRDS)
+ return(-EINVAL);
+
+ stli_brds = (stlibrd_t *) stli_memalloc((sizeof(stlibrd_t) * stli_nrbrds));
+ if (stli_brds == (stlibrd_t *) NULL) {
+ printk("STALLION: failed to allocate board structures\n");
+ return(-ENOMEM);
+ }
+ memset(stli_brds, 0, (sizeof(stlibrd_t) * stli_nrbrds));
+
+ for (i = 0; (i < stli_nrbrds); i++) {
+ brdp = &stli_brds[i];
+ confp = &stli_brdconf[i];
+ brdp->brdnr = i;
+ brdp->brdtype = confp->brdtype;
+
+ switch (confp->brdtype) {
+ case BRD_ECP:
+ case BRD_ECPE:
+ case BRD_ECPMC:
+ stli_initecp(brdp, confp);
+ break;
+ case BRD_ONBOARD:
+ case BRD_ONBOARDE:
+ case BRD_ONBOARD2:
+ case BRD_ONBOARD32:
+ case BRD_ONBOARD2_32:
+ case BRD_ONBOARDRS:
+ case BRD_BRUMBY4:
+ case BRD_BRUMBY8:
+ case BRD_BRUMBY16:
+ case BRD_STALLION:
+ stli_initonb(brdp, confp);
+ break;
+ case BRD_EASYIO:
+ case BRD_ECH:
+ case BRD_ECHMC:
+ case BRD_ECHPCI:
+ printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
+ break;
+ default:
+ printk("STALLION: unit=%d is unknown board type=%d\n", i, confp->brdtype);
+ break;
+ }
+
+ if ((brdp->state & BST_FOUND) == 0) {
+ printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr);
+ continue;
+ }
+
+ stli_initports(brdp);
+ printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr, brdp->nrpanels, brdp->nrports);
+ }
+
+/*
+ * All found boards are initialized. Now for a little optimization, if
+ * no boards are sharing the "shared memory" regions then we can just
+ * leave them all enabled. This is in fact the usual case.
+ */
+ stli_shared = 0;
+ if (stli_nrbrds > 1) {
+ for (i = 0; (i < stli_nrbrds); i++) {
+ for (j = i + 1; (j < stli_nrbrds); j++) {
+ brdp = &stli_brds[i];
+ if ((brdp->membase >= stli_brds[j].membase) &&
+ (brdp->membase <= (stli_brds[j].membase + stli_brds[j].memsize - 1))) {
+ stli_shared++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (stli_shared == 0) {
+ for (i = 0; (i < stli_nrbrds); i++) {
+ brdp = &stli_brds[i];
+ if (brdp->state & BST_FOUND) {
+ EBRDENABLE(brdp);
+ brdp->enable = NULL;
+ brdp->disable = NULL;
+ }
+ }
+ }
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Code to handle an "staliomem" read operation. This device is the
+ * contents of the board shared memory. It is used for down loading
+ * the slave image (and debugging :-)
+ */
+
+static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count)
+{
+ unsigned long flags;
+ void *memptr;
+ stlibrd_t *brdp;
+ int brdnr, size, n;
+
+#if DEBUG
+ printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, (int) fp, (int) buf, count);
+#endif
+
+ brdnr = MINOR(ip->i_rdev);
+ if (brdnr >= stli_nrbrds)
+ return(-ENODEV);
+ brdp = &stli_brds[brdnr];
+ if (brdp->state == 0)
+ return(-ENODEV);
+ if (fp->f_pos >= brdp->memsize)
+ return(0);
+
+ size = MIN(count, (brdp->memsize - fp->f_pos));
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ while (size > 0) {
+ memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
+ n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
+ memcpy_tofs(buf, memptr, n);
+ fp->f_pos += n;
+ buf += n;
+ size -= n;
+ }
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ * Code to handle an "staliomem" write operation. This device is the
+ * contents of the board shared memory. It is used for down loading
+ * the slave image (and debugging :-)
+ */
+
+static int stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count)
+{
+ unsigned long flags;
+ void *memptr;
+ stlibrd_t *brdp;
+ char *chbuf;
+ int brdnr, size, n;
+
+#if DEBUG
+ printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, (int) fp, (int) buf, count);
+#endif
+
+ brdnr = MINOR(ip->i_rdev);
+ if (brdnr >= stli_nrbrds)
+ return(-ENODEV);
+ brdp = &stli_brds[brdnr];
+ if (brdp->state == 0)
+ return(-ENODEV);
+ if (fp->f_pos >= brdp->memsize)
+ return(0);
+
+ chbuf = (char *) buf;
+ size = MIN(count, (brdp->memsize - fp->f_pos));
+
+ save_flags(flags);
+ cli();
+ EBRDENABLE(brdp);
+ while (size > 0) {
+ memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
+ n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
+ memcpy_fromfs(memptr, chbuf, n);
+ fp->f_pos += n;
+ chbuf += n;
+ size -= n;
+ }
+ EBRDDISABLE(brdp);
+ restore_flags(flags);
+
+ return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ * The "staliomem" device is also required to do some special operations on
+ * the board. We need to be able to send an interrupt to the board,
+ * reset it, and start/stop it.
+ */
+
+static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ stlibrd_t *brdp;
+ int brdnr, rc;
+
+#if DEBUG
+ printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+#endif
+
+ brdnr = MINOR(ip->i_rdev);
+ if (brdnr >= stli_nrbrds)
+ return(-ENODEV);
+ brdp = &stli_brds[brdnr];
+ if (brdp->state == 0)
+ return(-ENODEV);
+
+ rc = 0;
+
+ switch (cmd) {
+ case STL_BINTR:
+ EBRDINTR(brdp);
+ break;
+ case STL_BSTART:
+ rc = stli_startbrd(brdp);
+ break;
+ case STL_BSTOP:
+ brdp->state &= ~BST_STARTED;
+ break;
+ case STL_BRESET:
+ brdp->state &= ~BST_STARTED;
+ EBRDRESET(brdp);
+ if (stli_shared == 0) {
+ if (brdp->reenable != NULL)
+ (* brdp->reenable)(brdp);
+ }
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+long stli_init(long kmem_start)
+{
+ printk("%s: version %s\n", stli_drvname, stli_drvversion);
+
+#ifndef MODULE
+ stli_meminit(kmem_start);
+#endif
+
+ stli_brdinit();
+
+/*
+ * Allocate a temporary write buffer.
+ */
+ stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
+ if (stli_tmpwritebuf == (char *) NULL)
+ printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+ stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
+ if (stli_txcookbuf == (char *) NULL)
+ printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+
+/*
+ * Set up a character driver for the shared memory region. We need this
+ * to down load the slave code image. Also it is a useful debugging tool.
+ */
+ if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem))
+ printk("STALLION: failed to register serial memory device\n");
+
+/*
+ * Set up the tty driver structure and register us as a driver.
+ * Also setup the callout tty device.
+ */
+ memset(&stli_serial, 0, sizeof(struct tty_driver));
+ stli_serial.magic = TTY_DRIVER_MAGIC;
+ stli_serial.name = stli_serialname;
+ stli_serial.major = STL_SERIALMAJOR;
+ stli_serial.minor_start = 0;
+ stli_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+ stli_serial.type = TTY_DRIVER_TYPE_SERIAL;
+ stli_serial.subtype = STL_DRVTYPSERIAL;
+ stli_serial.init_termios = stli_deftermios;
+ stli_serial.flags = TTY_DRIVER_REAL_RAW;
+ stli_serial.refcount = &stli_refcount;
+ stli_serial.table = stli_ttys;
+ stli_serial.termios = stli_termios;
+ stli_serial.termios_locked = stli_termioslocked;
+
+ stli_serial.open = stli_open;
+ stli_serial.close = stli_close;
+ stli_serial.write = stli_write;
+ stli_serial.put_char = stli_putchar;
+ stli_serial.flush_chars = stli_flushchars;
+ stli_serial.write_room = stli_writeroom;
+ stli_serial.chars_in_buffer = stli_charsinbuffer;
+ stli_serial.ioctl = stli_ioctl;
+ stli_serial.set_termios = stli_settermios;
+ stli_serial.throttle = stli_throttle;
+ stli_serial.unthrottle = stli_unthrottle;
+ stli_serial.stop = stli_stop;
+ stli_serial.start = stli_start;
+ stli_serial.hangup = stli_hangup;
+ stli_serial.flush_buffer = stli_flushbuffer;
+
+ stli_callout = stli_serial;
+ stli_callout.name = stli_calloutname;
+ stli_callout.major = STL_CALLOUTMAJOR;
+ stli_callout.subtype = STL_DRVTYPCALLOUT;
+
+ if (tty_register_driver(&stli_serial))
+ printk("STALLION: failed to register serial driver\n");
+ if (tty_register_driver(&stli_callout))
+ printk("STALLION: failed to register callout driver\n");
+
+#ifndef MODULE
+ kmem_start = stli_memhalt();
+#endif
+ return(kmem_start);
+}
+
+/*****************************************************************************/
#include <linux/major.h>
#include <linux/malloc.h>
-#include "mouse.h"
-
/*
* Head entry for the doubly linked mouse list
*/
+++ /dev/null
-#ifndef __MOUSE_H
-#define __MOUSE_H
-
-struct mouse {
- int minor;
- const char *name;
- struct file_operations *fops;
- struct mouse * next, * prev;
-};
-
-extern int mouse_register(struct mouse * mouse);
-extern int mouse_deregister(struct mouse * mouse);
-
-#endif
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
+#include <linux/mouse.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/malloc.h>
+#include <linux/mouse.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <linux/config.h>
-#include "mouse.h"
-
#define PSMOUSE_MINOR 1 /* minor device # for this mouse */
/* aux controller ports */
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * stallion.c -- stallion multiport serial driver.
+ *
+ * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This code is loosely based on the Linux serial driver, written by
+ * Linus Torvalds, Theodore T'so and others.
+ *
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/cd1400.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Define different board types. At the moment I have only declared
+ * those boards that this driver supports. But I will use the standard
+ * "assigned" board numbers. In the future this driver will support
+ * some of the other Stallion boards. Currently supported boards are
+ * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32.
+ */
+#define BRD_EASYIO 20
+#define BRD_ECH 21
+#define BRD_ECHMC 22
+#define BRD_ECHPCI 26
+
+/*
+ * Define a configuration structure to hold the board configuration.
+ * Need to set this up in the code (for now) with the boards that are
+ * to be configured into the system. This is what needs to be modified
+ * when adding/removing/modifying boards. Each line entry in the
+ * stl_brdconf[] array is a board. Each line contains io/irq/memory
+ * ranges for that board (as well as what type of board it is).
+ * Some examples:
+ * { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }
+ * This line would configure an EasyIO board (4 or 8, no difference),
+ * at io addres 2a0 and irq 10.
+ * Another example:
+ * { BRD_ECH, 0x2a8, 0x280, 0, 12, 0 },
+ * This line will configure an EasyConnection 8/32 board at primary io
+ * addres 2a8, secondary io address 280 and irq 12.
+ * Enter as many lines into this array as you want (only the first 4
+ * will actually be used!). Any combination of EasyIO and EasyConnection
+ * boards can be specified. EasyConnection 8/32 boards can share their
+ * secondary io addresses between each other.
+ *
+ * NOTE: there is no need to put any entries in this table for PCI
+ * boards. They will be found automatically by the driver - provided
+ * PCI BIOS32 support is compiled into the kernel.
+ */
+
+typedef struct {
+ int brdtype;
+ int ioaddr1;
+ int ioaddr2;
+ unsigned long memaddr;
+ int irq;
+ int irqtype;
+} stlconf_t;
+
+static stlconf_t stl_brdconf[] = {
+ { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+};
+
+static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
+
+/*****************************************************************************/
+
+/*
+ * Define some important driver characteristics. Device major numbers
+ * allocated as per Linux Device Registery.
+ */
+#ifndef STL_SERIALMAJOR
+#define STL_SERIALMAJOR 24
+#endif
+#ifndef STL_CALLOUTMAJOR
+#define STL_CALLOUTMAJOR 25
+#endif
+
+#define STL_DRVTYPSERIAL 1
+#define STL_DRVTYPCALLOUT 2
+
+#define STL_MAXBRDS 4
+#define STL_MAXPANELS 4
+#define STL_PORTSPERPANEL 16
+#define STL_MAXPORTS 64
+#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS)
+
+/*
+ * I haven't really decided (or measured) what TX buffer size gives
+ * a good balance between performance and memory usage. These seem
+ * to work pretty well...
+ */
+#define STL_TXBUFLOW 256
+#define STL_TXBUFSIZE 2048
+
+/*****************************************************************************/
+
+/*
+ * Define our local driver identity first. Set up stuff to deal with
+ * all the local structures required by a serial tty driver.
+ */
+static char *stl_drvname = "Stallion Multiport Serial Driver";
+static char *stl_drvversion = "1.0.0";
+static char *stl_serialname = "ttyE";
+static char *stl_calloutname = "cue";
+
+static struct tty_driver stl_serial;
+static struct tty_driver stl_callout;
+static struct tty_struct *stl_ttys[STL_MAXDEVS];
+static struct termios *stl_termios[STL_MAXDEVS];
+static struct termios *stl_termioslocked[STL_MAXDEVS];
+static int stl_refcount = 0;
+
+/*
+ * We will need to allocate a temporary write buffer for chars that
+ * come direct from user space. The problem is that a copy from user
+ * space might cause a page fault (typically on a system that is
+ * swapping!). All ports will share one buffer - since if the system
+ * is already swapping a shared buffer won't make things any worse.
+ */
+static char *stl_tmpwritebuf;
+static struct semaphore stl_tmpwritesem = MUTEX;
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially. Basically all it defines is a raw port
+ * at 9600, 8 data bits, 1 stop bit.
+ */
+static struct termios stl_deftermios = {
+ 0,
+ 0,
+ (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+ 0,
+ 0,
+ INIT_C_CC
+};
+
+/*
+ * Keep track of what interrupts we have requested for us.
+ * We don't need to request an interrupt twice if it is being
+ * shared with another Stallion board.
+ */
+static int stl_gotintrs[STL_MAXBRDS];
+static int stl_numintrs = 0;
+
+/*****************************************************************************/
+
+/*
+ * Define a set of structures to hold all the board/panel/port info
+ * for our ports. These will be dynamically allocated as required.
+ */
+
+/*
+ * Define a ring queue structure for each port. This will hold the
+ * TX data waiting to be output. Characters are fed into this buffer
+ * from the line discipline (or even direct from user space!) and
+ * then fed into the UARTs during interrupts. Will use a clasic ring
+ * queue here for this. The good thing about this type of ring queue
+ * is that the head and tail pointers can be updated without interrupt
+ * protection - since "write" code only needs to change the head, and
+ * interrupt code only needs to change the tail.
+ */
+typedef struct {
+ char *buf;
+ char *head;
+ char *tail;
+} stlrq_t;
+
+/*
+ * Port, panel and board structures to hold status info about each.
+ * The board structure contains pointers to structures for each panel
+ * connected to it, and in turn each panel structure contains pointers
+ * for each port structure for each port on that panel. Note that
+ * the port structure also contains the board and panel number that it
+ * is associated with, this makes it (fairly) easy to get back to the
+ * board/panel info for a port.
+ */
+typedef struct {
+ int portnr;
+ int panelnr;
+ int brdnr;
+ int ioaddr;
+ int uartaddr;
+ int pagenr;
+ int istate;
+ int flags;
+ int baud_base;
+ int custom_divisor;
+ int close_delay;
+ int closing_wait;
+ int refcount;
+ int openwaitcnt;
+ int brklen;
+ long session;
+ long pgrp;
+ unsigned int sigs;
+ unsigned int rxignoremsk;
+ unsigned int rxmarkmsk;
+ struct tty_struct *tty;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct termios normaltermios;
+ struct termios callouttermios;
+ struct tq_struct tqueue;
+ stlrq_t tx;
+} stlport_t;
+
+typedef struct {
+ int panelnr;
+ int brdnr;
+ int pagenr;
+ int nrports;
+ int iobase;
+ unsigned int ackmask;
+ stlport_t *ports[STL_PORTSPERPANEL];
+} stlpanel_t;
+
+typedef struct {
+ int brdnr;
+ int brdtype;
+ int state;
+ int nrpanels;
+ int nrports;
+ int irq;
+ int irqtype;
+ unsigned int ioaddr1;
+ unsigned int ioaddr2;
+ unsigned int iostatus;
+ unsigned int ioctrl;
+ unsigned int ioctrlval;
+ stlpanel_t *panels[STL_MAXPANELS];
+} stlbrd_t;
+
+static stlbrd_t *stl_brds[STL_MAXBRDS];
+
+/*
+ * Per board state flags. Used with the state field of the board struct.
+ * Not really much here yet!
+ */
+#define BRD_FOUND 0x1
+
+/*
+ * Define the port structure istate flags. These set of flags are
+ * modified at interrupt time - so setting and reseting them needs
+ * to be atomic. Use the bit clear/setting routines for this.
+ */
+#define ASYI_TXBUSY 1
+#define ASYI_TXLOW 2
+#define ASYI_DCDCHANGE 3
+
+/*
+ * Define an array of board names as printable strings. Handy for
+ * referencing boards when printing trace and stuff.
+ */
+static char *stl_brdnames[] = {
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ "EasyIO",
+ "EC8/32-AT",
+ "EC8/32-MC",
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ "EC8/32-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ * Hardware ID bits for the EasyIO and ECH boards. These defines apply
+ * to the directly accessable io ports of these boards (not the cd1400
+ * uarts - they are in cd1400.h).
+ */
+#define EIO_8PORTRS 0x04
+#define EIO_4PORTRS 0x05
+#define EIO_8PORTDI 0x00
+#define EIO_8PORTM 0x06
+#define EIO_IDBITMASK 0x07
+#define EIO_INTRPEND 0x08
+#define EIO_INTEDGE 0x00
+#define EIO_INTLEVEL 0x08
+
+#define ECH_ID 0xa0
+#define ECH_IDBITMASK 0xe0
+#define ECH_BRDENABLE 0x08
+#define ECH_BRDDISABLE 0x00
+#define ECH_INTENABLE 0x01
+#define ECH_INTDISABLE 0x00
+#define ECH_INTLEVEL 0x02
+#define ECH_INTEDGE 0x00
+#define ECH_INTRPEND 0x01
+#define ECH_BRDRESET 0x01
+
+#define ECHMC_INTENABLE 0x01
+#define ECHMC_BRDRESET 0x02
+
+#define ECH_PNLSTATUS 2
+#define ECH_PNL16PORT 0x20
+#define ECH_PNLIDMASK 0x07
+#define ECH_PNLINTRPEND 0x80
+#define ECH_ADDR2MASK 0x1e0
+
+/*
+ * Define the offsets within the register bank for all io registers.
+ * These io address offsets are common to both the EIO and ECH.
+ */
+#define EREG_ADDR 0
+#define EREG_DATA 4
+#define EREG_RXACK 5
+#define EREG_TXACK 6
+#define EREG_MDACK 7
+
+#define EREG_BANKSIZE 8
+
+/*
+ * Define the vector mapping bits for the programmable interrupt board
+ * hardware. These bits encode the interrupt for the board to use - it
+ * is software selectable (except the EIO-8M).
+ */
+static unsigned char stl_vecmap[] = {
+ 0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07,
+ 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03
+};
+
+/*
+ * Set up enable and disable macros for the ECH boards. They require
+ * the secondary io address space to be activated and deactivated.
+ * This way all ECH boards can share their secondary io region.
+ * If this is an ECH-PCI board then also need to set the page pointer
+ * to point to the correct page.
+ */
+#define BRDENABLE(brdnr,pagenr) \
+ if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \
+ outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE), \
+ stl_brds[(brdnr)]->ioctrl); \
+ else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI) \
+ outb((pagenr), stl_brds[(brdnr)]->ioctrl);
+
+#define BRDDISABLE(brdnr) \
+ if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \
+ outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \
+ stl_brds[(brdnr)]->ioctrl);
+
+/*
+ * Define the cd1400 baud rate clocks. These are used when calculating
+ * what clock and divisor to use for the required baud rate. Also
+ * define the maximum baud rate allowed, and the default base baud.
+ */
+static int stl_cd1400clkdivs[] = {
+ CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+#define STL_MAXBAUD 230400
+#define STL_BAUDBASE 115200
+#define STL_CLOSEDELAY 50
+
+/*****************************************************************************/
+
+/*
+ * Define macros to extract a brd/port number from a minor number.
+ */
+#define MKDEV2BRD(min) (((min) & 0xc0) >> 6)
+#define MKDEV2PORT(min) ((min) & 0x3f)
+
+/*
+ * Define a baud rate table that converts termios baud rate selector
+ * into the actual baud rate value. All baud rate calculates are based
+ * on the actual baud rate required.
+ */
+static unsigned int stl_baudrates[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400
+};
+
+/*****************************************************************************/
+
+/*
+ * Memory allocation vars. These keep track of what memory allocation
+ * we can currently use. They help deal with memory in a consistent
+ * way, whether during init or run-time.
+ */
+static int stl_meminited = 0;
+static long stl_memend;
+
+/*****************************************************************************/
+
+/*
+ * Define some handy local macros...
+ */
+#ifndef MIN
+#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Declare all those functions in this driver!
+ */
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#else
+static void stl_meminit(long base);
+static long stl_memhalt(void);
+#endif
+static void *stl_memalloc(int len);
+
+long stl_init(long kmem_start);
+static int stl_open(struct tty_struct *tty, struct file *filp);
+static void stl_close(struct tty_struct *tty, struct file *filp);
+static int stl_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static void stl_putchar(struct tty_struct *tty, unsigned char ch);
+static void stl_flushchars(struct tty_struct *tty);
+static int stl_writeroom(struct tty_struct *tty);
+static int stl_charsinbuffer(struct tty_struct *tty);
+static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void stl_settermios(struct tty_struct *tty, struct termios *old);
+static void stl_throttle(struct tty_struct *tty);
+static void stl_unthrottle(struct tty_struct *tty);
+static void stl_stop(struct tty_struct *tty);
+static void stl_start(struct tty_struct *tty);
+static void stl_flushbuffer(struct tty_struct *tty);
+static void stl_hangup(struct tty_struct *tty);
+
+static int stl_initbrds(void);
+static int stl_brdinit(stlbrd_t *brdp);
+static int stl_initeio(stlbrd_t *brdp);
+static int stl_initech(stlbrd_t *brdp);
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp);
+static int stl_mapirq(int irq);
+static void stl_getserial(stlport_t *portp, struct serial_struct *sp);
+static int stl_setserial(stlport_t *portp, struct serial_struct *sp);
+static void stl_setreg(stlport_t *portp, int regnr, int value);
+static int stl_getreg(stlport_t *portp, int regnr);
+static int stl_updatereg(stlport_t *portp, int regnr, int value);
+static void stl_setport(stlport_t *portp, struct termios *tiosp);
+static void stl_getsignals(stlport_t *portp);
+static void stl_setsignals(stlport_t *portp, int dtr, int rts);
+static void stl_ccrwait(stlport_t *portp);
+static void stl_enablerxtx(stlport_t *portp, int rx, int tx);
+static void stl_startrxtx(stlport_t *portp, int rx, int tx);
+static void stl_disableintrs(stlport_t *portp);
+static void stl_sendbreak(stlport_t *portp, long len);
+static int stl_waitcarrier(stlport_t *portp, struct file *filp);
+static void stl_delay(int len);
+static void stl_intr(int irq, struct pt_regs *regs);
+static void stl_offintr(void *private);
+
+#ifdef CONFIG_PCI
+static int stl_findpcibrds(void);
+#endif
+
+/*****************************************************************************/
+
+#ifdef MODULE
+
+/*
+ * Use the kernel version number for modules.
+ */
+char kernel_version[] = UTS_RELEASE;
+
+int init_module()
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("init_module()\n");
+#endif
+
+ save_flags(flags);
+ cli();
+ stl_init(0);
+ restore_flags(flags);
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+void cleanup_module()
+{
+ stlbrd_t *brdp;
+ stlpanel_t *panelp;
+ stlport_t *portp;
+ unsigned long flags;
+ int i, j, k;
+
+#if DEBUG
+ printk("cleanup_module()\n");
+#endif
+
+ printk("Unloading %s: version %s\n", stl_drvname, stl_drvversion);
+
+ save_flags(flags);
+ cli();
+
+/*
+ * Free up all allocated resources used by the ports. This includes
+ * memory and interrupts. As part of this process we will also do
+ * a hangup on every open port - to try and flush out any processes
+ * hanging onto ports.
+ */
+ i = tty_unregister_driver(&stl_serial);
+ j = tty_unregister_driver(&stl_callout);
+ if (i || j) {
+ printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+ restore_flags(flags);
+ return;
+ }
+
+ if (stl_tmpwritebuf != (char *) NULL)
+ kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
+
+ for (i = 0; (i < stl_nrbrds); i++) {
+ brdp = stl_brds[i];
+ for (j = 0; (j < STL_MAXPANELS); j++) {
+ panelp = brdp->panels[j];
+ if (panelp != (stlpanel_t *) NULL) {
+ for (k = 0; (k < STL_PORTSPERPANEL); k++) {
+ portp = panelp->ports[k];
+ if (portp != (stlport_t *) NULL) {
+ if (portp->tty != (struct tty_struct *) NULL)
+ stl_hangup(portp->tty);
+ if (portp->tx.buf != (char *) NULL)
+ kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+ kfree_s(portp, sizeof(stlport_t));
+ }
+ }
+ kfree_s(panelp, sizeof(stlpanel_t));
+ }
+
+ }
+
+ if (brdp->brdtype == BRD_ECH) {
+ release_region(brdp->ioaddr1, 2);
+ release_region(brdp->ioaddr2, 32);
+ } else if (brdp->brdtype == BRD_ECHPCI) {
+ release_region(brdp->ioaddr1, 4);
+ release_region(brdp->ioaddr2, 8);
+ } else if (brdp->brdtype == BRD_ECHMC) {
+ release_region(brdp->ioaddr1, 64);
+ } else if (brdp->brdtype == BRD_EASYIO) {
+ release_region(brdp->ioaddr1, 8);
+ }
+
+ kfree_s(brdp, sizeof(stlbrd_t));
+ stl_brds[i] = (stlbrd_t *) NULL;
+ }
+
+ for (i = 0; (i < stl_numintrs); i++)
+ free_irq(stl_gotintrs[i]);
+
+ restore_flags(flags);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Local memory allocation routines. These are used so we can deal with
+ * memory allocation at init time and during run-time in a consistent
+ * way. Everbody just calls the stl_memalloc routine to allocate
+ * memory and it will do the right thing.
+ */
+
+#ifndef MODULE
+
+static void stl_meminit(long base)
+{
+ stl_memend = base;
+ stl_meminited = 1;
+}
+
+static long stl_memhalt()
+{
+ stl_meminited = 0;
+ return(stl_memend);
+}
+
+#endif
+
+static void *stl_memalloc(int len)
+{
+ void *mem;
+
+ if (stl_meminited) {
+ mem = (void *) stl_memend;
+ stl_memend += len;
+ } else {
+ mem = (void *) kmalloc(len, GFP_KERNEL);
+ }
+ return(mem);
+}
+
+/*****************************************************************************/
+
+static int stl_open(struct tty_struct *tty, struct file *filp)
+{
+ stlport_t *portp;
+ stlbrd_t *brdp;
+ unsigned int minordev;
+ int brdnr, panelnr, portnr, rc;
+
+#if DEBUG
+ printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+#endif
+
+ minordev = MINOR(tty->device);
+ brdnr = MKDEV2BRD(minordev);
+ if (brdnr >= stl_nrbrds)
+ return(-ENODEV);
+ brdp = stl_brds[brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return(-ENODEV);
+ minordev = MKDEV2PORT(minordev);
+ for (portnr = -1, panelnr = 0; (panelnr < STL_MAXPANELS); panelnr++) {
+ if (brdp->panels[panelnr] == (stlpanel_t *) NULL)
+ break;
+ if (minordev < brdp->panels[panelnr]->nrports) {
+ portnr = minordev;
+ break;
+ }
+ minordev -= brdp->panels[panelnr]->nrports;
+ }
+ if (portnr < 0)
+ return(-ENODEV);
+
+ portp = brdp->panels[panelnr]->ports[portnr];
+ if (portp == (stlport_t *) NULL)
+ return(-ENODEV);
+
+/*
+ * On the first open of the device setup the port hardware, and
+ * initialize the per port data structure.
+ */
+ portp->tty = tty;
+ tty->driver_data = portp;
+ portp->refcount++;
+
+ if ((portp->flags & ASYNC_INITIALIZED) == 0) {
+ if (portp->tx.buf == (char *) NULL) {
+ portp->tx.buf = (char *) stl_memalloc(STL_TXBUFSIZE);
+ if (portp->tx.buf == (char *) NULL)
+ return(-ENOMEM);
+ portp->tx.head = portp->tx.buf;
+ portp->tx.tail = portp->tx.buf;
+ }
+ stl_setport(portp, tty->termios);
+ stl_getsignals(portp);
+ stl_setsignals(portp, 1, 1);
+ stl_enablerxtx(portp, 1, 1);
+ stl_startrxtx(portp, 1, 0);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ portp->flags |= ASYNC_INITIALIZED;
+ }
+
+/*
+ * Check if this port is in the middle of closing. If so then wait
+ * until it is closed then return error status, based on flag settings.
+ * The sleep here does not need interrupt protection since the wakeup
+ * for it is done with the same context.
+ */
+ if (portp->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&portp->close_wait);
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ return(-EAGAIN);
+ return(-ERESTARTSYS);
+ }
+
+/*
+ * Based on type of open being done check if it can overlap with any
+ * previous opens still in effect. If we are a normal serial device
+ * then also we might have to wait for carrier.
+ */
+ if (tty->driver.subtype == STL_DRVTYPCALLOUT) {
+ if (portp->flags & ASYNC_NORMAL_ACTIVE)
+ return(-EBUSY);
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
+ if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
+ (portp->session != current->session))
+ return(-EBUSY);
+ if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
+ (portp->pgrp != current->pgrp))
+ return(-EBUSY);
+ }
+ portp->flags |= ASYNC_CALLOUT_ACTIVE;
+ } else {
+ if (filp->f_flags & O_NONBLOCK) {
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+ return(-EBUSY);
+ } else {
+ if ((rc = stl_waitcarrier(portp, filp)) != 0)
+ return(rc);
+ }
+ portp->flags |= ASYNC_NORMAL_ACTIVE;
+ }
+
+ if ((portp->refcount == 1) && (portp->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == STL_DRVTYPSERIAL)
+ *tty->termios = portp->normaltermios;
+ else
+ *tty->termios = portp->callouttermios;
+ stl_setport(portp, tty->termios);
+ }
+
+ portp->session = current->session;
+ portp->pgrp = current->pgrp;
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Possibly need to wait for carrier (DCD signal) to come high. Say
+ * maybe because if we are clocal then we don't need to wait...
+ */
+
+static int stl_waitcarrier(stlport_t *portp, struct file *filp)
+{
+ unsigned long flags;
+ int rc;
+
+#if DEBUG
+ printk("stl_waitcarrier(portp=%x,filp=%x)\n", (int) portp, (int) filp);
+#endif
+
+ rc = 0;
+
+ save_flags(flags);
+ cli();
+ portp->openwaitcnt++;
+ if (portp->refcount > 0)
+ portp->refcount--;
+
+ for (;;) {
+ if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0)
+ stl_setsignals(portp, 1, 1);
+ if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ rc = -EBUSY;
+ else
+ rc = -ERESTARTSYS;
+ break;
+ }
+ if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
+ ((portp->flags & ASYNC_CLOSING) == 0) &&
+ ((portp->tty->termios->c_cflag & CLOCAL) ||
+ (portp->sigs & TIOCM_CD))) {
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ rc = -ERESTARTSYS;
+ break;
+ }
+ interruptible_sleep_on(&portp->open_wait);
+ }
+
+ if (! tty_hung_up_p(filp))
+ portp->refcount++;
+ portp->openwaitcnt--;
+ restore_flags(flags);
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+static void stl_close(struct tty_struct *tty, struct file *filp)
+{
+ stlport_t *portp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
+#endif
+
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+ if (portp->refcount-- > 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ portp->refcount = 0;
+ portp->flags |= ASYNC_CLOSING;
+
+ if (portp->flags & ASYNC_NORMAL_ACTIVE)
+ portp->normaltermios = *tty->termios;
+ if (portp->flags & ASYNC_CALLOUT_ACTIVE)
+ portp->callouttermios = *tty->termios;
+
+/*
+ * May want to wait for any data to drain before closing. The BUSY
+ * flag keeps track of whether we are still sending or not - it allows
+ * for the FIFO in the cd1400.
+ */
+ tty->closing = 1;
+ if (test_bit(ASYI_TXBUSY, &portp->istate)) {
+ if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, portp->closing_wait);
+ }
+
+ portp->flags &= ~ASYNC_INITIALIZED;
+ stl_disableintrs(portp);
+ if (tty->termios->c_cflag & HUPCL)
+ stl_setsignals(portp, 0, 0);
+ stl_enablerxtx(portp, 0, 0);
+ stl_flushbuffer(tty);
+ portp->istate = 0;
+ if (portp->tx.buf != (char *) NULL) {
+ kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+ portp->tx.buf = (char *) NULL;
+ portp->tx.head = (char *) NULL;
+ portp->tx.tail = (char *) NULL;
+ }
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ if (tty->ldisc.flush_buffer)
+ (tty->ldisc.flush_buffer)(tty);
+
+ tty->closing = 0;
+ tty->driver_data = (void *) NULL;
+ portp->tty = (struct tty_struct *) NULL;
+
+ if (portp->openwaitcnt) {
+ if (portp->close_delay)
+ stl_delay(portp->close_delay);
+ wake_up_interruptible(&portp->open_wait);
+ }
+
+ portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+ wake_up_interruptible(&portp->close_wait);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Wait for a specified delay period, this is not a busy-loop. It will
+ * give up the processor while waiting. Unfortunately this has some
+ * rather intimate knowledge of the process management stuff.
+ */
+
+static void stl_delay(int len)
+{
+#if DEBUG
+ printk("stl_delay(len=%d)\n", len);
+#endif
+ if (len > 0) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + len;
+ schedule();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Write routine. Take data and stuff it in to the TX ring queue.
+ * If transmit interrupts are not running then start them.
+ */
+
+static int stl_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+ stlport_t *portp;
+ unsigned int len, stlen;
+ unsigned long flags;
+ unsigned char *chbuf;
+ char *head, *tail;
+
+#if DEBUG
+ printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+#endif
+
+ if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL))
+ return(0);
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return(0);
+ if (portp->tx.buf == (char *) NULL)
+ return(0);
+
+/*
+ * If copying direct from user space we must cater for page faults,
+ * causing us to "sleep" here for a while. To handle this copy in all
+ * the data we need now, into a local buffer. Then when we got it all
+ * copy it into the TX buffer.
+ */
+ chbuf = (unsigned char *) buf;
+ if (from_user) {
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) :
+ (tail - head - 1);
+ count = MIN(len, count);
+
+ save_flags(flags);
+ cli();
+ down(&stl_tmpwritesem);
+ memcpy_fromfs(stl_tmpwritebuf, chbuf, count);
+ up(&stl_tmpwritesem);
+ restore_flags(flags);
+ chbuf = &stl_tmpwritebuf[0];
+ }
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ if (head >= tail) {
+ len = STL_TXBUFSIZE - (head - tail) - 1;
+ stlen = STL_TXBUFSIZE - (head - portp->tx.buf);
+ } else {
+ len = tail - head - 1;
+ stlen = len;
+ }
+
+ len = MIN(len, count);
+ count = 0;
+ while (len > 0) {
+ stlen = MIN(len, stlen);
+ memcpy(head, chbuf, stlen);
+ len -= stlen;
+ chbuf += stlen;
+ count += stlen;
+ head += stlen;
+ if (head >= (portp->tx.buf + STL_TXBUFSIZE)) {
+ head = portp->tx.buf;
+ stlen = tail - head;
+ }
+ }
+ portp->tx.head = head;
+
+ clear_bit(ASYI_TXLOW, &portp->istate);
+ stl_startrxtx(portp, -1, 1);
+
+ return(count);
+}
+
+/*****************************************************************************/
+
+static void stl_putchar(struct tty_struct *tty, unsigned char ch)
+{
+ stlport_t *portp;
+ unsigned int len;
+ char *head, *tail;
+
+#if DEBUG
+ printk("stl_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+ if (portp->tx.buf == (char *) NULL)
+ return;
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+
+ len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head);
+ len--;
+
+ if (len > 0) {
+ *head++ = ch;
+ if (head >= (portp->tx.buf + STL_TXBUFSIZE))
+ head = portp->tx.buf;
+ }
+ portp->tx.head = head;
+}
+
+/*****************************************************************************/
+
+/*
+ * If there are any characters in the buffer then make sure that TX
+ * interrupts are on and get'em out. Normally used after the putchar
+ * routine has been called.
+ */
+
+static void stl_flushchars(struct tty_struct *tty)
+{
+ stlport_t *portp;
+
+#if DEBUG
+ printk("stl_flushchars(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+ if (portp->tx.buf == (char *) NULL)
+ return;
+
+#if 0
+ if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail))
+ return;
+#endif
+ stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+static int stl_writeroom(struct tty_struct *tty)
+{
+ stlport_t *portp;
+ char *head, *tail;
+
+#if DEBUG
+ printk("stl_writeroom(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(0);
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return(0);
+ if (portp->tx.buf == (char *) NULL)
+ return(0);
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ return((head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1));
+}
+
+/*****************************************************************************/
+
+/*
+ * Return number of chars in the TX buffer. Normally we would just
+ * calculate the number of chars in the buffer and return that, but if
+ * the buffer is empty and TX interrupts are still on then we return
+ * that the buffer still has 1 char in it. This way whoever called us
+ * will not think that ALL chars have drained - since the UART still
+ * must have some chars in it (we are busy after all).
+ */
+
+static int stl_charsinbuffer(struct tty_struct *tty)
+{
+ stlport_t *portp;
+ unsigned int size;
+ char *head, *tail;
+
+#if DEBUG
+ printk("stl_charsinbuffer(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(0);
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return(0);
+ if (portp->tx.buf == (char *) NULL)
+ return(0);
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+ if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate))
+ size = 1;
+ return(size);
+}
+
+/*****************************************************************************/
+
+/*
+ * Generate the serial struct info.
+ */
+
+static void stl_getserial(stlport_t *portp, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+ stlbrd_t *brdp;
+
+#if DEBUG
+ printk("stl_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+ memset(&sio, 0, sizeof(struct serial_struct));
+ sio.type = PORT_CIRRUS;
+ sio.line = portp->portnr;
+ sio.port = portp->ioaddr;
+ sio.flags = portp->flags;
+ sio.baud_base = portp->baud_base;
+ sio.close_delay = portp->close_delay;
+ sio.closing_wait = portp->closing_wait;
+ sio.custom_divisor = portp->custom_divisor;
+ sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+ sio.hub6 = 0;
+
+ brdp = stl_brds[portp->brdnr];
+ if (brdp != (stlbrd_t *) NULL)
+ sio.irq = brdp->irq;
+
+ memcpy_tofs(sp, &sio, sizeof(struct serial_struct));
+}
+
+/*****************************************************************************/
+
+/*
+ * Set port according to the serial struct info.
+ * At this point we do not do any auto-configure stuff, so we will
+ * just quietly ignore any requests to change irq, etc.
+ */
+
+static int stl_setserial(stlport_t *portp, struct serial_struct *sp)
+{
+ struct serial_struct sio;
+
+#if DEBUG
+ printk("stl_setserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
+#endif
+
+ memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
+ if (!suser()) {
+ if ((sio.baud_base != portp->baud_base) ||
+ (sio.close_delay != portp->close_delay) ||
+ ((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+ return(-EPERM);
+ }
+
+ portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+ portp->baud_base = sio.baud_base;
+ portp->close_delay = sio.close_delay;
+ portp->closing_wait = sio.closing_wait;
+ portp->custom_divisor = sio.custom_divisor;
+ stl_setport(portp, portp->tty->termios);
+ return(0);
+}
+
+/*****************************************************************************/
+
+static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ stlport_t *portp;
+ int rc;
+
+#if DEBUG
+ printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return(-ENODEV);
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return(-ENODEV);
+
+ rc = 0;
+
+ switch (cmd) {
+ case TCSBRK:
+ if ((rc = tty_check_change(tty)) == 0) {
+ tty_wait_until_sent(tty, 0);
+ if (! arg)
+ stl_sendbreak(portp, 250);
+ }
+ break;
+ case TCSBRKP:
+ if ((rc = tty_check_change(tty)) == 0) {
+ tty_wait_until_sent(tty, 0);
+ stl_sendbreak(portp, (arg ? (arg * 100) : 250));
+ }
+ break;
+ case TIOCGSOFTCAR:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
+ put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+ break;
+ case TIOCSSOFTCAR:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+ }
+ break;
+ case TIOCMGET:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+ stl_getsignals(portp);
+ put_fs_long(portp->sigs, (unsigned long *) arg);
+ }
+ break;
+ case TIOCMBIS:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
+ }
+ break;
+ case TIOCMBIC:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
+ }
+ break;
+ case TIOCMSET:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+ arg = get_fs_long((unsigned long *) arg);
+ stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
+ }
+ break;
+ case TIOCGSERIAL:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+ stl_getserial(portp, (struct serial_struct *) arg);
+ break;
+ case TIOCSSERIAL:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+ rc = stl_setserial(portp, (struct serial_struct *) arg);
+ break;
+ case TIOCSERCONFIG:
+ case TIOCSERGWILD:
+ case TIOCSERSWILD:
+ case TIOCSERGETLSR:
+ case TIOCSERGSTRUCT:
+ case TIOCSERGETMULTI:
+ case TIOCSERSETMULTI:
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+
+ return(rc);
+}
+
+/*****************************************************************************/
+
+static void stl_settermios(struct tty_struct *tty, struct termios *old)
+{
+ stlport_t *portp;
+ struct termios *tiosp;
+
+#if DEBUG
+ printk("stl_settermios(tty=%x,old=%x)\n", (int) tty, (int) old);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ tiosp = tty->termios;
+ if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+ return;
+
+ stl_setport(portp, tiosp);
+ stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1);
+ if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
+ tty->hw_stopped = 0;
+ stl_start(tty);
+ }
+ if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+ wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ * Attempt to flow control who ever is sending us data. Based on termios
+ * settings use software or/and hardware flow control.
+ */
+
+static void stl_throttle(struct tty_struct *tty)
+{
+ stlport_t *portp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_throttle(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ if (tty->termios->c_iflag & IXOFF) {
+ stl_ccrwait(portp);
+ stl_setreg(portp, CCR, CCR_SENDSCHR2);
+ stl_ccrwait(portp);
+ }
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0));
+ stl_setreg(portp, MSVR2, 0);
+ }
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Unflow control the device sending us data...
+ */
+
+static void stl_unthrottle(struct tty_struct *tty)
+{
+ stlport_t *portp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_unthrottle(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ if (tty->termios->c_iflag & IXOFF) {
+ stl_ccrwait(portp);
+ stl_setreg(portp, CCR, CCR_SENDSCHR1);
+ stl_ccrwait(portp);
+ }
+/*
+ * Question: should we return RTS to what it was before? It may have
+ * been set by an ioctl... Suppose not, since if you have hardware
+ * flow control set then it is pretty silly to go and set the RTS line
+ * by hand.
+ */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
+ stl_setreg(portp, MSVR2, MSVR2_RTS);
+ }
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Stop the transmitter. Basically to do this we will just turn TX
+ * interrupts off.
+ */
+
+static void stl_stop(struct tty_struct *tty)
+{
+ stlport_t *portp;
+
+#if DEBUG
+ printk("stl_stop(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ stl_startrxtx(portp, -1, 0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Start the transmitter again. Just turn TX interrupts back on.
+ */
+
+static void stl_start(struct tty_struct *tty)
+{
+ stlport_t *portp;
+
+#if DEBUG
+ printk("stl_start(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+/*
+ * Hangup this port. This is pretty much like closing the port, only
+ * a little more brutal. No waiting for data to drain. Shutdown the
+ * port and maybe drop signals.
+ */
+
+static void stl_hangup(struct tty_struct *tty)
+{
+ stlport_t *portp;
+
+#if DEBUG
+ printk("stl_hangup(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ portp->flags &= ~ASYNC_INITIALIZED;
+ stl_disableintrs(portp);
+ if (tty->termios->c_cflag & HUPCL)
+ stl_setsignals(portp, 0, 0);
+ stl_enablerxtx(portp, 0, 0);
+ stl_flushbuffer(tty);
+ portp->istate = 0;
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ if (portp->tx.buf != (char *) NULL) {
+ kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+ portp->tx.buf = (char *) NULL;
+ portp->tx.head = (char *) NULL;
+ portp->tx.tail = (char *) NULL;
+ }
+ tty->driver_data = (void *) NULL;
+ portp->tty = (struct tty_struct *) NULL;
+ portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+ portp->refcount = 0;
+ wake_up_interruptible(&portp->open_wait);
+}
+
+/*****************************************************************************/
+
+static void stl_flushbuffer(struct tty_struct *tty)
+{
+ stlport_t *portp;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_flushbuffer(tty=%x)\n", (int) tty);
+#endif
+
+ if (tty == (struct tty_struct *) NULL)
+ return;
+ portp = tty->driver_data;
+ if (portp == (stlport_t *) NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_ccrwait(portp);
+ stl_setreg(portp, CCR, CCR_TXFLUSHFIFO);
+ stl_ccrwait(portp);
+ portp->tx.tail = portp->tx.head;
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ * These functions get/set/update the registers of the cd1400 UARTs.
+ * Access to the cd1400 registers is via an address/data io port pair.
+ * (Maybe should make this inline...)
+ */
+
+static int stl_getreg(stlport_t *portp, int regnr)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ return(inb(portp->ioaddr + EREG_DATA));
+}
+
+static void stl_setreg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_updatereg(stlport_t *portp, int regnr, int value)
+{
+ outb((regnr + portp->uartaddr), portp->ioaddr);
+ if (inb(portp->ioaddr + EREG_DATA) != value) {
+ outb(value, portp->ioaddr + EREG_DATA);
+ return(1);
+ }
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Transmit interrupt handler. This has gotta be fast! Handling TX
+ * chars is pretty simple, stuff as many as possible from the TX buffer
+ * into the cd1400 FIFO. Must also handle TX breaks here, since they
+ * are embedded as commands in the data stream. Oh no, had to use a goto!
+ * This could be optimized more, will do when I get time...
+ * In practice it is possible that interrupts are enabled but that the
+ * port has been hung up. Need to handle not having any TX buffer here,
+ * this is done by using the side effect that head and tail will also
+ * be NULL if the buffer has been freed.
+ */
+
+static inline void stl_txisr(stlpanel_t *panelp, int ioaddr)
+{
+ stlport_t *portp;
+ int len, stlen;
+ char *head, *tail;
+ unsigned char ioack, srer;
+
+#if DEBUG
+ printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+ ioack = inb(ioaddr + EREG_TXACK);
+ if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+ printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+
+/*
+ * Unfortunately we need to handle breaks in the data stream, since
+ * this is the only way to generate them on the cd1400. Do it now if
+ * a break is to be sent.
+ */
+ if (portp->brklen != 0) {
+ if (portp->brklen > 0) {
+ outb((TDR + portp->uartaddr), ioaddr);
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_DELAY, (ioaddr + EREG_DATA));
+ outb(portp->brklen, (ioaddr + EREG_DATA));
+ outb(ETC_CMD, (ioaddr + EREG_DATA));
+ outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+ portp->brklen = -1;
+ goto stl_txalldone;
+ } else {
+ outb((COR2 + portp->uartaddr), ioaddr);
+ outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
+ portp->brklen = 0;
+ }
+ }
+
+ head = portp->tx.head;
+ tail = portp->tx.tail;
+ len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+ if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+ set_bit(ASYI_TXLOW, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ }
+
+ if (len == 0) {
+ outb((SRER + portp->uartaddr), ioaddr);
+ srer = inb(ioaddr + EREG_DATA);
+ if (srer & SRER_TXDATA) {
+ srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+ } else {
+ srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+ clear_bit(ASYI_TXBUSY, &portp->istate);
+ }
+ outb(srer, (ioaddr + EREG_DATA));
+ } else {
+ len = MIN(len, CD1400_TXFIFOSIZE);
+ stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+ outb((TDR + portp->uartaddr), ioaddr);
+ outsb((ioaddr + EREG_DATA), tail, stlen);
+ len -= stlen;
+ tail += stlen;
+ if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+ tail = portp->tx.buf;
+ if (len > 0) {
+ outsb((ioaddr + EREG_DATA), tail, len);
+ tail += len;
+ }
+ portp->tx.tail = tail;
+ }
+
+stl_txalldone:
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ * Receive character interrupt handler. Determine if we have good chars
+ * or bad chars and then process appropriately. Good chars are easy
+ * just shove the lot into the RX buffer and set all status byte to 0.
+ * If a bad RX char then process as required. This routine needs to be
+ * fast! In practice it is possible that we get an interrupt on a port
+ * that is closed. This can happen on hangups - since they completely
+ * shutdown a port not in user context. Need to handle this case.
+ */
+
+static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr)
+{
+ stlport_t *portp;
+ struct tty_struct *tty;
+ unsigned int ioack, len, buflen;
+ unsigned char status;
+ char ch;
+ static char unwanted[CD1400_RXFIFOSIZE];
+
+#if DEBUG
+ printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+ ioack = inb(ioaddr + EREG_RXACK);
+ if ((ioack & panelp->ackmask) != 0) {
+ printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+ tty = portp->tty;
+
+ if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+ outb((RDCR + portp->uartaddr), ioaddr);
+ len = inb(ioaddr + EREG_DATA);
+ if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
+ ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ insb((ioaddr + EREG_DATA), &unwanted[0], len);
+ } else {
+ len = MIN(len, buflen);
+ if (len > 0) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
+ memset(tty->flip.flag_buf_ptr, 0, len);
+ tty->flip.flag_buf_ptr += len;
+ tty->flip.char_buf_ptr += len;
+ tty->flip.count += len;
+ tty_schedule_flip(tty);
+ }
+ }
+ } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+ outb((RDSR + portp->uartaddr), ioaddr);
+ status = inb(ioaddr + EREG_DATA);
+ ch = inb(ioaddr + EREG_DATA);
+ if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
+ if (portp->rxmarkmsk & status) {
+ if (status & ST_BREAK) {
+ status = TTY_BREAK;
+#ifndef MODULE
+ if (portp->flags & ASYNC_SAK)
+ do_SAK(tty);
+#endif
+ } else if (status & ST_PARITY) {
+ status = TTY_PARITY;
+ } else if (status & ST_FRAMING) {
+ status = TTY_FRAME;
+ } else if(status & ST_OVERRUN) {
+ status = TTY_OVERRUN;
+ } else {
+ status = 0;
+ }
+ } else {
+ status = 0;
+ }
+ if (tty->flip.char_buf_ptr != (char *) NULL) {
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ *tty->flip.flag_buf_ptr++ = status;
+ *tty->flip.char_buf_ptr++ = ch;
+ tty->flip.count++;
+ }
+ tty_schedule_flip(tty);
+ }
+ }
+ } else {
+ printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ return;
+ }
+
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ * Modem interrupt handler. The is called when the modem signal line
+ * (DCD) has changed state. Leave most of the work to the off-level
+ * processing routine.
+ */
+
+static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr)
+{
+ stlport_t *portp;
+ unsigned int ioack;
+ unsigned char misr;
+
+#if DEBUG
+ printk("stl_mdmisr(panelp=%x)\n", (int) panelp);
+#endif
+
+ ioack = inb(ioaddr + EREG_MDACK);
+ if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+ printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+ return;
+ }
+ portp = panelp->ports[(ioack >> 3)];
+
+ outb((MISR + portp->uartaddr), ioaddr);
+ misr = inb(ioaddr + EREG_DATA);
+ if (misr & MISR_DCD) {
+ set_bit(ASYI_DCDCHANGE, &portp->istate);
+ queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+ }
+
+ outb((EOSRR + portp->uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ * Interrupt handler for EIO and ECH boards. This code ain't all that
+ * pretty, but the idea is to make it as fast as possible. This code is
+ * well suited to be assemblerized :-) We don't use the general purpose
+ * register access functions here, for speed we will go strait to the
+ * io region.
+ */
+
+static void stl_intr(int irq, struct pt_regs *regs)
+{
+ stlbrd_t *brdp;
+ stlpanel_t *panelp;
+ unsigned char svrtype;
+ int i, panelnr, iobase;
+
+#if DEBUG
+ printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+#endif
+
+ panelp = (stlpanel_t *) NULL;
+ for (i = 0; (i < stl_nrbrds); ) {
+ if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) {
+ i++;
+ continue;
+ }
+ if (brdp->state == 0) {
+ i++;
+ continue;
+ }
+/*
+ * The following section of code handles the subtle differences
+ * between board types. It is sort of similar, but different
+ * enough to handle each separately.
+ */
+ if (brdp->brdtype == BRD_EASYIO) {
+ if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) {
+ i++;
+ continue;
+ }
+ panelp = brdp->panels[0];
+ iobase = panelp->iobase;
+ outb(SVRR, iobase);
+ svrtype = inb(iobase + EREG_DATA);
+ if (brdp->nrports > 4) {
+ outb((SVRR + 0x80), iobase);
+ svrtype |= inb(iobase + EREG_DATA);
+ }
+ } else if (brdp->brdtype == BRD_ECH) {
+ if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
+ i++;
+ continue;
+ }
+ outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+ for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+ panelp = brdp->panels[panelnr];
+ iobase = panelp->iobase;
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ if (panelp->nrports > 8) {
+ iobase += 0x8;
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ }
+ }
+ if (panelnr >= brdp->nrpanels) {
+ i++;
+ continue;
+ }
+ outb(SVRR, iobase);
+ svrtype = inb(iobase + EREG_DATA);
+ outb((SVRR + 0x80), iobase);
+ svrtype |= inb(iobase + EREG_DATA);
+ } else if (brdp->brdtype == BRD_ECHPCI) {
+ iobase = brdp->ioaddr2;
+ for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+ panelp = brdp->panels[panelnr];
+ outb(panelp->pagenr, brdp->ioctrl);
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ if (panelp->nrports > 8) {
+ outb((panelp->pagenr + 1), brdp->ioctrl);
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ }
+ }
+ if (panelnr >= brdp->nrpanels) {
+ i++;
+ continue;
+ }
+ outb(SVRR, iobase);
+ svrtype = inb(iobase + EREG_DATA);
+ outb((SVRR + 0x80), iobase);
+ svrtype |= inb(iobase + EREG_DATA);
+ } else if (brdp->brdtype == BRD_ECHMC) {
+ if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
+ i++;
+ continue;
+ }
+ for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+ panelp = brdp->panels[panelnr];
+ iobase = panelp->iobase;
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ if (panelp->nrports > 8) {
+ iobase += 0x8;
+ if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
+ break;
+ }
+ }
+ if (panelnr >= brdp->nrpanels) {
+ i++;
+ continue;
+ }
+ outb(SVRR, iobase);
+ svrtype = inb(iobase + EREG_DATA);
+ outb((SVRR + 0x80), iobase);
+ svrtype |= inb(iobase + EREG_DATA);
+ } else {
+ printk("STALLION: unknown board type=%x\n", brdp->brdtype);
+ i++;
+ continue;
+ }
+
+/*
+ * We have determined what type of service is required for a
+ * port. From here on in the service of a port is the same no
+ * matter what the board type...
+ */
+ if (svrtype & SVRR_RX)
+ stl_rxisr(panelp, iobase);
+ if (svrtype & SVRR_TX)
+ stl_txisr(panelp, iobase);
+ if (svrtype & SVRR_MDM)
+ stl_mdmisr(panelp, iobase);
+
+ if (brdp->brdtype == BRD_ECH)
+ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Service an off-level request for some channel.
+ */
+
+static void stl_offintr(void *private)
+{
+ stlport_t *portp;
+ struct tty_struct *tty;
+ unsigned int oldsigs;
+
+ portp = private;
+#if DEBUG
+ printk("stl_offintr(portp=%x)\n", (int) portp);
+#endif
+
+ if (portp == (stlport_t *) NULL)
+ return;
+ tty = portp->tty;
+ if (tty == (struct tty_struct *) NULL)
+ return;
+
+ if (test_bit(ASYI_TXLOW, &portp->istate)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
+ clear_bit(ASYI_DCDCHANGE, &portp->istate);
+ oldsigs = portp->sigs;
+ stl_getsignals(portp);
+ if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+ wake_up_interruptible(&portp->open_wait);
+ if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+ if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (portp->flags & ASYNC_CALLOUT_NOHUP))) {
+ tty_hangup(tty);
+ }
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Wait for the command register to be ready. We will poll this,
+ * since it won't usually take too long to be ready.
+ */
+
+static void stl_ccrwait(stlport_t *portp)
+{
+ int i;
+
+ for (i = 0; (i < CCR_MAXWAIT); i++) {
+ if (stl_getreg(portp, CCR) == 0) {
+ return;
+ }
+ }
+
+ printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ * Set up the cd1400 registers for a port based on the termios port
+ * settings.
+ */
+
+static void stl_setport(stlport_t *portp, struct termios *tiosp)
+{
+ stlbrd_t *brdp;
+ unsigned long flags;
+ unsigned int clkdiv, baudrate;
+ unsigned char cor1, cor2, cor3;
+ unsigned char cor4, cor5, ccr;
+ unsigned char srer, sreron, sreroff;
+ unsigned char mcor1, mcor2, rtpr;
+ unsigned char clk, div;
+
+ cor1 = 0;
+ cor2 = 0;
+ cor3 = 0;
+ cor4 = 0;
+ cor5 = 0;
+ ccr = 0;
+ rtpr = 0;
+ clk = 0;
+ div = 0;
+ mcor1 = 0;
+ mcor2 = 0;
+ sreron = 0;
+ sreroff = 0;
+
+ brdp = stl_brds[portp->brdnr];
+ if (brdp == (stlbrd_t *) NULL)
+ return;
+
+/*
+ * Set up the RX char ignore mask with those RX error types we
+ * can ignore. We can get the cd1400 to help us out a little here,
+ * it will ignore parity errors and breaks for us.
+ */
+ portp->rxignoremsk = 0;
+ if (tiosp->c_iflag & IGNPAR) {
+ portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+ cor1 |= COR1_PARIGNORE;
+ }
+ if (tiosp->c_iflag & IGNBRK) {
+ portp->rxignoremsk |= ST_BREAK;
+ cor4 |= COR4_IGNBRK;
+ }
+
+ portp->rxmarkmsk = ST_OVERRUN;
+ if (tiosp->c_iflag & (INPCK | PARMRK))
+ portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+ if (tiosp->c_iflag & BRKINT)
+ portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ * Go through the char size, parity and stop bits and set all the
+ * option register appropriately.
+ */
+ switch (tiosp->c_cflag & CSIZE) {
+ case CS5:
+ cor1 |= COR1_CHL5;
+ break;
+ case CS6:
+ cor1 |= COR1_CHL6;
+ break;
+ case CS7:
+ cor1 |= COR1_CHL7;
+ break;
+ default:
+ cor1 |= COR1_CHL8;
+ break;
+ }
+
+ if (tiosp->c_cflag & CSTOPB)
+ cor1 |= COR1_STOP2;
+ else
+ cor1 |= COR1_STOP1;
+
+ if (tiosp->c_cflag & PARENB) {
+ if (tiosp->c_cflag & PARODD)
+ cor1 |= (COR1_PARENB | COR1_PARODD);
+ else
+ cor1 |= (COR1_PARENB | COR1_PAREVEN);
+ } else {
+ cor1 |= COR1_PARNONE;
+ }
+
+/*
+ * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
+ * space for hardware flow control and the like. This should be set to
+ * VMIN. Also here we will set the RX data timeout to 10ms - this should
+ * really be based on VTIME.
+ */
+ cor3 |= FIFO_RXTHRESHOLD;
+ rtpr = 2;
+
+/*
+ * Calculate the baud rate timers. For now we will just assume that
+ * the input and output baud are the same. Could have used a baud
+ * table here, but this way we can generate virtually any baud rate
+ * we like!
+ */
+ baudrate = tiosp->c_cflag & CBAUD;
+ if (baudrate & CBAUDEX) {
+ baudrate &= ~CBAUDEX;
+ if ((baudrate < 1) || (baudrate > 2))
+ tiosp->c_cflag &= ~CBAUDEX;
+ else
+ baudrate += 15;
+ }
+ baudrate = stl_baudrates[baudrate];
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baudrate = 57600;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baudrate = 115200;
+ else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ baudrate = (portp->baud_base / portp->custom_divisor);
+ }
+ if (baudrate > STL_MAXBAUD)
+ baudrate = STL_MAXBAUD;
+
+ if (baudrate > 0) {
+ for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
+ clkdiv = ((CD1400_CLKHZ / stl_cd1400clkdivs[clk]) / baudrate);
+ if (clkdiv < 0x100)
+ break;
+ }
+ div = (unsigned char) clkdiv;
+ }
+
+/*
+ * Check what form of modem signaling is required and set it up.
+ */
+ if ((tiosp->c_cflag & CLOCAL) == 0) {
+ mcor1 |= MCOR1_DCD;
+ mcor2 |= MCOR2_DCD;
+ sreron |= SRER_MODEM;
+ }
+
+/*
+ * Setup cd1400 enhanced modes if we can. In particular we want to
+ * handle as much of the flow control as possbile automatically. As
+ * well as saving a few CPU cycles it will also greatly improve flow
+ * control reliablilty.
+ */
+ if (tiosp->c_iflag & IXON) {
+ cor2 |= COR2_TXIBE;
+ cor3 |= (COR3_FCT | COR3_SCD12);
+ if (tiosp->c_iflag & IXANY)
+ cor2 |= COR2_IXM;
+ }
+
+ if (tiosp->c_cflag & CRTSCTS) {
+ cor2 |= COR2_CTSAE;
+ mcor1 |= FIFO_RTSTHRESHOLD;
+ }
+
+/*
+ * All register cd1400 register values calculated so go through and set
+ * them all up.
+ */
+
+#if DEBUG
+ printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+ printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
+ printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
+ printk(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+ printk(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x3));
+ srer = stl_getreg(portp, SRER);
+ stl_setreg(portp, SRER, 0);
+ if (stl_updatereg(portp, COR1, cor1))
+ ccr = 1;
+ if (stl_updatereg(portp, COR2, cor2))
+ ccr = 1;
+ if (stl_updatereg(portp, COR3, cor3))
+ ccr = 1;
+ if (ccr) {
+ stl_ccrwait(portp);
+ stl_setreg(portp, CCR, CCR_CORCHANGE);
+ }
+ stl_setreg(portp, COR4, cor4);
+ stl_setreg(portp, COR5, cor5);
+ stl_setreg(portp, MCOR1, mcor1);
+ stl_setreg(portp, MCOR2, mcor2);
+ if (baudrate > 0) {
+ stl_setreg(portp, TCOR, clk);
+ stl_setreg(portp, TBPR, div);
+ stl_setreg(portp, RCOR, clk);
+ stl_setreg(portp, RBPR, div);
+ }
+ stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+ stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+ stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+ stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+ stl_setreg(portp, RTPR, rtpr);
+ mcor1 = stl_getreg(portp, MSVR1);
+ if (mcor1 & MSVR1_DCD)
+ portp->sigs |= TIOCM_CD;
+ else
+ portp->sigs &= ~TIOCM_CD;
+ stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Set the state of the DTR and RTS signals.
+ */
+
+static void stl_setsignals(stlport_t *portp, int dtr, int rts)
+{
+ unsigned char msvr1, msvr2;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
+
+ msvr1 = 0;
+ msvr2 = 0;
+ if (dtr > 0)
+ msvr1 = MSVR1_DTR;
+ if (rts > 0)
+ msvr2 = MSVR2_RTS;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ if (rts >= 0)
+ stl_setreg(portp, MSVR2, msvr2);
+ if (dtr >= 0)
+ stl_setreg(portp, MSVR1, msvr1);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Get the state of the signals.
+ */
+
+static void stl_getsignals(stlport_t *portp)
+{
+ unsigned char msvr1, msvr2;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_getsignals(portp=%x)\n", (int) portp);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ msvr1 = stl_getreg(portp, MSVR1);
+ msvr2 = stl_getreg(portp, MSVR2);
+ BRDDISABLE(portp->brdnr);
+ portp->sigs = 0;
+ portp->sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+ portp->sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+ portp->sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+ portp->sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+ portp->sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+ portp->sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Enable/Disable the Transmitter and/or Reciever.
+ */
+
+static void stl_enablerxtx(stlport_t *portp, int rx, int tx)
+{
+ unsigned char ccr;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+ ccr = 0;
+
+ if (tx == 0)
+ ccr |= CCR_TXDISABLE;
+ else if (tx > 0)
+ ccr |= CCR_TXENABLE;
+ if (rx == 0)
+ ccr |= CCR_RXDISABLE;
+ else if (rx > 0)
+ ccr |= CCR_RXENABLE;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_ccrwait(portp);
+ stl_setreg(portp, CCR, ccr);
+ stl_ccrwait(portp);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Start/stop the Transmitter and/or Reciever.
+ */
+
+static void stl_startrxtx(stlport_t *portp, int rx, int tx)
+{
+ unsigned char sreron, sreroff;
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+
+ sreron = 0;
+ sreroff = 0;
+ if (tx == 0)
+ sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+ else if (tx == 1)
+ sreron |= SRER_TXDATA;
+ else if (tx >= 2)
+ sreron |= SRER_TXEMPTY;
+ if (rx == 0)
+ sreroff |= SRER_RXDATA;
+ else if (rx > 0)
+ sreron |= SRER_RXDATA;
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron));
+ BRDDISABLE(portp->brdnr);
+ if (tx > 0)
+ set_bit(ASYI_TXBUSY, &portp->istate);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Disable all interrupts from this port.
+ */
+
+static void stl_disableintrs(stlport_t *portp)
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_disableintrs(portp=%x)\n", (int) portp);
+#endif
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_setreg(portp, SRER, 0);
+ BRDDISABLE(portp->brdnr);
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static void stl_sendbreak(stlport_t *portp, long len)
+{
+ unsigned long flags;
+
+#if DEBUG
+ printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+#endif
+
+ save_flags(flags);
+ cli();
+ BRDENABLE(portp->brdnr, portp->pagenr);
+ stl_setreg(portp, CAR, (portp->portnr & 0x03));
+ stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC));
+ stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
+ BRDDISABLE(portp->brdnr);
+ len = len / 5;
+ portp->brklen = (len > 255) ? 255 : len;
+ restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ * Map in interrupt vector to this driver. Check that we don't
+ * already have this vector mapped, we might be sharing this
+ * interrupt accross multiple boards.
+ */
+
+static int stl_mapirq(int irq)
+{
+ int rc, i;
+
+#if DEBUG
+ printk("stl_mapirq(irq=%d)\n", irq);
+#endif
+
+ rc = 0;
+ for (i = 0; (i < stl_numintrs); i++) {
+ if (stl_gotintrs[i] == irq)
+ break;
+ }
+ if (i >= stl_numintrs) {
+ if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname) != 0) {
+ printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
+ rc = -ENODEV;
+ } else {
+ stl_gotintrs[stl_numintrs++] = irq;
+ }
+ }
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Try and find and initialize all the ports on a panel. We don't care
+ * what sort of board these ports are on - since the port io registers
+ * are almost identical when dealing with ports.
+ */
+
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
+{
+ stlport_t *portp;
+ unsigned int chipmask;
+ unsigned int gfrcr;
+ int nrchips, uartaddr, ioaddr;
+ int i, j;
+
+#if DEBUG
+ printk("stl_initports(panelp=%x)\n", (int) panelp);
+#endif
+
+ BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ * Check that each chip is present and started up OK.
+ */
+ chipmask = 0;
+ nrchips = panelp->nrports / CD1400_PORTS;
+ for (i = 0; (i < nrchips); i++) {
+ if (brdp->brdtype == BRD_ECHPCI) {
+ outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+ ioaddr = panelp->iobase;
+ } else {
+ ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+ }
+ uartaddr = (i & 0x01) ? 0x080 : 0;
+ outb((GFRCR + uartaddr), ioaddr);
+ outb(0, (ioaddr + EREG_DATA));
+ outb((CCR + uartaddr), ioaddr);
+ outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+ outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+ outb((GFRCR + uartaddr), ioaddr);
+ for (j = 0; (j < CCR_MAXWAIT); j++) {
+ if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+ break;
+ }
+ if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+ printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+ continue;
+ }
+ chipmask |= (0x1 << i);
+ outb((PPR + uartaddr), ioaddr);
+ outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+ }
+
+/*
+ * All cd1400's are initialized (if found!). Now go through and setup
+ * each ports data structures. Also init the LIVR register of cd1400
+ * for each port.
+ */
+ ioaddr = panelp->iobase;
+ for (i = 0; (i < panelp->nrports); i++) {
+ if (brdp->brdtype == BRD_ECHPCI) {
+ outb((panelp->pagenr + (i >> 3)), brdp->ioctrl);
+ ioaddr = panelp->iobase;
+ } else {
+ ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3));
+ }
+ if ((chipmask & (0x1 << (i / 4))) == 0)
+ continue;
+ portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
+ if (portp == (stlport_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
+ break;
+ }
+ memset(portp, 0, sizeof(stlport_t));
+ portp->portnr = i;
+ portp->brdnr = panelp->brdnr;
+ portp->panelnr = panelp->panelnr;
+ portp->ioaddr = ioaddr;
+ portp->uartaddr = (i & 0x04) << 5;
+ portp->pagenr = panelp->pagenr + (i >> 3);
+ portp->baud_base = STL_BAUDBASE;
+ portp->close_delay = STL_CLOSEDELAY;
+ portp->closing_wait = 30 * HZ;
+ portp->normaltermios = stl_deftermios;
+ portp->callouttermios = stl_deftermios;
+ portp->tqueue.routine = stl_offintr;
+ portp->tqueue.data = portp;
+ stl_setreg(portp, CAR, (i & 0x03));
+ stl_setreg(portp, LIVR, (i << 3));
+ panelp->ports[i] = portp;
+ }
+
+ BRDDISABLE(panelp->brdnr);
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Try to find and initialize an EasyIO board.
+ */
+
+static int stl_initeio(stlbrd_t *brdp)
+{
+ stlpanel_t *panelp;
+ unsigned int status;
+ int rc;
+
+#if DEBUG
+ printk("stl_initeio(brdp=%x)\n", (int) brdp);
+#endif
+
+ brdp->ioctrl = brdp->ioaddr1 + 1;
+ brdp->iostatus = brdp->ioaddr1 + 2;
+
+ status = inb(brdp->iostatus);
+ switch (status & EIO_IDBITMASK) {
+ case EIO_8PORTRS:
+ case EIO_8PORTM:
+ case EIO_8PORTDI:
+ brdp->nrports = 8;
+ break;
+ case EIO_4PORTRS:
+ brdp->nrports = 4;
+ break;
+ default:
+ return(-ENODEV);
+ }
+
+ request_region(brdp->ioaddr1, 8, "serial(EIO)");
+
+/*
+ * Check that the supplied IRQ is good and then use it to setup the
+ * programmable interrupt bits on EIO board. Also set the edge/level
+ * triggered interrupt bit.
+ */
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
+ }
+ outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
+
+ panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+ if (panelp == (stlpanel_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+ return(-ENOMEM);
+ }
+ memset(panelp, 0, sizeof(stlpanel_t));
+
+ panelp->brdnr = brdp->brdnr;
+ panelp->panelnr = 0;
+ panelp->nrports = brdp->nrports;
+ panelp->iobase = brdp->ioaddr1;
+ brdp->panels[0] = panelp;
+ brdp->nrpanels = 1;
+ brdp->state |= BRD_FOUND;
+ rc = stl_mapirq(brdp->irq);
+ return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ * Try to find an ECH board and initialize it. This code is capable of
+ * dealing with all types of ECH board.
+ */
+
+static int stl_initech(stlbrd_t *brdp)
+{
+ stlpanel_t *panelp;
+ unsigned int status, nxtid;
+ int panelnr, ioaddr, i;
+
+#if DEBUG
+ printk("stl_initech(brdp=%x)\n", (int) brdp);
+#endif
+
+/*
+ * Set up the initial board register contents for boards. This varys a
+ * bit between the different board types. So we need to handle each
+ * separately. Also do a check that the supplied IRQ is good.
+ */
+ if (brdp->brdtype == BRD_ECH) {
+ brdp->ioctrl = brdp->ioaddr1 + 1;
+ brdp->iostatus = brdp->ioaddr1 + 1;
+ status = inb(brdp->iostatus);
+ if ((status & ECH_IDBITMASK) != ECH_ID)
+ return(-ENODEV);
+
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
+ }
+ status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+ status |= (stl_vecmap[brdp->irq] << 1);
+ outb((status | ECH_BRDRESET), brdp->ioaddr1);
+ brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+ outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+ outb(status, brdp->ioaddr1);
+
+ request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
+ request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
+ } else if (brdp->brdtype == BRD_ECHMC) {
+ brdp->ioctrl = brdp->ioaddr1 + 0x20;
+ brdp->iostatus = brdp->ioctrl;
+ status = inb(brdp->iostatus);
+ if ((status & ECH_IDBITMASK) != ECH_ID)
+ return(-ENODEV);
+
+ if ((brdp->irq < 0) || (brdp->irq > 15) ||
+ (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+ printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+ return(-EINVAL);
+ }
+ outb(ECHMC_BRDRESET, brdp->ioctrl);
+ outb(ECHMC_INTENABLE, brdp->ioctrl);
+
+ request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
+ } else if (brdp->brdtype == BRD_ECHPCI) {
+ brdp->ioctrl = brdp->ioaddr1 + 2;
+ request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
+ request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
+ }
+
+/*
+ * Scan through the secondary io address space looking for panels.
+ * As we find'em allocate and initialize panel structures for each.
+ */
+ ioaddr = brdp->ioaddr2;
+ panelnr = 0;
+ nxtid = 0;
+
+ for (i = 0; (i < STL_MAXPANELS); i++) {
+ if (brdp->brdtype == BRD_ECHPCI) {
+ outb(nxtid, brdp->ioctrl);
+ ioaddr = brdp->ioaddr2;
+ }
+ status = inb(ioaddr + ECH_PNLSTATUS);
+ if ((status & ECH_PNLIDMASK) != nxtid)
+ break;
+ panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+ if (panelp == (stlpanel_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+ break;
+ }
+ memset(panelp, 0, sizeof(stlpanel_t));
+ panelp->brdnr = brdp->brdnr;
+ panelp->panelnr = panelnr;
+ panelp->iobase = ioaddr;
+ panelp->pagenr = nxtid;
+ if (status & ECH_PNL16PORT) {
+ if ((brdp->nrports + 16) > 32)
+ break;
+ panelp->nrports = 16;
+ panelp->ackmask = 0x80;
+ brdp->nrports += 16;
+ ioaddr += (EREG_BANKSIZE * 2);
+ nxtid += 2;
+ } else {
+ panelp->nrports = 8;
+ panelp->ackmask = 0xc0;
+ brdp->nrports += 8;
+ ioaddr += EREG_BANKSIZE;
+ nxtid++;
+ }
+ brdp->panels[panelnr++] = panelp;
+ brdp->nrpanels++;
+ if (ioaddr >= (brdp->ioaddr2 + 0x20))
+ break;
+ }
+
+ if (brdp->brdtype == BRD_ECH)
+ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+ brdp->state |= BRD_FOUND;
+ i = stl_mapirq(brdp->irq);
+ return(i);
+}
+
+/*****************************************************************************/
+
+/*
+ * Initialize and configure the specified board.
+ * Scan through all the boards in the configuration and see what we
+ * can find. Handle EIO and the ECH boards a little differently here
+ * since the initial search and setup is too different.
+ */
+
+static int stl_brdinit(stlbrd_t *brdp)
+{
+ int i;
+
+#if DEBUG
+ printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+ switch (brdp->brdtype) {
+ case BRD_EASYIO:
+ stl_initeio(brdp);
+ break;
+ case BRD_ECH:
+ case BRD_ECHMC:
+ case BRD_ECHPCI:
+ stl_initech(brdp);
+ break;
+ default:
+ printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+ return(ENODEV);
+ }
+
+ stl_brds[brdp->brdnr] = brdp;
+ if ((brdp->state & BRD_FOUND) == 0) {
+ printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
+ return(ENODEV);
+ }
+
+ for (i = 0; (i < STL_MAXPANELS); i++)
+ if (brdp->panels[i] != (stlpanel_t *) NULL)
+ stl_initports(brdp, brdp->panels[i]);
+
+ printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Find any ECH-PCI boards that might be installed. Initialize each
+ * one as it is found.
+ */
+
+#ifdef CONFIG_PCI
+
+static int stl_findpcibrds()
+{
+ stlbrd_t *brdp;
+ unsigned char busnr, devnr, irq;
+ unsigned short class;
+ unsigned int ioaddr;
+ int i, rc;
+
+#if DEBUG
+ printk("stl_findpcibrds()\n");
+#endif
+
+ if (pcibios_present()) {
+ for (i = 0; (i < STL_MAXBRDS); i++) {
+ if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
+ break;
+
+/*
+ * Found a device on the PCI bus that has our vendor and
+ * device ID. Need to check now that it is really us.
+ */
+ if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
+ printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ if (class == PCI_CLASS_STORAGE_IDE)
+ continue;
+
+ if (stl_nrbrds >= STL_MAXBRDS) {
+ printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
+ break;
+ }
+
+/*
+ * We have a Stallion board. Allocate a board structure
+ * and initialize it. Read its IO and IRQ resources
+ * from conf space.
+ */
+ brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+ if (brdp == (stlbrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+ return(-ENOMEM);
+ }
+ memset(brdp, 0, sizeof(stlbrd_t));
+ brdp->brdnr = stl_nrbrds++;
+ brdp->brdtype = BRD_ECHPCI;
+
+ if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+
+ if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+#if DEBUG
+ printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+#endif
+
+ if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
+ printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ continue;
+ }
+ brdp->irq = irq;
+
+#if 0
+ ioaddr = 0x0c000001;
+ if ((rc = pcibios_write_config_dword(busnr, devnr, 0x40, ioaddr))) {
+ printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
+ continue;
+ }
+ if ((rc = pcibios_write_config_dword(busnr, devnr, 0x48, ioaddr))) {
+ printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
+ continue;
+ }
+#endif
+
+ stl_brdinit(brdp);
+ }
+ }
+
+ return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Scan through all the boards in the configuration and see what we
+ * can find. Handle EIO and the ECH boards a little differently here
+ * since the initial search and setup is too different.
+ */
+
+static int stl_initbrds()
+{
+ stlbrd_t *brdp;
+ stlconf_t *confp;
+ int i;
+
+#if DEBUG
+ printk("stl_initbrds()\n");
+#endif
+
+ if (stl_nrbrds > STL_MAXBRDS) {
+ printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+ stl_nrbrds = STL_MAXBRDS;
+ }
+
+/*
+ * Firstly scan the list of static boards configured. Allocate
+ * resources and initialize the boards as found.
+ */
+ for (i = 0; (i < stl_nrbrds); i++) {
+ confp = &stl_brdconf[i];
+ brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+ if (brdp == (stlbrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+ return(-ENOMEM);
+ }
+ memset(brdp, 0, sizeof(stlbrd_t));
+
+ brdp->brdnr = i;
+ brdp->brdtype = confp->brdtype;
+ brdp->ioaddr1 = confp->ioaddr1;
+ brdp->ioaddr2 = confp->ioaddr2;
+ brdp->irq = confp->irq;
+ brdp->irqtype = confp->irqtype;
+ stl_brdinit(brdp);
+ }
+
+#ifdef CONFIG_PCI
+/*
+ * If the PCI BIOS support is compiled in then lets go looking for
+ * ECH-PCI boards.
+ */
+ stl_findpcibrds();
+#endif
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+long stl_init(long kmem_start)
+{
+ printk("%s: version %s\n", stl_drvname, stl_drvversion);
+
+#ifndef MODULE
+ stl_meminit(kmem_start);
+#endif
+
+ stl_initbrds();
+
+/*
+ * Allocate a temporary write buffer.
+ */
+ stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
+ if (stl_tmpwritebuf == (char *) NULL)
+ printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+
+/*
+ * Set up the tty driver structure and register us as a driver.
+ * Also setup the callout tty device.
+ */
+ memset(&stl_serial, 0, sizeof(struct tty_driver));
+ stl_serial.magic = TTY_DRIVER_MAGIC;
+ stl_serial.name = stl_serialname;
+ stl_serial.major = STL_SERIALMAJOR;
+ stl_serial.minor_start = 0;
+ stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+ stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
+ stl_serial.subtype = STL_DRVTYPSERIAL;
+ stl_serial.init_termios = stl_deftermios;
+ stl_serial.flags = TTY_DRIVER_REAL_RAW;
+ stl_serial.refcount = &stl_refcount;
+ stl_serial.table = stl_ttys;
+ stl_serial.termios = stl_termios;
+ stl_serial.termios_locked = stl_termioslocked;
+
+ stl_serial.open = stl_open;
+ stl_serial.close = stl_close;
+ stl_serial.write = stl_write;
+ stl_serial.put_char = stl_putchar;
+ stl_serial.flush_chars = stl_flushchars;
+ stl_serial.write_room = stl_writeroom;
+ stl_serial.chars_in_buffer = stl_charsinbuffer;
+ stl_serial.ioctl = stl_ioctl;
+ stl_serial.set_termios = stl_settermios;
+ stl_serial.throttle = stl_throttle;
+ stl_serial.unthrottle = stl_unthrottle;
+ stl_serial.stop = stl_stop;
+ stl_serial.start = stl_start;
+ stl_serial.hangup = stl_hangup;
+ stl_serial.flush_buffer = stl_flushbuffer;
+
+ stl_callout = stl_serial;
+ stl_callout.name = stl_calloutname;
+ stl_callout.major = STL_CALLOUTMAJOR;
+ stl_callout.subtype = STL_DRVTYPCALLOUT;
+
+ if (tty_register_driver(&stl_serial))
+ printk("STALLION: failed to register serial driver\n");
+ if (tty_register_driver(&stl_callout))
+ printk("STALLION: failed to register callout driver\n");
+
+#ifndef MODULE
+ kmem_start = stl_memhalt();
+#endif
+ return(kmem_start);
+}
+
+/*****************************************************************************/
#endif
#ifdef CONFIG_CYCLADES
kmem_start = cy_init(kmem_start);
+#endif
+#ifdef CONFIG_STALLION
+ kmem_start = stl_init(kmem_start);
+#endif
+#ifdef CONFIG_ISTALLION
+ kmem_start = stli_init(kmem_start);
#endif
kmem_start = pty_init(kmem_start);
kmem_start = vcs_init(kmem_start);
while (nrbuf-- > 0)
brelse(bh[nrbuf]);
free_page(address);
- ++current->mm->min_flt;
+ ++current->min_flt;
return page;
no_go:
while (nrbuf-- > 0)
read_buffers(arr,block);
while (block-- > 0)
brelse(arr[block]);
- ++current->mm->maj_flt;
+ ++current->maj_flt;
return address;
not_aligned:
while ((tmp = bh) != NULL) {
if (where)
return where;
}
- ++current->mm->maj_flt;
+ ++current->maj_flt;
for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
bh[i] = NULL;
if (b[i])
/*
* linux/fs/isofs/inode.c
*
- * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
- * 1995 Mark Dobie - patch to allow VideoCD and PhotoCD mounting.
- *
* (C) 1991 Linus Torvalds - minix filesystem
+ * 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
+ * 1994 Eberhard Moenkeberg - multi session handling.
+ * 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
+ *
*/
#ifdef MODULE
return 1;
}
-
+/*
+ * look if the driver can tell the multi session redirection value
+ */
static unsigned int isofs_get_last_session(int dev)
{
struct cdrom_multisession ms_info;
extern struct file_operations * get_blkfops(unsigned int);
int i;
- /*
- * look if the driver can tell the multi session redirection value
- * <emoenke@gwdg.de>
- */
vol_desc_start=0;
if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
{
inode->i_op = NULL;
- /* get the volume sequence numner */
+ /* get the volume sequence number */
volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
/*
tsk->tty ? tsk->tty->device : 0,
tty_pgrp,
tsk->flags,
- tsk->mm ? tsk->mm->min_flt : 0,
- tsk->mm ? tsk->mm->cmin_flt : 0,
- tsk->mm ? tsk->mm->maj_flt : 0,
- tsk->mm ? tsk->mm->cmaj_flt : 0,
+ tsk->min_flt,
+ tsk->cmin_flt,
+ tsk->maj_flt,
+ tsk->cmaj_flt,
tsk->utime,
tsk->stime,
tsk->cutime,
};
/* Function Prototypes */
-extern long mouse_init(long);
#endif
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * cd1400.h -- cd1400 UART hardware info.
+ *
+ * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifndef _CD1400_H
+#define _CD1400_H
+/*****************************************************************************/
+
+/*
+ * Define the number of async ports per cd1400 uart chip.
+ */
+#define CD1400_PORTS 4
+
+#define CD1400_CLKHZ 25000000
+
+/*
+ * Define the cd1400 uarts internal FIFO sizes.
+ */
+#define CD1400_TXFIFOSIZE 12
+#define CD1400_RXFIFOSIZE 12
+
+/*
+ * Local RX FIFO thresh hold level. Also define the RTS thresh hold
+ * based on the RX thresh hold.
+ */
+#define FIFO_RXTHRESHOLD 6
+#define FIFO_RTSTHRESHOLD 7
+
+/*****************************************************************************/
+
+/*
+ * Define the cd1400 register addresses. These are all the valid
+ * registers with the cd1400. Some are global, some virtual, some
+ * per port.
+ */
+#define GFRCR 0x40
+#define CAR 0x68
+#define GCR 0x4b
+#define SVRR 0x67
+#define RICR 0x44
+#define TICR 0x45
+#define MICR 0x46
+#define RIR 0x6b
+#define TIR 0x6a
+#define MIR 0x69
+#define PPR 0x7e
+
+#define RIVR 0x43
+#define TIVR 0x42
+#define MIVR 0x41
+#define TDR 0x63
+#define RDSR 0x62
+#define MISR 0x4c
+#define EOSRR 0x60
+
+#define LIVR 0x18
+#define CCR 0x05
+#define SRER 0x06
+#define COR1 0x08
+#define COR2 0x09
+#define COR3 0x0a
+#define COR4 0x1e
+#define COR5 0x1f
+#define CCSR 0x0b
+#define RDCR 0x0e
+#define SCHR1 0x1a
+#define SCHR2 0x1b
+#define SCHR3 0x1c
+#define SCHR4 0x1d
+#define SCRL 0x22
+#define SCRH 0x23
+#define LNC 0x24
+#define MCOR1 0x15
+#define MCOR2 0x16
+#define RTPR 0x21
+#define MSVR1 0x6c
+#define MSVR2 0x6d
+#define PSVR 0x6f
+#define RBPR 0x78
+#define RCOR 0x7c
+#define TBPR 0x72
+#define TCOR 0x76
+
+/*****************************************************************************/
+
+/*
+ * Define the set of baud rate clock divisors.
+ */
+#define CD1400_CLK0 8
+#define CD1400_CLK1 32
+#define CD1400_CLK2 128
+#define CD1400_CLK3 512
+#define CD1400_CLK4 2048
+
+#define CD1400_NUMCLKS 5
+
+/*****************************************************************************/
+
+/*
+ * Define the clock pre-scalar value to be a 5 ms clock. This should be
+ * OK for now. It would probably be better to make it 10 ms, but we
+ * can't fit that divisor into 8 bits!
+ */
+#define PPR_SCALAR 244
+
+/*****************************************************************************/
+
+/*
+ * Define values used to set character size options.
+ */
+#define COR1_CHL5 0x00
+#define COR1_CHL6 0x01
+#define COR1_CHL7 0x02
+#define COR1_CHL8 0x03
+
+/*
+ * Define values used to set the number of stop bits.
+ */
+#define COR1_STOP1 0x00
+#define COR1_STOP15 0x04
+#define COR1_STOP2 0x08
+
+/*
+ * Define values used to set the parity scheme in use.
+ */
+#define COR1_PARNONE 0x00
+#define COR1_PARFORCE 0x20
+#define COR1_PARENB 0x40
+#define COR1_PARIGNORE 0x10
+
+#define COR1_PARODD 0x80
+#define COR1_PAREVEN 0x00
+
+#define COR2_IXM 0x80
+#define COR2_TXIBE 0x40
+#define COR2_ETC 0x20
+#define COR2_LLM 0x10
+#define COR2_RLM 0x08
+#define COR2_RTSAO 0x04
+#define COR2_CTSAE 0x02
+
+#define COR3_SCDRNG 0x80
+#define COR3_SCD34 0x40
+#define COR3_FCT 0x20
+#define COR3_SCD12 0x10
+
+/*
+ * Define values used by COR4.
+ */
+#define COR4_BRKINT 0x08
+#define COR4_IGNBRK 0x18
+
+/*****************************************************************************/
+
+/*
+ * Define the modem control register values.
+ * Note that the actual hardware is a little different to the conventional
+ * pin names on the cd1400.
+ */
+#define MSVR1_DTR 0x01
+#define MSVR1_DSR 0x10
+#define MSVR1_RI 0x20
+#define MSVR1_CTS 0x40
+#define MSVR1_DCD 0x80
+
+#define MSVR2_RTS 0x02
+#define MSVR2_DSR 0x10
+#define MSVR2_RI 0x20
+#define MSVR2_CTS 0x40
+#define MSVR2_DCD 0x80
+
+#define MCOR1_DCD 0x80
+#define MCOR1_CTS 0x40
+#define MCOR1_RI 0x20
+#define MCOR1_DSR 0x10
+
+#define MCOR2_DCD 0x80
+#define MCOR2_CTS 0x40
+#define MCOR2_RI 0x20
+#define MCOR2_DSR 0x10
+
+/*****************************************************************************/
+
+/*
+ * Define the bits used with the service (interrupt) enable register.
+ */
+#define SRER_NNDT 0x01
+#define SRER_TXEMPTY 0x02
+#define SRER_TXDATA 0x04
+#define SRER_RXDATA 0x10
+#define SRER_MODEM 0x80
+
+/*****************************************************************************/
+
+/*
+ * Define operational commands for the command register.
+ */
+#define CCR_RESET 0x80
+#define CCR_CORCHANGE 0x4e
+#define CCR_SENDCH 0x20
+#define CCR_CHANCTRL 0x10
+
+#define CCR_TXENABLE (CCR_CHANCTRL | 0x08)
+#define CCR_TXDISABLE (CCR_CHANCTRL | 0x04)
+#define CCR_RXENABLE (CCR_CHANCTRL | 0x02)
+#define CCR_RXDISABLE (CCR_CHANCTRL | 0x01)
+
+#define CCR_SENDSCHR1 (CCR_SENDCH | 0x01)
+#define CCR_SENDSCHR2 (CCR_SENDCH | 0x02)
+#define CCR_SENDSCHR3 (CCR_SENDCH | 0x03)
+#define CCR_SENDSCHR4 (CCR_SENDCH | 0x04)
+
+#define CCR_RESETCHAN (CCR_RESET | 0x00)
+#define CCR_RESETFULL (CCR_RESET | 0x01)
+#define CCR_TXFLUSHFIFO (CCR_RESET | 0x02)
+
+#define CCR_MAXWAIT 10000
+
+/*****************************************************************************/
+
+/*
+ * Define the valid acknowledgement types (for hw ack cycle).
+ */
+#define ACK_TYPMASK 0x07
+#define ACK_TYPTX 0x02
+#define ACK_TYPMDM 0x01
+#define ACK_TYPRXGOOD 0x03
+#define ACK_TYPRXBAD 0x07
+
+#define SVRR_RX 0x01
+#define SVRR_TX 0x02
+#define SVRR_MDM 0x04
+
+#define ST_OVERRUN 0x01
+#define ST_FRAMING 0x02
+#define ST_PARITY 0x04
+#define ST_BREAK 0x08
+#define ST_SCHAR1 0x10
+#define ST_SCHAR2 0x20
+#define ST_SCHAR3 0x30
+#define ST_SCHAR4 0x40
+#define ST_RANGE 0x70
+#define ST_TIMEOUT 0x80
+
+#define MISR_DCD 0x80
+#define MISR_CTS 0x40
+#define MISR_RI 0x20
+#define MISR_DSR 0x10
+
+/*****************************************************************************/
+
+/*
+ * Defines for the CCSR status register.
+ */
+#define CCSR_RXENABLED 0x80
+#define CCSR_RXFLOWON 0x40
+#define CCSR_RXFLOWOFF 0x20
+#define CCSR_TXENABLED 0x08
+#define CCSR_TXFLOWON 0x04
+#define CCSR_TXFLOWOFF 0x02
+
+/*****************************************************************************/
+
+/*
+ * Define the embedded commands.
+ */
+#define ETC_CMD 0x00
+#define ETC_STARTBREAK 0x81
+#define ETC_DELAY 0x82
+#define ETC_STOPBREAK 0x83
+
+/*****************************************************************************/
+#endif
--- /dev/null
+/*****************************************************************************/
+
+/*
+ * cdk.h -- CDK interface definitions.
+ *
+ * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifndef _CDK_H
+#define _CDK_H
+/*****************************************************************************/
+
+#pragma pack(2)
+
+/*
+ * The following set of definitions is used to communicate with the
+ * shared memory interface of the Stallion intelligent multiport serial
+ * boards. The definitions in this file are taken directly from the
+ * document titled "Generic Stackable Interface, Downloader and
+ * Communications Development Kit".
+ */
+
+/*
+ * Define the set of importrant shared memory addresses. These are
+ * required to intialize the board and get things started. All of these
+ * addresses are relative to the start of the shared memory.
+ */
+#define CDK_SIGADDR 0x200
+#define CDK_FEATADDR 0x280
+#define CDK_CDKADDR 0x300
+#define CDK_RDYADDR 0x262
+
+#define CDK_ALIVEMARKER 13
+
+/*
+ * On hardware power up the ROMs located on the EasyConnection 8/64 will
+ * fill out the following signature information into shared memory. This
+ * way the host system can quickly determine that the board is present
+ * and is operational.
+ */
+typedef struct cdkecpsig {
+ unsigned long magic;
+ unsigned short romver;
+ unsigned short cputype;
+ unsigned char panelid[8];
+} cdkecpsig_t;
+
+#define ECP_MAGIC 0x21504345
+
+/*
+ * On hardware power up the ROMs located on the ONboard, Stallion and
+ * Brumbys will fill out the following signature information into shared
+ * memory. This way the host system can quickly determine that the board
+ * is present and is operational.
+ */
+typedef struct cdkonbsig {
+ unsigned short magic0;
+ unsigned short magic1;
+ unsigned short magic2;
+ unsigned short magic3;
+ unsigned short romver;
+ unsigned short memoff;
+ unsigned short memseg;
+ unsigned short amask0;
+ unsigned short pic;
+ unsigned short status;
+ unsigned short btype;
+ unsigned short clkticks;
+ unsigned short clkspeed;
+ unsigned short amask1;
+ unsigned short amask2;
+} cdkonbsig_t;
+
+#define ONB_MAGIC0 0xf2a7
+#define ONB_MAGIC1 0xa149
+#define ONB_MAGIC2 0x6352
+#define ONB_MAGIC3 0xf121
+
+/*
+ * Define the feature area structure. The feature area is the set of
+ * startup parameters used by the slave image when it starts executing.
+ * They allow for the specification of buffer sizes, debug trace, etc.
+ */
+typedef struct cdkfeature {
+ unsigned long debug;
+ unsigned long banner;
+ unsigned long etype;
+ unsigned long nrdevs;
+ unsigned long brdspec;
+ unsigned long txrqsize;
+ unsigned long rxrqsize;
+ unsigned long flags;
+} cdkfeature_t;
+
+#define ETYP_DDK 0
+#define ETYP_CDK 1
+
+/*
+ * Define the CDK header structure. This is the info that the slave
+ * environment sets up after it has been downloaded and started. It
+ * essentially provides a memory map for the shared memory interface.
+ */
+typedef struct cdkhdr {
+ unsigned short command;
+ unsigned short status;
+ unsigned short port;
+ unsigned short mode;
+ unsigned long cmd_buf[14];
+ unsigned short alive_cnt;
+ unsigned short intrpt_mode;
+ unsigned char intrpt_id[8];
+ unsigned char ver_release;
+ unsigned char ver_modification;
+ unsigned char ver_fix;
+ unsigned char deadman_restart;
+ unsigned short deadman;
+ unsigned short nrdevs;
+ unsigned long memp;
+ unsigned long hostp;
+ unsigned long slavep;
+ unsigned char hostreq;
+ unsigned char slavereq;
+ unsigned char cmd_reserved[30];
+} cdkhdr_t;
+
+#define MODE_DDK 0
+#define MODE_CDK 1
+
+#define IMD_INTR 0x0
+#define IMD_PPINTR 0x1
+#define IMD_POLL 0xff
+
+/*
+ * Define the memory mapping structure. This structure is pointed to by
+ * the memp field in the stlcdkhdr struct. As many as these structures
+ * as required are layed out in shared memory to define how the rest of
+ * shared memory is divided up. There will be one for each port.
+ */
+typedef struct cdkmem {
+ unsigned short dtype;
+ unsigned long offset;
+} cdkmem_t;
+
+#define TYP_UNDEFINED 0x0
+#define TYP_ASYNCTRL 0x1
+#define TYP_ASYNC 0x20
+#define TYP_PARALLEL 0x40
+#define TYP_SYNCX21 0x60
+
+/*****************************************************************************/
+
+/*
+ * Following is a set of defines and structures used to actually deal
+ * with the serial ports on the board. Firstly is the set of commands
+ * that can be applied to ports.
+ */
+#define ASYCMD (((unsigned long) 'a') << 8)
+
+#define A_NULL (ASYCMD | 0)
+#define A_FLUSH (ASYCMD | 1)
+#define A_BREAK (ASYCMD | 2)
+#define A_GETPORT (ASYCMD | 3)
+#define A_SETPORT (ASYCMD | 4)
+#define A_SETPORTF (ASYCMD | 5)
+#define A_SETPORTFTX (ASYCMD | 6)
+#define A_SETPORTFRX (ASYCMD | 7)
+#define A_GETSIGNALS (ASYCMD | 8)
+#define A_SETSIGNALS (ASYCMD | 9)
+#define A_SETSIGNALSF (ASYCMD | 10)
+#define A_SETSIGNALSFTX (ASYCMD | 11)
+#define A_SETSIGNALSFRX (ASYCMD | 12)
+#define A_GETNOTIFY (ASYCMD | 13)
+#define A_SETNOTIFY (ASYCMD | 14)
+#define A_NOTIFY (ASYCMD | 15)
+#define A_PORTCTRL (ASYCMD | 16)
+#define A_GETSTATS (ASYCMD | 17)
+#define A_RQSTATE (ASYCMD | 18)
+#define A_FLOWSTATE (ASYCMD | 19)
+
+/*
+ * Define those arguments used for simple commands.
+ */
+#define FLUSHRX 0x1
+#define FLUSHTX 0x2
+
+#define BREAKON -1
+#define BREAKOFF -2
+
+/*
+ * Define the port setting structure, and all those defines that go along
+ * with it. Basically this structure defines the charcateristics of this
+ * port: baud rate, chars, parity, input/output char cooking etc.
+ */
+typedef struct asyport {
+ unsigned long baudout;
+ unsigned long baudin;
+ unsigned long iflag;
+ unsigned long oflag;
+ unsigned long lflag;
+ unsigned long pflag;
+ unsigned long flow;
+ unsigned long spare1;
+ unsigned short vtime;
+ unsigned short vmin;
+ unsigned short txlo;
+ unsigned short txhi;
+ unsigned short rxlo;
+ unsigned short rxhi;
+ unsigned short rxhog;
+ unsigned short spare2;
+ unsigned char csize;
+ unsigned char stopbs;
+ unsigned char parity;
+ unsigned char stopin;
+ unsigned char startin;
+ unsigned char stopout;
+ unsigned char startout;
+ unsigned char parmark;
+ unsigned char brkmark;
+ unsigned char cc[11];
+} asyport_t;
+
+#define PT_STOP1 0x0
+#define PT_STOP15 0x1
+#define PT_STOP2 0x2
+
+#define PT_NOPARITY 0x0
+#define PT_ODDPARITY 0x1
+#define PT_EVENPARITY 0x2
+#define PT_MARKPARITY 0x3
+#define PT_SPACEPARITY 0x4
+
+#define F_NONE 0x0
+#define F_IXON 0x1
+#define F_IXOFF 0x2
+#define F_IXANY 0x4
+#define F_IOXANY 0x8
+#define F_RTSFLOW 0x10
+#define F_CTSFLOW 0x20
+#define F_DTRFLOW 0x40
+#define F_DCDFLOW 0x80
+#define F_DSROFLOW 0x100
+#define F_DSRIFLOW 0x200
+
+#define FI_NORX 0x1
+#define FI_RAW 0x2
+#define FI_ISTRIP 0x4
+#define FI_UCLC 0x8
+#define FI_INLCR 0x10
+#define FI_ICRNL 0x20
+#define FI_IGNCR 0x40
+#define FI_IGNBREAK 0x80
+#define FI_DSCRDBREAK 0x100
+#define FI_1MARKBREAK 0x200
+#define FI_2MARKBREAK 0x400
+#define FI_XCHNGBREAK 0x800
+#define FI_IGNRXERRS 0x1000
+#define FI_DSCDRXERRS 0x2000
+#define FI_1MARKRXERRS 0x4000
+#define FI_2MARKRXERRS 0x8000
+#define FI_XCHNGRXERRS 0x10000
+#define FI_DSCRDNULL 0x20000
+
+#define FO_OLCUC 0x1
+#define FO_ONLCR 0x2
+#define FO_OOCRNL 0x4
+#define FO_ONOCR 0x8
+#define FO_ONLRET 0x10
+#define FO_ONL 0x20
+#define FO_OBS 0x40
+#define FO_OVT 0x80
+#define FO_OFF 0x100
+#define FO_OTAB1 0x200
+#define FO_OTAB2 0x400
+#define FO_OTAB3 0x800
+#define FO_OCR1 0x1000
+#define FO_OCR2 0x2000
+#define FO_OCR3 0x4000
+#define FO_OFILL 0x8000
+#define FO_ODELL 0x10000
+
+#define P_RTSLOCK 0x1
+#define P_CTSLOCK 0x2
+#define P_MAPRTS 0x4
+#define P_MAPCTS 0x8
+#define P_LOOPBACK 0x10
+#define P_DTRFOLLOW 0x20
+#define P_FAKEDCD 0x40
+
+/*
+ * Define a structure to communicate serial port signal and data state
+ * information.
+ */
+typedef struct asysigs {
+ unsigned long data;
+ unsigned long signal;
+ unsigned long sigvalue;
+} asysigs_t;
+
+#define DT_TXBUSY 0x1
+#define DT_TXEMPTY 0x2
+#define DT_TXLOW 0x4
+#define DT_TXHIGH 0x8
+#define DT_TXFULL 0x10
+#define DT_TXHOG 0x20
+#define DT_TXFLOWED 0x40
+#define DT_TXBREAK 0x80
+
+#define DT_RXBUSY 0x100
+#define DT_RXEMPTY 0x200
+#define DT_RXLOW 0x400
+#define DT_RXHIGH 0x800
+#define DT_RXFULL 0x1000
+#define DT_RXHOG 0x2000
+#define DT_RXFLOWED 0x4000
+#define DT_RXBREAK 0x8000
+
+#define SG_DTR 0x1
+#define SG_DCD 0x2
+#define SG_RTS 0x4
+#define SG_CTS 0x8
+#define SG_DSR 0x10
+#define SG_RI 0x20
+
+/*
+ * Define the notification setting structure. This is used to tell the
+ * port what events we want to be informed about. Fields here use the
+ * same defines as for the asysigs structure above.
+ */
+typedef struct asynotify {
+ unsigned long ctrl;
+ unsigned long data;
+ unsigned long signal;
+ unsigned long sigvalue;
+} asynotify_t;
+
+/*
+ * Define the port control structure. It is used to do fine grain
+ * control operations on the port.
+ */
+typedef struct {
+ unsigned long rxctrl;
+ unsigned long txctrl;
+ char rximdch;
+ char tximdch;
+ char spare1;
+ char spare2;
+} asyctrl_t;
+
+#define CT_ENABLE 0x1
+#define CT_DISABLE 0x2
+#define CT_STOP 0x4
+#define CT_START 0x8
+#define CT_STARTFLOW 0x10
+#define CT_STOPFLOW 0x20
+#define CT_SENDCHR 0x40
+
+/*
+ * Define the stats structure kept for each port. This is a useful set
+ * of data collected for each port on the slave. The A_GETSTATS command
+ * is used to retrive this data from the slave.
+ */
+typedef struct asystats {
+ unsigned long opens;
+ unsigned long txchars;
+ unsigned long rxchars;
+ unsigned long txringq;
+ unsigned long rxringq;
+ unsigned long txmsgs;
+ unsigned long rxmsgs;
+ unsigned long overruns;
+ unsigned long framing;
+ unsigned long parity;
+ unsigned long ringover;
+ unsigned long lost;
+ unsigned long rxstart;
+ unsigned long rxstop;
+ unsigned long txstart;
+ unsigned long txstop;
+ unsigned long dcdcnt;
+ unsigned long dtrcnt;
+ unsigned long ctscnt;
+ unsigned long rtscnt;
+ unsigned long dsrcnt;
+ unsigned long ricnt;
+ unsigned long txbreaks;
+ unsigned long rxbreaks;
+ unsigned long signals;
+ unsigned long state;
+} asystats_t;
+
+/*****************************************************************************/
+
+/*
+ * All command and control communication with a device on the slave is
+ * via a control block in shared memory. Each device has its own control
+ * block, defined by the following structure. The control block allows
+ * the host to open, close and control the device on the slave.
+ */
+typedef struct cdkctrl {
+ unsigned char open;
+ unsigned char close;
+ unsigned long openarg;
+ unsigned long closearg;
+ unsigned long cmd;
+ unsigned long status;
+ unsigned long args[32];
+} cdkctrl_t;
+
+/*
+ * Each device on the slave passes data to and from the host via a ring
+ * queue in shared memory. Define a ring queue structure to hold the
+ * vital information about each ring queue. Two ring queues will be
+ * allocated for each port, one for reveice data and one for transmit
+ * data.
+ */
+typedef struct cdkasyrq {
+ unsigned long offset;
+ unsigned short size;
+ unsigned short head;
+ unsigned short tail;
+} cdkasyrq_t;
+
+/*
+ * Each asynchronous port is defined in shared memory by the following
+ * structure. It contains a control block to command a device, and also
+ * the neccessary data channel information as well.
+ */
+typedef struct cdkasy {
+ cdkctrl_t ctrl;
+ unsigned short notify;
+ asynotify_t changed;
+ unsigned short receive;
+ cdkasyrq_t rxq;
+ unsigned short transmit;
+ cdkasyrq_t txq;
+} cdkasy_t;
+
+#pragma pack()
+
+/*****************************************************************************/
+
+/*
+ * Define the set of ioctls used by the driver to do special things
+ * to the board. These include interrupting it, and initializeing
+ * the driver after board startup and shutdown.
+ */
+#define STLCMD (((unsigned long) 's') << 8)
+
+#define STL_BINTR (STLCMD | 20)
+#define STL_BSTART (STLCMD | 21)
+#define STL_BSTOP (STLCMD | 22)
+#define STL_BRESET (STLCMD | 23)
+
+/*****************************************************************************/
+#endif
-/****************************************************************************************
- * *
- * general (not only SCSI) header library for linux CDROM drivers *
- * (C) 1992 David Giller rafetmad@oxy.edu *
- * 1994 Eberhard Moenkeberg emoenke@gwdg.de ("read audio" and some other stuff) *
- * *
- * <linux/cdrom.h> -- CD-ROM IOCTLs and structs *
- * *
- ****************************************************************************************/
-
-#ifndef _LINUX_CDROM_H
-#define _LINUX_CDROM_H
-
-/*
- * some fix numbers
- */
-#define CD_MINS 74 /* max. minutes per CD */
-#define CD_SECS 60 /* seconds per minute */
-#define CD_FRAMES 75 /* frames per second */
-#define CD_CHUNK_SIZE 24 /* lowest-level "data bytes piece" */
-#define CD_NUM_OF_CHUNKS 98 /* chunks per frame */
-#define CD_FRAMESIZE 2048 /* bytes per frame, cooked mode */
-#define CD_FRAMESIZE_RAW0 2336 /* bytes per frame, "raw" mode */
-#define CD_FRAMESIZE_XA 2340 /* bytes per frame, "xa" mode */
-#define CD_FRAMESIZE_RAW 2352 /* bytes per frame, "raw" mode */
-#define CD_FRAMESIZE_SUB 96 /* subchannel data size */
-#define CD_BLOCK_OFFSET 150 /* offset of first logical frame */
-#define CD_XA_HEAD 12 /* header size of XA frame */
-#define CD_XA_TAIL 280 /* tail size of XA frame */
-#define CD_XA_SYNC_HEAD CD_XA_HEAD+12 /* sync bytes + header of XA frame */
/*
+ * -- <linux/cdrom.h>
+ * general (not only SCSI) header library for linux CDROM drivers
+ * (C) 1992 David Giller rafetmad@oxy.edu
+ * 1994, 1995 Eberhard Moenkeberg emoenke@gwdg.de
*
- * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
- *
- */
-
-/*
- * CD-ROM-specific SCSI command opcodes
*/
-/*
- * Group 2 (10-byte). All of these are called 'optional' by SCSI-II.
- */
-
-#define SCMD_READ_TOC 0x43 /* read table of contents */
-#define SCMD_PLAYAUDIO_MSF 0x47 /* play data at time offset */
-#define SCMD_PLAYAUDIO_TI 0x48 /* play data at track/index */
-#define SCMD_PAUSE_RESUME 0x4B /* pause/resume audio */
-#define SCMD_READ_SUBCHANNEL 0x42 /* read SC info on playing disc */
-#define SCMD_PLAYAUDIO10 0x45 /* play data at logical block */
-#define SCMD_READ_HEADER 0x44 /* read TOC header */
-
-/*
- * Group 5
- */
-
-#define SCMD_PLAYAUDIO12 0xA5 /* play data at logical block */
-#define SCMD_PLAYTRACK_REL12 0xA9 /* play track at relative offset*/
+#ifndef _LINUX_CDROM_H
+#define _LINUX_CDROM_H
/*
- * Group 6 Commands
+ * some fix numbers
*/
+#define CD_MINS 74 /* max. minutes per CD, not really a limit */
+#define CD_SECS 60 /* seconds per minute */
+#define CD_FRAMES 75 /* frames per second */
-#define SCMD_CD_PLAYBACK_CONTROL 0xC9 /* Sony vendor-specific audio */
-#define SCMD_CD_PLAYBACK_STATUS 0xC4 /* control opcodes. info please!*/
+#define CD_SYNC_SIZE 12 /* 12 sync bytes per raw data frame, not transfered by the drive */
+#define CD_HEAD_SIZE 4 /* header (address) bytes per raw data frame */
+#define CD_SUBHEAD_SIZE 8 /* subheader bytes per raw XA data frame */
+#define CD_XA_HEAD (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
+#define CD_XA_SYNC_HEAD (CD_XA_HEAD+12)/* sync bytes + header of XA frame */
-/*
- * CD-ROM capacity structure.
- */
+#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
+#define CD_FRAMESIZE_RAW 2352 /* bytes per frame, "raw" mode */
+/* most drives don't deliver everything: */
+#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
+#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
-struct scsi_capacity
- {
- u_long capacity;
- u_long lbasize;
- };
+#define CD_EDC_SIZE 4 /* bytes EDC per most raw data frame types */
+#define CD_ZERO_SIZE 8 /* bytes zero per yellow book mode 1 frame */
+#define CD_ECC_SIZE 276 /* bytes ECC per most raw data frame types */
+#define CD_XA_TAIL (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
-/*
- * CD-ROM MODE_SENSE/MODE_SELECT parameters
- */
+#define CD_FRAMESIZE_SUB 96 /* subchannel data "frame" size */
+#define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */
-#define ERR_RECOVERY_PARMS 0x01
-#define DISCO_RECO_PARMS 0x02
-#define FORMAT_PARMS 0x03
-#define GEOMETRY_PARMS 0x04
-#define CERTIFICATION_PARMS 0x06
-#define CACHE_PARMS 0x38
-
-/*
- * standard mode-select header prepended to all mode-select commands
- */
+#define CD_CHUNK_SIZE 24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS 98 /* chunks per frame */
-struct ccs_modesel_head
- {
- u_char _r1; /* reserved */
- u_char medium; /* device-specific medium type */
- u_char _r2; /* reserved */
- u_char block_desc_length; /* block descriptor length */
- u_char density; /* device-specific density code */
- u_char number_blocks_hi; /* number of blocks in this block desc */
- u_char number_blocks_med;
- u_char number_blocks_lo;
- u_char _r3;
- u_char block_length_hi; /* block length for blocks in this desc */
- u_short block_length;
- };
+#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */
+#define CD_BLOCK_OFFSET CD_MSF_OFFSET /* obsolete name */
/*
- * error recovery parameters
- */
-
-struct ccs_err_recovery
- {
- u_char _r1 : 2; /* reserved */
- u_char page_code : 6; /* page code */
- u_char page_length; /* page length */
- u_char awre : 1; /* auto write realloc enabled */
- u_char arre : 1; /* auto read realloc enabled */
- u_char tb : 1; /* transfer block */
- u_char rc : 1; /* read continuous */
- u_char eec : 1; /* enable early correction */
- u_char per : 1; /* post error */
- u_char dte : 1; /* disable transfer on error */
- u_char dcr : 1; /* disable correction */
- u_char retry_count; /* error retry count */
- u_char correction_span; /* largest recov. to be attempted, bits */
- u_char head_offset_count; /* head offset (2's C) for each retry */
- u_char strobe_offset_count; /* data strobe " */
- u_char recovery_time_limit; /* time limit on recovery attempts */
-};
-
-/*
- * disco/reco parameters
- */
-
-struct ccs_disco_reco
- {
- u_char _r1 : 2; /* reserved */
- u_char page_code : 6; /* page code */
- u_char page_length; /* page length */
- u_char buffer_full_ratio; /* write buffer reconnect threshold */
- u_char buffer_empty_ratio; /* read " */
- u_short bus_inactivity_limit; /* limit on bus inactivity time */
- u_short disconnect_time_limit; /* minimum disconnect time */
- u_short connect_time_limit; /* minimum connect time */
- u_short _r2; /* reserved */
-};
-
-/*
- * drive geometry parameters
- */
-
-struct ccs_geometry
- {
- u_char _r1 : 2; /* reserved */
- u_char page_code : 6; /* page code */
- u_char page_length; /* page length */
- u_char cyl_ub; /* #cyls */
- u_char cyl_mb;
- u_char cyl_lb;
- u_char heads; /* #heads */
- u_char precomp_cyl_ub; /* precomp start */
- u_char precomp_cyl_mb;
- u_char precomp_cyl_lb;
- u_char current_cyl_ub; /* reduced current start */
- u_char current_cyl_mb;
- u_char current_cyl_lb;
- u_short step_rate; /* stepping motor rate */
- u_char landing_cyl_ub; /* landing zone */
- u_char landing_cyl_mb;
- u_char landing_cyl_lb;
- u_char _r2;
- u_char _r3;
- u_char _r4;
- };
-
-/*
- * cache parameters
+ * the raw frame layout:
+ *
+ * - audio (red): | audio_sample_bytes |
+ * | 2352 |
+ *
+ * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC |
+ * | 12 - 4 - 2048 - 4 - 8 - 276 |
+ *
+ * - data (yellow, mode2): | sync - head - data |
+ * | 12 - 4 - 2336 |
+ *
+ * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
+ * | 12 - 4 - 8 - 2048 - 4 - 276 |
+ *
+ * - XA data (green, mode2 form2): | sync - head - sub - data - EDC |
+ * | 12 - 4 - 8 - 2324 - 4 |
*/
-struct ccs_cache
- {
- u_char _r1 : 2; /* reserved */
- u_char page_code : 6; /* page code */
- u_char page_length; /* page length */
- u_char mode; /* cache control byte */
- u_char threshold; /* prefetch threshold */
- u_char max_prefetch; /* maximum prefetch size */
- u_char max_multiplier; /* maximum prefetch multiplier */
- u_char min_prefetch; /* minimum prefetch size */
- u_char min_multiplier; /* minimum prefetch multiplier */
- u_char _r2[8];
- };
-
/*
* CDROM IOCTL structures
*/
struct cdrom_msf
- {
- u_char cdmsf_min0; /* start minute */
- u_char cdmsf_sec0; /* start second */
- u_char cdmsf_frame0; /* start frame */
- u_char cdmsf_min1; /* end minute */
- u_char cdmsf_sec1; /* end second */
- u_char cdmsf_frame1; /* end frame */
- };
+{
+ u_char cdmsf_min0; /* start minute */
+ u_char cdmsf_sec0; /* start second */
+ u_char cdmsf_frame0; /* start frame */
+ u_char cdmsf_min1; /* end minute */
+ u_char cdmsf_sec1; /* end second */
+ u_char cdmsf_frame1; /* end frame */
+};
struct cdrom_ti
- {
- u_char cdti_trk0; /* start track */
- u_char cdti_ind0; /* start index */
- u_char cdti_trk1; /* end track */
- u_char cdti_ind1; /* end index */
- };
+{
+ u_char cdti_trk0; /* start track */
+ u_char cdti_ind0; /* start index */
+ u_char cdti_trk1; /* end track */
+ u_char cdti_ind1; /* end index */
+};
struct cdrom_tochdr
- {
- u_char cdth_trk0; /* start track */
- u_char cdth_trk1; /* end track */
- };
+{
+ u_char cdth_trk0; /* start track */
+ u_char cdth_trk1; /* end track */
+};
struct cdrom_tocentry
- {
+{
u_char cdte_track;
u_char cdte_adr :4;
u_char cdte_ctrl :4;
u_char cdte_format;
union
- {
+ {
struct
- {
+ {
u_char minute;
u_char second;
u_char frame;
- } msf;
+ } msf;
int lba;
- } cdte_addr;
+ } cdte_addr;
u_char cdte_datamode;
- };
+};
/*
* CD-ROM address types (cdrom_tocentry.cdte_format)
#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
/*
- * bit to tell whether track is data or audio
+ * bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl)
*/
-
#define CDROM_DATA_TRACK 0x04
/*
* The leadout track is always 0xAA, regardless of # of tracks on disc
*/
-
#define CDROM_LEADOUT 0xAA
struct cdrom_subchnl
- {
+{
u_char cdsc_format;
u_char cdsc_audiostatus;
u_char cdsc_adr: 4;
u_char cdsc_trk;
u_char cdsc_ind;
union
- {
+ {
struct
- {
+ {
u_char minute;
u_char second;
u_char frame;
- } msf;
+ } msf;
int lba;
- } cdsc_absaddr;
+ } cdsc_absaddr;
union
- {
+ {
struct
- {
+ {
u_char minute;
u_char second;
u_char frame;
- } msf;
+ } msf;
int lba;
- } cdsc_reladdr;
- };
+ } cdsc_reladdr;
+};
/*
- * return value from READ SUBCHANNEL DATA
+ * audio states (from SCSI-2, but seen with other drives, too)
*/
-
-#define CDROM_AUDIO_INVALID 0x00 /* audio status not supported */
-#define CDROM_AUDIO_PLAY 0x11 /* audio play operation in progress */
-#define CDROM_AUDIO_PAUSED 0x12 /* audio play operation paused */
-#define CDROM_AUDIO_COMPLETED 0x13 /* audio play successfully completed */
-#define CDROM_AUDIO_ERROR 0x14 /* audio play stopped due to error */
-#define CDROM_AUDIO_NO_STATUS 0x15 /* no current audio status to return */
+#define CDROM_AUDIO_INVALID 0x00 /* audio status not supported */
+#define CDROM_AUDIO_PLAY 0x11 /* audio play operation in progress */
+#define CDROM_AUDIO_PAUSED 0x12 /* audio play operation paused */
+#define CDROM_AUDIO_COMPLETED 0x13 /* audio play successfully completed */
+#define CDROM_AUDIO_ERROR 0x14 /* audio play stopped due to error */
+#define CDROM_AUDIO_NO_STATUS 0x15 /* no current audio status to return */
struct cdrom_volctrl
- {
+{
u_char channel0;
u_char channel1;
u_char channel2;
u_char channel3;
- };
+};
struct cdrom_read
- {
+{
int cdread_lba;
caddr_t cdread_bufaddr;
int cdread_buflen;
- };
+};
/*
- * preliminary extensions for transferring audio frames
- * currently used by sbpcd.c
- * (still may change if other drivers will use it, too):
+ * extensions for transfering audio frames
+ * currently used by sbpcd.c, cdu31a.c, ide-cd.c
*/
struct cdrom_read_audio
+{
+ union
{
- union
- {
- struct
+ struct
{
- u_char minute;
- u_char second;
- u_char frame;
+ u_char minute;
+ u_char second;
+ u_char frame;
} msf;
- int lba;
- } addr; /* frame address */
- u_char addr_format; /* CDROM_LBA or CDROM_MSF */
- int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
- u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
- };
+ int lba;
+ } addr; /* frame address */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+ int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
+ u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+};
/*
* this has to be the "arg" of the CDROMMULTISESSION ioctl
* The returned "addr" is valid only if "xa_flag" is true.
*/
struct cdrom_multisession
+{
+ union
{
- union
- {
- struct
+ struct
{
- u_char minute;
- u_char second;
- u_char frame;
+ u_char minute;
+ u_char second;
+ u_char frame;
} msf;
- int lba;
- } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
- u_char xa_flag; /* 1: "is XA disk" */
- u_char addr_format; /* CDROM_LBA or CDROM_MSF */
- };
+ int lba;
+ } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+ u_char xa_flag; /* 1: "is XA disk" */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+};
#ifdef FIVETWELVE
#define CDROM_MODE1_SIZE 512
/*
* CD-ROM IOCTL commands
+ * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
*/
-#define CDROMPAUSE 0x5301 /* pause */
-#define CDROMRESUME 0x5302 /* resume */
+#define CDROMPAUSE 0x5301
+#define CDROMRESUME 0x5302
+#define CDROMPLAYMSF 0x5303 /* (struct cdrom_msf) */
+#define CDROMPLAYTRKIND 0x5304 /* (struct cdrom_ti) */
-#define CDROMPLAYMSF 0x5303 /* (struct cdrom_msf) */
- /* SCMD_PLAY_AUDIO_MSF */
+#define CDROMREADTOCHDR 0x5305 /* (struct cdrom_tochdr) */
+#define CDROMREADTOCENTRY 0x5306 /* (struct cdrom_tocentry) */
-#define CDROMPLAYTRKIND 0x5304 /* (struct cdrom_ti) */
- /* SCMD_PLAY_AUDIO_TI */
+#define CDROMSTOP 0x5307 /* stop the drive motor */
+#define CDROMSTART 0x5308 /* turn the motor on */
-#define CDROMREADTOCHDR 0x5305 /* (struct cdrom_tochdr) */
- /* read the TOC header */
-#define CDROMREADTOCENTRY 0x5306 /* (struct cdrom_tocentry) */
- /* read a TOC entry */
+#define CDROMEJECT 0x5309 /* eject CD-ROM media */
-#define CDROMSTOP 0x5307 /* stop the drive motor */
-#define CDROMSTART 0x5308 /* turn the motor on */
+#define CDROMVOLCTRL 0x530a /* (struct cdrom_volctrl) */
-#define CDROMEJECT 0x5309 /* eject CD-ROM media */
+#define CDROMSUBCHNL 0x530b /* (struct cdrom_subchnl) */
-#define CDROMVOLCTRL 0x530a /* (struct cdrom_volctrl) */
- /* volume control */
+#define CDROMREADMODE2 0x530c /* (struct cdrom_read) */
+ /* read type-2 data (not suppt) */
-#define CDROMSUBCHNL 0x530b /* (struct cdrom_subchnl) */
- /* read Q sub-channel data */
+#define CDROMREADMODE1 0x530d /* (struct cdrom_read) */
+ /* read type-1 data */
-#define CDROMREADMODE2 0x530c /* (struct cdrom_read) */
- /* read type-2 data (not suppt) */
+#define CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
-#define CDROMREADMODE1 0x530d /* (struct cdrom_read) */
- /* read type-1 data */
-/*
- * preliminary extension for transferring audio frames
- * currently used by cdu31a.c and sbpcd.c
- * (still may change if other drivers will use it, too):
- */
-#define CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
/*
- * preliminary extension for enable (1) / disable (0) auto-ejecting
- * currently used by sbpcd.c and sr.c
- * (still may change if other drivers will use it, too):
+ * enable (1) / disable (0) auto-ejecting
*/
-#define CDROMEJECT_SW 0x530f /* arg: 0 or 1 */
+#define CDROMEJECT_SW 0x530f /* arg: 0 or 1 */
/*
* obtain the start-of-last-session address of multi session disks
*/
-#define CDROMMULTISESSION 0x5310 /* (struct cdrom_multisession) */
+#define CDROMMULTISESSION 0x5310 /* (struct cdrom_multisession) */
/*
* obtain the "universal product code" number
* (only some data disks have it coded)
*/
-#define CDROM_GET_UPC 0x5311 /* 8 bytes returned */
+#define CDROM_GET_UPC 0x5311 /* 8 bytes returned */
-#define CDROMRESET 0x5312 /* hard-reset the drive */
-#define CDROMVOLREAD 0x5313 /* let the drive tell its volume setting */
+#define CDROMRESET 0x5312 /* hard-reset the drive */
+#define CDROMVOLREAD 0x5313 /* let the drive tell its volume setting */
/* (struct cdrom_volctrl) */
/*
- *these ioctls are used in aztcd.c
+ * these ioctls are used in aztcd.c
*/
-#define CDROMREADRAW 0x5314 /*read data in raw mode*/
-#define CDROMREADCOOKED 0x5315 /*read data in cooked mode*/
-#define CDROMSEEK 0x5316 /*seek msf address*/
+#define CDROMREADRAW 0x5314 /* read data in raw mode */
+#define CDROMREADCOOKED 0x5315 /* read data in cooked mode */
+#define CDROMSEEK 0x5316 /*seek msf address*/
+
+
+
+/*
+ * CD-ROM-specific SCSI command opcodes
+ */
+
+/*
+ * Group 2 (10-byte). All of these are called 'optional' by SCSI-II.
+ */
+#define SCMD_READ_TOC 0x43 /* read table of contents */
+#define SCMD_PLAYAUDIO_MSF 0x47 /* play data at time offset */
+#define SCMD_PLAYAUDIO_TI 0x48 /* play data at track/index */
+#define SCMD_PAUSE_RESUME 0x4B /* pause/resume audio */
+#define SCMD_READ_SUBCHANNEL 0x42 /* read SC info on playing disc */
+#define SCMD_PLAYAUDIO10 0x45 /* play data at logical block */
+#define SCMD_READ_HEADER 0x44 /* read TOC header */
+
+/*
+ * Group 5
+ */
+#define SCMD_PLAYAUDIO12 0xA5 /* play data at logical block */
+#define SCMD_PLAYTRACK_REL12 0xA9 /* play track at relative offset */
+
+/*
+ * Group 6 Commands
+ */
+#define SCMD_CD_PLAYBACK_CONTROL 0xC9 /* Sony vendor-specific audio */
+#define SCMD_CD_PLAYBACK_STATUS 0xC4 /* control opcodes */
+
+/*
+ * CD-ROM capacity structure.
+ */
+struct scsi_capacity
+{
+ u_long capacity;
+ u_long lbasize;
+};
+
+/*
+ * CD-ROM MODE_SENSE/MODE_SELECT parameters
+ */
+#define ERR_RECOVERY_PARMS 0x01
+#define DISCO_RECO_PARMS 0x02
+#define FORMAT_PARMS 0x03
+#define GEOMETRY_PARMS 0x04
+#define CERTIFICATION_PARMS 0x06
+#define CACHE_PARMS 0x38
+
+/*
+ * standard mode-select header prepended to all mode-select commands
+ */
+struct ccs_modesel_head
+{
+ u_char _r1; /* reserved */
+ u_char medium; /* device-specific medium type */
+ u_char _r2; /* reserved */
+ u_char block_desc_length; /* block descriptor length */
+ u_char density; /* device-specific density code */
+ u_char number_blocks_hi; /* number of blocks in this block desc */
+ u_char number_blocks_med;
+ u_char number_blocks_lo;
+ u_char _r3;
+ u_char block_length_hi; /* block length for blocks in this desc */
+ u_short block_length;
+};
+
+/*
+ * error recovery parameters
+ */
+struct ccs_err_recovery
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char awre : 1; /* auto write realloc enabled */
+ u_char arre : 1; /* auto read realloc enabled */
+ u_char tb : 1; /* transfer block */
+ u_char rc : 1; /* read continuous */
+ u_char eec : 1; /* enable early correction */
+ u_char per : 1; /* post error */
+ u_char dte : 1; /* disable transfer on error */
+ u_char dcr : 1; /* disable correction */
+ u_char retry_count; /* error retry count */
+ u_char correction_span; /* largest recov. to be attempted, bits */
+ u_char head_offset_count; /* head offset (2's C) for each retry */
+ u_char strobe_offset_count; /* data strobe */
+ u_char recovery_time_limit; /* time limit on recovery attempts */
+};
+
+/*
+ * disco/reco parameters
+ */
+struct ccs_disco_reco
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char buffer_full_ratio; /* write buffer reconnect threshold */
+ u_char buffer_empty_ratio; /* read */
+ u_short bus_inactivity_limit; /* limit on bus inactivity time */
+ u_short disconnect_time_limit; /* minimum disconnect time */
+ u_short connect_time_limit; /* minimum connect time */
+ u_short _r2; /* reserved */
+};
+
+/*
+ * drive geometry parameters
+ */
+struct ccs_geometry
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char cyl_ub; /* #cyls */
+ u_char cyl_mb;
+ u_char cyl_lb;
+ u_char heads; /* #heads */
+ u_char precomp_cyl_ub; /* precomp start */
+ u_char precomp_cyl_mb;
+ u_char precomp_cyl_lb;
+ u_char current_cyl_ub; /* reduced current start */
+ u_char current_cyl_mb;
+ u_char current_cyl_lb;
+ u_short step_rate; /* stepping motor rate */
+ u_char landing_cyl_ub; /* landing zone */
+ u_char landing_cyl_mb;
+ u_char landing_cyl_lb;
+ u_char _r2;
+ u_char _r3;
+ u_char _r4;
+};
+
+/*
+ * cache parameters
+ */
+struct ccs_cache
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char mode; /* cache control byte */
+ u_char threshold; /* prefetch threshold */
+ u_char max_prefetch; /* maximum prefetch size */
+ u_char max_multiplier; /* maximum prefetch multiplier */
+ u_char min_prefetch; /* minimum prefetch size */
+ u_char min_multiplier; /* minimum prefetch multiplier */
+ u_char _r2[8];
+};
#endif _LINUX_CDROM_H
+/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
unsigned long start;
};
-/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x30n/0x31n */
-#define HDIO_GETGEO 0x301 /* get device geometry */
-#define HDIO_GET_UNMASKINTR 0x302 /* get current unmask setting */
-#define HDIO_GET_MULTCOUNT 0x304 /* get current IDE blockmode setting */
-#define HDIO_GET_IDENTITY 0x307 /* get IDE identification info */
-#define HDIO_GET_KEEPSETTINGS 0x308 /* get keep-settings-on-reset flag */
-#define HDIO_GET_CHIPSET 0x309 /* get current interface type setting */
-#define HDIO_GET_NOWERR 0x30a /* get ignore-write-error flag */
-#define HDIO_GET_DMA 0x30b /* get use-dma flag */
-#define HDIO_DRIVE_CMD 0x31f /* execute a special drive command */
-
-/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x32n/0x33n */
-#define HDIO_SET_MULTCOUNT 0x321 /* set IDE blockmode */
-#define HDIO_SET_UNMASKINTR 0x322 /* permit other irqs during I/O */
-#define HDIO_SET_KEEPSETTINGS 0x323 /* keep ioctl settings on reset */
-#define HDIO_SET_CHIPSET 0x324 /* optimise driver for interface type */
-#define HDIO_SET_NOWERR 0x325 /* set ignore-write-error flag */
-#define HDIO_SET_DMA 0x326 /* set use-dma flag */
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define HDIO_GETGEO 0x0301 /* get device geometry */
+#define HDIO_GET_UNMASKINTR 0x0302 /* get current unmask setting */
+#define HDIO_GET_MULTCOUNT 0x0304 /* get current IDE blockmode setting */
+#define HDIO_GET_IDENTITY 0x0307 /* get IDE identification info */
+#define HDIO_GET_KEEPSETTINGS 0x0308 /* get keep-settings-on-reset flag */
+#define HDIO_GET_CHIPSET 0x0309 /* get current interface type setting */
+#define HDIO_GET_NOWERR 0x030a /* get ignore-write-error flag */
+#define HDIO_GET_DMA 0x030b /* get use-dma flag */
+#define HDIO_DRIVE_CMD 0x031f /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define HDIO_SET_MULTCOUNT 0x0321 /* set IDE blockmode */
+#define HDIO_SET_UNMASKINTR 0x0322 /* permit other irqs during I/O */
+#define HDIO_SET_KEEPSETTINGS 0x0323 /* keep ioctl settings on reset */
+#define HDIO_SET_CHIPSET 0x0324 /* optimise driver for interface type */
+#define HDIO_SET_NOWERR 0x0325 /* set ignore-write-error flag */
+#define HDIO_SET_DMA 0x0326 /* set use-dma flag */
/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
struct hd_driveid {
#define IDE1_MAJOR 22
#define MITSUMI_CDROM_MAJOR 23
#define CDU535_CDROM_MAJOR 24
+#define STL_SERIALMAJOR 24
#define MATSUSHITA_CDROM_MAJOR 25
+#define STL_CALLOUTMAJOR 25
#define MATSUSHITA_CDROM2_MAJOR 26
#define QIC117_TAPE_MAJOR 27
#define MATSUSHITA_CDROM3_MAJOR 27
#define MATSUSHITA_CDROM4_MAJOR 28
+#define STL_SIOMEMMAJOR 28
#define AZTECH_CDROM_MAJOR 29
#define CM206_CDROM_MAJOR 32
#define IDE2_MAJOR 33
unsigned long mouse_init(unsigned long);
+struct mouse {
+ int minor;
+ const char *name;
+ struct file_operations *fops;
+ struct mouse * next, * prev;
+};
+
+extern int mouse_register(struct mouse * mouse);
+extern int mouse_deregister(struct mouse * mouse);
+
#endif
* or
* sbpcd=0x300,LaserMate
* or
- * sbpcd=0x330,SPEA
+ * sbpcd=0x338,SoundScape
*
* If sbpcd gets used as a module, you can load it with
* insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
* or
* insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x300,0
* or
- * insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x330,2
+ * insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x338,2
* respective to override the configured address and type.
*/
* ========
* SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
* LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ...
- * SPEA addresses are from the LASERMATE type and range.
+ * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to
+ * specify the REAL address here, not the configuration port address. Look
+ * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let
+ * sbpcd auto-probe, if you are not firm with the address.
* There are some soundcards on the market with 0x0630, 0x0650, ...; their
* type is not obvious (both types are possible).
*
* example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1.
* if your soundcard has its CDROM port above 0x300, specify
* that address and try SBPRO 0 first.
+ * if your SoundScape configuration port is at 0x330, specify
+ * 0x338 and SBPRO 2.
*
* interface type:
* ===============
* set SBPRO to 1 for "true" SoundBlaster card
* set SBPRO to 0 for "compatible" soundcards and
* for "poor" (no sound) interface cards.
- * set SBPRO to 2 for the SPEA Media FX card
+ * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
*
* Almost all "compatible" sound boards need to set SBPRO to 0.
* If SBPRO is set wrong, the drives will get found - but any
unsigned long start_brk, brk, start_stack, start_mmap;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long rss;
- unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
- int swappable:1;
- unsigned long swap_address;
- unsigned long old_maj_flt; /* old value of maj_flt */
- unsigned long dec_flt; /* page fault count of the last time */
- unsigned long swap_cnt; /* number of pages to swap on next pass */
struct vm_area_struct * mmap;
struct vm_area_struct * mmap_avl;
};
0, 0, 0, 0, \
0, 0, 0, 0, \
0, \
-/* ?_flt */ 0, 0, 0, 0, \
- 0, \
-/* swap */ 0, 0, 0, 0, \
&init_mmap, &init_mmap }
struct signal_struct {
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
long utime, stime, cutime, cstime, start_time;
- struct rlimit rlim[RLIM_NLIMITS];
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+ unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
+ int swappable:1;
+ unsigned long swap_address;
+ unsigned long old_maj_flt; /* old value of maj_flt */
+ unsigned long dec_flt; /* page fault count of the last time */
+ unsigned long swap_cnt; /* number of pages to swap on next pass */
+/* limits */
+ struct rlimit rlim[RLIM_NLIMITS];
unsigned short used_math;
char comm[16];
/* file system info */
/* timeout */ 0,0,0,0,0,0,0, \
/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
/* utime */ 0,0,0,0,0, \
+/* flt */ 0,0,0,0, \
+/* swp */ 0,0,0,0,0, \
/* rlimits */ { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
{LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM}, \
{ 0, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
#ifdef CONFIG_CYCLADES
extern long cy_init(long);
#endif
+#ifdef CONFIG_STALLION
+extern long stl_init(long);
+#endif
+#ifdef CONFIG_ISTALLION
+extern long stli_init(long);
+#endif
extern int tty_paranoia_check(struct tty_struct *tty, dev_t device,
const char *routine);
pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
shp->shm_pages[idx] = pte_val(pte);
} else
- --current->mm->maj_flt; /* was incremented in do_no_page */
+ --current->maj_flt; /* was incremented in do_no_page */
done: /* pte_val(pte) == shp->shm_pages[idx] */
- current->mm->min_flt++;
+ current->min_flt++;
mem_map[MAP_NR(pte_page(pte))]++;
return pte_modify(pte, shmd->vm_page_prot);
}
if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
free_page(p->kernel_stack_page);
+ current->cmin_flt += p->min_flt + p->cmin_flt;
+ current->cmaj_flt += p->maj_flt + p->cmaj_flt;
kfree(p);
return;
}
{
struct mm_struct * mm = current->mm;
+ current->swappable = 0;
if (mm) {
if (!--mm->count) {
- current->p_pptr->mm->cmin_flt += mm->min_flt + mm->cmin_flt;
- current->p_pptr->mm->cmaj_flt += mm->maj_flt + mm->cmaj_flt;
exit_mmap(mm);
free_page_tables(current);
kfree(mm);
return -1;
*tsk->mm = *current->mm;
tsk->mm->count = 1;
- tsk->mm->min_flt = tsk->mm->maj_flt = 0;
- tsk->mm->cmin_flt = tsk->mm->cmaj_flt = 0;
+ tsk->min_flt = tsk->maj_flt = 0;
+ tsk->cmin_flt = tsk->cmaj_flt = 0;
if (new_page_tables(tsk))
return -1;
if (dup_mmap(tsk->mm)) {
(*p->binfmt->use_count)++;
p->did_exec = 0;
+ p->swappable = 0;
p->kernel_stack_page = new_stack;
*(unsigned long *) p->kernel_stack_page = STACK_MAGIC;
p->state = TASK_UNINTERRUPTIBLE;
p->semundo = NULL;
/* ok, now we should be set up.. */
- p->mm->swappable = 1;
+ p->swappable = 1;
p->exit_signal = clone_flags & CSIGNAL;
p->counter = current->counter >> 1;
wake_up_process(p); /* do this last, just in case */
r.ru_utime.tv_usec = CT_TO_USECS(p->utime);
r.ru_stime.tv_sec = CT_TO_SECS(p->stime);
r.ru_stime.tv_usec = CT_TO_USECS(p->stime);
- r.ru_minflt = p->mm->min_flt;
- r.ru_majflt = p->mm->maj_flt;
+ r.ru_minflt = p->min_flt;
+ r.ru_majflt = p->maj_flt;
break;
case RUSAGE_CHILDREN:
r.ru_utime.tv_sec = CT_TO_SECS(p->cutime);
r.ru_utime.tv_usec = CT_TO_USECS(p->cutime);
r.ru_stime.tv_sec = CT_TO_SECS(p->cstime);
r.ru_stime.tv_usec = CT_TO_USECS(p->cstime);
- r.ru_minflt = p->mm->cmin_flt;
- r.ru_majflt = p->mm->cmaj_flt;
+ r.ru_minflt = p->cmin_flt;
+ r.ru_majflt = p->cmaj_flt;
break;
default:
r.ru_utime.tv_sec = CT_TO_SECS(p->utime + p->cutime);
r.ru_utime.tv_usec = CT_TO_USECS(p->utime + p->cutime);
r.ru_stime.tv_sec = CT_TO_SECS(p->stime + p->cstime);
r.ru_stime.tv_usec = CT_TO_USECS(p->stime + p->cstime);
- r.ru_minflt = p->mm->min_flt + p->mm->cmin_flt;
- r.ru_majflt = p->mm->maj_flt + p->mm->cmaj_flt;
+ r.ru_minflt = p->min_flt + p->cmin_flt;
+ r.ru_majflt = p->maj_flt + p->cmaj_flt;
break;
}
memcpy_tofs(ru, &r, sizeof(r));
old_page = pte_page(pte);
if (old_page >= high_memory)
goto bad_wp_page;
- vma->vm_mm->min_flt++;
+ tsk->min_flt++;
/*
* Do we need to copy?
*/
if (mem_map[MAP_NR(pte_page(page))] > 1 && !(vma->vm_flags & VM_SHARED))
page = pte_wrprotect(page);
++vma->vm_mm->rss;
- ++vma->vm_mm->maj_flt;
+ ++tsk->maj_flt;
set_pte(page_table, page);
return;
}
address &= PAGE_MASK;
if (!vma->vm_ops || !vma->vm_ops->nopage) {
++vma->vm_mm->rss;
- ++vma->vm_mm->min_flt;
+ ++tsk->min_flt;
get_empty_page(tsk, vma, page_table);
return;
}
page = __get_free_page(GFP_KERNEL);
if (share_page(vma, address, write_access, page)) {
- ++vma->vm_mm->min_flt;
++vma->vm_mm->rss;
+ ++tsk->min_flt;
return;
}
if (!page) {
put_page(page_table, BAD_PAGE);
return;
}
- ++vma->vm_mm->maj_flt;
+ ++tsk->maj_flt;
++vma->vm_mm->rss;
/*
* The fourth argument is "no_share", which tells the low-level code
return;
}
vma->vm_mm->rss++;
- vma->vm_mm->maj_flt++;
+ tsk->maj_flt++;
if (!write_access && add_to_swap_cache(page, entry)) {
set_pte(page_table, mk_pte(page, vma->vm_page_prot));
return;
do {
int result;
- vma->vm_mm->swap_address = address + PAGE_SIZE;
+ tsk->swap_address = address + PAGE_SIZE;
result = try_to_swap_out(tsk, vma, address, pte, limit);
if (result)
return result;
/*
* Go through process' page directory.
*/
- address = p->mm->swap_address;
- p->mm->swap_address = 0;
+ address = p->swap_address;
+ p->swap_address = 0;
/*
* Find the proper vm-area
break;
address = vma->vm_start;
}
- p->mm->swap_address = 0;
+ p->swap_address = 0;
return 0;
}
}
p = task[swap_task];
- if (p && p->mm && p->mm->swappable && p->mm->rss)
+ if (p && p->swappable && p->mm->rss)
break;
swap_task++;
/*
* Determine the number of pages to swap from this process.
*/
- if (!p->mm->swap_cnt) {
- p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt;
- p->mm->old_maj_flt = p->mm->maj_flt;
-
- if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) {
- p->mm->dec_flt = SWAP_RATIO / SWAP_MIN;
- p->mm->swap_cnt = SWAP_MIN;
- } else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX)
- p->mm->swap_cnt = SWAP_MAX;
+ if (!p->swap_cnt) {
+ p->dec_flt = (p->dec_flt * 3) / 4 + p->maj_flt - p->old_maj_flt;
+ p->old_maj_flt = p->maj_flt;
+
+ if (p->dec_flt >= SWAP_RATIO / SWAP_MIN) {
+ p->dec_flt = SWAP_RATIO / SWAP_MIN;
+ p->swap_cnt = SWAP_MIN;
+ } else if (p->dec_flt <= SWAP_RATIO / SWAP_MAX)
+ p->swap_cnt = SWAP_MAX;
else
- p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt;
+ p->swap_cnt = SWAP_RATIO / p->dec_flt;
}
- if (!--p->mm->swap_cnt)
+ if (!--p->swap_cnt)
swap_task++;
switch (swap_out_process(p, limit)) {
case 0:
- if (p->mm->swap_cnt)
+ if (p->swap_cnt)
swap_task++;
break;
case 1: