From 111bb14a2ef9e1b50db09eab55dcc3a61625bf69 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:10:14 -0500 Subject: [PATCH] Import 1.3.27 --- Makefile | 2 +- arch/i386/config.in | 13 +- drivers/block/README.sbpcd | 216 +- drivers/block/sbpcd.c | 179 +- drivers/char/Makefile | 16 + drivers/char/atixlmouse.c | 3 +- drivers/char/busmouse.c | 3 +- drivers/char/istallion.c | 4096 ++++++++++++++++++++++++++++++++++++ drivers/char/mouse.c | 2 - drivers/char/mouse.h | 14 - drivers/char/msbusmouse.c | 1 + drivers/char/psaux.c | 3 +- drivers/char/stallion.c | 2973 ++++++++++++++++++++++++++ drivers/char/tty_io.c | 6 + fs/buffer.c | 6 +- fs/isofs/inode.c | 17 +- fs/proc/array.c | 8 +- include/linux/busmouse.h | 1 - include/linux/cd1400.h | 293 +++ include/linux/cdk.h | 472 +++++ include/linux/cdrom.h | 600 +++--- include/linux/hdreg.h | 36 +- include/linux/major.h | 3 + include/linux/mouse.h | 10 + include/linux/sbpcd.h | 13 +- include/linux/sched.h | 21 +- include/linux/tty.h | 6 + ipc/shm.c | 4 +- kernel/exit.c | 5 +- kernel/fork.c | 7 +- kernel/sys.c | 12 +- mm/memory.c | 10 +- mm/swap.c | 36 +- 33 files changed, 8528 insertions(+), 559 deletions(-) create mode 100644 drivers/char/istallion.c delete mode 100644 drivers/char/mouse.h create mode 100644 drivers/char/stallion.c create mode 100644 include/linux/cd1400.h create mode 100644 include/linux/cdk.h diff --git a/Makefile b/Makefile index a80c05708b27..63b44596dc5c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 26 +SUBLEVEL = 27 ARCH = i386 diff --git a/arch/i386/config.in b/arch/i386/config.in index eecf7e2956d9..beb7ee41d64e 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -226,13 +226,13 @@ if [ "$CONFIG_CD_NO_IDESCSI" = "y" ]; then 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 @@ -266,6 +266,11 @@ tristate 'SMB filesystem (to mount WfW shares etc..) support' CONFIG_SMB_FS n 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 diff --git a/drivers/block/README.sbpcd b/drivers/block/README.sbpcd index 6a40abfb0634..f0ad67bcf955 100644 --- a/drivers/block/README.sbpcd +++ b/drivers/block/README.sbpcd @@ -1,14 +1,33 @@ -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 @@ -16,39 +35,72 @@ of the "LaserMate" type), and it is possibly the best buy today (cheaper than 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 @@ -104,13 +156,15 @@ session of a photoCD). 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. @@ -119,21 +173,27 @@ To install: 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 @@ -142,16 +202,23 @@ To install: 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 @@ -160,10 +227,8 @@ To install: 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 @@ -179,11 +244,12 @@ To install: 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": @@ -214,6 +280,8 @@ No DMA and no IRQ is used. 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 @@ -225,13 +293,13 @@ distribution) which MUST get handled with a block_size of 1024. Generally, 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. @@ -284,8 +352,8 @@ If your I/O port address is not 0x340, you have to look for the #defines near 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. @@ -790,6 +858,21 @@ main(int argc, char *argv[]) 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) "); @@ -955,21 +1038,6 @@ main(int argc, char *argv[]) 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); @@ -978,20 +1046,4 @@ main(int argc, char *argv[]) } } /*==========================================================================*/ -/* - * 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: - */ diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c index 6888eec89b89..2cff9a512504 100644 --- a/drivers/block/sbpcd.c +++ b/drivers/block/sbpcd.c @@ -10,9 +10,9 @@ * 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 * @@ -217,12 +217,19 @@ * * 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 @@ -298,7 +305,7 @@ char kernel_version[]=UTS_RELEASE; #include "blk.h" -#define VERSION "v3.8 Eberhard Moenkeberg " +#define VERSION "v3.9 Eberhard Moenkeberg " /*==========================================================================*/ /* @@ -364,9 +371,9 @@ static int sbpcd[] = 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 */ @@ -380,7 +387,7 @@ static int sbpcd[] = */ 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 */ @@ -499,6 +506,8 @@ static const char *str_sb_l = "soundblaster"; 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) @@ -608,6 +617,7 @@ static struct { u_int lba_multi; int first_session; int last_session; + int track_of_last_session; u_char audio_state; u_int pos_audio_start; @@ -634,7 +644,6 @@ static struct { u_char UPC_ctl_adr; u_char UPC_buf[7]; - int CDsize_blk; int frame_size; int CDsize_frm; @@ -730,20 +739,26 @@ static int sbpcd_dbg_ioctl(unsigned long arg, int level) 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 /*==========================================================================*/ @@ -775,7 +790,7 @@ static void sbp_sleep(u_int time) */ 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; @@ -797,8 +812,8 @@ static INLINE u_int blk2msf(u_int blk) 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); @@ -835,7 +850,7 @@ static INLINE int msf2blk(int msfx) 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); } @@ -847,7 +862,7 @@ static INLINE int msf2lba(u_char *msf) { 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); } @@ -928,7 +943,7 @@ static int CDi_stat_loop(void) 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 @@ -950,7 +965,7 @@ static int CDi_stat_loop(void) 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 @@ -1025,7 +1040,7 @@ static int ResponseInfo(void) 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; @@ -1181,7 +1196,7 @@ static int ResponseStatus(void) if (!(i&s_not_result_ready)) break; } if ((j!=0)||(timeout0;j--) { @@ -2482,31 +2498,15 @@ static int cc_ReadCapacity(void) 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); @@ -2605,6 +2605,7 @@ static int cc_ReadTocDescr(void) { 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]; } @@ -2759,7 +2760,7 @@ static int cc_ReadUPC(void) D_S[d].diskstate_flags &= ~upc_bit; #if TEST_UPC - for (block=CD_BLOCK_OFFSET+1;block1) 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; @@ -5010,24 +5029,32 @@ void sbpcd_setup(const char *s, int *p) /*==========================================================================*/ /* * 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); @@ -5056,7 +5083,7 @@ static int config_spea(void) 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); } /*==========================================================================*/ @@ -5080,18 +5107,18 @@ int init_module(void) #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 */ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index c01464a31bb2..cf31434ec4af 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -24,6 +24,22 @@ ifdef CONFIG_CYCLADES 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 diff --git a/drivers/char/atixlmouse.c b/drivers/char/atixlmouse.c index 8a806ecdd35a..bce7ee7edb56 100644 --- a/drivers/char/atixlmouse.c +++ b/drivers/char/atixlmouse.c @@ -22,14 +22,13 @@ #include #include #include +#include #include #include #include #include -#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) */ diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index 4a53f1c3ca09..ddbc99e17c37 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -243,7 +244,7 @@ struct file_operations bus_mouse_fops = { fasync_mouse, }; -static struct mouse bus_mous = { +static struct mouse bus_mouse = { LOGITECH_BUSMOUSE, "busmouse", &bus_mouse_fops }; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c new file mode 100644 index 000000000000..277914c3c438 --- /dev/null +++ b/drivers/char/istallion.c @@ -0,0 +1,4096 @@ +/*****************************************************************************/ + +/* + * 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 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +/* + * 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 +#include +#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); +} + +/*****************************************************************************/ diff --git a/drivers/char/mouse.c b/drivers/char/mouse.c index 3a3654423595..eb8200e138f3 100644 --- a/drivers/char/mouse.c +++ b/drivers/char/mouse.c @@ -32,8 +32,6 @@ #include #include -#include "mouse.h" - /* * Head entry for the doubly linked mouse list */ diff --git a/drivers/char/mouse.h b/drivers/char/mouse.h deleted file mode 100644 index 5f61aa7d5e82..000000000000 --- a/drivers/char/mouse.h +++ /dev/null @@ -1,14 +0,0 @@ -#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 diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c index 4bb5e7729275..c19c103f1c66 100644 --- a/drivers/char/msbusmouse.c +++ b/drivers/char/msbusmouse.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index a72ee10464d0..05d5f01d763f 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -52,8 +53,6 @@ #include -#include "mouse.h" - #define PSMOUSE_MINOR 1 /* minor device # for this mouse */ /* aux controller ports */ diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c new file mode 100644 index 000000000000..67f1f8f3cff2 --- /dev/null +++ b/drivers/char/stallion.c @@ -0,0 +1,2973 @@ +/*****************************************************************************/ + +/* + * 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 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PCI +#include +#include +#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); +} + +/*****************************************************************************/ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d546c783f765..7b916bb59bf5 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1788,6 +1788,12 @@ long tty_init(long 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); diff --git a/fs/buffer.c b/fs/buffer.c index 90583ab7d6f7..8c2a03ab4ea8 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1042,7 +1042,7 @@ static unsigned long check_aligned(struct buffer_head * first, unsigned long add while (nrbuf-- > 0) brelse(bh[nrbuf]); free_page(address); - ++current->mm->min_flt; + ++current->min_flt; return page; no_go: while (nrbuf-- > 0) @@ -1100,7 +1100,7 @@ static unsigned long try_to_load_aligned(unsigned long address, 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) { @@ -1154,7 +1154,7 @@ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, in if (where) return where; } - ++current->mm->maj_flt; + ++current->maj_flt; for (i=0, j=0; j - */ vol_desc_start=0; if (get_blkfops(MAJOR(dev))->ioctl!=NULL) { @@ -565,7 +564,7 @@ void isofs_read_inode(struct inode * inode) inode->i_op = NULL; - /* get the volume sequence numner */ + /* get the volume sequence number */ volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ; /* diff --git a/fs/proc/array.c b/fs/proc/array.c index eee554b2e516..c9e90c4bda31 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -449,10 +449,10 @@ static int get_stat(int pid, char * buffer) 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, diff --git a/include/linux/busmouse.h b/include/linux/busmouse.h index 47b150882143..b67d12d1cb9b 100644 --- a/include/linux/busmouse.h +++ b/include/linux/busmouse.h @@ -95,7 +95,6 @@ struct mouse_status { }; /* Function Prototypes */ -extern long mouse_init(long); #endif diff --git a/include/linux/cd1400.h b/include/linux/cd1400.h new file mode 100644 index 000000000000..799fec062743 --- /dev/null +++ b/include/linux/cd1400.h @@ -0,0 +1,293 @@ +/*****************************************************************************/ + +/* + * 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 diff --git a/include/linux/cdk.h b/include/linux/cdk.h new file mode 100644 index 000000000000..915dc0bbd1ae --- /dev/null +++ b/include/linux/cdk.h @@ -0,0 +1,472 @@ +/*****************************************************************************/ + +/* + * 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 diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 474bc056093e..1541e0fdcb20 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -1,242 +1,112 @@ -/**************************************************************************************** - * * - * 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) * - * * - * -- 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 */ /* + * -- + * 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) @@ -245,19 +115,17 @@ struct cdrom_tocentry #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; @@ -265,74 +133,72 @@ struct cdrom_subchnl 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 @@ -340,20 +206,20 @@ struct cdrom_read_audio * 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 @@ -364,71 +230,225 @@ struct cdrom_multisession /* * 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: + */ diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index 1a44953b2ade..80043f478eb4 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -82,24 +82,24 @@ struct hd_geometry { 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 { diff --git a/include/linux/major.h b/include/linux/major.h index 35494cda7841..c3c700e929a0 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -83,11 +83,14 @@ #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 diff --git a/include/linux/mouse.h b/include/linux/mouse.h index df4e42718138..f9c93249c38a 100644 --- a/include/linux/mouse.h +++ b/include/linux/mouse.h @@ -8,4 +8,14 @@ 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 diff --git a/include/linux/sbpcd.h b/include/linux/sbpcd.h index 37d267b321f7..dd9fb38d328e 100644 --- a/include/linux/sbpcd.h +++ b/include/linux/sbpcd.h @@ -13,14 +13,14 @@ * 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. */ @@ -35,20 +35,25 @@ * ======== * 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 diff --git a/include/linux/sched.h b/include/linux/sched.h index 8843b0aa7be8..426c2884bf29 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -127,12 +127,6 @@ struct mm_struct { 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; }; @@ -144,9 +138,6 @@ struct mm_struct { 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 { @@ -195,7 +186,15 @@ struct task_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 */ @@ -254,6 +253,8 @@ struct task_struct { /* 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}, \ diff --git a/include/linux/tty.h b/include/linux/tty.h index 3fee0d665dd3..d4bb47a3b534 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -281,6 +281,12 @@ extern long vcs_init(long); #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); diff --git a/ipc/shm.c b/ipc/shm.c index f7c7f3580271..e563966b2eb9 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -660,10 +660,10 @@ static pte_t shm_swap_in(struct vm_area_struct * shmd, unsigned long offset, uns 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); } diff --git a/kernel/exit.c b/kernel/exit.c index ce25eec1c145..08fb1ac92466 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -97,6 +97,8 @@ void release(struct task_struct * p) 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; } @@ -405,10 +407,9 @@ static void exit_mm(void) { 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); diff --git a/kernel/fork.c b/kernel/fork.c index 35cdba3f946a..1d5cb66b97ec 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -108,8 +108,8 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk) 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)) { @@ -205,6 +205,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) (*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; @@ -242,7 +243,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) 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 */ diff --git a/kernel/sys.c b/kernel/sys.c index b90fc3dc4792..1dccd2eb1789 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -764,24 +764,24 @@ int getrusage(struct task_struct *p, int who, struct rusage *ru) 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)); diff --git a/mm/memory.c b/mm/memory.c index 1972443c94fe..e150d1b21d13 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -628,7 +628,7 @@ void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, 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? */ @@ -969,7 +969,7 @@ static inline void do_swap_page(struct task_struct * tsk, 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; } @@ -1000,14 +1000,14 @@ void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, 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) { @@ -1015,7 +1015,7 @@ void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, 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 diff --git a/mm/swap.c b/mm/swap.c index d6f01252c5b0..fe74f8073562 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -331,7 +331,7 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma, 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; @@ -461,7 +461,7 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * 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; @@ -530,8 +530,8 @@ static int swap_out_process(struct task_struct * p, unsigned long limit) /* * 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 @@ -551,7 +551,7 @@ static int swap_out_process(struct task_struct * p, unsigned long limit) break; address = vma->vm_start; } - p->mm->swap_address = 0; + p->swap_address = 0; return 0; } @@ -578,7 +578,7 @@ static int swap_out(unsigned int priority, unsigned long limit) } p = task[swap_task]; - if (p && p->mm && p->mm->swappable && p->mm->rss) + if (p && p->swappable && p->mm->rss) break; swap_task++; @@ -587,23 +587,23 @@ static int swap_out(unsigned int priority, unsigned long limit) /* * 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: -- 2.39.5