diff options
Diffstat (limited to 'drivers/char')
137 files changed, 31361 insertions, 7751 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 8aa4d68a9..1ae10a544 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -37,10 +37,12 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 + tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX if [ "$CONFIG_SPECIALIX" != "n" ]; then bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS fi + tristate 'Specialix SX (and SI) card support' CONFIG_SX tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m @@ -60,18 +62,25 @@ if [ "$CONFIG_PARPORT" != "n" ]; then dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT fi -bool 'Mouse Support (not serial mice)' CONFIG_MOUSE -if [ "$CONFIG_MOUSE" = "y" ]; then - mainmenu_option next_comment - comment 'Mice' - tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE - tristate 'Logitech busmouse support' CONFIG_BUSMOUSE - tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE +mainmenu_option next_comment +comment 'Mice' +tristate 'Bus Mouse Support' CONFIG_BUSMOUSE +if [ "$CONFIG_BUSMOUSE" != "n" ]; then + dep_tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE $CONFIG_BUSMOUSE + dep_tristate 'Logitech busmouse support' CONFIG_LOGIBUSMOUSE $CONFIG_BUSMOUSE + dep_tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE $CONFIG_BUSMOUSE + if [ "$CONFIG_PPC" = "y" ] ; then + dep_tristate 'Apple Desktop Bus mouse support' CONFIG_ADBMOUSE $CONFIG_BUSMOUSE + fi +fi + +tristate 'Mouse Support (not serial and bus mice)' CONFIG_MOUSE +if [ "$CONFIG_MOUSE" != "n" ]; then bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE tristate 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD - endmenu fi +endmenu tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE if [ "$CONFIG_QIC02_TAPE" != "n" ]; then @@ -85,7 +94,6 @@ if [ "$CONFIG_QIC02_TAPE" != "n" ]; then fi dep_tristate 'Zoran ZR36057/36060 support' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN - dep_tristate ' Include support for LML33' CONFIG_VIDEO_LML33 $CONFIG_VIDEO_ZORAN fi bool 'Watchdog Timer Support' CONFIG_WATCHDOG @@ -191,6 +199,9 @@ endmenu tristate 'Double Talk PC internal speech card support' CONFIG_DTLK +tristate 'Siemens R3964 line discipline' CONFIG_R3964 +tristate 'Applicom intelligent fieldbus card support' CONFIG_APPLICOM + mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE @@ -199,4 +210,11 @@ if [ "$CONFIG_FTAPE" != "n" ]; then fi endmenu +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM + if [ "$CONFIG_DRM" = "y" ]; then + dep_tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA m + fi +fi + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 9ac5bc5ab..b9d58b64c 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -18,14 +18,14 @@ ALL_SUB_DIRS := $(SUB_DIRS) ftape joystick # FONTMAPFILE = cp437.uni -L_TARGET := char.a +O_TARGET := char.o M_OBJS := -L_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o -LX_OBJS := pty.o misc.o +O_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o raw.o +OX_OBJS := pty.o misc.o ifdef CONFIG_VT -L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o -LX_OBJS += console.o selection.o +O_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o +OX_OBJS += console.o selection.o endif ifeq ($(CONFIG_SERIAL),y) @@ -33,7 +33,7 @@ ifeq ($(CONFIG_SERIAL),y) ifeq ($(CONFIG_SGI_SERIAL),) ifeq ($(CONFIG_DECSTATION),) ifeq ($(CONFIG_BAGET_MIPS),) - LX_OBJS += serial.o + OX_OBJS += serial.o endif endif endif @@ -56,26 +56,26 @@ ifndef CONFIG_SUN_KEYBOARD ifndef CONFIG_DECSTATION ifndef CONFIG_BAGET_MIPS ifdef CONFIG_VT -LX_OBJS += keyboard.o +OX_OBJS += keyboard.o endif ifneq ($(ARCH),m68k) - L_OBJS += pc_keyb.o defkeymap.o + O_OBJS += pc_keyb.o defkeymap.o endif else ifdef CONFIG_PCI -L_OBJS += defkeymap.o -LX_OBJS += keyboard.o +O_OBJS += defkeymap.o +OX_OBJS += keyboard.o endif endif ifdef CONFIG_MAGIC_SYSRQ -LX_OBJS += sysrq.o +OX_OBJS += sysrq.o endif endif endif ifeq ($(CONFIG_ATARI_DSP56K),y) -L_OBJS += dsp56k.o +O_OBJS += dsp56k.o S = y else ifeq ($(CONFIG_ATARI_DSP56K),m) @@ -85,7 +85,7 @@ else endif ifeq ($(CONFIG_ROCKETPORT),y) -L_OBJS += rocket.o +O_OBJS += rocket.o else ifeq ($(CONFIG_ROCKETPORT),m) M_OBJS += rocket.o @@ -93,7 +93,7 @@ else endif ifeq ($(CONFIG_DIGI),y) -L_OBJS += pcxx.o +O_OBJS += pcxx.o else ifeq ($(CONFIG_DIGI),m) M_OBJS += pcxx.o @@ -101,7 +101,7 @@ else endif ifeq ($(CONFIG_DIGIEPCA),y) -L_OBJS += epca.o +O_OBJS += epca.o else ifeq ($(CONFIG_DIGIEPCA),m) M_OBJS += epca.o @@ -109,7 +109,7 @@ else endif ifeq ($(CONFIG_CYCLADES),y) -L_OBJS += cyclades.o +O_OBJS += cyclades.o else ifeq ($(CONFIG_CYCLADES),m) M_OBJS += cyclades.o @@ -117,7 +117,7 @@ else endif ifeq ($(CONFIG_STALLION),y) -L_OBJS += stallion.o +O_OBJS += stallion.o else ifeq ($(CONFIG_STALLION),m) M_OBJS += stallion.o @@ -125,15 +125,23 @@ else endif ifeq ($(CONFIG_ISTALLION),y) -L_OBJS += istallion.o +O_OBJS += istallion.o else ifeq ($(CONFIG_ISTALLION),m) M_OBJS += istallion.o endif endif +ifeq ($(CONFIG_COMPUTONE),y) +L_OBJS += ip2.o ip2main.o +else + ifeq ($(CONFIG_COMPUTONE),m) + M_OBJS += ip2.o ip2main.o + endif +endif + ifeq ($(CONFIG_RISCOM8),y) -L_OBJS += riscom8.o +O_OBJS += riscom8.o else ifeq ($(CONFIG_RISCOM8),m) M_OBJS += riscom8.o @@ -141,7 +149,7 @@ else endif ifeq ($(CONFIG_ISI),y) -L_OBJS += isicom.o +O_OBJS += isicom.o else ifeq ($(CONFIG_ISI),m) M_OBJS += isicom.o @@ -149,7 +157,7 @@ else endif ifeq ($(CONFIG_ESPSERIAL),y) -L_OBJS += esp.o +O_OBJS += esp.o else ifeq ($(CONFIG_ESPSERIAL),m) M_OBJS += esp.o @@ -165,31 +173,39 @@ ifeq ($(CONFIG_N_HDLC),m) endif ifeq ($(CONFIG_SPECIALIX),y) -L_OBJS += specialix.o +O_OBJS += specialix.o else ifeq ($(CONFIG_SPECIALIX),m) M_OBJS += specialix.o endif endif +ifeq ($(CONFIG_SX),y) +L_OBJS += sx.o generic_serial.o +else + ifeq ($(CONFIG_SX),m) + M_OBJS += sx.o + endif +endif + ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) -L_OBJS += atixlmouse.o +O_OBJS += atixlmouse.o else ifeq ($(CONFIG_ATIXL_BUSMOUSE),m) M_OBJS += atixlmouse.o endif endif -ifeq ($(CONFIG_BUSMOUSE),y) -L_OBJS += busmouse.o +ifeq ($(CONFIG_LOGIBUSMOUSE),y) +O_OBJS += logibusmouse.o else - ifeq ($(CONFIG_BUSMOUSE),m) - M_OBJS += busmouse.o + ifeq ($(CONFIG_LOGIBUSMOUSE),m) + M_OBJS += logibusmouse.o endif endif ifeq ($(CONFIG_PRINTER),y) -L_OBJS += lp.o +O_OBJS += lp.o else ifeq ($(CONFIG_PRINTER),m) M_OBJS += lp.o @@ -197,7 +213,7 @@ else endif ifeq ($(CONFIG_JOYSTICK),y) -L_OBJS += joystick/js.o +O_OBJS += joystick/js.o SUB_DIRS += joystick MOD_SUB_DIRS += joystick else @@ -206,16 +222,42 @@ else endif endif +ifeq ($(CONFIG_BUSMOUSE),y) +M = y +OX_OBJS += busmouse.o +else + ifeq ($(CONFIG_BUSMOUSE),m) + MM = m + MX_OBJS += busmouse.o + endif +endif + ifeq ($(CONFIG_DTLK),y) -L_OBJS += dtlk.o +O_OBJS += dtlk.o else ifeq ($(CONFIG_DTLK),m) M_OBJS += dtlk.o endif endif +ifeq ($(CONFIG_R3964),y) +O_OBJS += n_r3964.o +else + ifeq ($(CONFIG_R3964),m) + M_OBJS += n_r3964.o + endif +endif + +ifeq ($(CONFIG_APPLICOM),y) +O_OBJS += applicom.o +else + ifeq ($(CONFIG_APPLICOM),m) + M_OBJS += applicom.o + endif +endif + ifeq ($(CONFIG_MS_BUSMOUSE),y) -L_OBJS += msbusmouse.o +O_OBJS += msbusmouse.o else ifeq ($(CONFIG_MS_BUSMOUSE),m) M_OBJS += msbusmouse.o @@ -223,7 +265,7 @@ else endif ifeq ($(CONFIG_82C710_MOUSE),y) -L_OBJS += qpmouse.o +O_OBJS += qpmouse.o else ifeq ($(CONFIG_82C710_MOUSE),m) M_OBJS += qpmouse.o @@ -231,7 +273,7 @@ else endif ifeq ($(CONFIG_SOFT_WATCHDOG),y) -L_OBJS += softdog.o +O_OBJS += softdog.o else ifeq ($(CONFIG_SOFT_WATCHDOG),m) M_OBJS += softdog.o @@ -239,7 +281,7 @@ else endif ifeq ($(CONFIG_PCWATCHDOG),y) -L_OBJS += pcwd.o +O_OBJS += pcwd.o else ifeq ($(CONFIG_PCWATCHDOG),m) M_OBJS += pcwd.o @@ -247,7 +289,7 @@ else endif ifeq ($(CONFIG_ACQUIRE_WDT),y) -L_OBJS += acquirewdt.o +O_OBJS += acquirewdt.o else ifeq ($(CONFIG_ACQUIRE_WDT),m) M_OBJS += acquirewdt.o @@ -255,7 +297,7 @@ else endif ifeq ($(CONFIG_AMIGAMOUSE),y) -L_OBJS += amigamouse.o +O_OBJS += amigamouse.o else ifeq ($(CONFIG_AMIGAMOUSE),m) M_OBJS += amigamouse.o @@ -263,7 +305,7 @@ else endif ifeq ($(CONFIG_ATARIMOUSE),y) -L_OBJS += atarimouse.o +O_OBJS += atarimouse.o else ifeq ($(CONFIG_ATARIMOUSE),m) M_OBJS += atarimouse.o @@ -271,7 +313,7 @@ else endif ifeq ($(CONFIG_ADBMOUSE),y) -L_OBJS += adbmouse.o +O_OBJS += adbmouse.o else ifeq ($(CONFIG_ADBMOUSE),m) M_OBJS += adbmouse.o @@ -279,7 +321,7 @@ else endif ifeq ($(CONFIG_PC110_PAD),y) -L_OBJS += pc110pad.o +O_OBJS += pc110pad.o else ifeq ($(CONFIG_PC110_PAD),m) M_OBJS += pc110pad.o @@ -287,7 +329,7 @@ else endif ifeq ($(CONFIG_WDT),y) -L_OBJS += wdt.o +O_OBJS += wdt.o else ifeq ($(CONFIG_WDT),m) M_OBJS += wdt.o @@ -295,12 +337,12 @@ else endif ifeq ($(CONFIG_RTC),y) -L_OBJS += rtc.o +O_OBJS += rtc.o endif ifeq ($(CONFIG_NVRAM),y) ifeq ($(CONFIG_PPC),) - L_OBJS += nvram.o + O_OBJS += nvram.o endif else ifeq ($(CONFIG_NVRAM),m) @@ -311,7 +353,7 @@ else endif ifeq ($(CONFIG_VIDEO_DEV),y) -LX_OBJS += videodev.o +OX_OBJS += videodev.o else ifeq ($(CONFIG_VIDEO_DEV),m) MX_OBJS += videodev.o @@ -327,7 +369,7 @@ else endif ifeq ($(CONFIG_VIDEO_BT848),y) -L_OBJS += bttv.o msp3400.o tuner.o +O_OBJS += bttv.o msp3400.o tuner.o L_I2C=y else ifeq ($(CONFIG_VIDEO_BT848),m) @@ -337,7 +379,7 @@ else endif ifeq ($(CONFIG_VIDEO_SAA5249),y) -L_OBJS += saa5249.o +O_OBJS += saa5249.o L_I2C=y else ifeq ($(CONFIG_VIDEO_SAA5249),m) @@ -347,7 +389,7 @@ else endif ifeq ($(CONFIG_I2C_PARPORT),y) -L_OBJS += i2c-parport.o +O_OBJS += i2c-parport.o L_I2C = y else ifeq ($(CONFIG_I2C_PARPORT),m) @@ -357,7 +399,7 @@ else endif ifeq ($(CONFIG_VIDEO_BWQCAM),y) -L_OBJS += bw-qcam.o +O_OBJS += bw-qcam.o else ifeq ($(CONFIG_VIDEO_BWQCAM),m) M_OBJS += bw-qcam.o @@ -365,7 +407,7 @@ else endif ifeq ($(CONFIG_VIDEO_CQCAM),y) -L_OBJS += c-qcam.o +O_OBJS += c-qcam.o else ifeq ($(CONFIG_VIDEO_CQCAM),m) M_OBJS += c-qcam.o @@ -373,15 +415,15 @@ else endif ifeq ($(CONFIG_VIDEO_ZORAN),y) -L_OBJS += buz.o +O_OBJS += buz.o else - ifeq ($(CONFIG_VIDEO_LML33),m) + ifeq ($(CONFIG_VIDEO_ZORAN),m) M_OBJS += buz.o endif endif ifeq ($(CONFIG_VIDEO_LML33),y) -L_OBJS += bt856.o bt819.o +O_OBJS += bt856.o bt819.o else ifeq ($(CONFIG_VIDEO_LML33),m) M_OBJS += bt856.o bt819.o @@ -389,7 +431,7 @@ else endif ifeq ($(CONFIG_VIDEO_BUZ),y) -L_OBJS += saa7111.o saa7185.o +O_OBJS += saa7111.o saa7185.o else ifeq ($(CONFIG_VIDEO_BUZ),m) M_OBJS += saa7111.o saa7185.o @@ -397,7 +439,7 @@ else endif ifeq ($(CONFIG_VIDEO_PMS),y) -L_OBJS += pms.o +O_OBJS += pms.o else ifeq ($(CONFIG_VIDEO_PMS),m) M_OBJS += pms.o @@ -405,7 +447,7 @@ else endif ifeq ($(CONFIG_VIDEO_PLANB),y) -L_OBJS += planb.o +O_OBJS += planb.o else ifeq ($(CONFIG_VIDEO_PLANB),m) M_OBJS += planb.o @@ -413,15 +455,7 @@ else endif ifeq ($(CONFIG_VIDEO_VINO),y) -L_OBJS += vino.o -else - ifeq ($(CONFIG_VIDEO_VINO),m) - M_OBJS += vino.o - endif -endif - -ifeq ($(CONFIG_VIDEO_VINO),y) -L_OBJS += vino.o +O_OBJS += vino.o else ifeq ($(CONFIG_VIDEO_VINO),m) M_OBJS += vino.o @@ -429,7 +463,7 @@ else endif ifeq ($(CONFIG_RADIO_AZTECH),y) -L_OBJS += radio-aztech.o +O_OBJS += radio-aztech.o else ifeq ($(CONFIG_RADIO_AZTECH),m) M_OBJS += radio-aztech.o @@ -437,7 +471,7 @@ else endif ifeq ($(CONFIG_RADIO_SF16FMI),y) -L_OBJS += radio-sf16fmi.o +O_OBJS += radio-sf16fmi.o else ifeq ($(CONFIG_RADIO_SF16FMI),m) M_OBJS += radio-sf16fmi.o @@ -445,7 +479,7 @@ else endif ifeq ($(CONFIG_RADIO_RTRACK),y) -L_OBJS += radio-aimslab.o +O_OBJS += radio-aimslab.o else ifeq ($(CONFIG_RADIO_RTRACK),m) M_OBJS += radio-aimslab.o @@ -453,7 +487,7 @@ else endif ifeq ($(CONFIG_RADIO_RTRACK2),y) -L_OBJS += radio-rtrack2.o +O_OBJS += radio-rtrack2.o else ifeq ($(CONFIG_RADIO_RTRACK2),m) M_OBJS += radio-rtrack2.o @@ -461,7 +495,7 @@ else endif ifeq ($(CONFIG_RADIO_TYPHOON),y) -L_OBJS += radio-typhoon.o +O_OBJS += radio-typhoon.o else ifeq ($(CONFIG_RADIO_TYPHOON),m) M_OBJS += radio-typhoon.o @@ -469,7 +503,7 @@ else endif ifeq ($(CONFIG_RADIO_ZOLTRIX),y) -L_OBJS += radio-zoltrix.o +O_OBJS += radio-zoltrix.o else ifeq ($(CONFIG_RADIO_ZOLTRIX),m) M_OBJS += radio-zoltrix.o @@ -477,7 +511,7 @@ else endif ifeq ($(CONFIG_RADIO_CADET),y) -L_OBJS += radio-cadet.o +O_OBJS += radio-cadet.o else ifeq ($(CONFIG_RADIO_CADET),m) M_OBJS += radio-cadet.o @@ -485,7 +519,7 @@ else endif ifeq ($(CONFIG_RADIO_MIROPCM20),y) -L_OBJS += radio-miropcm20.o +O_OBJS += radio-miropcm20.o else ifeq ($(CONFIG_RADIO_MIROPCM20),m) M_OBJS += radio-miropcm20.o @@ -493,7 +527,7 @@ else endif ifeq ($(CONFIG_RADIO_GEMTEK),y) -L_OBJS += radio-gemtek.o +O_OBJS += radio-gemtek.o else ifeq ($(CONFIG_RADIO_GEMTEK),m) M_OBJS += radio-gemtek.o @@ -501,7 +535,7 @@ else endif ifeq ($(CONFIG_RADIO_TERRATEC),y) -L_OBJS += radio-terratec.o +O_OBJS += radio-terratec.o else ifeq ($(CONFIG_RADIO_TERRATEC),m) M_OBJS += radio-terratec.o @@ -509,7 +543,7 @@ else endif ifeq ($(CONFIG_QIC02_TAPE),y) -L_OBJS += tpqic02.o +O_OBJS += tpqic02.o else ifeq ($(CONFIG_QIC02_TAPE),m) M_OBJS += tpqic02.o @@ -517,7 +551,7 @@ else endif ifeq ($(CONFIG_FTAPE),y) -L_OBJS += ftape/ftape.o +O_OBJS += ftape/ftape.o SUB_DIRS += ftape ifneq ($(CONFIG_ZFTAPE),n) MOD_SUB_DIRS += ftape @@ -529,11 +563,11 @@ else endif ifdef CONFIG_H8 -LX_OBJS += h8.o +OX_OBJS += h8.o endif ifeq ($(CONFIG_PPDEV),y) -L_OBJS += ppdev.o +O_OBJS += ppdev.o else ifeq ($(CONFIG_PPDEV),m) M_OBJS += ppdev.o @@ -541,30 +575,22 @@ else endif ifeq ($(L_I2C),y) -LX_OBJS += i2c.o +OX_OBJS += i2c.o else ifeq ($(M_I2C),y) MX_OBJS += i2c.o endif endif - -ifeq ($(CONFIG_HFMODEM),y) -ALL_SUB_DIRS += hfmodem -SUB_DIRS += hfmodem -L_OBJS += hfmodem/hfmodem.o -else - ifeq ($(CONFIG_HFMODEM),m) - ALL_SUB_DIRS += hfmodem - MOD_SUB_DIRS += hfmodem - endif - -endif - ifeq ($(CONFIG_DZ),y) L_OBJS += dz.o endif +ifeq ($(CONFIG_DRM),y) + ALL_SUB_DIRS += drm + MOD_SUB_DIRS += drm +endif + include $(TOPDIR)/Rules.make fastdep: diff --git a/drivers/char/README.computone b/drivers/char/README.computone new file mode 100644 index 000000000..51c3666b6 --- /dev/null +++ b/drivers/char/README.computone @@ -0,0 +1,195 @@ + +Computone Intelliport II/Plus Multiport Serial Driver +----------------------------------------------------- + +Release Notes For Linux Kernel 2.2 +These notes have been tested on Linux kernels 2.0 and 2.2. + + +Version: 1.2.4 +Date: 08/04/99 +Fixes and Updates: Doug McNash +Historical Author: Andrew Manison + +1. INTRODUCTION + +This driver supports the entire family of Intelliport II/Plus controllers +with the exception of the MicroChannel controllers. + +This driver was developed on the v2.0.x Linux source tree and has been +tested up to v2.2.10; it will probably not work with earlier v1.X kernels, +and has not yet been tested on the v2.1.x tree. The most likely problems +will be in patching the kernel sources to support the driver. For this +reason there are 2 different patch files for 2.0.XX and 2.2.XX kernels. +Make sure you use the right one! +Someday soon it should be included in the 2.3.XX tree. + +2. QUICK INSTALLATION + +Hardware - If you have an ISA card, find a free interrupt and io port. + List those in use with `cat /proc/interrupts` and + `cat /proc/ioports`. Set the card dip switches to that free + address. You may need to configure your BIOS to reserve the + irq for the ISA card. PCI and EISA parameters are set + automagically and need no attention. Insert card into + computer with the power off before or after driver installation. + +Software - + +Module installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (m) module for CONFIG_COMPUTONE under character + devices. CONFIG_PCI and CONFIG_MODULES also may need to be set. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h if needed + or + edit /etc/conf.modules (or /etc/modules.conf) if needed (module). + or both to match this setting. +f) Run "make dep" +g) Run "make modules" +h) Run "make modules_install" +i) Run "/sbin/depmod -a" +i) install driver using `modprobe ip2 <options>` (options listed below) +j) run mkip2dev + + +Kernel installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (y) kernel for CONFIG_COMPUTONE under character + devices. CONFIG_PCI may need to be set if you have PCI bus. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h +f) Run "make dep" +g) Run "make zImage" or whatever target you prefer. +h) mv /usr/src/linux/arch/i386/boot/zImage to /boot. +i) add new config for this kernel into /etc/lilo.conf, run "lilo" +j) reboot using this kernel +k) make and run ip2/mkip2dev + +3. INSTALLATION + +Previously, the driver sources were packaged with a set of patch files +to update the character drivers' makefile and configuration file, and other +kernel source files. A build script (ip2build) was included which applies +the patches if needed, and build any utilities needed. +What you recieve may be a single patch file in conventional kernel +patch format build script. That form can also be applied by +running patch -p1 < ThePatchFile. Otherwise run ip2build. + +The driver can be installed as a module (recommended) or built into the +kernel. This is selected as for other drivers through the `make config` +command from the root of the Linux source tree. If the driver is built +into the kernel you will need to edit the file ip2.h to match the boards +you are installing. See that file for instructions. If the driver is +installed as a module the configuration can also be specified on the +modprobe command line as follows: + + modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4 + +where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11, +12,15) and addr1-4 are the base addresses for up to four controllers. If +the irqs are not specified the driver uses the default in ip2/ip2.h (which +selects polled mode). If no base addresses are specified the defaults in +ip2.h are used. If you are autoloading the driver module with kerneld or +kmod the base addresses and interrupt number must also be set in ip2/ip2.h +and recompile or just insert and options line in /etc/modules.conf or both. +The options line is equivalent to the command line and takes precidence over +what is in ip2.h. + +/etc/modules.conf sample: + options ip2 io=1,0x328 irq=1,10 + alias char-major-71 ip2 + alias char-major-72 ip2 + alias char-major-73 ip2 + +equivelant ip2.h: +static ip2config_t ip2config = +{ + {1,10,0,0}, + { + 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */ + 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */ + 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */ + 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */ + } +}; + +Specifying an invalid or in-use irq will default the driver into +running in polled mode for that card. If all irq entries are 0 then +all cards will operate in polled mode. + +Tarball Install: + +The whole tarfile should be untarred in the /usr/src/linux/drivers/char/ +directory. Most files required for the driver are placed in the ip2 +subdirectory. Then execute the script + + ip2build + +which will patch the files. + +Kernel Patch Install: + + cd to the Linux source root, run patch -p1 < ThePatchFile. + +Now return to the root directory of the Linux +source tree and run make config or make menuconfig. You will be prompted +for the Computone drivers, either as a module or part of the kernel. +If you have a PCI card you many need to select PCI bios support (CONFIG_PCI) +if not enabled already. Ditto for CONFIG_MODULES if you use modules. + +If you select the driver as part of the kernel run : + + make depend + make zlilo (or whatever you do to create a bootable kernel) + +If you selected a module run : + + make modules && make modules_install + +The utility ip2mkdev creates all the device nodes required by the driver. +For a device to be created it must be configured in the driver and the +board must be installed. Only devices corresponding to real IntelliPort II +ports are created. With multiple boards and expansion boxes this will +leave gaps in the sequence of device names. ip2mkdev uses Linux tty naming +conventions: ttyF0 - ttyF255 for normal devices, and cuf0 - cuf255 for +callout devices. + +4. USING THE DRIVERS + +As noted above, the driver implements the ports in accordance with Linux +conventions, and the devices should be interchangeable with the standard +serial devices. (This is a key point for problem reporting: please make +sure that what you are trying do works on the ttySx/cuax ports first; then +tell us what went wrong with the ip2 ports!) + +Higher speeds can be obtained using the setserial utility which remaps +38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. +Intelliport II installations using the PowerPort expansion module can +use the custom speed setting to select the highest speeds: 153,600 bps, +230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for +custom baud rate configuration is fixed at 921,600 for cards/expantion +modules with ST654's and 115200 for those with Cirrus CD1400's. This +corresponds to the maximum bit rates those chips are capable. +For example if the baud base is 921600 and the baud divisor is 18 then +the custom rate is 921600/18 = 51200 bps. See the setserial man page for +complete details. Of course if stty accepts the higher rates now you can +use that as well as the standard ioctls(). + +5. NOTES + +This is a release version of the driver, but it is impossible to test it +in all configurations of Linux. If there is any anomalous behaviour that +does not match the standard serial port's behaviour please let us know. + +Author: dmcnash@computine.com +Testing: larryg@computone.com +Spport: support@computone.com diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c index 24ed4d9d9..eaf1a8357 100644 --- a/drivers/char/adbmouse.c +++ b/drivers/char/adbmouse.c @@ -21,6 +21,9 @@ * 1996/02/11 Andreas Schwab * Module support * Allow multiple open's + * + * Converted to use new generic busmouse code. 11 July 1998 + * Russell King <rmk@arm.uk.linux.org> */ #include <linux/module.h> @@ -34,233 +37,136 @@ #include <linux/init.h> #include <asm/adb_mouse.h> -#include <asm/uaccess.h> #ifdef __powerpc__ #include <asm/processor.h> #endif +#if defined(__mc68000__) || defined(MODULE) #include <asm/setup.h> +#endif + +#include "busmouse.h" -static struct mouse_status mouse; +static int msedev; static unsigned char adb_mouse_buttons[16]; extern void (*adb_mouse_interrupt_hook)(unsigned char *, int); extern int adb_emulate_buttons; extern int adb_button2_keycode; extern int adb_button3_keycode; - extern int console_loglevel; /* - * XXX: need to figure out what ADB mouse packets mean ... - * This is the stuff stolen from the Atari driver ... + * XXX: need to figure out what ADB mouse packets mean ... + * This is the stuff stolen from the Atari driver ... */ static void adb_mouse_interrupt(unsigned char *buf, int nb) { - int buttons, id; + int buttons, id; + char dx, dy; -/* - Handler 1 -- 100cpi original Apple mouse protocol. - Handler 2 -- 200cpi original Apple mouse protocol. - - For Apple's standard one-button mouse protocol the data array will - contain the following values: - - BITS COMMENTS - data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. - data[1] = bxxx xxxx First button and x-axis motion. - data[2] = byyy yyyy Second button and y-axis motion. - - Handler 4 -- Apple Extended mouse protocol. - - For Apple's 3-button mouse protocol the data array will contain the - following values: - - BITS COMMENTS - data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. - data[1] = bxxx xxxx Left button and x-axis motion. - data[2] = byyy yyyy Second button and y-axis motion. - data[3] = byyy bxxx Third button and fourth button. - Y is additional high bits of y-axis motion. - X is additional high bits of x-axis motion. - - This procedure also gets called from the keyboard code if we - are emulating mouse buttons with keys. In this case data[0] == 0 - (data[0] cannot be 0 for a real ADB packet). - - 'buttons' here means 'button down' states! - Button 1 (left) : bit 2, busmouse button 3 - Button 2 (middle): bit 1, busmouse button 2 - Button 3 (right) : bit 0, busmouse button 1 -*/ - - /* x/y and buttons swapped */ - - if (console_loglevel >= 8) - printk("KERN_DEBUG adb_mouse: %s data; ", buf[0]? "real": "fake"); - - id = (buf[0] >> 4) & 0xf; - buttons = adb_mouse_buttons[id]; - - /* button 1 (left, bit 2) */ - buttons = (buttons&3) | (buf[1] & 0x80 ? 4 : 0); /* 1+2 unchanged */ - - /* button 2 (middle) */ - buttons = (buttons&5) | (buf[2] & 0x80 ? 2 : 0); /* 2+3 unchanged */ - - /* button 3 (right) present? - * on a logitech mouseman, the right and mid buttons sometimes behave - * strangely until they both have been pressed after booting. */ - /* data valid only if extended mouse format ! */ - if (nb >= 4) - buttons = (buttons&6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */ - - add_mouse_randomness(((~buttons&7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f)); - - adb_mouse_buttons[id] = buttons; - /* a button is down if it is down on any mouse */ - for (id = 0; id < 16; ++id) - buttons &= adb_mouse_buttons[id]; - - mouse.buttons = buttons; - mouse.dx += ((buf[2]&0x7f) < 64 ? (buf[2]&0x7f) : (buf[2]&0x7f)-128 ); - mouse.dy -= ((buf[1]&0x7f) < 64 ? (buf[1]&0x7f) : (buf[1]&0x7f)-128 ); - - if (console_loglevel >= 8) - printk(" %X %X %X buttons %x dx %d dy %d \n", - buf[1], buf[2], buf[3], mouse.buttons, mouse.dx, mouse.dy); - - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - if (mouse.fasyncptr) - kill_fasync(mouse.fasyncptr, SIGIO); -} + /* + Handler 1 -- 100cpi original Apple mouse protocol. + Handler 2 -- 200cpi original Apple mouse protocol. -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; + For Apple's standard one-button mouse protocol the data array will + contain the following values: - retval = fasync_helper(fd, filp, on, &mouse.fasyncptr); - if (retval < 0) - return retval; - return 0; -} + BITS COMMENTS + data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. + data[1] = bxxx xxxx First button and x-axis motion. + data[2] = byyy yyyy Second button and y-axis motion. -static int release_mouse(struct inode *inode, struct file *file) -{ - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; + Handler 4 -- Apple Extended mouse protocol. - adb_mouse_interrupt_hook = NULL; - MOD_DEC_USE_COUNT; - return 0; -} + For Apple's 3-button mouse protocol the data array will contain the + following values: -static int open_mouse(struct inode *inode, struct file *file) -{ - int id; + BITS COMMENTS + data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. + data[1] = bxxx xxxx Left button and x-axis motion. + data[2] = byyy yyyy Second button and y-axis motion. + data[3] = byyy bxxx Third button and fourth button. + Y is additiona. high bits of y-axis motion. + X is additional high bits of x-axis motion. - if (mouse.active++) - return 0; - - mouse.ready = 0; - - mouse.dx = mouse.dy = 0; - for (id = 0; id < 16; ++id) - adb_mouse_buttons[id] = 7; /* all buttons up */ - MOD_INC_USE_COUNT; - adb_mouse_interrupt_hook = adb_mouse_interrupt; - return 0; -} + 'buttons' here means 'button down' states! + Button 1 (left) : bit 2, busmouse button 3 + Button 2 (right) : bit 0, busmouse button 1 + Button 3 (middle): bit 1, busmouse button 2 + */ -static ssize_t write_mouse(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; + /* x/y and buttons swapped */ + + id = (buf[0] >> 4) & 0xf; + + buttons = adb_mouse_buttons[id]; + + /* button 1 (left, bit 2) */ + buttons = (buttons & 3) | (buf[1] & 0x80 ? 4 : 0); /* 1+2 unchanged */ + + /* button 2 (middle) */ + buttons = (buttons & 5) | (buf[2] & 0x80 ? 2 : 0); /* 2+3 unchanged */ + + /* button 3 (right) present? + * on a logitech mouseman, the right and mid buttons sometimes behave + * strangely until they both have been pressed after booting. */ + /* data valid only if extended mouse format ! */ + if (nb >= 4) + buttons = (buttons & 6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */ + + adb_mouse_buttons[id] = buttons; + + /* a button is down if it is down on any mouse */ + for (id = 0; id < 16; ++id) + buttons &= adb_mouse_buttons[id]; + + dx = ((buf[2] & 0x7f) < 64 ? (buf[2] & 0x7f) : (buf[2] & 0x7f) - 128); + dy = ((buf[1] & 0x7f) < 64 ? (buf[1] & 0x7f) : (buf[1] & 0x7f) - 128); + busmouse_add_movementbuttons(msedev, dx, -dy, buttons); + + if (console_loglevel >= 8) + printk(" %X %X %X dx %d dy %d \n", + buf[1], buf[2], buf[3], dx, dy); } -static ssize_t read_mouse(struct file *file, char *buffer, size_t count, - loff_t *ppos) +static int release_mouse(struct inode *inode, struct file *file) { - int dx, dy, buttons; - - if (count < 3) - return -EINVAL; - if (!mouse.ready) - return -EAGAIN; - dx = mouse.dx; - dy = mouse.dy; - buttons = mouse.buttons; - if (dx > 127) - dx = 127; - else if (dx < -128) - dx = -128; - if (dy > 127) - dy = 127; - else if (dy < -128) - dy = -128; - mouse.dx -= dx; - mouse.dy -= dy; - if (mouse.dx == 0 && mouse.dy == 0) - mouse.ready = 0; - if (put_user(buttons | 0x80, buffer++) || - put_user((char) dx, buffer++) || - put_user((char) dy, buffer++)) - return -EFAULT; - if (count > 3) - if (clear_user(buffer, count - 3)) - return -EFAULT; - return count; + adb_mouse_interrupt_hook = NULL; + MOD_DEC_USE_COUNT; + return 0; } -static unsigned int mouse_poll(struct file *file, poll_table *wait) +static int open_mouse(struct inode *inode, struct file *file) { - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) - return POLLIN | POLLRDNORM; + MOD_INC_USE_COUNT; + adb_mouse_interrupt_hook = adb_mouse_interrupt; return 0; } -struct file_operations adb_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -#define ADB_MOUSE_MINOR 10 - -static struct miscdevice adb_mouse = { - ADB_MOUSE_MINOR, "adbmouse", &adb_mouse_fops +static struct busmouse adb_mouse = +{ + ADB_MOUSE_MINOR, "adbmouse", open_mouse, release_mouse, 7 }; int __init adb_mouse_init(void) { - mouse.active = 0; - mouse.ready = 0; - init_waitqueue_head(&mouse.wait); - #ifdef __powerpc__ - if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return -ENODEV; + if ((_machine != _MACH_chrp) && (_machine != _MACH_Pmac)) + return -ENODEV; #endif #ifdef __mc68000__ - if (!MACH_IS_MAC) - return -ENODEV; + if (!MACH_IS_MAC) + return -ENODEV; #endif - printk(KERN_INFO "Macintosh ADB mouse driver installed.\n"); - misc_register(&adb_mouse); - return 0; -} + msedev = register_busmouse(&adb_mouse); + if (msedev < 0) + printk(KERN_WARNING "Unable to register ADB mouse driver.\n"); + else + printk(KERN_INFO "Macintosh ADB mouse driver installed.\n"); + + return msedev < 0 ? msedev : 0; +} /* * XXX this function is misnamed. @@ -268,26 +174,32 @@ int __init adb_mouse_init(void) * option, which is about using ADB keyboard buttons to emulate * mouse buttons. -- paulus */ -void __init adb_mouse_setup(char *str, int *ints) +static int __init adb_mouse_setup(char *str) { + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); if (ints[0] >= 1) { - adb_emulate_buttons = ints[1] > 0; - if (ints[1] > 1) - adb_button2_keycode = ints[1]; + adb_emulate_buttons = ints[1]; if (ints[0] >= 2) - adb_button3_keycode = ints[2]; + adb_button2_keycode = ints[2]; + if (ints[0] >= 3) + adb_button3_keycode = ints[3]; } + return 1; } -#ifdef MODULE +__setup("adb_buttons=", adb_mouse_setup); +#ifdef MODULE int init_module(void) { - return adb_mouse_init(); + return adb_mouse_init(); } void cleanup_module(void) { - misc_deregister(&adb_mouse); + unregister_busmouse(msedev); } + #endif diff --git a/drivers/char/amigamouse.c b/drivers/char/amigamouse.c index 70e20b887..f3f0b372d 100644 --- a/drivers/char/amigamouse.c +++ b/drivers/char/amigamouse.c @@ -30,6 +30,9 @@ * Moved the isr-allocation to the mouse_{open,close} calls, as there * is no reason to service the mouse in the vertical blank isr if * the mouse is not in use. Jes Sorensen + * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> */ #include <linux/module.h> @@ -44,7 +47,7 @@ #include <linux/random.h> #include <linux/poll.h> #include <linux/init.h> -#include <linux/busmouse.h> +#include <linux/logibusmouse.h> #include <asm/setup.h> #include <asm/system.h> @@ -53,13 +56,15 @@ #include <asm/amigahw.h> #include <asm/amigaints.h> +#include "busmouse.h" + +#if AMIGA_OLD_INT #define AMI_MSE_INT_ON() mouseint_allowed = 1 #define AMI_MSE_INT_OFF() mouseint_allowed = 0 - - -static struct mouse_status mouse; - static int mouseint_allowed; +#endif + +static int msedev; static void mouse_interrupt(int irq, void *dummy, struct pt_regs *fp) { @@ -70,9 +75,11 @@ static void mouse_interrupt(int irq, void *dummy, struct pt_regs *fp) unsigned short joy0dat, potgor; +#if AMIGA_OLD_INT if(!mouseint_allowed) return; AMI_MSE_INT_OFF(); +#endif /* * This routine assumes, just like Kickstart, that the mouse @@ -128,44 +135,10 @@ static void mouse_interrupt(int irq, void *dummy, struct pt_regs *fp) (potgor & 0x0400 ? 1 : 0); /* right button */ - if (dx != 0 || dy != 0 || buttons != mouse.buttons) { - add_mouse_randomness((buttons << 16) + (dy << 8) + dx); - mouse.buttons = buttons; - mouse.dx += dx; - mouse.dy -= dy; - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - - /* - * keep dx/dy reasonable, but still able to track when X (or - * whatever) must page or is busy (i.e. long waits between - * reads) - */ - if (mouse.dx < -2048) - mouse.dx = -2048; - else - if (mouse.dx > 2048) - mouse.dx = 2048; - - if (mouse.dy < -2048) - mouse.dy = -2048; - else - if (mouse.dy > 2048) - mouse.dy = 2048; - - if (mouse.fasyncptr) - kill_fasync(mouse.fasyncptr, SIGIO); - } + busmouse_add_movementbuttons(msedev, dx, -dy, buttons); +#if AMIGA_OLD_INT AMI_MSE_INT_ON(); -} - -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - retval = fasync_helper(fd, filp, on, &mouse.fasyncptr); - if (retval < 0) - return retval; - return 0; +#endif } /* @@ -174,11 +147,10 @@ static int fasync_mouse(int fd, struct file *filp, int on) static int release_mouse(struct inode * inode, struct file * file) { - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; free_irq(IRQ_AMIGA_VERTB, mouse_interrupt); +#if AMIGA_OLD_INT AMI_MSE_INT_OFF(); +#endif MOD_DEC_USE_COUNT; return 0; } @@ -190,123 +162,25 @@ static int release_mouse(struct inode * inode, struct file * file) static int open_mouse(struct inode * inode, struct file * file) { - if (!mouse.present) - return -EINVAL; - if (mouse.active++) - return 0; /* * use VBL to poll mouse deltas */ if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0, "Amiga mouse", mouse_interrupt)) { - mouse.present = 0; printk(KERN_INFO "Installing Amiga mouse failed.\n"); return -EIO; } - mouse.ready = 0; - mouse.dx = 0; - mouse.dy = 0; - mouse.buttons = 0x87; - mouse.active = 1; MOD_INC_USE_COUNT; +#if AMIGA_OLD_INT AMI_MSE_INT_ON(); +#endif return 0; } -/* - * writes are disallowed - */ - -static ssize_t write_mouse(struct file * file, const char * buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/* - * read mouse data. Currently never blocks. - */ - -static ssize_t read_mouse(struct file * file, char * buffer, - size_t count, loff_t *ppos) -{ - int dx; - int dy; - unsigned char buttons; - - if (count < 3) - return -EINVAL; - if (!mouse.ready) - return -EAGAIN; - - /* - * Obtain the current mouse parameters and limit as appropriate for - * the return data format. Interrupts are only disabled while - * obtaining the parameters, NOT during the puts_user() calls, - * so paging in put_user() does not effect mouse tracking. - */ - - AMI_MSE_INT_OFF(); - dx = mouse.dx; - dy = mouse.dy; - if (dx < -127) - dx = -127; - else - if (dx > 127) - dx = 127; - if (dy < -127) - dy = -127; - else - if (dy > 127) - dy = 127; - buttons = mouse.buttons; - mouse.dx -= dx; - mouse.dy -= dy; - mouse.ready = 0; - AMI_MSE_INT_ON(); - - if (put_user(buttons | 0x80, buffer++) || - put_user((char)dx, buffer++) || - put_user((char)dy, buffer++)) - return -EINVAL; - - if (count > 3) - if (clear_user(buffer, count - 3)) - return -EFAULT; - return count; -} - -/* - * poll for mouse input - */ - -static unsigned int mouse_poll(struct file *file, poll_table * wait) -{ - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations amiga_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -static struct miscdevice amiga_mouse = { - AMIGAMOUSE_MINOR, "amigamouse", &amiga_mouse_fops +static struct busmouse amigamouse = { + AMIGAMOUSE_MINOR, "amigamouse", open_mouse, release_mouse, 7 }; int __init amiga_mouse_init(void) @@ -315,32 +189,23 @@ int __init amiga_mouse_init(void) return -ENODEV; custom.joytest = 0; /* reset counters */ - +#if AMIGA_OLD_INT AMI_MSE_INT_OFF(); - - mouse.active = 0; - mouse.ready = 0; - mouse.buttons = 0x87; - mouse.dx = 0; - mouse.dy = 0; - mouse.wait = NULL; - - mouse.present = 1; - - printk(KERN_INFO "Amiga mouse installed.\n"); - misc_register(&amiga_mouse); - return 0; +#endif + msedev = register_busmouse(&amigamouse); + if (msedev < 0) + printk(KERN_WARNING "Unable to install Amiga mouse driver.\n"); + else + printk(KERN_INFO "Amiga mouse installed.\n"); + return msedev < 0 ? msedev : 0; } -#ifdef MODULE - -int init_module(void) +void __exit amiga_mouse_exit(void) { - return amiga_mouse_init(); + unregister_busmouse(msedev); } -void cleanup_module(void) -{ - misc_deregister(&amiga_mouse); -} +#ifdef MODULE +module_init(amiga_mouse_init) +module_exit(amiga_mouse_exit) #endif diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c index d9ebd1084..42e1ea7c8 100644 --- a/drivers/char/amikeyb.c +++ b/drivers/char/amikeyb.c @@ -257,7 +257,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; add_timer(&amikeyb_rep_timer); } - handle_scancode(scancode, !break_flag); + handle_scancode(keycode, !break_flag); } else switch (keycode) { case 0x78: @@ -341,8 +341,3 @@ int amiga_kbdrate( struct kbd_repeat *k ) return( 0 ); } - -/* for "kbd-reset" cmdline param */ -void __init amiga_kbd_reset_setup(char *str, int *ints) -{ -} diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c new file mode 100644 index 000000000..c311e58cd --- /dev/null +++ b/drivers/char/applicom.c @@ -0,0 +1,842 @@ +/* Derived from Applicom driver ac.c for SCO Unix */ +/* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ +/* Dave@mvhi.com 30/8/98 */ +/* $Id: ac.c,v 1.16 1999/08/28 15:11:50 dwmw2 Exp $ */ +/* This module is for Linux 2.1 and 2.2 series kernels. */ +/*****************************************************************************/ +/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt */ +/* ceci pour reseter correctement apres une sortie sauvage */ +/* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption, */ +/* LoopCount n'etait pas initialise a 0. */ +/* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte */ +/* pour liberer le bus */ +/* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */ +/* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter */ +/* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3 */ +/* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes */ +/* adresses de base des cartes, IOCTL 6 plus complet */ +/* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification */ +/* de code autre que le texte V2.6.1 en V2.8.0 */ +/*****************************************************************************/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/wait.h> +#include <linux/init.h> +#include "applicom.h" +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +#undef DEBUG +#define DEVPRIO PZERO+8 +#define FALSE 0 +#define TRUE ~FALSE +#define MAX_BOARD 8 /* maximum of pc board possible */ +#define MAX_ISA_BOARD 4 +#define LEN_RAM_IO 0x800 +#define AC_MINOR 157 + +#ifndef PCI_VENDOR_ID_APPLICOM +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 +#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 +#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 +#define MAX_PCI_DEVICE_NUM 3 +#endif + +static char *applicom_pci_devnames[]={ + "PCI board", "PCI2000IBS / PCI2000CAN", "PCI2000PFB"}; + +MODULE_AUTHOR("David Woodhouse & Applicom International"); +MODULE_DESCRIPTION("Driver for Applicom Profibus card"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "IRQ of the Applicom board"); +MODULE_PARM(mem,"i"); +MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board"); + +MODULE_SUPPORTED_DEVICE("ac"); + + +struct applicom_board { + unsigned long PhysIO; + unsigned long RamIO; +#if LINUX_VERSION_CODE > 0x20300 + wait_queue_head_t FlagSleepSend; +#else + struct wait_queue *FlagSleepSend; +#endif + long irq; +} apbs[MAX_BOARD]; + +static unsigned int irq=0; /* interrupt number IRQ */ +static unsigned long mem=0; /* physical segment of board */ + +static unsigned int numboards; /* number of installed boards */ +static volatile unsigned char Dummy; +#if LINUX_VERSION_CODE > 0x20300 +static DECLARE_WAIT_QUEUE_HEAD (FlagSleepRec); +#else +static struct wait_queue *FlagSleepRec; +#endif +static unsigned int WriteErrorCount; /* number of write error */ +static unsigned int ReadErrorCount; /* number of read error */ +static unsigned int DeviceErrorCount; /* number of device error */ + +static loff_t ac_llseek(struct file *file, loff_t offset, int origin); +static int ac_open(struct inode *inode, struct file *filp); +static ssize_t ac_read (struct file *filp, char *buf, size_t count, loff_t *ptr); +static ssize_t ac_write (struct file *file, const char *buf, size_t count, loff_t *ppos); +static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int ac_release(struct inode *inode, struct file *file); +static void ac_interrupt(int irq, void *dev_instance, struct pt_regs *regs); + +struct file_operations ac_fops={ + ac_llseek, /* llseek */ + ac_read, /* read */ + ac_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + ac_ioctl, /* ioctl */ + NULL, /* mmap */ + ac_open, /* open */ + NULL, /* flush */ + ac_release, /* release */ + NULL /* fsync */ +}; + +struct miscdevice ac_miscdev={ + AC_MINOR, + "ac", + &ac_fops +}; + +int ac_register_board(unsigned long physloc, unsigned long loc, + unsigned char boardno) +{ + volatile unsigned char byte_reset_it; + + if((readb(loc + CONF_END_TEST) != 0x00) || + (readb(loc + CONF_END_TEST + 1) != 0x55) || + (readb(loc + CONF_END_TEST + 2) != 0xAA) || + (readb(loc + CONF_END_TEST + 3) != 0xFF)) + return 0; + + + if (!boardno) + boardno = readb(loc + NUMCARD_OWNER_TO_PC); + + if (!boardno && boardno > MAX_BOARD) + { + printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n",boardno, physloc, MAX_BOARD); + return 0; + } + + if (apbs[boardno-1].RamIO) + { + printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", + boardno, physloc, boardno, apbs[boardno-1].PhysIO); + return 0; + } + + boardno--; + + apbs[boardno].PhysIO = physloc; + apbs[boardno].RamIO = loc; +#if LINUX_VERSION_CODE > 0x20300 + init_waitqueue_head(&apbs[boardno].FlagSleepSend); +#else + apbs[boardno].FlagSleepSend = NULL; +#endif + byte_reset_it = readb(loc + RAM_IT_TO_PC); + + numboards++; + return boardno+1; +} + +#ifdef MODULE + +#define applicom_init init_module + +void cleanup_module(void) +{ + int i; + + misc_deregister(&ac_miscdev); + for (i=0; i< MAX_BOARD; i++) + { + if (!apbs[i].RamIO) + continue; + iounmap((void *)apbs[i].RamIO); + if (apbs[i].irq) + free_irq(apbs[i].irq,&ac_open); + } + // printk("Removing Applicom module\n"); +} + +#endif /* MODULE */ + +int __init applicom_init(void) +{ + int i, numisa=0; + struct pci_dev *dev = NULL; + void *RamIO; + int boardno; +#if LINUX_VERSION_CODE > 0x20300 +#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start) +#else +#define PCI_BASE_ADDRESS(dev) (dev->base_address[0]) +#endif + + printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.16 1999/08/28 15:11:50 dwmw2 Exp $\n"); + + /* No mem and irq given - check for a PCI card */ + + while ( (dev = pci_find_device(PCI_VENDOR_ID_APPLICOM, 1, dev))) + { + // mem = dev->base_address[0]; + // irq = dev->irq; + + RamIO = ioremap(PCI_BASE_ADDRESS(dev), LEN_RAM_IO); + + if (!RamIO) { + printk(KERN_INFO "ac.o: Failed to ioremap PCI memory space at 0x%lx\n", PCI_BASE_ADDRESS(dev)); + return -EIO; + } + + printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n", + applicom_pci_devnames[dev->device-1], PCI_BASE_ADDRESS(dev), + dev->irq); + + if (!(boardno = ac_register_board(PCI_BASE_ADDRESS(dev), + (unsigned long)RamIO,0))) + { + printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n"); + iounmap(RamIO); + continue; + } + + if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &ac_open)) + { + printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); + iounmap(RamIO); + apbs[boardno-1].RamIO = 0; + continue; + } + + /* Enable interrupts. */ + + writeb(0x40, apbs[boardno-1].RamIO + RAM_IT_FROM_PC); + + apbs[boardno-1].irq = dev->irq; + } + + /* Finished with PCI cards. If none registered, + * and there was no mem/irq specified, exit */ + + if (!mem || !irq) + { + if (numboards) + goto fin; + else + { + printk(KERN_INFO "ac.o: No PCI boards found.\n"); + printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n"); + return -ENXIO; + } + } + + /* Now try the specified ISA cards */ + + RamIO = ioremap(mem, LEN_RAM_IO * MAX_ISA_BOARD); + + if (!RamIO) { + printk(KERN_INFO "ac.o: Failed to ioremap ISA memory space at 0x%lx\n",mem); + } + + for (i=0; i< MAX_ISA_BOARD; i++) + { + RamIO = ioremap(mem+ (LEN_RAM_IO*i), LEN_RAM_IO); + + if (!RamIO) { + printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n",i+1); + continue; + } + + if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i), + (unsigned long)RamIO,i+1))) { + iounmap(RamIO); + continue; + } + + printk("Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq); + + if (!numisa) + { + if (request_irq(irq, &ac_interrupt, SA_SHIRQ, "Applicom ISA", &ac_open)) + { + printk("Could not allocate IRQ %d for ISA Applicom device.\n", irq); + iounmap((void *)RamIO); + apbs[boardno-1].RamIO = 0; + } + apbs[boardno-1].irq=irq; + } + else + apbs[boardno-1].irq=0; + + numisa++; + } + + if (!numisa) + printk("ac.o: No valid ISA Applicom boards found at mem 0x%lx\n",mem); + +#if LINUX_VERSION_CODE > 0x20300 + init_waitqueue_head(&FlagSleepRec); +#else + FlagSleepRec = NULL; +#endif + WriteErrorCount = 0; + ReadErrorCount = 0; + DeviceErrorCount = 0; + +fin: + if (numboards) + { + misc_register (&ac_miscdev); + for (i=0; i<MAX_BOARD; i++) + { + int serial; + char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; + + if (!apbs[i].RamIO) + continue; + + for(serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) + boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); + boardname[serial]=0; + + + printk("Applicom board %d: %s, PROM V%d.%d", + i+1, boardname, + (int)(readb(apbs[i].RamIO + VERS) >> 4), + (int)(readb(apbs[i].RamIO + VERS) & 0xF)); + + serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); + + if (serial != 0) + printk (" S/N %d\n", serial); + else + printk("\n"); + } + return 0; + } + + else + return -ENXIO; +} + + +#ifndef MODULE +__initcall (applicom_init); +#endif + +static loff_t ac_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int ac_open(struct inode *inode, struct file *filp) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int ac_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + + +static ssize_t ac_write (struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + unsigned int NumCard; /* Board number 1 -> 8 */ + unsigned int IndexCard; /* Index board number 0 -> 7 */ + unsigned char TicCard; /* Board TIC to send */ + unsigned long flags; /* Current priority */ + struct st_ram_io st_loc; + struct mailbox tmpmailbox; + + if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { + printk("Hmmm. write() of Applicom card, length %d != expected %d\n",count,sizeof(struct st_ram_io) + sizeof(struct mailbox)); + return -EINVAL; + } + + if(copy_from_user (&st_loc, buf, sizeof(struct st_ram_io))) { + return -EFAULT; + } + if(copy_from_user (&tmpmailbox, &buf[sizeof(struct st_ram_io)], sizeof(struct mailbox))) { + return -EFAULT; + } + + NumCard = st_loc.num_card; /* board number to send */ + TicCard = st_loc.tic_des_from_pc; /* tic number to send */ + IndexCard = NumCard -1; + if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO) + { /* User board number not OK */ + // printk("Write to invalid Applicom board %d\n", NumCard); + return -EINVAL; /* Return error code user buffer */ + } + +#ifdef DEBUG + { + int c; + + printk("Write to applicom card #%d. struct st_ram_io follows:",NumCard); + + + + for (c=0; c< sizeof(struct st_ram_io);) + { + printk("\n%5.5X: %2.2X",c,((unsigned char *)&st_loc)[c]); + + for (c++ ; c%8 && c<sizeof(struct st_ram_io); c++) + { + printk(" %2.2X",((unsigned char *)&st_loc)[c]); + } + } + + printk("\nstruct mailbox follows:"); + + for (c=0; c< sizeof(struct mailbox);) + { + printk("\n%5.5X: %2.2X",c,((unsigned char *)&tmpmailbox)[c]); + + for (c++ ; c%8 && c<sizeof(struct mailbox); c++) + { + printk(" %2.2X",((unsigned char *)&tmpmailbox)[c]); + } + } + + printk("\n"); + } + +#endif + + save_flags (flags); + cli(); /* disable interrupt */ + + if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) /* Test octet ready correct */ + { + Dummy = readb(apbs[IndexCard].RamIO + VERS); + restore_flags(flags); + printk("APPLICOM driver write error board %d, DataFromPcReady = %d\n", + IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY)); + DeviceErrorCount++; + return -EIO; + } + while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) + { + Dummy = readb(apbs[IndexCard].RamIO + VERS); + restore_flags(flags); + interruptible_sleep_on (&apbs[IndexCard].FlagSleepSend); + if (signal_pending(current)) + return -EINTR; + save_flags(flags); + cli(); + } + writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); + + // memcpy_toio ((void *)apbs[IndexCard].PtrRamFromPc, (void *)&tmpmailbox, sizeof(struct mailbox)); + { + unsigned char *from = (unsigned char *)&tmpmailbox; + unsigned long to = (unsigned long)apbs[IndexCard].RamIO + RAM_FROM_PC; + int c; + + for (c=0; c<sizeof(struct mailbox) ; c++) + writeb(*(from++), to++); + } + writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC); + writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC); + writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC); + writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); + writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); + writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); + Dummy = readb(apbs[IndexCard].RamIO + VERS); + restore_flags (flags); + return 0; +} + +static ssize_t ac_read (struct file *filp, char *buf, size_t count, loff_t *ptr) +{ + unsigned int NumCard; /* board number 1 -> 8 */ + unsigned int IndexCard; /* index board number 0 -> 7 */ + unsigned long flags; + unsigned int i; + unsigned char tmp=0; + struct st_ram_io st_loc; + struct mailbox tmpmailbox; /* bounce buffer - can't copy to user space with cli() */ + + + if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { + printk("Hmmm. read() of Applicom card, length %d != expected %d\n",count,sizeof(struct st_ram_io) + sizeof(struct mailbox)); + return -EINVAL; + } + + save_flags(flags); + cli(); + + i = 0; + + while (tmp != 2) + { + for (i=0; i < MAX_BOARD; i++) + { + if (!apbs[i].RamIO) + continue; + + tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY); + + if (tmp == 2) + break; + + if (tmp > 2) /* Test octet ready correct */ + { + Dummy = readb(apbs[i].RamIO + VERS); + restore_flags(flags); + printk("APPLICOM driver read error board %d, DataToPcReady = %d\n", + i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); + DeviceErrorCount++; + return -EIO; + } + Dummy = readb(apbs[i].RamIO + VERS); + + } + if (tmp != 2) + { + restore_flags(flags); + interruptible_sleep_on (&FlagSleepRec); + if (signal_pending(current)) + return -EINTR; + save_flags(flags); + cli(); + } + } + + IndexCard = i; + NumCard = i+1; + st_loc.tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC); + st_loc.numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); + + + // memcpy_fromio(&tmpmailbox, apbs[IndexCard].PtrRamToPc, sizeof(struct mailbox)); + { + unsigned long from = (unsigned long)apbs[IndexCard].RamIO + RAM_TO_PC; + unsigned char *to = (unsigned char *)&tmpmailbox; + int c; + + for (c=0; c<sizeof(struct mailbox) ; c++) + *(to++) = readb(from++); + } + writeb(1,apbs[IndexCard].RamIO + ACK_FROM_PC_READY); + writeb(1,apbs[IndexCard].RamIO + TYP_ACK_FROM_PC); + writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); + writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), + apbs[IndexCard].RamIO + TIC_ACK_FROM_PC); + writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); + writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY); + writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); + Dummy = readb(apbs[IndexCard].RamIO + VERS); + restore_flags(flags); + +#ifdef DEBUG + { int c; + + printk("Read from applicom card #%d. struct st_ram_io follows:",NumCard); + + for (c=0; c< sizeof(struct st_ram_io);) + { + printk("\n%5.5X: %2.2X",c,((unsigned char *)&st_loc)[c]); + + for (c++ ; c%8 && c<sizeof(struct st_ram_io); c++) + { + printk(" %2.2X",((unsigned char *)&st_loc)[c]); + } + } + + printk("\nstruct mailbox follows:"); + + for (c=0; c< sizeof(struct mailbox);) + { + printk("\n%5.5X: %2.2X",c,((unsigned char *)&tmpmailbox)[c]); + + for (c++ ; c%8 && c<sizeof(struct mailbox); c++) + { + printk(" %2.2X",((unsigned char *)&tmpmailbox)[c]); + } + } + printk("\n"); + + } +#endif + + + /* Je suis stupide. DW. */ + + if(copy_to_user (buf, &st_loc, sizeof(struct st_ram_io))) + return -EFAULT; + if(copy_to_user (&buf[sizeof(struct st_ram_io)], &tmpmailbox, sizeof(struct mailbox))) + return -EFAULT; + + return 0; +} + +static void ac_interrupt(int vec, void *dev_instance, struct pt_regs *regs) +{ + unsigned int i; + unsigned int FlagInt; + unsigned int LoopCount; + // volatile unsigned char ResetIntBoard; + + // printk("Applicom interrupt on IRQ %d occurred\n", vec); + + LoopCount = 0; + // for(i=boardno;i<MAX_BOARD;i++) /* loop for not configured board */ + // if (apbs[i].RamIO) + // ResetIntBoard = *apbs[i].PtrRamItToPc; /* reset interrupt of unused boards */ + + do + { + FlagInt = FALSE; + for(i=0;i<MAX_BOARD;i++) + { + if (!apbs[i].RamIO) + continue; + + if(readb(apbs[i].RamIO + RAM_IT_TO_PC) != 0) + FlagInt = TRUE; + writeb(0, apbs[i].RamIO + RAM_IT_TO_PC); + + if(readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) + { + printk("APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", + i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); + DeviceErrorCount++; + } + if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && + (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) + { + printk("APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", + i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY)); + DeviceErrorCount++; + } + if(readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) /* mailbox sent by the card ? */ + { + wake_up_interruptible(&FlagSleepRec); + } + if(readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) /* ram i/o free for write by pc ? */ + { + if(waitqueue_active(&apbs[i].FlagSleepSend)) /* process sleep during read ? */ + { + wake_up_interruptible(&apbs[i].FlagSleepSend); + } + } + Dummy = readb(apbs[i].RamIO + VERS); + + if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) + i--; /* There's another int waiting on this card */ + } + if(FlagInt) LoopCount = 0; + else LoopCount++; + } + while(LoopCount < 2); +} + + +static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) + +{ /* @ ADG ou ATO selon le cas */ + int i; + unsigned char IndexCard; + unsigned long pmem ; + volatile unsigned char byte_reset_it; + struct st_ram_io adgl ; + unsigned char TmpRamIo[sizeof(struct st_ram_io)]; + + + if (copy_from_user (&adgl, (void *)arg,sizeof(struct st_ram_io))) + return -EFAULT; + + IndexCard = adgl.num_card-1; + if(cmd != 0 && cmd != 6 && + ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) + { + printk("APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1); + printk("apbs[%d].RamIO = %lx\n",IndexCard, apbs[IndexCard].RamIO); + return -EINVAL; + } + + switch( cmd ) + { + case 0 : + pmem = apbs[IndexCard].RamIO; + for(i=0;i<sizeof(struct st_ram_io);i++)TmpRamIo[i]=readb(pmem++); + if (copy_to_user((void *)arg, TmpRamIo, sizeof(struct st_ram_io))) + return -EFAULT; + break; + case 1 : + pmem = apbs[IndexCard].RamIO + CONF_END_TEST; + for (i=0;i<4;i++) + adgl.conf_end_test[i] = readb(pmem++); + for (i=0;i<2;i++) + adgl.error_code[i] = readb(pmem++); + for (i=0;i<4;i++) + adgl.parameter_error[i] = readb(pmem++); + pmem = apbs[IndexCard].RamIO + VERS; + adgl.vers = readb(pmem); + pmem = apbs[IndexCard].RamIO + TYPE_CARD; + for (i=0;i<20;i++) + adgl.reserv1[i] = readb(pmem++); + *(int *)&adgl.reserv1[20] = + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) ); + + if (copy_to_user ((void *)arg, &adgl, sizeof(struct st_ram_io))) + return -EFAULT; + break; + case 2 : + pmem = apbs[IndexCard].RamIO + CONF_END_TEST; + for (i=0;i<10;i++) + writeb(0xff, pmem++); + writeb(adgl.data_from_pc_ready, + apbs[IndexCard].RamIO + DATA_FROM_PC_READY); + + writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); +#if LINUX_VERSION_CODE > 0x20300 + init_waitqueue_head (&FlagSleepRec); +#else + FlagSleepRec = NULL; +#endif + for (i=0;i<MAX_BOARD;i++) + { + if (apbs[i].RamIO) + { +#if LINUX_VERSION_CODE > 0x20300 + init_waitqueue_head (&apbs[i].FlagSleepSend); +#else + apbs[i].FlagSleepSend = NULL; +#endif + byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC); + } + } + break ; + case 3 : + pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC; + writeb(adgl.tic_des_from_pc, pmem); + break; + case 4 : + pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC; + adgl.tic_owner_to_pc = readb(pmem++); + adgl.numcard_owner_to_pc = readb(pmem); + if (copy_to_user ((void *)arg, &adgl,sizeof(struct st_ram_io))) + return -EFAULT; + break; + case 5 : + writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); + writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); + writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); + writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); + writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); + break ; + case 6 : + printk("APPLICOM driver release .... V2.8.0\n"); + printk("Number of installed boards . %d\n",(int)numboards); + printk("Segment of board ........... %X\n",(int)mem); + printk("Interrupt IRQ number ....... %d\n",(int)irq); + for(i=0;i<MAX_BOARD;i++) + { + int serial; + char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; + + if (!apbs[i].RamIO) + continue; + + + for(serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) + boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); + boardname[serial]=0; + + + printk("Prom version board %d ....... V%d.%d %s", + i+1, + (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4), + (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF), + boardname); + + + serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); + + if (serial != 0) + printk (" S/N %d\n", serial); + else + printk("\n"); + } + if(DeviceErrorCount != 0) + printk("DeviceErrorCount ........... %d\n",DeviceErrorCount); + if(ReadErrorCount != 0) + printk("ReadErrorCount ............. %d\n",ReadErrorCount); + if(WriteErrorCount != 0) + printk("WriteErrorCount ............ %d\n",WriteErrorCount); + if(waitqueue_active(&FlagSleepRec)) + printk("Process in read pending\n"); + for(i=0;i<MAX_BOARD;i++) + { + if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend)) + printk("Process in write pending board %d\n",i+1); + } + break; + default : + printk("APPLICOM driver ioctl, unknown function code %d\n",cmd) ; + return -EINVAL; + break; + } + Dummy = readb(apbs[IndexCard].RamIO + VERS); + return 0; +} + +#ifndef MODULE +static int __init applicom_setup(char *str) +{ + int ints[4]; + + (void)get_options(str, 4, ints); + + if (ints[0] > 2) { + printk(KERN_WARNING "Too many arguments to 'applicom=', expected mem,irq only.\n"); + } + + if (ints[0] < 2) { + printk("applicom numargs: %d\n", ints[0]); + return 0; + } + + mem=ints[1]; + irq=ints[2]; + return 1; +} +#if LINUX_VERSION_CODE > 0x20300 +__setup("applicom=", applicom_setup); +#endif +#endif /* MODULE */ + diff --git a/drivers/char/applicom.h b/drivers/char/applicom.h new file mode 100644 index 000000000..35530b3d9 --- /dev/null +++ b/drivers/char/applicom.h @@ -0,0 +1,85 @@ +/* $Id: applicom.h,v 1.2 1999/08/28 15:09:49 dwmw2 Exp $ */ + + +#ifndef __LINUX_APPLICOM_H__ +#define __LINUX_APPLICOM_H__ + + +#define DATA_TO_PC_READY 0x00 +#define TIC_OWNER_TO_PC 0x01 +#define NUMCARD_OWNER_TO_PC 0x02 +#define TIC_DES_TO_PC 0x03 +#define NUMCARD_DES_TO_PC 0x04 +#define DATA_FROM_PC_READY 0x05 +#define TIC_OWNER_FROM_PC 0x06 +#define NUMCARD_OWNER_FROM_PC 0x07 +#define TIC_DES_FROM_PC 0x08 +#define NUMCARD_DES_FROM_PC 0x09 +#define ACK_FROM_PC_READY 0x0E +#define TIC_ACK_FROM_PC 0x0F +#define NUMCARD_ACK_FROM_PC 0x010 +#define TYP_ACK_FROM_PC 0x011 +#define CONF_END_TEST 0x012 +#define ERROR_CODE 0x016 +#define PARAMETER_ERROR 0x018 +#define VERS 0x01E +#define RAM_TO_PC 0x040 +#define RAM_FROM_PC 0x0170 +#define TYPE_CARD 0x03C0 +#define SERIAL_NUMBER 0x03DA +#define RAM_IT_FROM_PC 0x03FE +#define RAM_IT_TO_PC 0x03FF + +struct mailbox{ + u16 stjb_codef; /* offset 00 */ + s16 stjb_status; /* offset 02 */ + u16 stjb_ticuser_root; /* offset 04 */ + u8 stjb_piduser[4]; /* offset 06 */ + u16 stjb_mode; /* offset 0A */ + u16 stjb_time; /* offset 0C */ + u16 stjb_stop; /* offset 0E */ + u16 stjb_nfonc; /* offset 10 */ + u16 stjb_ncard; /* offset 12 */ + u16 stjb_nchan; /* offset 14 */ + u16 stjb_nes; /* offset 16 */ + u16 stjb_nb; /* offset 18 */ + u16 stjb_typvar; /* offset 1A */ + u32 stjb_adr; /* offset 1C */ + u16 stjb_ticuser_dispcyc; /* offset 20 */ + u16 stjb_ticuser_protocol; /* offset 22 */ + u8 stjb_filler[12]; /* offset 24 */ + u8 stjb_data[256]; /* offset 30 */ + }; + +struct st_ram_io +{ + unsigned char data_to_pc_ready; + unsigned char tic_owner_to_pc; + unsigned char numcard_owner_to_pc; + unsigned char tic_des_to_pc; + unsigned char numcard_des_to_pc; + unsigned char data_from_pc_ready; + unsigned char tic_owner_from_pc; + unsigned char numcard_owner_from_pc; + unsigned char tic_des_from_pc; + unsigned char numcard_des_from_pc; + unsigned char ack_to_pc_ready; + unsigned char tic_ack_to_pc; + unsigned char numcard_ack_to_pc; + unsigned char typ_ack_to_pc; + unsigned char ack_from_pc_ready; + unsigned char tic_ack_from_pc; + unsigned char numcard_ack_from_pc; + unsigned char typ_ack_from_pc; + unsigned char conf_end_test[4]; + unsigned char error_code[2]; + unsigned char parameter_error[4]; + unsigned char time_base; + unsigned char nul_inc; + unsigned char vers; + unsigned char num_card; + unsigned char reserv1[32]; +}; + + +#endif /* __LINUX_APPLICOM_H__ */ diff --git a/drivers/char/atarimouse.c b/drivers/char/atarimouse.c index b813510eb..b831bd49c 100644 --- a/drivers/char/atarimouse.c +++ b/drivers/char/atarimouse.c @@ -10,6 +10,9 @@ * 1996/02/11 Andreas Schwab * Module support * Allow multiple open's + * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> */ #include <linux/module.h> @@ -21,13 +24,15 @@ #include <linux/random.h> #include <linux/poll.h> #include <linux/init.h> -#include <linux/busmouse.h> +#include <linux/logibusmouse.h> #include <asm/setup.h> #include <asm/atarikb.h> #include <asm/uaccess.h> -static struct mouse_status mouse; +#include "busmouse.h" + +static int msedev; static int mouse_threshold[2] = {2,2}; MODULE_PARM(mouse_threshold, "2i"); extern int atari_mouse_buttons; @@ -38,36 +43,17 @@ static void atari_mouse_interrupt(char *buf) /* ikbd_mouse_disable(); */ - buttons = ((buf[0] & 1 ? 1 : 0) - | (buf[0] & 2 ? 4 : 0) + buttons = ((buf[0] & 1) + | ((buf[0] & 2) << 1) | (atari_mouse_buttons & 2)); atari_mouse_buttons = buttons; - add_mouse_randomness((buttons << 16) + (buf[2] << 8) + buf[1]); - mouse.buttons = ~buttons & 7; - mouse.dx += buf[1]; - mouse.dy -= buf[2]; - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - if (mouse.fasyncptr) - kill_fasync(mouse.fasyncptr, SIGIO); + busmouse_add_movementbuttons(msedev, buf[1], -buf[2], buttons ^ 7); /* ikbd_mouse_rel_pos(); */ } -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - retval = fasync_helper(fd, filp, on, &mouse.fasyncptr); - if (retval < 0) - return retval; - return 0; -} - static int release_mouse(struct inode *inode, struct file *file) { - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; ikbd_mouse_disable(); atari_mouse_interrupt_hook = NULL; @@ -77,10 +63,6 @@ static int release_mouse(struct inode *inode, struct file *file) static int open_mouse(struct inode *inode, struct file *file) { - if (mouse.active++) - return 0; - mouse.ready = 0; - mouse.dx = mouse.dy = 0; atari_mouse_buttons = 0; ikbd_mouse_y0_top (); ikbd_mouse_thresh (mouse_threshold[0], mouse_threshold[1]); @@ -90,92 +72,20 @@ static int open_mouse(struct inode *inode, struct file *file) return 0; } -static ssize_t write_mouse(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static ssize_t read_mouse(struct file * file, char * buffer, - size_t count, loff_t *ppos) -{ - int dx, dy, buttons; - - if (count < 3) - return -EINVAL; - if (!mouse.ready) - return -EAGAIN; - /* ikbd_mouse_disable */ - dx = mouse.dx; - dy = mouse.dy; - buttons = mouse.buttons; - if (dx > 127) - dx = 127; - else if (dx < -128) - dx = -128; - if (dy > 127) - dy = 127; - else if (dy < -128) - dy = -128; - mouse.dx -= dx; - mouse.dy -= dy; - if (mouse.dx == 0 && mouse.dy == 0) - mouse.ready = 0; - /* ikbd_mouse_rel_pos(); */ - if (put_user(buttons | 0x80, buffer++) || - put_user((char) dx, buffer++) || - put_user((char) dy, buffer++)) - return -EFAULT; - if (count > 3) - if (clear_user(buffer, count - 3)) - return -EFAULT; - return count; -} - -static unsigned int mouse_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations atari_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -static struct miscdevice atari_mouse = { - ATARIMOUSE_MINOR, "atarimouse", &atari_mouse_fops +static struct busmouse atarimouse = { + ATARIMOUSE_MINOR, "atarimouse", open_mouse, release_mouse, 0 }; int __init atari_mouse_init(void) { - int r; - - if (!MACH_IS_ATARI) - return -ENODEV; - - mouse.active = 0; - mouse.ready = 0; - mouse.wait = NULL; - - r = misc_register(&atari_mouse); - if (r) - return r; - - printk(KERN_INFO "Atari mouse installed.\n"); - return 0; + if (!MACH_IS_ATARI) + return -ENODEV; + msedev = register_busmouse(&atarimouse); + if (msedev < 0) + printk(KERN_WARNING "Unable to register Atari mouse driver.\n"); + else + printk(KERN_INFO "Atari mouse installed.\n"); + return msedev < 0 ? msedev : 0; } @@ -215,6 +125,6 @@ int init_module(void) void cleanup_module(void) { - misc_deregister(&atari_mouse); + unregister_busmouse(msedev); } #endif diff --git a/drivers/char/atixlmouse.c b/drivers/char/atixlmouse.c index ecddff26c..1ab9a211f 100644 --- a/drivers/char/atixlmouse.c +++ b/drivers/char/atixlmouse.c @@ -7,6 +7,9 @@ * Modified by Chris Colohan (colohan@eecg.toronto.edu) * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> + * * version 0.3a */ @@ -26,6 +29,8 @@ #include <asm/system.h> #include <asm/irq.h> +#include "busmouse.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) */ @@ -59,17 +64,7 @@ /* Same general mouse structure */ -static struct mouse_status { - char buttons; - char latch_buttons; - int dx; - int dy; - int present; - int ready; - int active; - wait_queue_head_t wait; - struct fasync_struct *fasync; -} mouse; +static int msedev; void mouse_interrupt(int irq, void *dev_id, struct pt_regs * regs) { @@ -82,35 +77,13 @@ void mouse_interrupt(int irq, void *dev_id, struct pt_regs * regs) dy = inb( ATIXL_MSE_DATA_PORT); outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */ buttons = inb( ATIXL_MSE_DATA_PORT); - if (dx != 0 || dy != 0 || buttons != mouse.latch_buttons) { - add_mouse_randomness((buttons << 16) + (dy << 8) + dx); - mouse.latch_buttons |= buttons; - mouse.dx += dx; - mouse.dy += dy; - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - if (mouse.fasync) - kill_fasync(mouse.fasync, SIGIO); - } + busmouse_add_movementbuttons(msedev, dx, -dy, buttons); ATIXL_MSE_ENABLE_UPDATE(); } -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - retval = fasync_helper(fd, filp, on, &mouse.fasync); - if (retval < 0) - return retval; - return 0; -} - static int release_mouse(struct inode * inode, struct file * file) { - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; ATIXL_MSE_INT_OFF(); /* Interrupts are really shut down here */ - mouse.ready = 0; free_irq(ATIXL_MOUSE_IRQ, NULL); MOD_DEC_USE_COUNT; return 0; @@ -118,90 +91,17 @@ static int release_mouse(struct inode * inode, struct file * file) static int open_mouse(struct inode * inode, struct file * file) { - if (!mouse.present) - return -EINVAL; - if (mouse.active++) - return 0; - if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt, 0, "ATIXL mouse", NULL)) { - mouse.active--; + if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt, 0, "ATIXL mouse", NULL)) return -EBUSY; - } - mouse.ready = 0; - mouse.dx = 0; - mouse.dy = 0; - mouse.buttons = mouse.latch_buttons = 0; ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */ MOD_INC_USE_COUNT; return 0; } - -static ssize_t write_mouse(struct file * file, const char * buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static ssize_t read_mouse(struct file * file, char * buffer, - size_t count, loff_t *ppos) -{ - ssize_t i; - - if (count < 3) - return -EINVAL; - if (!mouse.ready) - return -EAGAIN; - ATIXL_MSE_DISABLE_UPDATE(); - /* Allowed interrupts to occur during data gathering - shouldn't hurt */ - put_user((char)(~mouse.latch_buttons&7) | 0x80 , buffer); - if (mouse.dx < -127) - mouse.dx = -127; - if (mouse.dx > 127) - mouse.dx = 127; - put_user((char)mouse.dx, buffer + 1); - if (mouse.dy < -127) - mouse.dy = -127; - if (mouse.dy > 127) - mouse.dy = 127; - put_user((char)-mouse.dy, buffer + 2); - for(i = 3; i < count; i++) - put_user(0x00, buffer + i); - mouse.dx = 0; - mouse.dy = 0; - mouse.latch_buttons = mouse.buttons; - mouse.ready = 0; - ATIXL_MSE_ENABLE_UPDATE(); - return i; /* i data bytes returned */ -} - -static unsigned int mouse_poll(struct file *file, poll_table * wait) -{ - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations atixl_busmouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, +static struct busmouse atixlmouse = { + ATIXL_BUSMOUSE, "atixl", open_mouse, release_mouse, 0 }; -static struct miscdevice atixl_mouse = { - ATIXL_BUSMOUSE, "atixl", &atixl_busmouse_fops -}; - - int __init atixl_busmouse_init(void) { unsigned char a,b,c; @@ -211,22 +111,18 @@ int __init atixl_busmouse_init(void) c = inb( ATIXL_MSE_SIGNATURE_PORT ); if (( a != b ) && ( a == c )) printk(KERN_INFO "\nATI Inport "); - else{ - mouse.present = 0; + else return -EIO; - } outb(0x80, ATIXL_MSE_CONTROL_PORT); /* Reset the Inport device */ outb(0x07, ATIXL_MSE_CONTROL_PORT); /* Select Internal Register 7 */ outb(0x0a, ATIXL_MSE_DATA_PORT); /* Data Interrupts 8+, 1=30hz, 2=50hz, 3=100hz, 4=200hz rate */ - mouse.present = 1; - mouse.active = 0; - mouse.ready = 0; - mouse.buttons = mouse.latch_buttons = 0; - mouse.dx = mouse.dy = 0; - init_waitqueue_head(&mouse.wait); - printk("Bus mouse detected and installed.\n"); - misc_register(&atixl_mouse); - return 0; + + msedev = register_busmouse(&atixlmouse); + if (msedev < 0) + printk("Bus mouse initialisation error.\n"); + else + printk("Bus mouse detected and installed.\n"); + return msedev < 0 ? msedev : 0; } #ifdef MODULE @@ -238,6 +134,6 @@ int init_module(void) void cleanup_module(void) { - misc_deregister(&atixl_mouse); + unregister_busmouse(msedev); } #endif diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index c24d96b27..a177e7de6 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -29,9 +29,7 @@ #include <linux/major.h> #include <linux/malloc.h> #include <linux/mm.h> -#if LINUX_VERSION_CODE >= 0x020100 #include <linux/poll.h> -#endif #include <linux/pci.h> #include <linux/signal.h> #include <asm/io.h> @@ -44,36 +42,8 @@ #include <linux/wrapper.h> #include <linux/interrupt.h> -#if LINUX_VERSION_CODE >= 0x020100 #include <asm/uaccess.h> #include <linux/vmalloc.h> -#else -#include <linux/bios32.h> -#define mdelay(x) udelay((x)*1000) -#define signal_pending(current) (current->signal & ~current->blocked) -#define sigfillset(set) - -static inline int time_before(unsigned long a, unsigned long b) -{ - return((long)((a) - (b)) < 0L); -} - -static inline unsigned long -copy_to_user(void *to, const void *from, unsigned long n) -{ - memcpy_tofs(to,from,n); - return 0; -} - -static inline unsigned long -copy_from_user(void *to, const void *from, unsigned long n) -{ - memcpy_fromfs(to,from,n); - return 0; -} -#define ioremap vremap -#define iounmap vfree -#endif #include <linux/videodev.h> #include <linux/i2c.h> @@ -83,19 +53,10 @@ copy_from_user(void *to, const void *from, unsigned long n) #define DEBUG(x) /* Debug driver */ #define IDEBUG(x) /* Debug interrupt handler */ -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(vidmem,"i"); -MODULE_PARM(triton1,"i"); -MODULE_PARM(remap,"1-4i"); -MODULE_PARM(radio,"1-4i"); -MODULE_PARM(card,"1-4i"); -MODULE_PARM(pll,"1-4i"); -#endif /* Anybody who uses more than four? */ #define BTTV_MAX 4 -static int find_vga(void); static void bt848_set_risc_jmps(struct bttv *btv); static unsigned int vidmem=0; /* manually set video mem address */ @@ -243,6 +204,12 @@ static void rvfree(void * mem, unsigned long size) } } +MODULE_PARM(vidmem,"i"); +MODULE_PARM(triton1,"i"); +MODULE_PARM(remap,"1-4i"); +MODULE_PARM(radio,"1-4i"); +MODULE_PARM(card,"1-4i"); +MODULE_PARM(pll,"1-4i"); /* @@ -403,7 +370,7 @@ static void writeee(struct i2c_bus *bus, unsigned char *eedata) } } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; @@ -422,7 +389,7 @@ void attach_inform(struct i2c_bus *bus, int id) } } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; @@ -576,6 +543,8 @@ static struct tvcard tvcards[] = { 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, {0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}}, /* AVEC Intercapture */ { 3, 2, 0, 2, 0, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0}}, + /* LifeView FlyKit w/o Tuner */ + { 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}} }; #define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) @@ -1286,7 +1255,7 @@ static inline void bt848_set_eogeo(struct bttv *btv, int odd, u8 vtc, } -static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) +static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt, int pllset) { u16 vscale, hscale; u32 xsf, sr; @@ -1295,9 +1264,13 @@ static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) u16 inter; u8 crop, vtc; struct tvnorm *tvn; + unsigned long flags; if (!width || !height) return; + + save_flags(flags); + cli(); tvn=&tvnorms[btv->win.norm]; @@ -1322,7 +1295,8 @@ static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) btwrite(1, BT848_VBI_PACK_DEL); btv->pll.pll_ofreq = tvn->Fsc; - set_pll(btv); + if(pllset) + set_pll(btv); btwrite(fmt, BT848_COLOR_FMT); #ifdef __sparc__ @@ -1370,6 +1344,8 @@ static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) hdelay, vdelay, crop); bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive, hdelay, vdelay, crop); + + restore_flags(flags); } @@ -1398,7 +1374,7 @@ static void bt848_set_winsize(struct bttv *btv) else btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - bt848_set_geo(btv, btv->win.width, btv->win.height, format); + bt848_set_geo(btv, btv->win.width, btv->win.height, format, 1); } /* @@ -1582,20 +1558,18 @@ static long bttv_read(struct video_device *v, char *buf, unsigned long count, in static int bttv_open(struct video_device *dev, int flags) { struct bttv *btv = (struct bttv *)dev; - int users, i; - + int i, ret; + + ret = -EBUSY; + down(&btv->lock); if (btv->user) - return -EBUSY; - audio(btv, AUDIO_UNMUTE); - for (i=users=0; i<bttv_num; i++) - users+=bttvs[i].user; - if (users==1) - find_vga(); - btv->fbuffer=NULL; - if (!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); + goto out_unlock; + + btv->fbuffer= (unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); + ret = -ENOMEM; if (!btv->fbuffer) - return -EINVAL; + goto out_unlock; + audio(btv, AUDIO_UNMUTE); btv->grabbing = 0; btv->grab = 0; btv->lastgrab = 0; @@ -1603,14 +1577,20 @@ static int bttv_open(struct video_device *dev, int flags) btv->frame_stat[i] = GBUFFER_UNUSED; btv->user++; + up(&btv->lock); MOD_INC_USE_COUNT; return 0; + + out_unlock: + up(&btv->lock); + return ret; } static void bttv_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)dev; - + + down(&btv->lock); btv->user--; audio(btv, AUDIO_INTERN); btv->cap&=~3; @@ -1640,6 +1620,7 @@ static void bttv_close(struct video_device *dev) if(btv->fbuffer) rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF); btv->fbuffer=0; + up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -1760,14 +1741,16 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if (v.channel>tvcards[btv->type].video_inputs) return -EINVAL; - bt848_muxsel(btv, v.channel); if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC &&v.norm!=VIDEO_MODE_SECAM) return -EOPNOTSUPP; + down(&btv->lock); + bt848_muxsel(btv, v.channel); btv->win.norm = v.norm; make_vbitab(btv); bt848_set_winsize(btv); btv->channel=v.channel; + up(&btv->lock); return 0; } case VIDIOCGTUNER: @@ -1812,7 +1795,9 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) &&v.mode!=VIDEO_MODE_SECAM) return -EOPNOTSUPP; btv->win.norm = v.mode; + down(&btv->lock); bt848_set_winsize(btv); + up(&btv->lock); return 0; } case VIDIOCGPICT: @@ -1839,6 +1824,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) int format; if(copy_from_user(&p, arg,sizeof(p))) return -EFAULT; + down(&btv->lock); /* We want -128 to 127 we get 0-65535 */ bt848_bright(btv, (p.brightness>>8)-128); /* 0-511 for the colour */ @@ -1856,6 +1842,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if (fmtbppx2[format&0x0f]/2 == btv->win.bpp) btv->win.color_fmt = format; } + up(&btv->lock); return 0; } case VIDIOCSWIN: @@ -1869,7 +1856,9 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if(vw.flags || vw.width < 16 || vw.height < 16) { + down(&btv->lock); bt848_cap(btv,0); + up(&btv->lock); return -EINVAL; } if (btv->win.bpp < 4) @@ -1879,6 +1868,8 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) i = vw.x - i; vw.width -= i; } + + down(&btv->lock); btv->win.x=vw.x; btv->win.y=vw.y; btv->win.width=vw.width; @@ -1893,6 +1884,8 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) bt848_cap(btv,0); bt848_set_winsize(btv); + + up(&btv->lock); /* * Do any clips. @@ -1916,11 +1909,13 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; } } + down(&btv->lock); make_clip_tab(btv, vcp, vw.clipcount); if (vw.clipcount != 0) vfree(vcp); if(on && btv->win.vidadr!=0) bt848_cap(btv,1); + up(&btv->lock); return 0; } case VIDIOCGWIN: @@ -1944,17 +1939,17 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) int v; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; + if(v!=0 && (btv->win.vidadr==0 || btv->win.width==0 + || btv->win.height==0)) + return -EINVAL; + + down(&btv->lock); if(v==0) - { bt848_cap(btv,0); - } else - { - if(btv->win.vidadr==0 || btv->win.width==0 - || btv->win.height==0) - return -EINVAL; bt848_cap(btv,1); - } + up(&btv->lock); + return 0; } case VIDIOCGFBUF: @@ -1974,7 +1969,8 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_buffer v; #if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN)) + if(!capable(CAP_SYS_ADMIN) + && !capable(CAP_SYS_RAWIO)) #else if(!suser()) #endif @@ -1985,13 +1981,9 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.depth!=24 && v.depth!=32 && v.width > 16 && v.height > 16 && v.bytesperline > 16) return -EINVAL; + down(&btv->lock); if (v.base) - { - if ((unsigned long)v.base&1) - btv->win.vidadr=(unsigned long)(PAGE_OFFSET|uvirt_to_bus((unsigned long)v.base)); - else - btv->win.vidadr=(unsigned long)v.base; - } + btv->win.vidadr=(unsigned long)v.base; btv->win.sheight=v.height; btv->win.swidth=v.width; btv->win.bpp=((v.depth+7)&0x38)/8; @@ -2001,6 +1993,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); bt848_set_winsize(btv); + up(&btv->lock); return 0; } case VIDIOCKEY: @@ -2067,13 +2060,17 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) struct video_audio v; if(copy_from_user(&v,arg, sizeof(v))) return -EFAULT; + down(&btv->lock); if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); /* One audio source per tuner */ /* if(v.audio!=0) */ /* ADSTech TV card has more than one */ if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) + { + up(&btv->lock); return -EINVAL; + } bt848_muxsel(btv,v.audio); if(!(v.flags&VIDEO_AUDIO_MUTE)) audio(btv, AUDIO_UNMUTE); @@ -2137,6 +2134,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) MSP_SET_STEREO,&(v.mode)); } btv->audio_dev=v; + up(&btv->lock); return 0; } @@ -2163,25 +2161,21 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return 0; case BTTV_WRITEE: -#if LINUX_VERSION_CODE >= 0x020100 if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif return -EPERM; if(copy_from_user((void *) eedata, (void *) arg, 256)) return -EFAULT; + down(&btv->lock); writeee(&(btv->i2c), eedata); + up(&btv->lock); return 0; case BTTV_READEE: -#if LINUX_VERSION_CODE >= 0x020100 if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif return -EPERM; + down(&btv->lock); readee(&(btv->i2c), eedata); + up(&btv->lock); if(copy_to_user((void *) arg, (void *) eedata, 256)) return -EFAULT; break; @@ -2194,28 +2188,29 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case BTTV_PLLSET: { struct bttv_pll_info p; -#if LINUX_VERSION_CODE >= 0x020100 if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif return -EPERM; if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) return -EFAULT; + down(&btv->lock); btv->pll.pll_ifreq = p.pll_ifreq; btv->pll.pll_ofreq = p.pll_ofreq; btv->pll.pll_crystal = p.pll_crystal; - + up(&btv->lock); break; } case VIDIOCMCAPTURE: { struct video_mmap vm; + int v; if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) return -EFAULT; if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING) return -EBUSY; - return vgrab(btv, &vm); + down(&btv->lock); + v=vgrab(btv, &vm); + up(&btv->lock); + return v; } case VIDIOCGMBUF: @@ -2297,9 +2292,8 @@ static int bttv_init_done(struct video_device *dev) * But e.g. pte_alloc() does not work in modules ... :-( */ -static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) +static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size) { - struct bttv *btv=(struct bttv *)dev; unsigned long start=(unsigned long) adr; unsigned long page,pos; @@ -2323,6 +2317,17 @@ static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long si return 0; } +static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct bttv *btv=(struct bttv *)dev; + int r; + + down(&btv->lock); + r=do_bttv_mmap(btv, adr, size); + up(&btv->lock); + return r; +} + static struct video_device bttv_template= { "UNSET", @@ -2388,7 +2393,6 @@ static long vbi_read(struct video_device *v, char *buf, unsigned long count, return count; } -#if LINUX_VERSION_CODE >= 0x020100 static unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait) { @@ -2402,15 +2406,16 @@ static unsigned int vbi_poll(struct video_device *dev, struct file *file, return mask; } -#endif static int vbi_open(struct video_device *dev, int flags) { struct bttv *btv=(struct bttv *)(dev-2); + down(&btv->lock); btv->vbip=VBIBUF_SIZE; btv->cap|=0x0c; bt848_set_risc_jmps(btv); + up(&btv->lock); MOD_INC_USE_COUNT; return 0; @@ -2419,9 +2424,11 @@ static int vbi_open(struct video_device *dev, int flags) static void vbi_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)(dev-2); - + + down(&btv->lock); btv->cap&=~0x0c; bt848_set_risc_jmps(btv); + up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -2457,25 +2464,34 @@ static int radio_open(struct video_device *dev, int flags) { struct bttv *btv = (struct bttv *)(dev-1); + down(&btv->lock); if (btv->user) - return -EBUSY; + goto busy_unlock; btv->user++; + set_freq(btv,400*16); btv->radio = 1; bt848_muxsel(btv,0); audio(btv, AUDIO_UNMUTE); + up(&btv->lock); MOD_INC_USE_COUNT; return 0; + + busy_unlock: + up(&btv->lock); + return -EBUSY; } static void radio_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)(dev-1); + down(&btv->lock); btv->user--; btv->radio = 0; /*audio(btv, AUDIO_MUTE);*/ + up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -2565,233 +2581,6 @@ static struct video_device radio_template= }; -struct vidbases -{ - unsigned short vendor, device; - char *name; - uint badr; -}; - -static struct vidbases vbs[] = { - { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D, - "Alliance AT3D", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222, - "ATI MACH64 CT", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX, - "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT, - "ATI MACH64 GT", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, - "DEC DC21030", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, - "Matrox Millennium", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, - "Matrox Millennium II", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, - "Matrox Millennium II AGP", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_MATROX, 0x0521, "Matrox G200", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, - "Number Nine Imagine 128", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128_2, - "Number Nine Imagine 128 Series 2", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128, - "Riva128", PCI_BASE_ADDRESS_1}, -}; - - -/* DEC TGA offsets stolen from XFree-3.2 */ - -static uint dec_offsets[4] = { - 0x200000, - 0x804000, - 0, - 0x1004000 -}; - -#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases)) - -/* Scan for PCI display adapter - if more than one card is present the last one is used for now */ - -#if LINUX_VERSION_CODE >= 0x020100 - -static int find_vga(void) -{ - unsigned short badr; - int found = 0, i, tga_type; - unsigned int vidadr=0; - struct pci_dev *dev; - - - for (dev = pci_devices; dev != NULL; dev = dev->next) - { - if (dev->class != PCI_CLASS_NOT_DEFINED_VGA && - ((dev->class) >> 16 != PCI_BASE_CLASS_DISPLAY)) - { - continue; - } - if (PCI_FUNC(dev->devfn) != 0) - continue; - - badr=0; - printk(KERN_INFO "bttv: PCI display adapter: "); - for (i=0; i<NR_CARDS; i++) - { - if (dev->vendor == vbs[i].vendor) - { - if (vbs[i].device) - if (vbs[i].device!=dev->device) - continue; - printk("%s.\n", vbs[i].name); - badr=vbs[i].badr; - break; - } - } - if (!badr) - { - printk(KERN_ERR "bttv: Unknown video memory base address.\n"); - continue; - } - pci_read_config_dword(dev, badr, &vidadr); - if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) - { - printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n"); - printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n"); - continue; - } - vidadr &= PCI_BASE_ADDRESS_MEM_MASK; - if (!vidadr) - { - printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!"); - continue; - } - - if (dev->vendor == PCI_VENDOR_ID_DEC && - dev->device == PCI_DEVICE_ID_DEC_TGA) - { - tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) - { - printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); - found--; - } - vidadr+=dec_offsets[tga_type]; - } - DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); - DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", dev->devfn)); - found++; - } - - if (vidmem) - { - vidadr=vidmem<<20; - printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr); - found=1; - } - for (i=0; i<BTTV_MAX; i++) - bttvs[i].win.vidadr=vidadr; - - return found; -} - -#else -static int find_vga(void) -{ - unsigned int devfn, class, vendev; - unsigned short vendor, device, badr; - int found=0, bus=0, i, tga_type; - unsigned int vidadr=0; - - - for (devfn = 0; devfn < 0xff; devfn++) - { - if (PCI_FUNC(devfn) != 0) - continue; - pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev); - if (vendev == 0xffffffff || vendev == 0x00000000) - continue; - pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device); - pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class); - class = class >> 16; -/* if (class == PCI_CLASS_DISPLAY_VGA) {*/ - if ((class>>8) == PCI_BASE_CLASS_DISPLAY || - /* Number 9 GXE64Pro needs this */ - class == PCI_CLASS_NOT_DEFINED_VGA) - { - badr=0; - printk(KERN_INFO "bttv: PCI display adapter: "); - for (i=0; i<NR_CARDS; i++) - { - if (vendor==vbs[i].vendor) - { - if (vbs[i].device) - if (vbs[i].device!=device) - continue; - printk("%s.\n", vbs[i].name); - badr=vbs[i].badr; - break; - } - } - if (NR_CARDS == i) - printk("UNKNOWN.\n"); - if (!badr) - { - printk(KERN_ERR "bttv: Unknown video memory base address.\n"); - continue; - } - pcibios_read_config_dword(bus, devfn, badr, &vidadr); - if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) - { - printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n"); - printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n"); - continue; - } - vidadr &= PCI_BASE_ADDRESS_MEM_MASK; - if (!vidadr) - { - printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!\n"); - continue; - } - - if (vendor==PCI_VENDOR_ID_DEC) - if (device==PCI_DEVICE_ID_DEC_TGA) - { - tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) - { - printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); - found--; - } - vidadr+=dec_offsets[tga_type]; - } - - DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); - DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn)); - found++; - } - } - - if (vidmem) - { - if (vidmem < 0x1000) - vidadr=vidmem<<20; - else - vidadr=vidmem; - printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr); - found=1; - } - for (i=0; i<BTTV_MAX; i++) - bttvs[i].win.vidadr=vidadr; - - return found; -} -#endif - #define TRITON_PCON 0x50 #define TRITON_BUS_CONCURRENCY (1<<0) @@ -2800,8 +2589,6 @@ static int find_vga(void) #define TRITON_PEER_CONCURRENCY (1<<3) -#if LINUX_VERSION_CODE >= 0x020100 - static void handle_chipset(void) { struct pci_dev *dev = NULL; @@ -2833,110 +2620,8 @@ static void handle_chipset(void) printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); triton1=BT848_INT_ETBF; - -#if 0 - /* The ETBF bit SHOULD make all this unnecessary */ - /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - - pci_read_config_byte(dev, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } - - if (b!=bo) - { - pci_write_config_byte(dev, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); - } -#endif - } -} -#else -static void handle_chipset(void) -{ - int index; - - for (index = 0; index < 8; index++) - { - unsigned char bus, devfn; - unsigned char b; - - /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - - if (!pcibios_find_device(PCI_VENDOR_ID_SI, - PCI_DEVICE_ID_SI_496, - index, &bus, &devfn)) - { - printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); - } - - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82441, - index, &bus, &devfn)) - { - pcibios_read_config_byte(bus, devfn, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } - - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, - index, &bus, &devfn)) - { - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - triton1=BT848_INT_ETBF; - -#if 0 - /* The ETBF bit SHOULD make all this unnecessary */ - /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - { - unsigned char bo; - - pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } - - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } - - if (b!=bo) - { - pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); - } - } -#endif - } } } -#endif static void init_tea6300(struct i2c_bus *bus) { @@ -2950,14 +2635,14 @@ static void init_tea6300(struct i2c_bus *bus) static void init_tea6320(struct i2c_bus *bus) { - I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */ + I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */ + I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */ } static void init_tda8425(struct i2c_bus *bus) @@ -3087,16 +2772,16 @@ static void idcard(int i) if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0) { - if(btv->type==BTTV_AVEC_INTERCAP) - { - printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr); - btv->audio_chip = TEA6320; - init_tea6320(&(btv->i2c)); - } else { - printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr); - btv->audio_chip = TEA6300; - init_tea6300(&(btv->i2c)); - } + if(btv->type==BTTV_AVEC_INTERCAP) + { + printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr); + btv->audio_chip = TEA6320; + init_tea6320(&(btv->i2c)); + } else { + printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr); + btv->audio_chip = TEA6300; + init_tea6300(&(btv->i2c)); + } } else printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr); @@ -3145,7 +2830,7 @@ static void idcard(int i) break; } printk("%s\n",btv->video_dev.name); - audio(btv, AUDIO_MUTE); + audio(btv, AUDIO_INTERN); } @@ -3207,6 +2892,8 @@ static int init_bt848(int i) struct bttv *btv = &bttvs[i]; btv->user=0; + + init_MUTEX(&btv->lock); /* reset the bt848 */ btwrite(0, BT848_SRESET); @@ -3435,14 +3122,14 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) btv->risc_jmp[11]=cpu_to_le32(btv->gre); bt848_set_geo(btv, btv->gwidth, btv->gheight, - btv->gfmt); + btv->gfmt, 0); } else { bt848_set_risc_jmps(btv); btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI); btand(~BT848_VSCALE_COMB, BT848_O_VSCALE_HI); bt848_set_geo(btv, btv->win.width, btv->win.height, - btv->win.color_fmt); + btv->win.color_fmt, 0); } wake_up_interruptible(&btv->capq); break; @@ -3453,7 +3140,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) btv->risc_jmp[11]=cpu_to_le32(btv->gre); btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); bt848_set_geo(btv, btv->gwidth, btv->gheight, - btv->gfmt); + btv->gfmt, 0); } } if (astat&BT848_INT_OCERR) @@ -3515,7 +3202,6 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) * Scan for a Bt848 card, request the irq and map the io memory */ -#if LINUX_VERSION_CODE >= 0x020100 int configure_bt848(struct pci_dev *dev, int bttv_num) { int result; @@ -3538,27 +3224,12 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) btv->id=dev->device; btv->irq=dev->irq; - btv->bt848_adr=dev->base_address[0]; + btv->bt848_adr=dev->resource[0].start; if (btv->id >= 878) btv->i2c_command = 0x83; else btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); - if (remap[bttv_num]) - { - unsigned int dw = btv->bt848_adr; - - if (remap[bttv_num] < 0x1000) - remap[bttv_num]<<=20; - remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK; - printk(KERN_INFO "bttv%d: remapping to : 0x%lx.\n", - bttv_num,remap[bttv_num]); - remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, remap[bttv_num]); - pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dw); - btv->dev->base_address[0] = btv->bt848_adr; - } - btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ", bttv_num,btv->id, btv->revision); @@ -3588,11 +3259,7 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) } } -#ifdef __sparc__ - btv->bt848_mem=(unsigned char *)btv->bt848_adr; -#else - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); -#endif + btv->bt848_mem = ioremap(btv->bt848_adr, 0x1000); /* clear interrupt mask */ btwrite(0, BT848_INT_MASK); @@ -3657,170 +3324,6 @@ static int find_bt848(void) printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); return bttv_num; } -#else -static int find_bt848(void) -{ - short pci_index; - unsigned char command, latency; - int result; - unsigned char bus, devfn; - struct bttv *btv; - - bttv_num=0; - - if (!pcibios_present()) - { - DEBUG(printk(KERN_DEBUG "bttv%d: PCI-BIOS not present or not accessable!\n",bttv_num)); - return 0; - } - - for (pci_index = 0; - !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, - pci_index, &bus, &devfn); - ++pci_index) - { - btv=&bttvs[bttv_num]; - btv->nr = bttv_num; - btv->bus=bus; - btv->devfn=devfn; - btv->bt848_mem=NULL; - btv->vbibuf=NULL; - btv->risc_jmp=NULL; - btv->vbi_odd=NULL; - btv->vbi_even=NULL; - btv->vbiq=NULL; - btv->capq=NULL; - btv->capqo=NULL; - btv->capqe=NULL; - - btv->vbip=VBIBUF_SIZE; - - pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID, - &btv->id); - pcibios_read_config_byte(btv->bus, btv->devfn, - PCI_INTERRUPT_LINE, &btv->irq); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); - if (btv->id >= 878) - btv->i2c_command = 0x83; - else - btv->i2c_command= - (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); - - if (remap[bttv_num]) - { - if (remap[bttv_num] < 0x1000) - remap[bttv_num]<<=20; - remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK; - printk(KERN_INFO "bttv%d: remapping to : 0x%08x.\n", - bttv_num,remap[bttv_num]); - remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); - pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - remap[bttv_num]); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); - } - - btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION, - &btv->revision); - printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ", - bttv_num,btv->id, btv->revision); - printk("bus: %d, devfn: %d, ", - btv->bus, btv->devfn); - printk("irq: %d, ",btv->irq); - printk("memory: 0x%08x.\n", btv->bt848_adr); - - btv->pll.pll_crystal = 0; - btv->pll.pll_ifreq = 0; - btv->pll.pll_ofreq = 0; - btv->pll.pll_current = 0; - if (!(btv->id==848 && btv->revision==0x11)) { - switch (pll[btv->nr]) { - case 0: - /* off */ - break; - case 1: - /* 28 MHz crystal installed */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; - case 2: - /* 35 MHz crystal installed */ - btv->pll.pll_ifreq=35468950; - btv->pll.pll_crystal=BT848_IFORM_XT1; - break; - } - } - - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); - - result = request_irq(btv->irq, bttv_irq, - SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv); - if (result==-EINVAL) - { - printk(KERN_ERR "bttv%d: Bad irq number or handler\n", - bttv_num); - return -EINVAL; - } - if (result==-EBUSY) - { - printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); - return result; - } - if (result < 0) - return result; - - /* Enable bus-mastering */ - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - command|=PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - if (!(command&PCI_COMMAND_MASTER)) - { - printk(KERN_ERR "bttv%d: PCI bus-mastering could not be enabled\n",bttv_num); - return -1; - } - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, - &latency); - if (!latency) - { - latency=32; - pcibios_write_config_byte(btv->bus, btv->devfn, - PCI_LATENCY_TIMER, latency); - } - DEBUG(printk(KERN_DEBUG "bttv%d: latency: %02x\n", - bttv_num, latency)); - - btv->triton1=triton1 ? BT848_INT_ETBF : 0; - if (triton1 && btv->id >= 878) - { - triton1 = 0; - printk("bttv: Enabling 430FX compatibilty for bt878\n"); - pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command); - command|=BT878_EN_TBFX; - pcibios_write_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, command); - pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command); - if (!(command&BT878_EN_TBFX)) - { - printk("bttv: 430FX compatibility could not be enabled\n"); - return -1; - } - } - - bttv_num++; - } - if(bttv_num) - printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); - return bttv_num; -} -#endif static void release_bttv(void) { @@ -3845,17 +3348,11 @@ static void release_bttv(void) i2c_unregister_bus((&btv->i2c)); /* disable PCI bus-mastering */ -#if LINUX_VERSION_CODE >= 0x020100 + pci_read_config_byte(btv->dev, PCI_COMMAND, &command); /* Should this be &=~ ?? */ command&=~PCI_COMMAND_MASTER; pci_write_config_byte(btv->dev, PCI_COMMAND, command); -#else - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - command&=~PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); - -#endif /* unmap and free memory */ if (btv->grisc) diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index 847336116..83c9f1e8f 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -84,6 +84,7 @@ struct bttv struct video_picture picture; /* Current picture params */ struct video_audio audio_dev; /* Current audio params */ + struct semaphore lock; int user; int capuser; struct device_open open_data[MAX_OPENS]; diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index 70b75ecc1..4620a3d60 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -1,299 +1,485 @@ /* - * Logitech Bus Mouse Driver for Linux - * by James Banks + * linux/drivers/char/mouse.c * - * Mods by Matthew Dillon - * calls verify_area() - * tracks better when X is busy or paging + * Copyright (C) 1995 - 1998 Russell King + * Protocol taken from busmouse.c + * read() waiting taken from psaux.c * - * Heavily modified by David Giller - * changed from queue- to counter- driven - * hacked out a (probably incorrect) mouse_select + * Medium-level interface for quadrature or bus mice. * - * Modified again by Nathan Laredo to interface with - * 0.96c-pl1 IRQ handling changes (13JUL92) - * didn't bother touching select code. + * Currently, the majority of kernel busmice drivers in the + * kernel common code to talk to userspace. This driver + * attempts to rectify this situation by presenting a + * simple and safe interface to the mice and user. * - * Modified the select() code blindly to conform to the VFS - * requirements. 92.07.14 - Linus. Somebody should test it out. - * - * Modified by Johan Myreen to make room for other mice (9AUG92) - * removed assignment chr_fops[10] = &mouse_fops; see mouse.c - * renamed mouse_fops => bus_mouse_fops, made bus_mouse_fops public. - * renamed this file mouse.c => busmouse.c - * - * Minor addition by Cliff Matthews - * added fasync support - * - * Modularised 6-Sep-95 Philip Blundell <pjb27@cam.ac.uk> - * - * Replaced dumb busy loop with udelay() 16 Nov 95 - * Nathan Laredo <laredo@gnu.ai.mit.edu> - * - * Track I/O ports with request_region(). 12 Dec 95 Philip Blundell + * This driver: + * - is SMP safe + * - handles multiple opens + * - handles the wakeups and locking + * - has optional blocking reads */ #include <linux/module.h> - +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/busmouse.h> #include <linux/signal.h> +#include <linux/malloc.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/poll.h> #include <linux/miscdevice.h> #include <linux/random.h> -#include <linux/delay.h> -#include <linux/ioport.h> #include <linux/init.h> -#include <asm/io.h> #include <asm/uaccess.h> #include <asm/system.h> -#include <asm/irq.h> +#include <asm/io.h> -static struct mouse_status mouse; -static int mouse_irq = MOUSE_IRQ; +#include "busmouse.h" -#ifdef MODULE -MODULE_PARM(mouse_irq, "i"); -#endif +/* Uncomment this if your mouse drivers expect the kernel to + * return with EAGAIN if the mouse does not have any events + * available, even if the mouse is opened in nonblocking mode. + * + * Should this be on a per-mouse basis? If so, add an entry to + * the struct busmouse structure and add the relevent flag to + * the drivers. + */ +/*#define BROKEN_MOUSE*/ + +extern int adb_mouse_init(void); +extern int logi_busmouse_init(void); +extern int ms_bus_mouse_init(void); +extern int atixl_busmouse_init(void); +extern int amiga_mouse_init(void); +extern int atari_mouse_init(void); +extern int sun_mouse_init(void); +extern void mouse_rpc_init (void); + +struct busmouse_data { + struct miscdevice miscdev; + struct busmouse *ops; + spinlock_t lock; + + wait_queue_head_t wait; + struct fasync_struct *fasyncptr; + char active; + char buttons; + char latch_buttons; + char ready; + int dxpos; + int dypos; +}; + +#define NR_MICE 15 +#define FIRST_MOUSE 0 +#define DEV_TO_MOUSE(dev) MINOR_TO_MOUSE(MINOR(dev)) +#define MINOR_TO_MOUSE(minor) ((minor) - FIRST_MOUSE) + +static struct busmouse_data *busmouse_data[NR_MICE]; -void __init bmouse_setup(char *str, int *ints) +/* a mouse driver just has to interface with these functions + * These are !!!OLD!!! Do not use!!! + */ +void add_mouse_movement(int dx, int dy) +{ + struct busmouse_data *mse = busmouse_data[MINOR_TO_MOUSE(6)]; + + mse->dxpos += dx; + mse->dypos += dy; + mse->ready = 1; + wake_up(&mse->wait); +} + +int add_mouse_buttonchange(int set, int value) { - if (ints[0] > 0) - mouse_irq=ints[1]; + struct busmouse_data *mse = busmouse_data[MINOR_TO_MOUSE(6)]; + + mse->buttons = (mse->buttons & ~set) ^ value; + mse->ready = 1; + wake_up(&mse->wait); + return mse->buttons; } -static void mouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) +/* New interface. !!! Use this one !!! + * These routines will most probably be called from interrupt. + */ +void +busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons) { - char dx, dy; - unsigned char buttons; - - outb(MSE_READ_X_LOW, MSE_CONTROL_PORT); - dx = (inb(MSE_DATA_PORT) & 0xf); - outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT); - dx |= (inb(MSE_DATA_PORT) & 0xf) << 4; - outb(MSE_READ_Y_LOW, MSE_CONTROL_PORT ); - dy = (inb(MSE_DATA_PORT) & 0xf); - outb(MSE_READ_Y_HIGH, MSE_CONTROL_PORT); - buttons = inb(MSE_DATA_PORT); - dy |= (buttons & 0xf) << 4; - buttons = ((buttons >> 5) & 0x07); - if (dx != 0 || dy != 0 || buttons != mouse.buttons) { - add_mouse_randomness((buttons << 16) + (dy << 8) + dx); - mouse.buttons = buttons; - mouse.dx += dx; - mouse.dy -= dy; - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - - /* - * keep dx/dy reasonable, but still able to track when X (or - * whatever) must page or is busy (i.e. long waits between - * reads) - */ - if (mouse.dx < -2048) - mouse.dx = -2048; - if (mouse.dx > 2048) - mouse.dx = 2048; - - if (mouse.dy < -2048) - mouse.dy = -2048; - if (mouse.dy > 2048) - mouse.dy = 2048; - - if (mouse.fasyncptr) - kill_fasync(mouse.fasyncptr, SIGIO); + struct busmouse_data *mse = busmouse_data[mousedev]; + int changed; + + spin_lock(&mse->lock); + changed = (dx != 0 || dy != 0 || mse->buttons != buttons); + + if (changed) { + add_mouse_randomness((buttons << 16) + (dy << 8) + dx); + + mse->buttons = buttons; +// mse->latch_buttons |= buttons; + mse->dxpos += dx; + mse->dypos += dy; + mse->ready = 1; + + /* + * keep dx/dy reasonable, but still able to track when X (or + * whatever) must page or is busy (i.e. long waits between + * reads) + */ + if (mse->dxpos < -2048) + mse->dxpos = -2048; + if (mse->dxpos > 2048) + mse->dxpos = 2048; + if (mse->dypos < -2048) + mse->dypos = -2048; + if (mse->dypos > 2048) + mse->dypos = 2048; } - MSE_INT_ON(); + + spin_unlock(&mse->lock); + + if (changed) { + wake_up(&mse->wait); + + if (mse->fasyncptr) + kill_fasync(mse->fasyncptr, SIGIO); + } +} + +void +busmouse_add_movement(int mousedev, int dx, int dy) +{ + struct busmouse_data *mse = busmouse_data[mousedev]; + + busmouse_add_movementbuttons(mousedev, dx, dy, mse->buttons); } -static int fasync_mouse(int fd, struct file *filp, int on) +void +busmouse_add_buttons(int mousedev, int clear, int eor) { + struct busmouse_data *mse = busmouse_data[mousedev]; + + busmouse_add_movementbuttons(mousedev, 0, 0, (mse->buttons & ~clear) ^ eor); +} + +static int +busmouse_fasync(int fd, struct file *filp, int on) +{ + struct busmouse_data *mse = (struct busmouse_data *)filp->private_data; int retval; - retval = fasync_helper(fd, filp, on, &mouse.fasyncptr); + retval = fasync_helper(fd, filp, on, &mse->fasyncptr); if (retval < 0) return retval; return 0; } -/* - * close access to the mouse - */ - -static int close_mouse(struct inode * inode, struct file * file) +static int +busmouse_release(struct inode *inode, struct file *file) { - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; - MSE_INT_OFF(); - free_irq(mouse_irq, NULL); - MOD_DEC_USE_COUNT; - return 0; -} + struct busmouse_data *mse = (struct busmouse_data *)file->private_data; + int ret = 0; -/* - * open access to the mouse - */ + busmouse_fasync(-1, file, 0); -static int open_mouse(struct inode * inode, struct file * file) + if (--mse->active == 0) { + if (mse->ops && + mse->ops->release) + ret = mse->ops->release(inode, file); + + mse->ready = 0; + + MOD_DEC_USE_COUNT; + } + + return ret; +} + +static int +busmouse_open(struct inode *inode, struct file *file) { - if (!mouse.present) + struct busmouse_data *mse; + unsigned long flags; + unsigned int mousedev; + int ret = 0; + + mousedev = DEV_TO_MOUSE(inode->i_rdev); + if (mousedev >= NR_MICE) return -EINVAL; - if (mouse.active++) + mse = busmouse_data[mousedev]; + if (!mse) + /* shouldn't happen, but... */ + return -ENODEV; + + if (mse->ops && + mse->ops->open) + ret = mse->ops->open(inode, file); + + if (ret) + return ret; + + file->private_data = mse; + + if (mse->active++) return 0; - if (request_irq(mouse_irq, mouse_interrupt, 0, "busmouse", NULL)) { - mouse.active--; - return -EBUSY; - } - mouse.ready = 0; - mouse.dx = 0; - mouse.dy = 0; - mouse.buttons = 0x87; + MOD_INC_USE_COUNT; - MSE_INT_ON(); + + spin_lock_irqsave(&mse->lock, flags); + + mse->ready = 0; + mse->dxpos = 0; + mse->dypos = 0; + if (mse->ops) + mse->buttons = mse->ops->init_button_state; + else + mse->buttons = 7; + + spin_unlock_irqrestore(&mse->lock, flags); + return 0; } -/* - * writes are disallowed - */ - -static ssize_t write_mouse(struct file * file, - const char * buffer, size_t count, loff_t *ppos) +static ssize_t +busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { return -EINVAL; } -/* - * read mouse data. Currently never blocks. - */ - -static ssize_t read_mouse(struct file * file, - char * buffer, size_t count, loff_t *ppos) +static ssize_t +busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { - int r; - int dx; - int dy; - unsigned char buttons; - /* long flags; */ + struct busmouse_data *mse = (struct busmouse_data *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int dxpos, dypos, buttons; if (count < 3) return -EINVAL; - if ((r = verify_area(VERIFY_WRITE, buffer, count))) - return r; - if (!mouse.ready) + + spin_lock_irqsave(&mse->lock, flags); + + if (!mse->ready) { +#ifdef BROKEN_MOUSE + spin_unlock_irqrestore(&mse->lock, flags); return -EAGAIN; +#else + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore(&mse->lock, flags); + return -EAGAIN; + } + + add_wait_queue(&mse->wait, &wait); +repeat: + set_current_state(TASK_INTERRUPTIBLE); + if (!mse->ready && !signal_pending(current)) { + spin_unlock_irqrestore(&mse->lock, flags); + schedule(); + spin_lock_irqsave(&mse->lock, flags); + goto repeat; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&mse->wait, &wait); + + if (signal_pending(current)) { + spin_unlock_irqrestore(&mse->lock, flags); + return -ERESTARTSYS; + } +#endif + } - /* - * Obtain the current mouse parameters and limit as appropriate for - * the return data format. Interrupts are only disabled while - * obtaining the parameters, NOT during the puts_fs_byte() calls, - * so paging in put_user() does not effect mouse tracking. + dxpos = mse->dxpos; + dypos = mse->dypos; + buttons = mse->buttons; +// mse->latch_buttons = mse->buttons; + + if (dxpos < -127) + dxpos =- 127; + if (dxpos > 127) + dxpos = 127; + if (dypos < -127) + dypos =- 127; + if (dypos > 127) + dypos = 127; + + mse->dxpos -= dxpos; + mse->dypos -= dypos; + + /* This is something that many drivers have apparantly + * forgotten... If the X and Y positions still contain + * information, we still have some info ready for the + * user program... */ + mse->ready = mse->dxpos || mse->dypos; + + spin_unlock_irqrestore(&mse->lock, flags); + + /* Write out data to the user. Format is: + * byte 0 - identifer (0x80) and (inverted) mouse buttons + * byte 1 - X delta position +/- 127 + * byte 2 - Y delta position +/- 127 + */ + if (put_user((char)buttons | 128, buffer) || + put_user((char)dxpos, buffer + 1) || + put_user((char)dypos, buffer + 2)) + return -EFAULT; + + if (count > 3 && clear_user(buffer + 3, count - 3)) + return -EFAULT; - /* save_flags(flags); cli(); */ - disable_irq(mouse_irq); - dx = mouse.dx; - dy = mouse.dy; - if (dx < -127) - dx = -127; - if (dx > 127) - dx = 127; - if (dy < -127) - dy = -127; - if (dy > 127) - dy = 127; - buttons = mouse.buttons; - mouse.dx -= dx; - mouse.dy -= dy; - mouse.ready = 0; - enable_irq(mouse_irq); - /* restore_flags(flags); */ - - put_user(buttons | 0x80, buffer); - put_user((char)dx, buffer + 1); - put_user((char)dy, buffer + 2); - for (r = 3; r < count; r++) - put_user(0x00, buffer + r); - return r; + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + + return count; } -/* - * poll for mouse input - */ -static unsigned int mouse_poll(struct file *file, poll_table * wait) +static unsigned int +busmouse_poll(struct file *file, poll_table *wait) { - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) + struct busmouse_data *mse = (struct busmouse_data *)file->private_data; + + poll_wait(file, &mse->wait, wait); + + if (mse->ready) return POLLIN | POLLRDNORM; + return 0; } -struct file_operations bus_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - close_mouse, +struct file_operations busmouse_fops= +{ + NULL, /* busmouse_seek */ + busmouse_read, + busmouse_write, + NULL, /* busmouse_readdir */ + busmouse_poll, + NULL, /* busmouse_ioctl */ + NULL, /* busmouse_mmap */ + busmouse_open, + NULL, /* busmouse_flush */ + busmouse_release, NULL, - fasync_mouse, + busmouse_fasync, }; -static struct miscdevice bus_mouse = { - LOGITECH_BUSMOUSE, "busmouse", &bus_mouse_fops -}; +int +register_busmouse(struct busmouse *ops) +{ + unsigned int msedev = MINOR_TO_MOUSE(ops->minor); + struct busmouse_data *mse; + int ret; + + if (msedev >= NR_MICE) { + printk(KERN_ERR "busmouse: trying to allocate mouse on minor %d\n", + ops->minor); + return -EINVAL; + } + + if (busmouse_data[msedev]) + return -EBUSY; + + mse = kmalloc(sizeof(*mse), GFP_KERNEL); + if (!mse) + return -ENOMEM; -int __init bus_mouse_init(void) + memset(mse, 0, sizeof(*mse)); + + mse->miscdev.minor = ops->minor; + mse->miscdev.name = ops->name; + mse->miscdev.fops = &busmouse_fops; + mse->ops = ops; + mse->lock = (spinlock_t)SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&mse->wait); + + busmouse_data[msedev] = mse; + + ret = misc_register(&mse->miscdev); + if (!ret) + ret = msedev; + + return ret; +} + +int +unregister_busmouse(int mousedev) { - if (check_region(LOGIBM_BASE, LOGIBM_EXTENT)) { - mouse.present = 0; - return -EIO; + if (mousedev < 0) + return 0; + if (mousedev >= NR_MICE) { + printk(KERN_ERR "busmouse: trying to free mouse on" + " mousedev %d\n", mousedev); + return -EINVAL; } - outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT); - outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT); - udelay(100L); /* wait for reply from mouse */ - if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) { - mouse.present = 0; - return -EIO; + if (!busmouse_data[mousedev]) { + printk(KERN_WARNING "busmouse: trying to free free mouse" + " on mousedev %d\n", mousedev); + return -EINVAL; + } + + if (busmouse_data[mousedev]->active) { + printk(KERN_ERR "busmouse: trying to free active mouse" + " on mousedev %d\n", mousedev); + return -EINVAL; } - outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT); - MSE_INT_OFF(); - - request_region(LOGIBM_BASE, LOGIBM_EXTENT, "busmouse"); - - mouse.present = 1; - mouse.active = 0; - mouse.ready = 0; - mouse.buttons = 0x87; - mouse.dx = 0; - mouse.dy = 0; - init_waitqueue_head(&mouse.wait); - printk(KERN_INFO "Logitech bus mouse detected, using IRQ %d.\n", - mouse_irq); - misc_register(&bus_mouse); + + misc_deregister(&busmouse_data[mousedev]->miscdev); + + kfree(busmouse_data[mousedev]); + busmouse_data[mousedev] = NULL; return 0; } -#ifdef MODULE +int __init +bus_mouse_init(void) +{ +#ifdef CONFIG_LOGIBUSMOUSE + logi_busmouse_init(); +#endif +#ifdef CONFIG_MS_BUSMOUSE + ms_bus_mouse_init(); +#endif +#ifdef CONFIG_ATIXL_BUSMOUSE + atixl_busmouse_init(); +#endif +#ifdef CONFIG_AMIGAMOUSE + amiga_mouse_init(); +#endif +#ifdef CONFIG_ATARIMOUSE + atari_mouse_init(); +#endif +#ifdef CONFIG_MAC_MOUSE + mac_mouse_init(); +#endif +#ifdef CONFIG_SUN_MOUSE + sun_mouse_init(); +#endif +#ifdef CONFIG_ADBMOUSE + adb_mouse_init(); +#endif +#ifdef CONFIG_RPCMOUSE + mouse_rpc_init(); +#endif + return 0; +} + +EXPORT_SYMBOL(busmouse_add_movementbuttons); +EXPORT_SYMBOL(busmouse_add_movement); +EXPORT_SYMBOL(busmouse_add_buttons); +EXPORT_SYMBOL(register_busmouse); +EXPORT_SYMBOL(unregister_busmouse); -int init_module(void) +#ifdef MODULE +int +init_module(void) { return bus_mouse_init(); } -void cleanup_module(void) +void +cleanup_module(void) { - misc_deregister(&bus_mouse); - release_region(LOGIBM_BASE, LOGIBM_EXTENT); } #endif diff --git a/drivers/char/busmouse.h b/drivers/char/busmouse.h new file mode 100644 index 000000000..3626334d9 --- /dev/null +++ b/drivers/char/busmouse.h @@ -0,0 +1,28 @@ +/* + * linux/drivers/char/mouse.h + * + * Copyright (C) 1995 - 1998 Russell King + * + * Prototypes for generic busmouse interface + */ +#ifndef MOUSE_H +#define MOUSE_H + +struct busmouse { + int minor; + const char *name; + int (*open)(struct inode * inode, struct file * file); + int (*release)(struct inode * inode, struct file * file); + int init_button_state; +}; + +extern void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons); +extern void busmouse_add_movement(int mousedev, int dx, int dy); +extern void busmouse_add_buttons(int mousedev, int clear, int eor); + +extern int register_busmouse(struct busmouse *ops); +extern int unregister_busmouse(int mousedev); + +extern int bus_mouse_init(void); + +#endif diff --git a/drivers/char/buz.c b/drivers/char/buz.c index 1a37e2944..b535ac05e 100644 --- a/drivers/char/buz.c +++ b/drivers/char/buz.c @@ -47,7 +47,7 @@ #include <asm/segment.h> #include <linux/types.h> #include <linux/wrapper.h> -#include <asm/spinlock.h> +#include <linux/spinlock.h> #include <linux/videodev.h> @@ -374,13 +374,13 @@ static int i2c_getdataline(struct i2c_bus *bus) return (btread(ZR36057_I2CBR) >> 1) & 1; } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); @@ -2650,7 +2650,8 @@ static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_buffer v; - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) return -EPERM; if (copy_from_user(&v, arg, sizeof(v))) @@ -3219,7 +3220,7 @@ static int zr36057_init(int i) } /* i2c */ memcpy(&zr->i2c, &zoran_i2c_bus_template, sizeof(struct i2c_bus)); - sprintf(zr->i2c.name, "zoran%u%u", zr->id); + sprintf(zr->i2c.name, "zoran%u", zr->id); zr->i2c.data = zr; if (i2c_register_bus(&zr->i2c) < 0) { kfree((void *) zr->stat_com); @@ -3327,7 +3328,7 @@ static int find_zr36057(void) spin_lock_init(&zr->lock); - zr->zr36057_adr = zr->pci_dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; + zr->zr36057_adr = zr->pci_dev->resource[0].start; pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); if (zr->revision < 2) { printk(KERN_INFO "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", diff --git a/drivers/char/bw-qcam.c b/drivers/char/bw-qcam.c index 29cdac314..17f7d25dc 100644 --- a/drivers/char/bw-qcam.c +++ b/drivers/char/bw-qcam.c @@ -76,18 +76,19 @@ OTHER DEALINGS IN THE SOFTWARE. #include <linux/sched.h> #include <linux/version.h> #include <linux/videodev.h> +#include <asm/semaphore.h> #include <asm/uaccess.h> #include "bw-qcam.h" +static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines=4; /* Yield after this many during capture */ + #if LINUX_VERSION_CODE >= 0x020117 MODULE_PARM(maxpoll,"i"); MODULE_PARM(yieldlines,"i"); #endif -static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ -static unsigned int yieldlines=4; /* Yield after this many during capture */ - extern __inline__ int read_lpstatus(struct qcam_device *q) { return parport_read_status(q->pport); @@ -174,6 +175,8 @@ static struct qcam_device *qcam_init(struct parport *port) } memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + + init_MUTEX(&q->lock); q->port_mode = (QC_ANY | QC_NOTSET); q->width = 320; @@ -817,14 +820,12 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) qcam->contrast = p.contrast>>8; qcam->whitebal = p.whiteness>>8; qcam->bpp = p.depth; - + + down(&qcam->lock); qc_setscanmode(qcam); + up(&qcam->lock); qcam->status |= QC_PARAM_CHANGE; -/* parport_claim_or_block(qcam->pdev); - qc_set(qcam); - parport_release(qcam->pdev); -*/ return 0; } case VIDIOCSWIN: @@ -855,7 +856,9 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) qcam->height = 240; qcam->transfer_scale = 1; } + down(&qcam->lock); qc_setscanmode(qcam); + up(&qcam->lock); /* We must update the camera before we grab. We could just have changed the grab size */ @@ -904,7 +907,9 @@ static long qcam_read(struct video_device *v, char *buf, unsigned long count, i struct qcam_device *qcam=(struct qcam_device *)v; int len; parport_claim_or_block(qcam->pdev); - /* Probably should have a semaphore against multiple users */ + + down(&qcam->lock); + qc_reset(qcam); /* Update the camera parameters if we need to */ @@ -912,6 +917,9 @@ static long qcam_read(struct video_device *v, char *buf, unsigned long count, i qc_set(qcam); len=qc_capture(qcam, buf,count); + + up(&qcam->lock); + parport_release(qcam->pdev); return len; } diff --git a/drivers/char/bw-qcam.h b/drivers/char/bw-qcam.h index bb49cde10..723e8ad9e 100644 --- a/drivers/char/bw-qcam.h +++ b/drivers/char/bw-qcam.h @@ -55,6 +55,7 @@ struct qcam_device { struct video_device vdev; struct pardevice *pdev; struct parport *pport; + struct semaphore lock; int width, height; int bpp; int mode; diff --git a/drivers/char/c-qcam.c b/drivers/char/c-qcam.c index 3c7cc317a..90eb5405d 100644 --- a/drivers/char/c-qcam.c +++ b/drivers/char/c-qcam.c @@ -16,6 +16,7 @@ #include <linux/sched.h> #include <linux/version.h> #include <linux/videodev.h> +#include <asm/semaphore.h> #include <asm/uaccess.h> struct qcam_device { @@ -28,6 +29,7 @@ struct qcam_device { int contrast, brightness, whitebal; int top, left; unsigned int bidirectional; + struct semaphore lock; }; /* The three possible QuickCam modes */ @@ -516,10 +518,12 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) qcam->brightness = p.brightness>>8; qcam->contrast = p.contrast>>8; qcam->whitebal = p.whiteness>>8; - + + down(&qcam->lock); parport_claim_or_block(qcam->pdev); qc_setup(qcam); parport_release(qcam->pdev); + up(&qcam->lock); return 0; } case VIDIOCSWIN: @@ -564,9 +568,11 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) #endif /* Ok we figured out what to use from our wide choice */ + down(&qcam->lock); parport_claim_or_block(qcam->pdev); qc_setup(qcam); parport_release(qcam->pdev); + up(&qcam->lock); return 0; } case VIDIOCGWIN: @@ -608,10 +614,13 @@ static long qcam_read(struct video_device *v, char *buf, unsigned long count, i { struct qcam_device *qcam=(struct qcam_device *)v; int len; + + down(&qcam->lock); parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ len = qc_capture(qcam, buf,count); parport_release(qcam->pdev); + up(&qcam->lock); return len; } @@ -660,6 +669,7 @@ static struct qcam_device *qcam_init(struct parport *port) memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + init_MUTEX(&q->lock); q->width = q->ccd_width = 320; q->height = q->ccd_height = 240; q->mode = QC_MILLIONS | QC_DECIMATION_1; diff --git a/drivers/char/console.c b/drivers/char/console.c index db165915c..d0503cdcb 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -33,7 +33,7 @@ * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp> * * Merge with the abstract console driver by Geert Uytterhoeven - * <Geert.Uytterhoeven@cs.kuleuven.ac.be>, Jan 1997. + * <geert@linux-m68k.org>, Jan 1997. * * Original m68k console driver modifications by * @@ -1984,7 +1984,7 @@ void vt_console_print(struct console *co, const char * b, unsigned count) static unsigned long printing = 0; const ushort *start; ushort cnt = 0; - ushort myx = x; + ushort myx; /* console busy or not yet initialized */ if (!printable || test_and_set_bit(0, &printing)) @@ -1993,6 +1993,10 @@ void vt_console_print(struct console *co, const char * b, unsigned count) if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; + /* read `x' only after setting currecons properly (otherwise + the `x' macro will read the x of the foreground console). */ + myx = x; + if (!vc_cons_allocated(currcons)) { /* impossible */ printk("vt_console_print: tty %d not allocated ??\n", currcons+1); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index cae9bb818..f795f96bb 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ -#define BLOCKMOVE +#undef BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.2.2.3 $$Date: 1999/06/28 11:13:29 $"; +"$Revision: 2.3.2.2 $$Date: 1999/10/01 11:27:43 $"; /* * linux/drivers/char/cyclades.c @@ -9,9 +9,8 @@ static char rcsid[] = * This file contains the driver for the Cyclades Cyclom-Y multiport * serial boards. * - * Maintained by Ivan Passos (ivan@cyclades.com), - * Marcio Saito (marcio@cyclades.com) and - * Randolph Bentson (bentson@grieg.seaslug.org). + * Initially written by Randolph Bentson (bentson@grieg.seaslug.org). + * Maintained by Ivan Passos (ivan@cyclades.com). * * For Technical support and installation problems, please send e-mail * to support@cyclades.com. @@ -31,6 +30,39 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.3.2.2 1999/10/01 11:27:43 ivan + * Fixed bug in cyz_poll that would make all ports but port 0 + * unable to transmit/receive data (Cyclades-Z only); + * Implemented logic to prevent the RX buffer from being stuck with + * due to a driver / firmware race condition in interrupt op mode + * (Cyclades-Z only); + * Fixed bug in block_til_ready logic that would lead to a system crash; + * Revisited cy_close spinlock usage; + * + * Revision 2.3.2.1 1999/09/28 11:01:22 ivan + * Revisited CONFIG_PCI conditional compilation for PCI board support; + * Implemented TIOCGICOUNT and TIOCMIWAIT ioctl support; + * _Major_ cleanup on the Cyclades-Z interrupt support code / logic; + * Removed CTS handling from the driver -- this is now completely handled + * by the firmware (Cyclades-Z only); + * Flush RX on-board buffers on a port open (Cyclades-Z only); + * Fixed handling of ASYNC_SPD_* TTY flags; + * Module unload now unmaps all memory area allocated by ioremap; + * + * Revision 2.3.1.1 1999/07/15 16:45:53 ivan + * Removed CY_PROC conditional compilation; + * Implemented SMP-awareness for the driver; + * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] + * functions; + * The driver now accepts memory addresses (maddr=0xMMMMM) and IRQs + * (irq=NN) as parameters (only for ISA boards); + * Fixed bug in set_line_char that would prevent the Cyclades-Z + * ports from being configured at speeds above 115.2Kbps; + * Fixed bug in cy_set_termios that would prevent XON/XOFF flow control + * switching from working properly; + * The driver now only prints IRQ info for the Cyclades-Z if it's + * configured to work in interrupt mode; + * * Revision 2.2.2.3 1999/06/28 11:13:29 ivan * Added support for interrupt mode operation for the Z cards; * Removed the driver inactivity control for the Z; @@ -165,7 +197,7 @@ static char rcsid[] = * Change queue_task_irq_off to queue_task_irq. * The inline function queue_task_irq_off (tqueue.h) * was removed from latest releases of 2.1.x kernel. - * Use of macro __initfunc to mark the initialization + * Use of macro __init to mark the initialization * routines, so memory can be reused. * Also incorporate implementation of critical region * in function cleanup_module() created by anonymous @@ -557,7 +589,6 @@ static char rcsid[] = #undef CY_16Y_HACK #undef CY_ENABLE_MONITORING #undef CY_PCI_DEBUG -#undef CY_PROC #if 0 #define PAUSE __asm__("nop"); @@ -606,6 +637,7 @@ static char rcsid[] = #include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/spinlock.h> #include <asm/system.h> #include <asm/io.h> @@ -613,6 +645,16 @@ static char rcsid[] = #include <asm/uaccess.h> #include <asm/bitops.h> +#define CY_LOCK(info,flags) \ + do { \ + spin_lock_irqsave(&cy_card[info->card].card_lock, flags); \ + } while (0) + +#define CY_UNLOCK(info,flags) \ + do { \ + spin_unlock_irqrestore(&cy_card[info->card].card_lock, flags); \ + } while (0) + #include <linux/types.h> #include <linux/kernel.h> #include <linux/pci.h> @@ -631,7 +673,8 @@ static char rcsid[] = #define cy_put_user put_user -static unsigned long cy_get_user(unsigned long *addr) +static unsigned long +cy_get_user(unsigned long *addr) { unsigned long result = 0; int error = get_user (result, addr); @@ -668,11 +711,6 @@ static struct tty_driver cy_serial_driver, cy_callout_driver; static int serial_refcount; #ifndef CONFIG_COBALT_27 -static volatile int cy_irq_triggered; -static volatile int cy_triggered; -static int cy_wild_int_mask; -static volatile ucchar *intr_base_addr; - /* This is the address lookup table. The driver will probe for Cyclom-Y/ISA boards at all addresses in here. If you want the driver to probe addresses at a different address, add it to @@ -695,6 +733,14 @@ static unsigned char *cy_isa_addresses[] = { }; #define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) +#ifdef MODULE +static int maddr[NR_CARDS] = { 0, }; +static int irq[NR_CARDS] = { 0, }; + +MODULE_PARM(maddr, "1-" __MODULE_STRING(NR_CARDS) "l"); +MODULE_PARM(irq, "1-" __MODULE_STRING(NR_CARDS) "i"); +#endif + #endif /* CONFIG_COBALT_27 */ /* This is the per-card data structure containing address, irq, number of @@ -802,6 +848,7 @@ static int cy_chip_offset [] = static unsigned short cy_pci_nboard = 0; static unsigned short cy_isa_nboard = 0; static unsigned short cy_nboard = 0; +#ifdef CONFIG_PCI static unsigned short cy_pci_dev_id[] = { PCI_DEVICE_ID_CYCLOM_Y_Lo, /* PCI < 1Mb */ PCI_DEVICE_ID_CYCLOM_Y_Hi, /* PCI > 1Mb */ @@ -813,12 +860,13 @@ static unsigned short cy_pci_dev_id[] = { PCI_DEVICE_ID_CYCLOM_Z_Hi, /* Z PCI > 1Mb */ 0 /* end of table */ }; - +#endif static void cy_start(struct tty_struct *); static void set_line_char(struct cyclades_port *); +static int cyz_issue_cmd(struct cyclades_card *, uclong, ucchar, uclong); #ifndef CONFIG_COBALT_27 -static void cy_probe(int, void *, struct pt_regs *); +static unsigned detect_isa_irq (volatile ucchar *); #endif /* CONFIG_COBALT_27 */ #ifdef CYCLOM_SHOW_STATUS static void show_status(int); @@ -837,6 +885,9 @@ static struct timer_list cyz_timerlist = { NULL, NULL, 0, 0, cyz_poll }; +#else /* CONFIG_CYZ_INTR */ +static void cyz_rx_restart(unsigned long); +static struct timer_list cyz_rx_full_timer[NR_PORTS]; #endif /* CONFIG_CYZ_INTR */ /************************************************** @@ -941,6 +992,17 @@ do_softint(void *private_) if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { wake_up_interruptible(&info->open_wait); } +#ifdef CONFIG_CYZ_INTR + if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) { + cyz_rx_full_timer[info->line].expires = jiffies + 1; + cyz_rx_full_timer[info->line].function = cyz_rx_restart; + cyz_rx_full_timer[info->line].data = (unsigned long)info; + add_timer(&cyz_rx_full_timer[info->line]); + } +#endif + if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) { + wake_up_interruptible(&info->delta_msr_wait); + } if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ @@ -963,226 +1025,80 @@ do_softint(void *private_) command to the Cirrus chip to complete and then issues the new command. An error is returned if the previous command didn't finish within the time limit. + + This function is only called from inside spinlock-protected code. */ static int cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index) { - unsigned long flags; volatile int i; - save_flags(flags); cli(); - /* Check to see that the previous command has completed */ - for(i = 0 ; i < 100 ; i++){ - if (cy_readb(base_addr+(CyCCR<<index)) == 0){ - break; - } - udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if ( i == 100 ) { - restore_flags(flags); - return (-1); - } + /* Check to see that the previous command has completed */ + for(i = 0 ; i < 100 ; i++){ + if (cy_readb(base_addr+(CyCCR<<index)) == 0){ + break; + } + udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if (i == 100) return (-1); + + /* Issue the new command */ + cy_writeb((u_long)base_addr+(CyCCR<<index), cmd); - /* Issue the new command */ - cy_writeb((u_long)base_addr+(CyCCR<<index), cmd); - restore_flags(flags); return(0); } /* cyy_issue_cmd */ #ifndef CONFIG_COBALT_27 /* ISA interrupt detection code */ - -static int probe_ready; - -/* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ -static int -grab_all_interrupts(int dontgrab) +static unsigned +detect_isa_irq (volatile ucchar *address) { - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) - && !request_irq(i, cy_probe, - SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } - } - return irq_lines; -} /* grab_all_interrupts */ + int irq; + unsigned long irqs, flags; + int save_xir, save_car; + int index = 0; /* IRQ probing is only for ISA */ -/* - * Release all interrupts grabbed by grab_all_interrupts - */ -static void -free_all_interrupts(int irq_lines) -{ - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) { - free_irq(i,NULL); - } - } -} /* free_all_interrupts */ + /* forget possible initially masked and pending IRQ */ + irq = probe_irq_off(probe_irq_on()); -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int -check_wild_interrupts(void) -{ - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /*Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + /* Clear interrupts on the board first */ + cy_writeb((u_long)address + (Cy_ClrIntr<<index), 0); + /* Cy_ClrIntr is 0x1800 */ - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+(HZ/10); - while (time_after_eq(timeout, jiffies)) - ; - - cy_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+(HZ/10); - while (time_after_eq(timeout, jiffies)) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((cy_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; - } - } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ + irqs = probe_irq_on(); + /* Wait ... */ + udelay(5000L); -/* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. - */ -static int -get_auto_irq(volatile ucchar *address) -{ - unsigned long timeout; - volatile ucchar *base_addr; - int index; - unsigned long flags; - - index = 0; /* IRQ probing is only for ISA */ - base_addr = address; - intr_base_addr = address; - - /* - * Enable interrupts and see who answers - */ - cy_irq_triggered = 0; + /* Enable the Tx interrupts on the CD1400 */ save_flags(flags); cli(); - cy_writeb((u_long)base_addr+(CyCAR<<index), 0); - cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); - cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); - probe_ready = 1; - restore_flags(flags); - - timeout = jiffies+(HZ/50); - while (time_after_eq(timeout, jiffies)) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ - -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int -do_auto_irq(volatile ucchar *address) -{ - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; - - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + cy_writeb((u_long)address + (CyCAR<<index), 0); + cyy_issue_cmd(address, CyCHAN_CTL|CyENB_XMTR, index); - probe_ready = 0; - - cy_wild_int_mask = check_wild_interrupts(); - - irq_lines = grab_all_interrupts(cy_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(address); - if (!irq_try_2) - irq_try_2 = get_auto_irq(address); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; - } - } + cy_writeb((u_long)address + (CyCAR<<index), 0); + cy_writeb((u_long)address + (CySRER<<index), + cy_readb(address + (CySRER<<index)) | CyTxMpty); restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; -} /* do_auto_irq */ + /* Wait ... */ + udelay(5000L); -/* - * This interrupt routine is used - * while we are probing for submarines. - */ -static void -cy_probe(int irq, void *dev_id, struct pt_regs *regs) -{ - int save_xir, save_car; - int index = 0; /* probing interrupts is only for ISA */ - - if (!probe_ready) { - cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0); - return; - } + /* Check which interrupt is in use */ + irq = probe_irq_off(irqs); - cy_irq_triggered = irq; - cy_triggered |= 1 << irq; - - if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) { - save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index)); - save_car = cy_readb(intr_base_addr+(CyCAR<<index)); - cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3)); - cy_writeb((u_long)intr_base_addr+(CySRER<<index), - cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty); - cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f)); - cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car)); - } - cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0); - /* Cy_ClrIntr is 0x1800 */ - return; -} /* cy_probe */ + /* Clean up */ + save_xir = (u_char) cy_readb(address + (CyTIR<<index)); + save_car = cy_readb(address + (CyCAR<<index)); + cy_writeb((u_long)address + (CyCAR<<index), (save_xir & 0x3)); + cy_writeb((u_long)address + (CySRER<<index), + cy_readb(address + (CySRER<<index)) & ~CyTxMpty); + cy_writeb((u_long)address + (CyTIR<<index), (save_xir & 0x3f)); + cy_writeb((u_long)address + (CyCAR<<index), (save_car)); + cy_writeb((u_long)address + (Cy_ClrIntr<<index), 0); + /* Cy_ClrIntr is 0x1800 */ + return (irq > 0)? irq : 0; +} #endif /* CONFIG_COBALT_27 */ /* The real interrupt service routine is called @@ -1245,6 +1161,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) printk("cyy_interrupt: rcvd intr, chip %d\n\r", chip); #endif /* determine the channel & change to that context */ + spin_lock(&cinfo->card_lock); save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index)); channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; @@ -1252,6 +1169,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) info->last_active = jiffies; save_car = cy_readb(base_addr+(CyCAR<<index)); cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir); + spin_unlock(&cinfo->card_lock); /* if there is nowhere to put the data, discard it */ if(info->tty == 0){ @@ -1269,7 +1187,19 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask); if ( j == CyIVRRxEx ) { /* exception */ data = cy_readb(base_addr+(CyRDSR<<index)); + + /* For statistics only */ + if (data & CyBREAK) + info->icount.brk++; + else if(data & CyFRAME) + info->icount.frame++; + else if(data & CyPARITY) + info->icount.parity++; + else if(data & CyOVERRUN) + info->icount.overrun++; + if(data & info->ignore_status_mask){ + info->icount.rx++; continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ @@ -1277,9 +1207,10 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (data & info->read_status_mask){ if(data & CyBREAK){ *tty->flip.flag_buf_ptr++ = - TTY_BREAK; + TTY_BREAK; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->icount.rx++; if (info->flags & ASYNC_SAK){ do_SAK(tty); } @@ -1288,17 +1219,20 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) TTY_FRAME; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->icount.rx++; info->idle_stats.frame_errs++; }else if(data & CyPARITY){ *tty->flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->icount.rx++; info->idle_stats.parity_errs++; }else if(data & CyOVERRUN){ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; /* If the flip buffer itself is overflowing, we still lose the next incoming character. @@ -1310,6 +1244,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) TTY_NORMAL; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->icount.rx++; } info->idle_stats.overruns++; /* These two conditions may imply */ @@ -1319,15 +1254,18 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) }else{ *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; } }else{ *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; } }else{ /* there was a software buffer overrun and nothing could be done about it!!! */ + info->icount.buf_overrun++; info->idle_stats.overruns++; } } else { /* normal character reception */ @@ -1351,6 +1289,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) data = cy_readb(base_addr+(CyRDSR<<index)); *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; + info->icount.rx++; #ifdef CY_16Y_HACK udelay(10L); #endif @@ -1359,8 +1298,10 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f)); cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car)); + spin_unlock(&cinfo->card_lock); } @@ -1373,34 +1314,40 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif /* determine the channel & change to that context */ + spin_lock(&cinfo->card_lock); save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index)); channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; save_car = cy_readb(base_addr+(CyCAR<<index)); cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir); + spin_unlock(&cinfo->card_lock); /* validate the port# (as configured and open) */ if( (i < 0) || (NR_PORTS <= i) ){ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); goto txend; } info = &cy_port[i]; info->last_active = jiffies; if(info->tty == 0){ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); goto txdone; } /* load the on-chip space for outbound data */ char_count = info->xmit_fifo_size; - if(info->x_char) { /* send special char */ outch = info->x_char; cy_writeb((u_long)base_addr+(CyTDR<<index), outch); char_count--; + info->icount.tx++; info->x_char = 0; } @@ -1421,21 +1368,27 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) while (char_count-- > 0){ if (!info->xmit_cnt){ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); goto txdone; } if (info->xmit_buf == 0){ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); goto txdone; } if (info->tty->stopped || info->tty->hw_stopped){ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); goto txdone; } /* Because the Embedded Transmit Commands have @@ -1455,6 +1408,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); cy_writeb((u_long)base_addr+(CyTDR<<index), outch); + info->icount.tx++; }else{ if(char_count > 1){ info->xmit_cnt--; @@ -1463,6 +1417,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) cy_writeb((u_long)base_addr+(CyTDR<<index), outch); cy_writeb((u_long)base_addr+(CyTDR<<index), 0); + info->icount.tx++; char_count--; }else{ } @@ -1475,14 +1430,17 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } txend: /* end of service */ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyTIR<<index), (save_xir & 0x3f)); cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car)); + spin_unlock(&cinfo->card_lock); } if (status & CySRModem) { /* modem interrupt */ /* determine the channel & change to that context */ + spin_lock(&cinfo->card_lock); save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index)); channel = (u_short ) (save_xir & CyIRChannel); info = &cy_port[channel + chip * 4 @@ -1493,10 +1451,21 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) mdm_change = cy_readb(base_addr+(CyMISR<<index)); mdm_status = cy_readb(base_addr+(CyMSVR1<<index)); + spin_unlock(&cinfo->card_lock); if(info->tty == 0){/* no place for data, ignore it*/ ; }else{ + if (mdm_change & CyANY_DELTA) { + /* For statistics only */ + if (mdm_change & CyDCD) info->icount.dcd++; + if (mdm_change & CyCTS) info->icount.cts++; + if (mdm_change & CyDSR) info->icount.dsr++; + if (mdm_change & CyRI) info->icount.rng++; + + cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP); + } + if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){ @@ -1517,9 +1486,11 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* cy_start isn't used because... !!! */ info->tty->hw_stopped = 0; + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); + spin_unlock(&cinfo->card_lock); cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } @@ -1528,29 +1499,35 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* cy_stop isn't used because ... !!! */ info->tty->hw_stopped = 1; + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + spin_unlock(&cinfo->card_lock); } } } - if(mdm_status & CyDSR){ + if(mdm_change & CyDSR){ } - if(mdm_status & CyRI){ + if(mdm_change & CyRI){ } } /* end of service */ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyMIR<<index), (save_xir & 0x3f)); cy_writeb((u_long)base_addr+(CyCAR<<index), save_car); + spin_unlock(&cinfo->card_lock); } } /* end while status != 0 */ } /* end loop for chips... */ } while(had_work); /* clear interrupts */ + spin_lock(&cinfo->card_lock); cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0); /* Cy_ClrIntr is 0x1800 */ + spin_unlock(&cinfo->card_lock); } /* cyy_interrupt */ /***********************************************************/ @@ -1589,7 +1566,6 @@ cyz_fetch_msg( struct cyclades_card *cinfo, return 0; } /* cyz_fetch_msg */ - static int cyz_issue_cmd( struct cyclades_card *cinfo, uclong channel, ucchar cmd, uclong param) @@ -1624,37 +1600,184 @@ cyz_issue_cmd( struct cyclades_card *cinfo, return(0); } /* cyz_issue_cmd */ +static void +cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) +{ + struct cyclades_card *cinfo = &cy_card[info->card]; + struct tty_struct *tty = info->tty; + volatile int char_count; +#ifdef BLOCKMOVE + int small_count; +#else + char data; +#endif + volatile uclong rx_put, rx_get, rx_bufsize; -#if 0 -static int -cyz_update_channel( struct cyclades_card *cinfo, - u_long channel, u_char mode, u_char cmd) -{ - struct FIRM_ID *firm_id = - (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - struct ZFW_CTRL *zfw_ctrl; - struct CH_CTRL *ch_ctrl; +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ - if (!ISZLOADED(*cinfo)){ - return (-1); + rx_get = cy_readl(&buf_ctrl->rx_get); + rx_put = cy_readl(&buf_ctrl->rx_put); + rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + + if ( char_count ) { + info->last_active = jiffies; + info->jiffies[1] = jiffies; + +#ifdef CY_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + if(tty == 0){ + /* flush received characters */ + rx_get = (rx_get + char_count) & (rx_bufsize - 1); + info->rflush_count++; + }else{ +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while(0 < (small_count = + cy_min((rx_bufsize - rx_get), + cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), char_count)) + )) { + memcpy_fromio(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + + rx_get), + small_count); + + tty->flip.char_buf_ptr += small_count; + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, small_count); + tty->flip.flag_buf_ptr += small_count; + rx_get = (rx_get + small_count) & (rx_bufsize - 1); + char_count -= small_count; + info->icount.rx += small_count; + info->idle_stats.recv_bytes += small_count; + tty->flip.count += small_count; + } +#else + while(char_count--){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ +#ifdef CONFIG_CYZ_INTR + cy_sched_event(info, Cy_EVENT_Z_RX_FULL); +#endif + break; + } + data = cy_readb(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); + rx_get = (rx_get + 1) & (rx_bufsize - 1); + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + info->idle_stats.recv_bytes++; + info->icount.rx++; + } +#endif + info->idle_stats.recv_idle = jiffies; + queue_task(&tty->flip.tqueue, &tq_timer); + } + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, rx_get); } - zfw_ctrl = (struct ZFW_CTRL *) - (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); - ch_ctrl = zfw_ctrl->ch_ctrl; +} - cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode); +static void +cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) +{ + struct cyclades_card *cinfo = &cy_card[info->card]; + struct tty_struct *tty = info->tty; + char data; + volatile int char_count; +#ifdef BLOCKMOVE + int small_count; +#endif + volatile uclong tx_put, tx_get, tx_bufsize; - return cyz_issue_cmd(cinfo, channel, cmd, 0L); +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ -} /* cyz_update_channel */ + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_get - tx_put - 1 + tx_bufsize; + else + char_count = tx_get - tx_put - 1; + + if ( char_count ) { + + if( tty == 0 ){ + goto ztxdone; + } + + if(info->x_char) { /* send special char */ + data = info->x_char; + + cy_writeb((cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + info->x_char = 0; + char_count--; + info->icount.tx++; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#ifdef BLOCKMOVE + while(0 < (small_count = + cy_min((tx_bufsize - tx_put), + cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), + cy_min(info->xmit_cnt, char_count))))){ + + memcpy_toio((char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); + char_count -= small_count; + info->icount.tx += small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = + (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#else + while (info->xmit_cnt && char_count){ + data = info->xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); + + cy_writeb(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + char_count--; + info->icount.tx++; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } #endif + ztxdone: + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); + } +} -#ifdef CONFIG_CYZ_INTR static void -cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) +cyz_handle_cmd(struct cyclades_card *cinfo) { struct tty_struct *tty; - struct cyclades_card *cinfo; struct cyclades_port *info; static volatile struct FIRM_ID *firm_id; static volatile struct ZFW_CTRL *zfw_ctrl; @@ -1665,541 +1788,200 @@ cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) ucchar cmd; uclong param; uclong hw_ver, fw_ver; - char data; - volatile int char_count, special_count; -#ifdef BLOCKMOVE - int small_count; -#endif - volatile uclong tx_put, tx_get, tx_bufsize; - volatile uclong rx_put, rx_get, rx_bufsize; - - if((cinfo = (struct cyclades_card *)dev_id) == 0){ -#ifdef CY_DEBUG_INTERRUPTS - printk("cyz_interrupt: spurious interrupt %d\n\r", irq); -#endif - return; /* spurious interrupt */ - } + int special_count; + int delta_count; - firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (!ISZLOADED(*cinfo)) { -#ifdef CY_DEBUG_INTERRUPTS - printk("cyz_interrupt: board not yet loaded (INT %d).\n\r", irq); -#endif - return; + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + fw_ver = cy_readl(&board_ctrl->fw_version); + hw_ver = cy_readl(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0); + + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { + special_count = 0; + delta_count = 0; + info = &cy_port[channel + cinfo->first_line]; + if((tty = info->tty) == 0) { + continue; } + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - zfw_ctrl = (struct ZFW_CTRL *) - (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); - board_ctrl = &(zfw_ctrl->board_ctrl); - fw_ver = cy_readl(&board_ctrl->fw_version); - hw_ver = cy_readl(&((struct RUNTIME_9060 *) - (cinfo->ctl_addr))->mail_box_0); - - while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { - special_count = 0; - info = &cy_port[channel + cinfo->first_line]; - if((tty = info->tty) == 0) continue; - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - - switch(cmd){ + switch(cmd) { case C_CM_PR_ERROR: tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; special_count++; - break; + break; case C_CM_FR_ERROR: tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_FRAME; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; special_count++; - break; + break; case C_CM_RXBRK: tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = 0; + info->icount.rx++; special_count++; - break; + break; case C_CM_MDCD: + info->icount.dcd++; + delta_count++; if (info->flags & ASYNC_CHECK_CD){ if ((fw_ver > 241 ? - ((u_long)param) : - cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { - /* SP("Open Wakeup\n"); */ - cy_sched_event(info, - Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags - & ASYNC_CALLOUT_ACTIVE) - &&(info->flags - & ASYNC_CALLOUT_NOHUP))){ - /* SP("Hangup\n"); */ - cy_sched_event(info, - Cy_EVENT_HANGUP); + ((u_long)param) : + cy_readl(&ch_ctrl->rs_status)) & C_RS_DCD) { + cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) + &&(info->flags & ASYNC_CALLOUT_NOHUP))){ + cy_sched_event(info, Cy_EVENT_HANGUP); } } - break; + break; case C_CM_MCTS: - if (info->flags & ASYNC_CTS_FLOW) { - if(info->tty->hw_stopped){ - if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ - /* cy_start isn't used because... - HW flow is handled by the board */ - /* SP("Write Wakeup\n"); */ - cy_sched_event(info, - Cy_EVENT_WRITE_WAKEUP); - } - }else{ - if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ - /* cy_stop isn't used because - HW flow is handled by the board */ - /* SP("Write stop\n"); */ - } - } - } - break; + info->icount.cts++; + delta_count++; + break; case C_CM_MRI: - break; + info->icount.rng++; + delta_count++; + break; case C_CM_MDSR: - break; + info->icount.dsr++; + delta_count++; + break; #ifdef Z_WAKE case C_CM_IOCTLW: cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); - break; + break; #endif +#ifdef CONFIG_CYZ_INTR case C_CM_RXHIWM: case C_CM_RXNNDT: + case C_CM_INTBACK2: /* Reception Interrupt */ #ifdef CY_DEBUG_INTERRUPTS - printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", - info->card, channel); + printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", + info->card, channel); #endif - - rx_get = cy_readl(&buf_ctrl->rx_get); - rx_put = cy_readl(&buf_ctrl->rx_put); - rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - - if ( char_count ){ - -#ifdef CY_ENABLE_MONITORING - info->mon.int_count++; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - info->idle_stats.recv_bytes += char_count; - info->idle_stats.recv_idle = jiffies; - if( tty == 0){ - /* flush received characters */ - rx_get = (rx_get + char_count) & (rx_bufsize - 1); - /* SP("-"); */ - info->rflush_count++; - }else{ -#ifdef BLOCKMOVE - /* we'd like to use memcpy(t, f, n) and memset(s, c, count) - for performance, but because of buffer boundaries, there - may be several steps to the operation */ - while(0 < (small_count - = cy_min((rx_bufsize - rx_get), - cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), - char_count)))){ - - memcpy_fromio(tty->flip.char_buf_ptr, - (char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->rx_bufaddr) - + rx_get), - small_count); - - tty->flip.char_buf_ptr += small_count; - memset(tty->flip.flag_buf_ptr, - TTY_NORMAL, - small_count); - tty->flip.flag_buf_ptr += small_count; - rx_get = (rx_get + small_count) & (rx_bufsize - 1); - char_count -= small_count; - tty->flip.count += small_count; - } -#else - while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ - break; - } - data = cy_readb(cinfo->base_addr + - cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); - rx_get = (rx_get + 1) & (rx_bufsize - 1); - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; - } -#endif - queue_task(&tty->flip.tqueue, &tq_timer); - } - /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, rx_get); - } + cyz_handle_rx(info, buf_ctrl); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: case C_CM_INTBACK: /* Transmission Interrupt */ #ifdef CY_DEBUG_INTERRUPTS - printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", - info->card, channel); + printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", + info->card, channel); #endif - - tx_get = cy_readl(&buf_ctrl->tx_get); - tx_put = cy_readl(&buf_ctrl->tx_put); - tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); - if (tx_put >= tx_get) - char_count = tx_get - tx_put - 1 + tx_bufsize; - else - char_count = tx_get - tx_put - 1; - - if ( char_count ){ - - if( tty == 0 ){ - goto ztxdone; - } - - if(info->x_char) { /* send special char */ - data = info->x_char; - - cy_writeb((cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - info->x_char = 0; - char_count--; - } -#ifdef BLOCKMOVE - while(0 < (small_count - = cy_min((tx_bufsize - tx_put), - cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), - cy_min(info->xmit_cnt, char_count))))){ - - memcpy_toio((char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), - &info->xmit_buf[info->xmit_tail], - small_count); - - tx_put = (tx_put + small_count) & (tx_bufsize - 1); - char_count -= small_count; - info->xmit_cnt -= small_count; - info->xmit_tail = - (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); - } -#else - while (info->xmit_cnt && char_count){ - data = info->xmit_buf[info->xmit_tail]; - info->xmit_cnt--; - info->xmit_tail = - (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - - cy_writeb(cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, - data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - char_count--; - } - -#endif - ztxdone: - if (info->xmit_cnt < WAKEUP_CHARS) { - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); - } - /* Update tx_put */ - cy_writel(&buf_ctrl->tx_put, tx_put); - } + cyz_handle_tx(info, buf_ctrl); break; +#endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: /* should do something with this !!! */ - break; - } - if(special_count){ - queue_task(&tty->flip.tqueue, &tq_timer); - } + break; + default: + break; } + if(delta_count) + cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP); + if(special_count) + queue_task(&tty->flip.tqueue, &tq_timer); + } +} + +#ifdef CONFIG_CYZ_INTR +static void +cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cyclades_card *cinfo; + + if((cinfo = (struct cyclades_card *)dev_id) == 0){ +#ifdef CY_DEBUG_INTERRUPTS + printk("cyz_interrupt: spurious interrupt %d\n\r", irq); +#endif + return; /* spurious interrupt */ + } + + if (!ISZLOADED(*cinfo)) { +#ifdef CY_DEBUG_INTERRUPTS + printk("cyz_interrupt: board not yet loaded (IRQ%d).\n\r", irq); +#endif + return; + } + + /* Handle the interrupts */ + cyz_handle_cmd(cinfo); return; } /* cyz_interrupt */ +static void +cyz_rx_restart(unsigned long arg) +{ + struct cyclades_port *info = (struct cyclades_port *)arg; + int retval; + int card = info->card; + uclong channel = (info->line) - (cy_card[card].first_line); + + cyz_rx_full_timer[info->card].expires = jiffies + HZ; + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK2, 0L); + if (retval != 0){ + printk("cyc:cyz_rx_restart retval was %x\n", retval); + } +} + #else /* CONFIG_CYZ_INTR */ static void cyz_poll(unsigned long arg) { + struct cyclades_card *cinfo; + struct cyclades_port *info; + struct tty_struct *tty; static volatile struct FIRM_ID *firm_id; static volatile struct ZFW_CTRL *zfw_ctrl; static volatile struct BOARD_CTRL *board_ctrl; static volatile struct CH_CTRL *ch_ctrl; static volatile struct BUF_CTRL *buf_ctrl; - struct cyclades_card *cinfo; - struct cyclades_port *info; - struct tty_struct *tty; int card, port; - int char_count; -#ifdef BLOCKMOVE - int small_count; -#endif - char data; - uclong channel; - ucchar cmd; - uclong param; - uclong hw_ver, fw_ver; - volatile uclong tx_put, tx_get, tx_bufsize; - volatile uclong rx_put, rx_get, rx_bufsize; cyz_timerlist.expires = jiffies + (HZ); for (card = 0 ; card < NR_CARDS ; card++){ cinfo = &cy_card[card]; - if (!IS_CYC_Z(*cinfo)) continue; + if (!IS_CYC_Z(*cinfo)) continue; + if (!ISZLOADED(*cinfo)) continue; - firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (!ISZLOADED(*cinfo)) { + /* Skip first polling cycle to avoid racing conditions with the FW */ + if (!cinfo->intr_enabled) { + cinfo->intr_enabled = 1; continue; } + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); zfw_ctrl = (struct ZFW_CTRL *) - (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &(zfw_ctrl->board_ctrl); - fw_ver = cy_readl(&board_ctrl->fw_version); - hw_ver = cy_readl(&((struct RUNTIME_9060 *) - (cinfo->ctl_addr))->mail_box_0); - - while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1){ - char_count = 0; - info = &cy_port[ channel + cinfo->first_line ]; - if((tty = info->tty) == 0) continue; - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - info->jiffies[0] = jiffies; - - switch(cmd){ - case C_CM_PR_ERROR: - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_PARITY; - *tty->flip.char_buf_ptr++ = 0; - char_count++; - break; - case C_CM_FR_ERROR: - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_FRAME; - *tty->flip.char_buf_ptr++ = 0; - char_count++; - break; - case C_CM_RXBRK: - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_BREAK; - *tty->flip.char_buf_ptr++ = 0; - char_count++; - break; - case C_CM_MDCD: - if (info->flags & ASYNC_CHECK_CD){ - if ((fw_ver > 241 ? - ((u_long)param) : - cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { - cy_sched_event(info, - Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags - & ASYNC_CALLOUT_ACTIVE) - &&(info->flags - & ASYNC_CALLOUT_NOHUP))){ - cy_sched_event(info, - Cy_EVENT_HANGUP); - } - } - break; - case C_CM_MCTS: - if (info->flags & ASYNC_CTS_FLOW) { - if(info->tty->hw_stopped){ - if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ - /* cy_start isn't used because... - HW flow is handled by the board */ - cy_sched_event(info, - Cy_EVENT_WRITE_WAKEUP); - } - }else{ - if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ - /* cy_stop isn't used because - HW flow is handled by the board */ - } - } - } - break; - case C_CM_MRI: - break; - case C_CM_MDSR: - break; -#ifdef Z_WAKE - case C_CM_IOCTLW: - cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); - break; -#endif - case C_CM_FATAL: - /* should do something with this !!! */ - break; - } - if(char_count){ - queue_task(&tty->flip.tqueue, &tq_timer); - } - } + + cyz_handle_cmd(cinfo); + for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){ info = &cy_port[ port + cinfo->first_line ]; tty = info->tty; ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); -/* Removed due to compilation problems in Alpha systems */ -// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ - - rx_get = cy_readl(&buf_ctrl->rx_get); - rx_put = cy_readl(&buf_ctrl->rx_put); - rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - - if ( char_count ){ - - info->last_active = jiffies; - info->jiffies[1] = jiffies; - -#ifdef CY_ENABLE_MONITORING - info->mon.int_count++; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; -#endif - info->idle_stats.recv_bytes += char_count; - info->idle_stats.recv_idle = jiffies; - if( tty == 0){ - /* flush received characters */ - rx_get = (rx_get + char_count) & (rx_bufsize - 1); - info->rflush_count++; - }else{ -#ifdef BLOCKMOVE - /* we'd like to use memcpy(t, f, n) and memset(s, c, count) - for performance, but because of buffer boundaries, there - may be several steps to the operation */ - while(0 < (small_count - = cy_min((rx_bufsize - rx_get), - cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), - char_count)))){ - - memcpy_fromio(tty->flip.char_buf_ptr, - (char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->rx_bufaddr) - + rx_get), - small_count); - - tty->flip.char_buf_ptr += small_count; - memset(tty->flip.flag_buf_ptr, - TTY_NORMAL, - small_count); - tty->flip.flag_buf_ptr += small_count; - rx_get = (rx_get + small_count) & (rx_bufsize - 1); - char_count -= small_count; - tty->flip.count += small_count; - } -#else - while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ - break; - } - data = cy_readb(cinfo->base_addr + - cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); - rx_get = (rx_get + 1) & (rx_bufsize - 1); - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; - } -#endif - queue_task(&tty->flip.tqueue, &tq_timer); - } - /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, rx_get); - } - -/* Removed due to compilation problems in Alpha systems */ -// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ - - tx_get = cy_readl(&buf_ctrl->tx_get); - tx_put = cy_readl(&buf_ctrl->tx_put); - tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); - if (tx_put >= tx_get) - char_count = tx_get - tx_put - 1 + tx_bufsize; - else - char_count = tx_get - tx_put - 1; - - if ( char_count ){ - - if( tty == 0 ){ - goto ztxdone; - } - - if(info->x_char) { /* send special char */ - data = info->x_char; - - cy_writeb((cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - info->x_char = 0; - char_count--; - info->last_active = jiffies; - info->jiffies[2] = jiffies; - } -#ifdef BLOCKMOVE - while(0 < (small_count - = cy_min((tx_bufsize - tx_put), - cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), - cy_min(info->xmit_cnt, char_count))))){ - - memcpy_toio((char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), - &info->xmit_buf[info->xmit_tail], - small_count); - - tx_put = (tx_put + small_count) & (tx_bufsize - 1); - char_count -= small_count; - info->xmit_cnt -= small_count; - info->xmit_tail = - (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); - info->last_active = jiffies; - info->jiffies[2] = jiffies; - } -#else - while (info->xmit_cnt && char_count){ - data = info->xmit_buf[info->xmit_tail]; - info->xmit_cnt--; - info->xmit_tail = - (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - - cy_writeb(cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, - data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - char_count--; - info->last_active = jiffies; - info->jiffies[2] = jiffies; - } - -#endif - ztxdone: - if (info->xmit_cnt < WAKEUP_CHARS) { - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); - } - /* Update tx_put */ - cy_writel(&buf_ctrl->tx_put, tx_put); - } + cyz_handle_rx(info, buf_ctrl); + cyz_handle_tx(info, buf_ctrl); } - /* poll every 40 ms */ + /* poll every 'cyz_polling_cycle' period */ cyz_timerlist.expires = jiffies + cyz_polling_cycle; } add_timer(&cyz_timerlist); @@ -2225,11 +2007,14 @@ startup(struct cyclades_port * info) int card,chip,channel,index; unsigned long page; + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; - save_flags(flags); cli(); + CY_LOCK(info, flags); if (info->flags & ASYNC_INITIALIZED){ free_page(page); @@ -2249,10 +2034,10 @@ startup(struct cyclades_port * info) else info->xmit_buf = (unsigned char *) page; + CY_UNLOCK(info, flags); + set_line_char(info); - card = info->card; - channel = (info->line) - (cy_card[card].first_line); if (!IS_CYC_Z(cy_card[card])) { chip = channel>>2; channel &= 0x03; @@ -2265,6 +2050,8 @@ startup(struct cyclades_port * info) card, chip, channel, (long)base_addr);/**/ #endif + CY_LOCK(info, flags); + cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel); cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout @@ -2297,7 +2084,7 @@ startup(struct cyclades_port * info) info->idle_stats.recv_idle = info->idle_stats.xmit_idle = jiffies; - restore_flags(flags); + CY_UNLOCK(info, flags); } else { struct FIRM_ID *firm_id; @@ -2306,8 +2093,6 @@ startup(struct cyclades_port * info) struct CH_CTRL *ch_ctrl; int retval; - restore_flags(flags); - base_addr = (unsigned char*) (cy_card[card].base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); @@ -2326,35 +2111,42 @@ startup(struct cyclades_port * info) card, channel, (long)base_addr);/**/ #endif + CY_LOCK(info, flags); + cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); #ifdef Z_WAKE #ifdef CONFIG_CYZ_INTR cy_writel(&ch_ctrl[channel].intr_enable, C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT| C_IN_IOCTLW| - C_IN_MDCD|C_IN_MCTS); + C_IN_MDCD); #else cy_writel(&ch_ctrl[channel].intr_enable, C_IN_IOCTLW| - C_IN_MDCD|C_IN_MCTS); + C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #else #ifdef CONFIG_CYZ_INTR cy_writel(&ch_ctrl[channel].intr_enable, C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT| - C_IN_MDCD|C_IN_MCTS); + C_IN_MDCD); #else cy_writel(&ch_ctrl[channel].intr_enable, - C_IN_MDCD|C_IN_MCTS); + C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #endif /* Z_WAKE */ - retval = cyz_issue_cmd( &cy_card[card], - channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); if (retval != 0){ printk("cyc:startup(1) retval was %x\n", retval); } + /* Flush RX buffers before raising DTR and RTS */ + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_RX, 0L); + if (retval != 0){ + printk("cyc:startup(2) retval was %x\n", retval); + } + /* set timeout !!! */ /* set RTS and DTR !!! */ cy_writel(&ch_ctrl[channel].rs_control, @@ -2362,7 +2154,7 @@ startup(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:startup(2) retval was %x\n", retval); + printk("cyc:startup(3) retval was %x\n", retval); } #ifdef CY_DEBUG_DTR printk("cyc:startup raising Z DTR\n"); @@ -2380,6 +2172,8 @@ startup(struct cyclades_port * info) info->idle_stats.in_use = info->idle_stats.recv_idle = info->idle_stats.xmit_idle = jiffies; + + CY_UNLOCK(info, flags); } #ifdef CY_DEBUG_OPEN @@ -2388,7 +2182,7 @@ startup(struct cyclades_port * info) return 0; errout: - restore_flags(flags); + CY_UNLOCK(info, flags); return retval; } /* startup */ @@ -2410,21 +2204,21 @@ start_xmit( struct cyclades_port *info ) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), channel); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); - restore_flags(flags); + CY_UNLOCK(info, flags); } else { #ifdef CONFIG_CYZ_INTR int retval; - save_flags(flags); cli(); + CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK, 0L); if (retval != 0){ printk("cyc:start_xmit retval was %x\n", retval); } - restore_flags(flags); + CY_UNLOCK(info, flags); #else /* CONFIG_CYZ_INTR */ /* Don't have to do anything at this time */ #endif /* CONFIG_CYZ_INTR */ @@ -2461,7 +2255,10 @@ shutdown(struct cyclades_port * info) card, chip, channel, (long)base_addr); #endif - save_flags(flags); cli(); + CY_LOCK(info, flags); + + /* Clear delta_msr_wait queue to avoid mem leaks. */ + wake_up_interruptible(&info->delta_msr_wait); if (info->xmit_buf){ unsigned char * temp; @@ -2488,7 +2285,7 @@ shutdown(struct cyclades_port * info) set_bit(TTY_IO_ERROR, &info->tty->flags); } info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + CY_UNLOCK(info, flags); } else { struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; @@ -2513,7 +2310,7 @@ shutdown(struct cyclades_port * info) board_ctrl = &(zfw_ctrl->board_ctrl); ch_ctrl = zfw_ctrl->ch_ctrl; - save_flags(flags); cli(); + CY_LOCK(info, flags); if (info->xmit_buf){ unsigned char * temp; @@ -2529,7 +2326,7 @@ shutdown(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:shutdown retval (2) was %x\n", retval); + printk("cyc:shutdown retval was %x\n", retval); } #ifdef CY_DEBUG_DTR printk("cyc:shutdown dropping Z DTR\n"); @@ -2541,7 +2338,7 @@ shutdown(struct cyclades_port * info) } info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + CY_UNLOCK(info, flags); } #ifdef CY_DEBUG_OPEN @@ -2568,6 +2365,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, int retval; char *base_addr; + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + /* * If the device is in the middle of being closed, then block * until it's done, and then try again. @@ -2627,18 +2427,16 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("cyc block_til_ready before block: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif - save_flags(flags); cli(); + CY_LOCK(info, flags); if (!tty_hung_up_p(filp)) info->count--; - restore_flags(flags); + CY_UNLOCK(info, flags); #ifdef CY_DEBUG_COUNT printk("cyc block_til_ready: (%d): decrementing count to %d\n", current->pid, info->count); #endif info->blocked_open++; - cinfo = &cy_card[info->card]; - channel = info->line - cinfo->first_line; if (!IS_CYC_Z(*cinfo)) { chip = channel>>2; channel &= 0x03; @@ -2647,7 +2445,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, + (cy_chip_offset[chip]<<index)); while (1) { - save_flags(flags); cli(); + CY_LOCK(info, flags); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)){ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); @@ -2660,24 +2458,27 @@ block_til_ready(struct tty_struct *tty, struct file * filp, cy_readb(base_addr+(CyMSVR2<<index))); #endif } - restore_flags(flags); - current->state = TASK_INTERRUPTIBLE; + CY_UNLOCK(info, flags); + + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ){ - return ((info->flags & ASYNC_HUP_NOTIFY) ? + retval = ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } - save_flags(flags); cli(); + + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) { - restore_flags(flags); + CY_UNLOCK(info, flags); break; } - restore_flags(flags); + CY_UNLOCK(info, flags); + if (signal_pending(current)) { retval = -ERESTARTSYS; break; @@ -2720,10 +2521,10 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("cyc:block_til_ready raising Z DTR\n"); #endif - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ){ - return ((info->flags & ASYNC_HUP_NOTIFY) ? + retval = ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } @@ -2813,6 +2614,10 @@ cy_open(struct tty_struct *tty, struct file * filp) interrupts should be enabled as soon as the first open happens to one of its ports. */ if (!cy_card[info->card].intr_enabled) { + /* Enable interrupts on the PLX chip */ + cy_writew(cy_card[info->card].ctl_addr+0x68, + cy_readw(cy_card[info->card].ctl_addr+0x68)|0x0900); + /* Enable interrupts on the FW */ retval = cyz_issue_cmd(&cy_card[info->card], 0, C_CM_IRQ_ENBL, 0L); if (retval != 0){ @@ -2897,7 +2702,8 @@ cy_open(struct tty_struct *tty, struct file * filp) /* * cy_wait_until_sent() --- wait until the transmitter is empty */ -static void cy_wait_until_sent(struct tty_struct *tty, int timeout) +static void +cy_wait_until_sent(struct tty_struct *tty, int timeout) { struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned char *base_addr; @@ -2979,9 +2785,9 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) * This routine is called when a particular tty device is closed. */ static void -cy_close(struct tty_struct * tty, struct file * filp) +cy_close(struct tty_struct *tty, struct file *filp) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -2992,12 +2798,11 @@ cy_close(struct tty_struct * tty, struct file * filp) return; } - save_flags(flags); cli(); - + CY_LOCK(info, flags); /* If the TTY is being hung up, nothing to do */ if (tty_hung_up_p(filp)) { MOD_DEC_USE_COUNT; - restore_flags(flags); + CY_UNLOCK(info, flags); return; } @@ -3028,7 +2833,7 @@ cy_close(struct tty_struct * tty, struct file * filp) } if (info->count) { MOD_DEC_USE_COUNT; - restore_flags(flags); + CY_UNLOCK(info, flags); return; } info->flags |= ASYNC_CLOSING; @@ -3046,9 +2851,11 @@ cy_close(struct tty_struct * tty, struct file * filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; + CY_UNLOCK(info, flags); if (info->closing_wait != CY_CLOSING_WAIT_NONE) { tty_wait_until_sent(tty, info->closing_wait); } + CY_LOCK(info, flags); if (!IS_CYC_Z(cy_card[info->card])) { int channel = info->line - cy_card[info->card].first_line; @@ -3064,7 +2871,9 @@ cy_close(struct tty_struct * tty, struct file * filp) if (info->flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ + CY_UNLOCK(info, flags); cy_wait_until_sent(tty, info->timeout); + CY_LOCK(info, flags); } } else { #ifdef Z_WAKE @@ -3082,27 +2891,34 @@ cy_close(struct tty_struct * tty, struct file * filp) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLW, 0L); if (retval != 0){ - printk("cyc:shutdown retval (1) was %x\n", retval); + printk("cyc:cy_close retval was %x\n", retval); } + CY_UNLOCK(info, flags); interruptible_sleep_on(&info->shutdown_wait); + CY_LOCK(info, flags); } #endif } + CY_UNLOCK(info, flags); shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); + CY_LOCK(info, flags); + tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { + CY_UNLOCK(info, flags); if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); + CY_LOCK(info, flags); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); @@ -3113,7 +2929,7 @@ cy_close(struct tty_struct * tty, struct file * filp) #endif MOD_DEC_USE_COUNT; - restore_flags(flags); + CY_UNLOCK(info, flags); return; } /* cy_close */ @@ -3151,8 +2967,7 @@ cy_write(struct tty_struct * tty, int from_user, return 0; } - save_flags(flags); - + CY_LOCK(info, flags); if (from_user) { down(&tmp_buf_sem); while (1) { @@ -3168,13 +2983,11 @@ cy_write(struct tty_struct * tty, int from_user, } break; } - cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); info->xmit_cnt += c; - restore_flags(flags); buf += c; count -= c; ret += c; @@ -3182,22 +2995,20 @@ cy_write(struct tty_struct * tty, int from_user, up(&tmp_buf_sem); } else { while (1) { - cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) { - restore_flags(flags); break; } memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; - restore_flags(flags); buf += c; count -= c; ret += c; } } + CY_UNLOCK(info, flags); info->idle_stats.xmit_bytes += ret; info->idle_stats.xmit_idle = jiffies; @@ -3232,9 +3043,9 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) if (!tty || !info->xmit_buf) return; - save_flags(flags); cli(); + CY_LOCK(info, flags); if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - restore_flags(flags); + CY_UNLOCK(info, flags); return; } @@ -3243,7 +3054,7 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) info->xmit_cnt++; info->idle_stats.xmit_bytes++; info->idle_stats.xmit_idle = jiffies; - restore_flags(flags); + CY_UNLOCK(info, flags); } /* cy_put_char */ @@ -3255,9 +3066,6 @@ static void cy_flush_chars(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; #ifdef CY_DEBUG_IO printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ @@ -3270,25 +3078,7 @@ cy_flush_chars(struct tty_struct *tty) || tty->hw_stopped || !info->xmit_buf) return; - card = info->card; - channel = info->line - cy_card[card].first_line; - if (!IS_CYC_Z(cy_card[card])) { - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr - + (cy_chip_offset[chip]<<index)); - - save_flags(flags); cli(); - cy_writeb((u_long)base_addr+(CyCAR<<index), channel); - cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); - restore_flags(flags); - } else { - /* Since polling is already in place, - nothing further need be done. */ - } + start_xmit(info); } /* cy_flush_chars */ @@ -3397,6 +3187,20 @@ set_line_char(struct cyclades_port * info) cflag = info->tty->termios->c_cflag; iflag = info->tty->termios->c_iflag; + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + card = info->card; channel = (info->line) - (cy_card[card].first_line); chip_number = channel / 4; @@ -3406,7 +3210,11 @@ set_line_char(struct cyclades_port * info) index = cy_card[card].bus_index; /* baud rate */ - baud = tty_get_baud_rate(info->tty); + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + baud = info->baud; + } else { + baud = tty_get_baud_rate(info->tty); + } if (baud > CD1400_MAX_SPEED) { baud = CD1400_MAX_SPEED; } @@ -3509,7 +3317,7 @@ set_line_char(struct cyclades_port * info) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); /* tx and rx baud rate */ @@ -3599,14 +3407,15 @@ set_line_char(struct cyclades_port * info) if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } + CY_UNLOCK(info, flags); - restore_flags(flags); } else { struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; struct BOARD_CTRL *board_ctrl; struct CH_CTRL *ch_ctrl; struct BUF_CTRL *buf_ctrl; + uclong sw_flow; int retval; firm_id = (struct FIRM_ID *) @@ -3622,9 +3431,13 @@ set_line_char(struct cyclades_port * info) buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; /* baud rate */ - baud = tty_get_baud_rate(info->tty); - if (baud > CD1400_MAX_SPEED) { - baud = CD1400_MAX_SPEED; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + baud = info->baud; + } else { + baud = tty_get_baud_rate(info->tty); + } + if (baud > CYZ_MAX_SPEED) { + baud = CYZ_MAX_SPEED; } cy_writel(&ch_ctrl->comm_baud , baud); @@ -3664,14 +3477,24 @@ set_line_char(struct cyclades_port * info) /* CTS flow control flag */ if (cflag & CRTSCTS){ - info->flags |= ASYNC_CTS_FLOW; cy_writel(&ch_ctrl->hw_flow, cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); }else{ - info->flags &= ~ASYNC_CTS_FLOW; cy_writel(&ch_ctrl->hw_flow, cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); } + /* As the HW flow control is done in firmware, the driver doesn't + need to care about it */ + info->flags &= ~ASYNC_CTS_FLOW; + + /* XON/XOFF/XANY flow control flags */ + sw_flow = 0; + if (iflag & IXON){ + sw_flow |= C_FL_OXX; + if (iflag & IXANY) + sw_flow |= C_FL_OIXANY; + } + cy_writel(&ch_ctrl->sw_flow, sw_flow); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); if (retval != 0){ @@ -3686,14 +3509,6 @@ set_line_char(struct cyclades_port * info) info->flags |= ASYNC_CHECK_CD; } - if (iflag & IXON){ - cy_writel(&ch_ctrl->sw_flow, - cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX); - } else { - cy_writel(&ch_ctrl->sw_flow, - cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX); - } - if(baud == 0){ /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); @@ -3719,20 +3534,6 @@ set_line_char(struct cyclades_port * info) } } - /* - * Set up the tty->alt_speed kludge - */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - } - } /* set_line_char */ @@ -3829,12 +3630,11 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); status = cy_readb(base_addr+(CyMSVR1<<index)); status |= cy_readb(base_addr+(CyMSVR2<<index)); - restore_flags(flags); - + CY_UNLOCK(info, flags); if (info->rtsdtr_inv) { result = ((status & CyRTS) ? TIOCM_DTR : 0) @@ -3874,7 +3674,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } } - return cy_put_user(result,(unsigned long *) value); + return cy_put_user(result, value); } /* get_modem_info */ @@ -3905,17 +3705,17 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); @@ -3928,12 +3728,12 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, cy_readb(base_addr+(CyMSVR1<<index)), cy_readb(base_addr+(CyMSVR2<<index))); #endif - restore_flags(flags); + CY_UNLOCK(info, flags); } break; case TIOCMBIC: if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { @@ -3941,10 +3741,10 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); @@ -3957,31 +3757,31 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, cy_readb(base_addr+(CyMSVR1<<index)), cy_readb(base_addr+(CyMSVR2<<index))); #endif - restore_flags(flags); + CY_UNLOCK(info, flags); } break; case TIOCMSET: if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); }else{ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); @@ -3994,9 +3794,9 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, cy_readb(base_addr+(CyMSVR1<<index)), cy_readb(base_addr+(CyMSVR2<<index))); #endif - restore_flags(flags); + CY_UNLOCK(info, flags); }else{ - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); @@ -4010,7 +3810,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, cy_readb(base_addr+(CyMSVR1<<index)), cy_readb(base_addr+(CyMSVR2<<index))); #endif - restore_flags(flags); + CY_UNLOCK(info, flags); } break; default: @@ -4030,50 +3830,66 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); #ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif + CY_UNLOCK(info, flags); } break; case TIOCMBIC: if (arg & TIOCM_RTS){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); #ifdef CY_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif + CY_UNLOCK(info, flags); } break; case TIOCMSET: if (arg & TIOCM_RTS){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + CY_UNLOCK(info, flags); }else{ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + CY_UNLOCK(info, flags); } if (arg & TIOCM_DTR){ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); #ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif + CY_UNLOCK(info, flags); }else{ + CY_LOCK(info, flags); cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); #ifdef CY_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif + CY_UNLOCK(info, flags); } break; default: @@ -4082,12 +3898,14 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, }else{ return -ENODEV; } + CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM,0L); if (retval != 0){ printk("cyc:set_modem_info retval at %d was %x\n", __LINE__, retval); } + CY_UNLOCK(info, flags); } return 0; } /* set_modem_info */ @@ -4104,7 +3922,7 @@ cy_break(struct tty_struct *tty, int break_state) if (serial_paranoia_check(info, tty->device, "cy_break")) return; - save_flags(flags); cli(); + CY_LOCK(info, flags); if (!IS_CYC_Z(cy_card[info->card])) { /* Let the transmit ISR take care of this (since it requires stuffing characters into the output stream). @@ -4113,14 +3931,18 @@ cy_break(struct tty_struct *tty, int break_state) if (!info->breakon) { info->breakon = 1; if (!info->xmit_cnt) { + CY_UNLOCK(info, flags); start_xmit(info); + CY_LOCK(info, flags); } } } else { if (!info->breakoff) { info->breakoff = 1; if (!info->xmit_cnt) { + CY_UNLOCK(info, flags); start_xmit(info); + CY_LOCK(info, flags); } } } @@ -4145,8 +3967,7 @@ cy_break(struct tty_struct *tty, int break_state) } } } - restore_flags(flags); - + CY_UNLOCK(info, flags); } /* cy_break */ static int @@ -4168,6 +3989,7 @@ set_threshold(struct cyclades_port * info, unsigned long value) { unsigned char *base_addr; int card,channel,chip,index; + unsigned long flags; card = info->card; channel = info->line - cy_card[card].first_line; @@ -4181,8 +4003,11 @@ set_threshold(struct cyclades_port * info, unsigned long value) info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; - cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3); - cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + + CY_LOCK(info, flags); + cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3); + cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + CY_UNLOCK(info, flags); } else { // Nothing to do! } @@ -4236,6 +4061,7 @@ set_timeout(struct cyclades_port * info, unsigned long value) { unsigned char *base_addr; int card,channel,chip,index; + unsigned long flags; card = info->card; channel = info->line - cy_card[card].first_line; @@ -4247,7 +4073,9 @@ set_timeout(struct cyclades_port * info, unsigned long value) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff); + CY_LOCK(info, flags); + cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff); + CY_UNLOCK(info, flags); } else { // Nothing to do! } @@ -4305,7 +4133,10 @@ cy_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ int ret_val = 0; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "cy_ioctl")) return -ENODEV; @@ -4361,6 +4192,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, if (copy_to_user((void *)arg, (void *)&cy_card[info->card], sizeof (struct cyclades_card))) { ret_val = -EFAULT; + break; } ret_val = 0; break; @@ -4397,6 +4229,77 @@ cy_ioctl(struct tty_struct *tty, struct file * file, case TIOCSSERIAL: ret_val = set_serial_info(info, (struct serial_struct *) arg); break; + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + CY_LOCK(info, flags); + /* note the counters on entry */ + cprev = info->icount; + CY_UNLOCK(info, flags); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) { + return -ERESTARTSYS; + } + + CY_LOCK(info, flags); + cnow = info->icount; /* atomic copy */ + CY_UNLOCK(info, flags); + + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + return -EIO; /* no change => error */ + } + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + CY_LOCK(info, flags); + cnow = info->icount; + CY_UNLOCK(info, flags); + p_cuser = (struct serial_icounter_struct *) arg; + ret_val = put_user(cnow.cts, &p_cuser->cts); + if (ret_val) return ret_val; + ret_val = put_user(cnow.dsr, &p_cuser->dsr); + if (ret_val) return ret_val; + ret_val = put_user(cnow.rng, &p_cuser->rng); + if (ret_val) return ret_val; + ret_val = put_user(cnow.dcd, &p_cuser->dcd); + if (ret_val) return ret_val; + ret_val = put_user(cnow.rx, &p_cuser->rx); + if (ret_val) return ret_val; + ret_val = put_user(cnow.tx, &p_cuser->tx); + if (ret_val) return ret_val; + ret_val = put_user(cnow.frame, &p_cuser->frame); + if (ret_val) return ret_val; + ret_val = put_user(cnow.overrun, &p_cuser->overrun); + if (ret_val) return ret_val; + ret_val = put_user(cnow.parity, &p_cuser->parity); + if (ret_val) return ret_val; + ret_val = put_user(cnow.brk, &p_cuser->brk); + if (ret_val) return ret_val; + ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); + if (ret_val) return ret_val; + ret_val = 0; + break; default: ret_val = -ENOIOCTLCMD; } @@ -4424,7 +4327,9 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif - if (tty->termios->c_cflag == old_termios->c_cflag) + if ((tty->termios->c_cflag == old_termios->c_cflag) && + ((tty->termios->c_iflag & (IXON|IXANY)) == + (old_termios->c_iflag & (IXON|IXANY)))) return; set_line_char(info); @@ -4487,14 +4392,14 @@ cy_throttle(struct tty_struct * tty) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); } else { // Nothing to do! } @@ -4546,14 +4451,14 @@ cy_unthrottle(struct tty_struct * tty) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (info->rtsdtr_inv) { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } else { cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); } - restore_flags(flags); + CY_UNLOCK(info, flags); }else{ // Nothing to do! } @@ -4591,12 +4496,12 @@ cy_stop(struct tty_struct *tty) (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)(channel & 0x0003)); /* index channel */ cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - restore_flags(flags); + CY_UNLOCK(info, flags); } else { // Nothing to do! } @@ -4631,12 +4536,12 @@ cy_start(struct tty_struct *tty) (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); + CY_LOCK(info, flags); cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)(channel & 0x0003)); /* index channel */ cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); - restore_flags(flags); + CY_UNLOCK(info, flags); } else { // Nothing to do! } @@ -4658,19 +4563,22 @@ cy_flush_buffer(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); card = info->card; channel = (info->line) - (cy_card[card].first_line); + CY_LOCK(info, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + CY_UNLOCK(info, flags); + if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board buffers as well */ + CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_TX, 0L); if (retval != 0) { printk("cyc: flush_buffer retval was %x\n", retval); } + CY_UNLOCK(info, flags); } wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) @@ -4717,7 +4625,7 @@ cy_hangup(struct tty_struct *tty) /* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ -static unsigned short __init +static unsigned short __init cyy_init_card(volatile ucchar *true_base_addr,int index) { unsigned int chip_number; @@ -4803,14 +4711,30 @@ cyy_init_card(volatile ucchar *true_base_addr,int index) * sets global variables and return the number of ISA boards found. * --------------------------------------------------------------------- */ -static int __init cy_detect_isa(void) +static int __init +cy_detect_isa(void) { unsigned short cy_isa_irq,nboard; volatile ucchar *cy_isa_address; unsigned short i,j,cy_isa_nchan; +#ifdef MODULE + int isparam = 0; +#endif nboard = 0; +#ifdef MODULE + /* Check for module parameters */ + for(i = 0 ; i < NR_CARDS; i++) { + if (maddr[i] || i) { + isparam = 1; + cy_isa_addresses[i] = (ucchar *)maddr[i]; + } + if (!maddr[i]) + break; + } +#endif + /* scan the address table probing for Cyclom-Y/ISA boards */ for (i = 0 ; i < NR_ISA_ADDRS ; i++) { cy_isa_address = cy_isa_addresses[i]; @@ -4829,8 +4753,13 @@ static int __init cy_detect_isa(void) continue; } +#ifdef MODULE + if (isparam && irq[i]) + cy_isa_irq = irq[i]; + else +#endif /* find out the board's irq by probing */ - cy_isa_irq = do_auto_irq(cy_isa_address); + cy_isa_irq = detect_isa_irq(cy_isa_address); if (cy_isa_irq == 0) { printk("Cyclom-Y/ISA found at 0x%lx ", (unsigned long) cy_isa_address); @@ -4891,7 +4820,8 @@ static int __init cy_detect_isa(void) } /* cy_detect_isa */ #endif /* CONFIG_COBALT_27 */ -static void plx_init(uclong addr, uclong initctl) +static void +plx_init(uclong addr, uclong initctl) { /* Reset PLX */ cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000); @@ -4910,7 +4840,7 @@ static void plx_init(uclong addr, uclong initctl) * sets global variables and return the number of PCI boards found. * --------------------------------------------------------------------- */ -static int __init +static int __init cy_detect_pci(void) { #ifdef CONFIG_PCI @@ -4923,6 +4853,7 @@ cy_detect_pci(void) unsigned short device_id,dev_index = 0; uclong mailbox; uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; + unsigned char Ze_irq[NR_CARDS]; if(pci_present() == 0) { /* PCI bus not present */ return(0); @@ -4943,9 +4874,9 @@ cy_detect_pci(void) /* read PCI configuration area */ cy_pci_irq = pdev->irq; - cy_pci_addr0 = pdev->base_address[0]; - cy_pci_addr1 = pdev->base_address[1]; - cy_pci_addr2 = pdev->base_address[2]; + cy_pci_addr0 = pdev->resource[0].start; + cy_pci_addr1 = pdev->resource[1].start; + cy_pci_addr2 = pdev->resource[2].start; pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id); device_id &= ~PCI_DEVICE_ID_MASK; @@ -4960,13 +4891,11 @@ cy_detect_pci(void) printk("Cyclom-Y/PCI:found winaddr=0x%lx ctladdr=0x%lx\n", (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); #endif - cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; - cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; - if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) { + if (pdev->resource[2].flags & ~PCI_BASE_ADDRESS_IO_MASK) { printk(" Warning: PCI I/O bit incorrectly set. " "Ignoring it...\n"); - cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK; + pdev->resource[2].flags &= PCI_BASE_ADDRESS_IO_MASK; } #if defined(__alpha__) @@ -5054,6 +4983,11 @@ cy_detect_pci(void) default: /* Old boards, use PLX_9060 */ plx_init(cy_pci_addr0, 0x6c); + /* For some yet unknown reason, once the PLX9060 reloads + the EEPROM, the IRQ is lost and, thus, we have to + re-write it to the PCI config. registers. + This will remain here until we find a permanent fix. */ + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq); cy_writew(cy_pci_addr0+0x68, cy_readw(cy_pci_addr0+0x68)|0x0900); @@ -5089,21 +5023,28 @@ cy_detect_pci(void) printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); #endif - cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; #if !defined(__alpha__) cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl); #endif + /* Disable interrupts on the PLX before resetting it */ + cy_writew(cy_pci_addr0+0x68, + cy_readw(cy_pci_addr0+0x68) & ~0x0900); + plx_init(cy_pci_addr0, 0x6c); + /* For some yet unknown reason, once the PLX9060 reloads + the EEPROM, the IRQ is lost and, thus, we have to + re-write it to the PCI config. registers. + This will remain here until we find a permanent fix. */ + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq); mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0); - cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; - if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) { + if (pdev->resource[2].flags & ~PCI_BASE_ADDRESS_IO_MASK) { printk(" Warning: PCI I/O bit incorrectly set. " "Ignoring it...\n"); - cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK; + pdev->resource[2].flags &= PCI_BASE_ADDRESS_IO_MASK; } if (mailbox == ZE_V1) { #if !defined(__alpha__) @@ -5117,6 +5058,7 @@ cy_detect_pci(void) } else { Ze_addr0[ZeIndex] = cy_pci_addr0; Ze_addr2[ZeIndex] = cy_pci_addr2; + Ze_irq[ZeIndex] = cy_pci_irq; ZeIndex++; } i--; @@ -5203,24 +5145,21 @@ cy_detect_pci(void) cy_card[j].num_chips = -1; /* print message */ +#ifdef CONFIG_CYZ_INTR /* don't report IRQ if board is no IRQ */ - if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) { + if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", j+1,(ulong)cy_pci_addr2, (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1), (int)cy_pci_irq); - }else{ + else +#endif /* CONFIG_CYZ_INTR */ printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ", j+1,(ulong)cy_pci_addr2, (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1)); - } + printk("%d channels starting from port %d.\n", cy_pci_nchan,cy_next_channel); -#ifdef CONFIG_CYZ_INTR - /* Enable interrupts on the PLX chip */ - cy_writew(cy_pci_addr0+0x68, - cy_readw(cy_pci_addr0+0x68)|0x0900); -#endif /* CONFIG_CYZ_INTR */ cy_next_channel += cy_pci_nchan; } } @@ -5228,9 +5167,11 @@ cy_detect_pci(void) for (; ZeIndex != 0 && i < NR_CARDS; i++) { cy_pci_addr0 = Ze_addr0[0]; cy_pci_addr2 = Ze_addr2[0]; + cy_pci_irq = Ze_irq[0]; for (j = 0 ; j < ZeIndex-1 ; j++) { Ze_addr0[j] = Ze_addr0[j+1]; Ze_addr2[j] = Ze_addr2[j+1]; + Ze_irq[j] = Ze_irq[j+1]; } ZeIndex--; mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) @@ -5288,24 +5229,21 @@ cy_detect_pci(void) cy_card[j].num_chips = -1; /* print message */ +#ifdef CONFIG_CYZ_INTR /* don't report IRQ if board is no IRQ */ - if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) { + if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", j+1,(ulong)cy_pci_addr2, (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1), (int)cy_pci_irq); - }else{ + else +#endif /* CONFIG_CYZ_INTR */ printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ", j+1,(ulong)cy_pci_addr2, (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1)); - } + printk("%d channels starting from port %d.\n", cy_pci_nchan,cy_next_channel); -#ifdef CONFIG_CYZ_INTR - /* Enable interrupts on the PLX chip */ - cy_writew(cy_pci_addr0+0x68, - cy_readw(cy_pci_addr0+0x68)|0x0900); -#endif /* CONFIG_CYZ_INTR */ cy_next_channel += cy_pci_nchan; } if (ZeIndex != 0) { @@ -5414,7 +5352,7 @@ done: extra ports are ignored. */ -int __init +int __init cy_init(void) { struct cyclades_port *info; @@ -5424,9 +5362,6 @@ cy_init(void) unsigned long mailbox; unsigned short chip_number; int nports; -#ifdef CY_PROC - struct proc_dir_entry *ent; -#endif init_bh(CYCLADES_BH, do_cyclades_bh); @@ -5530,12 +5465,13 @@ cy_init(void) /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { cinfo = &cy_card[board]; - if (cinfo->num_chips == -1){ /* Cyclades-Z */ + if (cinfo->num_chips == -1) { /* Cyclades-Z */ number_z_boards++; mailbox = cy_readl(&((struct RUNTIME_9060 *) cy_card[board].ctl_addr)->mail_box_0); nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; cinfo->intr_enabled = 0; + spin_lock_init(&cinfo->card_lock); for (port = cinfo->first_line ; port < cinfo->first_line + nports; port++) @@ -5563,6 +5499,11 @@ cy_init(void) info->rco = 0; info->close_delay = 5*HZ/10; info->closing_wait = CLOSING_WAIT_DELAY; + info->icount.cts = info->icount.dsr = + info->icount.rng = info->icount.dcd = 0; + info->icount.rx = info->icount.tx = 0; + info->icount.frame = info->icount.parity = 0; + info->icount.overrun = info->icount.brk = 0; info->x_char = 0; info->event = 0; info->count = 0; @@ -5578,9 +5519,10 @@ cy_init(void) cy_callout_driver.init_termios; info->normal_termios = cy_serial_driver.init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->shutdown_wait); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->shutdown_wait); + init_waitqueue_head(&info->delta_msr_wait); /* info->session */ /* info->pgrp */ info->read_status_mask = 0; @@ -5594,6 +5536,7 @@ cy_init(void) continue; }else{ /* Cyclom-Y of some kind*/ index = cinfo->bus_index; + spin_lock_init(&cinfo->card_lock); for (port = cinfo->first_line ; port < cinfo->first_line + 4*cinfo->num_chips ; port++) @@ -5613,6 +5556,11 @@ cy_init(void) info->cor5 = 0; info->close_delay = 5*HZ/10; info->closing_wait = CLOSING_WAIT_DELAY; + info->icount.cts = info->icount.dsr = + info->icount.rng = info->icount.dcd = 0; + info->icount.rx = info->icount.tx = 0; + info->icount.frame = info->icount.parity = 0; + info->icount.overrun = info->icount.brk = 0; chip_number = (port - cinfo->first_line) / 4; if ((info->chip_rev = cy_readb(cinfo->base_addr + (cy_chip_offset[chip_number]<<index) + @@ -5647,9 +5595,10 @@ cy_init(void) cy_callout_driver.init_termios; info->normal_termios = cy_serial_driver.init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->shutdown_wait); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->shutdown_wait); + init_waitqueue_head(&info->delta_msr_wait); /* info->session */ /* info->pgrp */ info->read_status_mask = @@ -5671,11 +5620,6 @@ cy_init(void) } #endif /* CONFIG_CYZ_INTR */ -#ifdef CY_PROC - ent = create_proc_entry("cyclades", S_IFREG | S_IRUGO, 0); - ent->read_proc = cyclades_get_proc_info; -#endif - return 0; } /* cy_init */ @@ -5716,23 +5660,22 @@ cleanup_module(void) restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr != 0 + if (cy_card[i].base_addr != 0) { + iounmap((void *)cy_card[i].base_addr); + if (cy_card[i].ctl_addr != 0) + iounmap((void *)cy_card[i].ctl_addr); + if (cy_card[i].irq #ifndef CONFIG_CYZ_INTR - && cy_card[i].num_chips != -1 /* not a Z card */ + && cy_card[i].num_chips != -1 /* not a Z card */ #endif /* CONFIG_CYZ_INTR */ - && cy_card[i].irq) - { - free_irq(cy_card[i].irq, &cy_card[i]); + ) + free_irq(cy_card[i].irq, &cy_card[i]); } } if (tmp_buf) { free_page((unsigned long) tmp_buf); tmp_buf = NULL; } -#ifdef CY_PROC - remove_proc_entry("cyclades", 0); -#endif - } /* cleanup_module */ #else /* called by linux/init/main.c to parse command line options */ @@ -5796,8 +5739,7 @@ show_status(int line_num) printk(" session pgrp open_wait = %lx %lx %lx\n", info->session, info->pgrp, (long)info->open_wait); - - save_flags(flags); cli(); + CY_LOCK(info, flags); base_addr = (unsigned char*) (cy_card[card].base_addr @@ -5854,7 +5796,7 @@ show_status(int line_num) printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index)); printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index)); - restore_flags(flags); + CY_UNLOCK(info, flags); } /* show_status */ #endif diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c index 5d8b98e55..42fae3568 100644 --- a/drivers/char/defkeymap.c +++ b/drivers/char/defkeymap.c @@ -1,4 +1,3 @@ - /* Do not edit this file! It was automatically generated by */ /* loadkeys --mktable defkeymap.map > defkeymap.c */ @@ -25,7 +24,7 @@ u_short plain_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short shift_map[NR_KEYS] = { +u_short shift_map[NR_KEYS] = { 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, @@ -38,13 +37,13 @@ static u_short shift_map[NR_KEYS] = { 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short altgr_map[NR_KEYS] = { +u_short altgr_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, @@ -57,13 +56,13 @@ static u_short altgr_map[NR_KEYS] = { 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short ctrl_map[NR_KEYS] = { +u_short ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -82,7 +81,7 @@ static u_short ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short shift_ctrl_map[NR_KEYS] = { +u_short shift_ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, @@ -95,13 +94,13 @@ static u_short shift_ctrl_map[NR_KEYS] = { 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short alt_map[NR_KEYS] = { +u_short alt_map[NR_KEYS] = { 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, @@ -120,7 +119,7 @@ static u_short alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, }; -static u_short ctrl_alt_map[NR_KEYS] = { +u_short ctrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, @@ -133,7 +132,7 @@ static u_short ctrl_alt_map[NR_KEYS] = { 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, @@ -148,7 +147,6 @@ ushort *key_maps[MAX_NR_KEYMAPS] = { unsigned int keymap_count = 7; - /* * Philosophy: most people do not define more strings, but they who do * often want quite a lot of string space. So, we statically allocate @@ -186,7 +184,6 @@ char func_buf[] = { '\033', '[', 'P', 0, }; - char *funcbufptr = func_buf; int funcbufsize = sizeof(func_buf); int funcbufleft = 0; /* space left */ @@ -236,6 +233,7 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'A', 'A', '\305'}, {'a', 'a', '\345'}, {'A', 'E', '\306'}, {'a', 'e', '\346'}, {',', 'C', '\307'}, {',', 'c', '\347'}, + {'\'', 'C', '\307'}, {'\'', 'c', '\347'}, {'`', 'E', '\310'}, {'`', 'e', '\350'}, {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, {'^', 'E', '\312'}, {'^', 'e', '\352'}, @@ -262,4 +260,4 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'s', 'z', '\337'}, {'i', 'j', '\377'}, }; -unsigned int accent_table_size = 68; +unsigned int accent_table_size = 70; diff --git a/drivers/char/dn_keyb.c b/drivers/char/dn_keyb.c index eceb50f6c..97b237ca3 100644 --- a/drivers/char/dn_keyb.c +++ b/drivers/char/dn_keyb.c @@ -55,7 +55,8 @@ static void debug_keyb_timer_handler(unsigned long ignored); static u_char debug_buf1[4096],debug_buf2[4096],*debug_buf=&debug_buf1[0]; static u_char *shadow_buf=&debug_buf2[0]; static short debug_buf_count=0; -static int debug_buf_overrun=0,debug_timer_running=0,debug_buffer_updated=0; +static int debug_buf_overrun=0,debug_timer_running=0; +static unsigned long debug_buffer_updated=0; static struct timer_list debug_keyb_timer = { NULL, NULL, 0, 0, debug_keyb_timer_handler }; #endif @@ -280,7 +281,7 @@ static void debug_keyb_timer_handler(unsigned long ignored) { u_char *swap; short length,i; - if((jiffies-debug_buffer_updated) > 100) { + if (time_after(jiffies, debug_buffer_updated + 100)) { save_flags(flags); cli(); length=debug_buf_count; @@ -422,7 +423,7 @@ static void dn_keyb_process_key_event(unsigned char scancode) { !(prev_scancode==DNKEY_CTRL || prev_scancode==DNKEY_LSHIFT || prev_scancode==DNKEY_RSHIFT || prev_scancode==DNKEY_REPT || prev_scancode==DNKEY_LALT || prev_scancode==DNKEY_RALT)) { - if(jiffies-lastkeypress > DNKEY_REPEAT_DELAY) { + if (time_after(jiffies, lastkeypress + DNKEY_REPEAT_DELAY)) { /* printk("handle_scancode: %02x\n",prev_scancode); */ handle_scancode(prev_scancode, 1); } @@ -491,8 +492,8 @@ static void dn_keyb_int(int irq, void *dummy, struct pt_regs *fp) { debug_buf[debug_buf_count++]=data; debug_buffer_updated=jiffies; if(!debug_timer_running) { - add_timer(&debug_keyb_timer); debug_keyb_timer.expires=jiffies+10; + add_timer(&debug_keyb_timer); debug_timer_running=1; } } diff --git a/drivers/char/drm/.cvsignore b/drivers/char/drm/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/char/drm/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile new file mode 100644 index 000000000..d72b726c0 --- /dev/null +++ b/drivers/char/drm/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the drm device driver. This driver provides support for +# the Direct Rendering Infrastructure (DRI) in XFree86 4.x. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +L_TARGET := libdrm.a + +L_OBJS := init.o memory.o proc.o auth.o context.o drawable.o bufs.o \ + lists.o lock.o ioctl.o fops.o vm.o dma.o + +M_OBJS := + +ifdef CONFIG_DRM_GAMMA +M_OBJS += gamma.o +endif + +include $(TOPDIR)/Rules.make + +gamma.o: gamma_drv.o gamma_dma.o $(L_TARGET) + $(LD) $(LD_RFLAG) -r -o $@ gamma_drv.o gamma_dma.o -L. -ldrm diff --git a/drivers/char/drm/README.drm b/drivers/char/drm/README.drm new file mode 100644 index 000000000..fc3c680e1 --- /dev/null +++ b/drivers/char/drm/README.drm @@ -0,0 +1,39 @@ + +The Direct Rendering Manager (drm) is a device-independent kernel-level +device driver that provides support for the XFree86 Direct Rendering +Infrastructure (DRI). + +The DRM supports the Direct Rendering Infrastructure (DRI) in four major +ways: + + 1. The DRM provides synchronized access to the graphics hardware via + the use of an optimized two-tiered lock. + + 2. The DRM enforces the DRI security policy for access to the graphics + hardware by only allowing authenticated X11 clients access to + restricted regions of memory. + + 3. The DRM provides a generic DMA engine, complete with multiple + queues and the ability to detect the need for an OpenGL context + switch. + + 4. The DRM is extensible via the use of small device-specific modules + that rely extensively on the API exported by the DRM module. + + +Documentation on the DRI is available from: + http://precisioninsight.com/piinsights.html + +For specific information about kernel-level support, see: + + The Direct Rendering Manager, Kernel Support for the Direct Rendering + Infrastructure + http://precisioninsight.com/dr/drm.html + + Hardware Locking for the Direct Rendering Infrastructure + http://precisioninsight.com/dr/locking.html + + A Security Analysis of the Direct Rendering Infrastructure + http://precisioninsight.com/dr/security.html + + diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c new file mode 100644 index 000000000..2561264ff --- /dev/null +++ b/drivers/char/drm/auth.c @@ -0,0 +1,161 @@ +/* auth.c -- IOCTLs for authentication -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/gen_ioctl.c,v 1.2 1999/06/27 14:08:27 dawes Exp $ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_hash_magic(drm_magic_t magic) +{ + return magic & (DRM_HASH_SIZE-1); +} + +static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic) +{ + drm_file_t *retval = NULL; + drm_magic_entry_t *pt; + int hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { + if (pt->priv->authenticated) continue; + if (pt->magic == magic) { + retval = pt->priv; + break; + } + } + up(&dev->struct_sem); + return retval; +} + +int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) +{ + int hash; + drm_magic_entry_t *entry; + + DRM_DEBUG("%d\n", magic); + + hash = drm_hash_magic(magic); + entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); + if (!entry) return -ENOMEM; + entry->magic = magic; + entry->priv = priv; + entry->next = NULL; + + down(&dev->struct_sem); + if (dev->magiclist[hash].tail) { + dev->magiclist[hash].tail->next = entry; + dev->magiclist[hash].tail = entry; + } else { + dev->magiclist[hash].head = entry; + dev->magiclist[hash].tail = entry; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_remove_magic(drm_device_t *dev, drm_magic_t magic) +{ + drm_magic_entry_t *prev = NULL; + drm_magic_entry_t *pt; + int hash; + + DRM_DEBUG("%d\n", magic); + hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { + if (pt->magic == magic) { + if (dev->magiclist[hash].head == pt) { + dev->magiclist[hash].head = pt->next; + } + if (dev->magiclist[hash].tail == pt) { + dev->magiclist[hash].tail = prev; + } + if (prev) { + prev->next = pt->next; + } + up(&dev->struct_sem); + return 0; + } + } + up(&dev->struct_sem); + + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + + return -EINVAL; +} + +int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + static drm_magic_t sequence = 0; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + + /* Find unique magic */ + if (priv->magic) { + auth.magic = priv->magic; + } else { + spin_lock(&lock); + do { + if (!sequence) ++sequence; /* reserve 0 */ + auth.magic = sequence++; + } while (drm_find_file(dev, auth.magic)); + spin_unlock(&lock); + priv->magic = auth.magic; + drm_add_magic(dev, priv, auth.magic); + } + + DRM_DEBUG("%u\n", auth.magic); + copy_to_user_ret((drm_auth_t *)arg, &auth, sizeof(auth), -EFAULT); + return 0; +} + +int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + drm_file_t *file; + + copy_from_user_ret(&auth, (drm_auth_t *)arg, sizeof(auth), -EFAULT); + DRM_DEBUG("%u\n", auth.magic); + if ((file = drm_find_file(dev, auth.magic))) { + file->authenticated = 1; + drm_remove_magic(dev, auth.magic); + return 0; + } + return -EINVAL; +} diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c new file mode 100644 index 000000000..32469a83a --- /dev/null +++ b/drivers/char/drm/bufs.c @@ -0,0 +1,527 @@ +/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 22:48:10 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "linux/un.h" + + /* Compute order. Can be made faster. */ +int drm_order(unsigned long size) +{ + int order; + unsigned long tmp; + + for (order = 0, tmp = size; tmp >>= 1; ++order); + if (size & ~(1 << order)) ++order; + return order; +} + +int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + + if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */ + + map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); + if (!map) return -ENOMEM; + if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EFAULT; + } + + DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type); + if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: + if (map->offset + map->size < map->offset + || map->offset < virt_to_phys(high_memory)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } +#ifdef CONFIG_MTRR + if (map->type == _DRM_FRAME_BUFFER + || (map->flags & _DRM_WRITE_COMBINING)) { + map->mtrr = mtrr_add(map->offset, map->size, + MTRR_TYPE_WRCOMB, 1); + } +#endif + map->handle = drm_ioremap(map->offset, map->size); + break; + + + case _DRM_SHM: + DRM_DEBUG("%ld %d\n", map->size, drm_order(map->size)); + map->handle = (void *)drm_alloc_pages(drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + if (!map->handle) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if (map->flags & _DRM_CONTAINS_LOCK) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; + default: + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + + down(&dev->struct_sem); + if (dev->maplist) { + ++dev->map_count; + dev->maplist = drm_realloc(dev->maplist, + (dev->map_count-1) + * sizeof(*dev->maplist), + dev->map_count + * sizeof(*dev->maplist), + DRM_MEM_MAPS); + } else { + dev->map_count = 1; + dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist), + DRM_MEM_MAPS); + } + dev->maplist[dev->map_count-1] = map; + up(&dev->struct_sem); + + copy_to_user_ret((drm_map_t *)arg, map, sizeof(*map), -EFAULT); + if (map->type != _DRM_SHM) { + copy_to_user_ret(&((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset), + -EFAULT); + } + return 0; +} + +int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_info_t *)arg, + sizeof(request), + -EFAULT); + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + copy_to_user_ret(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count), + -EFAULT); + copy_to_user_ret(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size), + -EFAULT); + copy_to_user_ret(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark), + -EFAULT); + copy_to_user_ret(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark), + -EFAULT); + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + copy_to_user_ret((drm_buf_info_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return 0; +} + +int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_free_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + copy_from_user_ret(&idx, + &request.list[i], + sizeof(idx), + -EFAULT); + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_map_t *)arg, + sizeof(request), + -EFAULT); + + if (request.count >= dma->buf_count) { + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } +done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + copy_to_user_ret((drm_buf_map_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return retcode; +} diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c new file mode 100644 index 000000000..b85aed606 --- /dev/null +++ b/drivers/char/drm/context.c @@ -0,0 +1,308 @@ +/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/gen_ioctl.c,v 1.2 1999/06/27 14:08:27 dawes Exp $ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx) +{ + DRM_DEBUG("\n"); + + if (atomic_read(&q->use_count) != 1 + || atomic_read(&q->finalization) + || atomic_read(&q->block_count)) { + DRM_ERROR("New queue is already in use: u%d f%d b%d\n", + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count)); + } + + atomic_set(&q->finalization, 0); + atomic_set(&q->block_count, 0); + atomic_set(&q->block_read, 0); + atomic_set(&q->block_write, 0); + atomic_set(&q->total_queued, 0); + atomic_set(&q->total_flushed, 0); + atomic_set(&q->total_locks, 0); + + init_waitqueue_head(&q->write_queue); + init_waitqueue_head(&q->read_queue); + init_waitqueue_head(&q->flush_queue); + + q->flags = ctx->flags; + + drm_waitlist_create(&q->waitlist, dev->dma->buf_count); + + return 0; +} + + +/* drm_alloc_queue: +PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not + disappear (so all deallocation must be done after IOCTLs are off) + 2) dev->queue_count < dev->queue_slots + 3) dev->queuelist[i].use_count == 0 and + dev->queuelist[i].finalization == 0 if i not in use +POST: 1) dev->queuelist[i].use_count == 1 + 2) dev->queue_count < dev->queue_slots */ + +static int drm_alloc_queue(drm_device_t *dev) +{ + int i; + drm_queue_t *queue; + int oldslots; + int newslots; + /* Check for a free queue */ + for (i = 0; i < dev->queue_count; i++) { + atomic_inc(&dev->queuelist[i]->use_count); + if (atomic_read(&dev->queuelist[i]->use_count) == 1 + && !atomic_read(&dev->queuelist[i]->finalization)) { + DRM_DEBUG("%d (free)\n", i); + return i; + } + atomic_dec(&dev->queuelist[i]->use_count); + } + /* Allocate a new queue */ + down(&dev->struct_sem); + + queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); + memset(queue, 0, sizeof(*queue)); + atomic_set(&queue->use_count, 1); + + ++dev->queue_count; + if (dev->queue_count >= dev->queue_slots) { + oldslots = dev->queue_slots * sizeof(*dev->queuelist); + if (!dev->queue_slots) dev->queue_slots = 1; + dev->queue_slots *= 2; + newslots = dev->queue_slots * sizeof(*dev->queuelist); + + dev->queuelist = drm_realloc(dev->queuelist, + oldslots, + newslots, + DRM_MEM_QUEUES); + if (!dev->queuelist) { + up(&dev->struct_sem); + DRM_DEBUG("out of memory\n"); + return -ENOMEM; + } + } + dev->queuelist[dev->queue_count-1] = queue; + + up(&dev->struct_sem); + DRM_DEBUG("%d (new)\n", dev->queue_count - 1); + return dev->queue_count - 1; +} + +int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Init kernel's context and get a new one. */ + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + ctx.handle = drm_alloc_queue(dev); + } + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + DRM_DEBUG("%d\n", ctx.handle); + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + if (DRM_BUFCOUNT(&q->waitlist)) { + atomic_dec(&q->use_count); + return -EBUSY; + } + + q->flags = ctx.flags; + + atomic_dec(&q->use_count); + return 0; +} + +int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + ctx.flags = q->flags; + atomic_dec(&q->use_count); + + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + + return 0; +} + +int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return drm_context_switch(dev, dev->last_context, ctx.handle); +} + +int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + drm_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); /* Mark queue as unused (pending + finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + /* Remove queued buffers */ + while ((buf = drm_waitlist_get(&q->waitlist))) { + drm_free_buffer(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wake_up_interruptible(&q->read_queue); + wake_up_interruptible(&q->write_queue); + wake_up_interruptible(&q->flush_queue); + + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + return 0; +} diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c new file mode 100644 index 000000000..0ccbfc146 --- /dev/null +++ b/drivers/char/drm/dma.c @@ -0,0 +1,530 @@ +/* dma.c -- DMA IOCTL and function support -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 13:06:51 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/dma.c,v 1.6 1999/08/20 20:00:53 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#include <linux/interrupt.h> /* For task queue support */ + +void drm_dma_setup(drm_device_t *dev) +{ + int i; + + dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER); + memset(dev->dma, 0, sizeof(*dev->dma)); + for (i = 0; i <= DRM_MAX_ORDER; i++) + memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); +} + +void drm_dma_takedown(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i, j; + + if (!dma) return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].seg_count) { + DRM_DEBUG("order %d: buf_count = %d," + " seg_count = %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].seg_count); + for (j = 0; j < dma->bufs[i].seg_count; j++) { + drm_free_pages(dma->bufs[i].seglist[j], + dma->bufs[i].page_order, + DRM_MEM_DMA); + } + drm_free(dma->bufs[i].buflist, + dma->buf_count + * sizeof(*dma->bufs[0].buflist), + DRM_MEM_BUFS); + drm_free(dma->bufs[i].seglist, + dma->buf_count + * sizeof(*dma->bufs[0].seglist), + DRM_MEM_SEGS); + drm_freelist_destroy(&dma->bufs[i].freelist); + } + } + + if (dma->buflist) { + drm_free(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + DRM_MEM_BUFS); + } + + if (dma->pagelist) { + drm_free(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + } + drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); + dev->dma = NULL; +} + +#if DRM_DMA_HISTOGRAM +/* This is slow, but is useful for debugging. */ +int drm_histogram_slot(unsigned long count) +{ + int value = DRM_DMA_HISTOGRAM_INITIAL; + int slot; + + for (slot = 0; + slot < DRM_DMA_HISTOGRAM_SLOTS; + ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) { + if (count < value) return slot; + } + return DRM_DMA_HISTOGRAM_SLOTS - 1; +} + +void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf) +{ + cycles_t queued_to_dispatched; + cycles_t dispatched_to_completed; + cycles_t completed_to_freed; + int q2d, d2c, c2f, q2c, q2f; + + if (buf->time_queued) { + queued_to_dispatched = (buf->time_dispatched + - buf->time_queued); + dispatched_to_completed = (buf->time_completed + - buf->time_dispatched); + completed_to_freed = (buf->time_freed + - buf->time_completed); + + q2d = drm_histogram_slot(queued_to_dispatched); + d2c = drm_histogram_slot(dispatched_to_completed); + c2f = drm_histogram_slot(completed_to_freed); + + q2c = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed); + q2f = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed + + completed_to_freed); + + atomic_inc(&dev->histo.total); + atomic_inc(&dev->histo.queued_to_dispatched[q2d]); + atomic_inc(&dev->histo.dispatched_to_completed[d2c]); + atomic_inc(&dev->histo.completed_to_freed[c2f]); + + atomic_inc(&dev->histo.queued_to_completed[q2c]); + atomic_inc(&dev->histo.queued_to_freed[q2f]); + + } + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +} +#endif + +void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!buf) return; + + buf->waiting = 0; + buf->pending = 0; + buf->pid = 0; + buf->used = 0; +#if DRM_DMA_HISTOGRAM + buf->time_completed = get_cycles(); +#endif + if (waitqueue_active(&buf->dma_wait)) { + wake_up_interruptible(&buf->dma_wait); + } else { + /* If processes are waiting, the last one + to wake will put the buffer on the free + list. If no processes are waiting, we + put the buffer on the freelist here. */ + drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf); + } +} + +void drm_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->pid == pid) { + switch (dma->buflist[i]->list) { + case DRM_LIST_NONE: + drm_free_buffer(dev, dma->buflist[i]); + break; + case DRM_LIST_WAIT: + dma->buflist[i]->list = DRM_LIST_RECLAIM; + break; + default: + /* Buffer already on hardware. */ + break; + } + } + } +} + +int drm_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + drm_queue_t *q; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new >= dev->queue_count) { + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + q = dev->queuelist[new]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + atomic_dec(&q->use_count); + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + drm_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + atomic_dec(&q->use_count); + + return 0; +} + +int drm_context_switch_complete(drm_device_t *dev, int new) +{ + drm_device_dma_t *dma = dev->dma; + + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("Cannot free lock\n"); + } + } + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up_interruptible(&dev->context_wait); + + return 0; +} + +void drm_clear_next_buffer(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + dma->next_buffer = NULL; + if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) { + wake_up_interruptible(&dma->next_queue->flush_queue); + } + dma->next_queue = NULL; +} + + +int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long)) +{ + int i; + int candidate = -1; + int j = jiffies; + + if (!dev) { + DRM_ERROR("No device\n"); + return -1; + } + if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) { + /* This only happens between the time the + interrupt is initialized and the time + the queues are initialized. */ + return -1; + } + + /* Doing "while locked" DMA? */ + if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { + return DRM_KERNEL_CONTEXT; + } + + /* If there are buffers on the last_context + queue, and we have not been executing + this context very long, continue to + execute this context. */ + if (dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j + && DRM_WAITCOUNT(dev, dev->last_context)) { + return dev->last_context; + } + + /* Otherwise, find a candidate */ + for (i = dev->last_checked + 1; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + + if (candidate < 0) { + for (i = 0; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + } + + if (wrapper + && candidate >= 0 + && candidate != dev->last_context + && dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j) { + if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) { + del_timer(&dev->timer); + dev->timer.function = wrapper; + dev->timer.data = (unsigned long)dev; + dev->timer.expires = dev->last_switch+DRM_TIME_SLICE; + add_timer(&dev->timer); + } + return -1; + } + + return candidate; +} + + +int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d) +{ + int i; + drm_queue_t *q; + drm_buf_t *buf; + int idx; + int while_locked = 0; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + DRM_DEBUG("%d\n", d->send_count); + + if (d->flags & _DRM_DMA_WHILE_LOCKED) { + int context = dev->lock.hw_lock->lock; + + if (!_DRM_LOCK_IS_HELD(context)) { + DRM_ERROR("No lock held during \"while locked\"" + " request\n"); + return -EINVAL; + } + if (d->context != _DRM_LOCKING_CONTEXT(context) + && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) { + DRM_ERROR("Lock held by %d while %d makes" + " \"while locked\" request\n", + _DRM_LOCKING_CONTEXT(context), + d->context); + return -EINVAL; + } + q = dev->queuelist[DRM_KERNEL_CONTEXT]; + while_locked = 1; + } else { + q = dev->queuelist[d->context]; + } + + + atomic_inc(&q->use_count); + if (atomic_read(&q->block_write)) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&q->write_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + if (!atomic_read(&q->block_write)) break; + schedule(); + if (signal_pending(current)) { + atomic_dec(&q->use_count); + return -EINTR; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->write_queue, &entry); + } + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + atomic_dec(&q->use_count); + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->list != DRM_LIST_NONE) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer %d on list %d\n", + current->pid, buf->idx, buf->list); + } + buf->used = d->send_sizes[i]; + buf->while_locked = while_locked; + buf->context = d->context; + if (!buf->used) { + DRM_ERROR("Queueing 0 length buffer\n"); + } + if (buf->pending) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + if (buf->waiting) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + buf->waiting = 1; + if (atomic_read(&q->use_count) == 1 + || atomic_read(&q->finalization)) { + drm_free_buffer(dev, buf); + } else { + drm_waitlist_put(&q->waitlist, buf); + atomic_inc(&q->total_queued); + } + } + atomic_dec(&q->use_count); + + return 0; +} + +static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d, + int order) +{ + int i; + drm_buf_t *buf; + drm_device_dma_t *dma = dev->dma; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = drm_freelist_get(&dma->bufs[order].freelist, + d->flags & _DRM_DMA_WAIT); + if (!buf) break; + if (buf->pending || buf->waiting) { + DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n", + buf->idx, + buf->pid, + buf->waiting, + buf->pending); + } + buf->pid = current->pid; + copy_to_user_ret(&d->request_indices[i], + &buf->idx, + sizeof(buf->idx), + -EFAULT); + copy_to_user_ret(&d->request_sizes[i], + &buf->total, + sizeof(buf->total), + -EFAULT); + ++d->granted_count; + } + return 0; +} + + +int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma) +{ + int order; + int retcode = 0; + int tmp_order; + + order = drm_order(dma->request_size); + + dma->granted_count = 0; + retcode = drm_dma_get_buffers_of_order(dev, dma, order); + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_SMALLER_OK)) { + for (tmp_order = order - 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order >= DRM_MIN_ORDER; + --tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_LARGER_OK)) { + for (tmp_order = order + 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order <= DRM_MAX_ORDER; + ++tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + return 0; +} diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c new file mode 100644 index 000000000..6a8fc753a --- /dev/null +++ b/drivers/char/drm/drawable.c @@ -0,0 +1,50 @@ +/* drawable.c -- IOCTLs for drawables -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_draw_t draw; + + draw.handle = 0; /* NOOP */ + DRM_DEBUG("%d\n", draw.handle); + copy_to_user_ret((drm_draw_t *)arg, &draw, sizeof(draw), -EFAULT); + return 0; +} + +int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return 0; /* NOOP */ +} diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h new file mode 100644 index 000000000..2251462e8 --- /dev/null +++ b/drivers/char/drm/drm.h @@ -0,0 +1,277 @@ +/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 13:08:18 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/drm.h,v 1.2 1999/06/27 14:08:21 dawes Exp $ + * + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#include <asm/ioctl.h> /* For _IO* macros */ + +#define DRM_PROC_DEVICES "/proc/devices" +#define DRM_PROC_MISC "/proc/misc" +#define DRM_PROC_DRM "/proc/drm" +#define DRM_DEV_DRM "/dev/drm" +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 + + +#define DRM_NAME "drm" /* Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /* At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /* Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /* How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000 /* Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000 /* Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned long drm_handle_t; +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + + +typedef struct drm_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + size_t name_len; /* Length of name buffer */ + char *name; /* Name of driver */ + size_t date_len; /* Length of date buffer */ + char *date; /* User-space buffer to hold date */ + size_t desc_len; /* Length of desc buffer */ + char *desc; /* User-space buffer to hold desc */ +} drm_version_t; + +typedef struct drm_unique { + size_t unique_len; /* Length of unique */ + char *unique; /* Unique name for driver instantiation */ +} drm_unique_t; + +typedef struct drm_list { + int count; /* Length of user-space structures */ + drm_version_t *version; +} drm_list_t; + +typedef struct drm_block { + int unused; +} drm_block_t; + +typedef struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +} drm_control_t; + +typedef enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2 /* shared, cached */ +} drm_map_type_t; + +typedef enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /* Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /* shared, cached, locked */ + _DRM_KERNEL = 0x08, /* kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /* use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20 /* SHM page that contains lock */ +} drm_map_flags_t; + +typedef struct drm_map { + unsigned long offset; /* Requested physical address (0 for SAREA)*/ + unsigned long size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + void *handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm_map_t; + +typedef enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /* Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /* Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /* Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /* Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /* Halt all current queues */ +} drm_lock_flags_t; + +typedef struct drm_lock { + int context; + drm_lock_flags_t flags; +} drm_lock_t; + +typedef enum drm_dma_flags { /* These values *MUST* match xf86drm.h */ + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /* Block until buffer dispatched. + Note, the buffer may not yet have + been processed by the hardware -- + getting a hardware lock with the + hardware quiescent will ensure + that the buffer has been + processed. */ + _DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /* High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /* Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /* Smaller-than-requested buffers ok */ + _DRM_DMA_LARGER_OK = 0x40 /* Larger-than-requested buffers ok */ +} drm_dma_flags_t; + +typedef struct drm_buf_desc { + int count; /* Number of buffers of this size */ + int size; /* Size in bytes */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + enum { + DRM_PAGE_ALIGN = 0x01 /* Align on page boundaries for DMA */ + } flags; +} drm_buf_desc_t; + +typedef struct drm_buf_info { + int count; /* Entries in list */ + drm_buf_desc_t *list; +} drm_buf_info_t; + +typedef struct drm_buf_free { + int count; + int *list; +} drm_buf_free_t; + +typedef struct drm_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + void *address; /* Address of buffer */ +} drm_buf_pub_t; + +typedef struct drm_buf_map { + int count; /* Length of buflist */ + void *virtual; /* Mmaped area in user-virtual */ + drm_buf_pub_t *list; /* Buffer information */ +} drm_buf_map_t; + +typedef struct drm_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + int *send_indices; /* List of handles to buffers */ + int *send_sizes; /* Lengths of data to send */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + int *request_indices; /* Buffer information */ + int *request_sizes; + int granted_count; /* Number of buffers granted */ +} drm_dma_t; + +typedef enum { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +} drm_ctx_flags_t; + +typedef struct drm_ctx { + drm_context_t handle; + drm_ctx_flags_t flags; +} drm_ctx_t; + +typedef struct drm_ctx_res { + int count; + drm_ctx_t *contexts; +} drm_ctx_res_t; + +typedef struct drm_draw { + drm_drawable_t handle; +} drm_draw_t; + +typedef struct drm_auth { + drm_magic_t magic; +} drm_auth_t; + +typedef struct drm_irq_busid { + int irq; + int busnum; + int devnum; + int funcnum; +} drm_irq_busid_t; + +#define DRM_IOCTL_BASE 'd' +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) +#define DRM_IOW(nr,size) _IOW(DRM_IOCTL_BASE,nr,size) +#define DRM_IOWR(nr,size) _IOWR(DRM_IOCTL_BASE,nr,size) + + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t) +#define DRM_IOCTL_GET_MAGIC DRM_IOW( 0x02, drm_auth_t) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, drm_irq_busid_t) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, drm_block_t) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, drm_block_t) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, drm_control_t) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, drm_map_t) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, drm_buf_desc_t) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, drm_buf_desc_t) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm_buf_info_t) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm_buf_map_t) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm_buf_free_t) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, drm_ctx_t) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, drm_ctx_t) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, drm_ctx_t) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, drm_ctx_t) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, drm_ctx_t) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, drm_ctx_t) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, drm_ctx_res_t) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, drm_draw_t) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, drm_draw_t) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, drm_dma_t) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, drm_lock_t) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, drm_lock_t) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, drm_lock_t) + +#endif diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h new file mode 100644 index 000000000..75103b074 --- /dev/null +++ b/drivers/char/drm/drmP.h @@ -0,0 +1,584 @@ +/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 13:04:33 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/drmP.h,v 1.2 1999/06/27 14:08:24 dawes Exp $ + * + */ + +#ifndef _DRM_P_H_ +#define _DRM_P_H_ + +#ifdef __KERNEL__ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/file.h> +#include <linux/pci.h> +#include <linux/wrapper.h> +#include <linux/version.h> +#include <asm/io.h> +#include <asm/mman.h> +#include <asm/uaccess.h> +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif +#include "drm.h" + +#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then + also include looping detection. */ +#define DRM_DMA_HISTOGRAM 1 /* Make histogram of DMA latency. */ + +#define DRM_HASH_SIZE 16 /* Size of key hash table */ +#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ +#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ +#define DRM_LOOPING_LIMIT 5000000 +#define DRM_BSZ 1024 /* Buffer size for /dev/drm? output */ +#define DRM_TIME_SLICE (HZ/20) /* Time slice for GLXContexts */ +#define DRM_LOCK_SLICE 1 /* Time slice for lock, in jiffies */ + +#define DRM_FLAG_DEBUG 0x01 +#define DRM_FLAG_NOCTX 0x02 + +#define DRM_MEM_DMA 0 +#define DRM_MEM_SAREA 1 +#define DRM_MEM_DRIVER 2 +#define DRM_MEM_MAGIC 3 +#define DRM_MEM_IOCTLS 4 +#define DRM_MEM_MAPS 5 +#define DRM_MEM_VMAS 6 +#define DRM_MEM_BUFS 7 +#define DRM_MEM_SEGS 8 +#define DRM_MEM_PAGES 9 +#define DRM_MEM_FILES 10 +#define DRM_MEM_QUEUES 11 +#define DRM_MEM_CMDS 12 +#define DRM_MEM_MAPPINGS 13 +#define DRM_MEM_BUFLISTS 14 + + /* Backward compatibility section */ +#ifndef _PAGE_PWT + /* The name of _PAGE_WT was changed to + _PAGE_PWT in Linux 2.2.6 */ +#define _PAGE_PWT _PAGE_WT +#endif + /* Wait queue declarations changes in 2.3.1 */ +#ifndef DECLARE_WAITQUEUE +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL } +typedef struct wait_queue *wait_queue_head_t; +#define init_waitqueue_head(q) *q = NULL; +#endif + +#define __drm_dummy_lock(lock) (*(__volatile__ unsigned int *)lock) +#define _DRM_CAS(lock,old,new,__ret) \ + do { \ + int __dummy; /* Can't mark eax as clobbered */ \ + __asm__ __volatile__( \ + "lock ; cmpxchg %4,%1\n\t" \ + "setnz %0" \ + : "=d" (__ret), \ + "=m" (__drm_dummy_lock(lock)), \ + "=a" (__dummy) \ + : "2" (old), \ + "r" (new)); \ + } while (0) + + + + /* Macros to make printk easier */ +#define DRM_ERROR(fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) +#define DRM_MEM_ERROR(area, fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \ + drm_mem_stats[area].name , ##arg) +#define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) + +#if DRM_DEBUG_CODE +#define DRM_DEBUG(fmt, arg...) \ + do { \ + if (drm_flags&DRM_FLAG_DEBUG) \ + printk(KERN_DEBUG \ + "[" DRM_NAME ":" __FUNCTION__ "] " fmt , \ + ##arg); \ + } while (0) +#else +#define DRM_DEBUG(fmt, arg...) do { } while (0) +#endif + +#define DRM_PROC_LIMIT (PAGE_SIZE-80) + +#define DRM_PROC_PRINT(fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) return len; + +#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) { ret; return len; } + + /* Internal types and structures */ +#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define DRM_MIN(a,b) ((a)<(b)?(a):(b)) +#define DRM_MAX(a,b) ((a)>(b)?(a):(b)) + +#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) +#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) +#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) + +typedef int drm_ioctl_t(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +typedef struct drm_ioctl_desc { + drm_ioctl_t *func; + int auth_needed; + int root_only; +} drm_ioctl_desc_t; + +typedef struct drm_devstate { + pid_t owner; /* X server pid holding x_lock */ + +} drm_devstate_t; + +typedef struct drm_magic_entry { + drm_magic_t magic; + struct drm_file *priv; + struct drm_magic_entry *next; +} drm_magic_entry_t; + +typedef struct drm_magic_head { + struct drm_magic_entry *head; + struct drm_magic_entry *tail; +} drm_magic_head_t; + +typedef struct drm_vma_entry { + struct vm_area_struct *vma; + struct drm_vma_entry *next; + pid_t pid; +} drm_vma_entry_t; + +typedef struct drm_buf { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int order; /* log-base-2(total) */ + int used; /* Amount of buffer in use (for DMA) */ + unsigned long offset; /* Byte offset (used internally) */ + void *address; /* Address of buffer */ + struct drm_buf *next; /* Kernel-only: used for free list */ + __volatile__ int waiting; /* On kernel DMA queue */ + __volatile__ int pending; /* On hardware DMA queue */ + wait_queue_head_t dma_wait; /* Processes waiting */ + pid_t pid; /* PID of holding process */ + int context; /* Kernel queue for this buffer */ + int while_locked;/* Dispatch this buffer while locked */ + enum { + DRM_LIST_NONE = 0, + DRM_LIST_FREE = 1, + DRM_LIST_WAIT = 2, + DRM_LIST_PEND = 3, + DRM_LIST_PRIO = 4, + DRM_LIST_RECLAIM = 5 + } list; /* Which list we're on */ +#if DRM_DMA_HISTOGRAM + cycles_t time_queued; /* Queued to kernel DMA queue */ + cycles_t time_dispatched; /* Dispatched to hardware */ + cycles_t time_completed; /* Completed by hardware */ + cycles_t time_freed; /* Back on freelist */ +#endif +} drm_buf_t; + +#if DRM_DMA_HISTOGRAM +#define DRM_DMA_HISTOGRAM_SLOTS 9 +#define DRM_DMA_HISTOGRAM_INITIAL 10 +#define DRM_DMA_HISTOGRAM_NEXT(current) ((current)*10) +typedef struct drm_histogram { + atomic_t total; + + atomic_t queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t dma[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t schedule[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t ctx[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lacq[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lhld[DRM_DMA_HISTOGRAM_SLOTS]; +} drm_histogram_t; +#endif + + /* bufs is one longer than it has to be */ +typedef struct drm_waitlist { + int count; /* Number of possible buffers */ + drm_buf_t **bufs; /* List of pointers to buffers */ + drm_buf_t **rp; /* Read pointer */ + drm_buf_t **wp; /* Write pointer */ + drm_buf_t **end; /* End pointer */ + spinlock_t read_lock; + spinlock_t write_lock; +} drm_waitlist_t; + +typedef struct drm_freelist { + int initialized; /* Freelist in use */ + atomic_t count; /* Number of free buffers */ + drm_buf_t *next; /* End pointer */ + + wait_queue_head_t waiting; /* Processes waiting on free bufs */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + atomic_t wfh; /* If waiting for high mark */ +} drm_freelist_t; + +typedef struct drm_buf_entry { + int buf_size; + int buf_count; + drm_buf_t *buflist; + int seg_count; + int page_order; + unsigned long *seglist; + + drm_freelist_t freelist; +} drm_buf_entry_t; + +typedef struct drm_hw_lock { + __volatile__ unsigned int lock; + char padding[60]; /* Pad to cache line */ +} drm_hw_lock_t; + +typedef struct drm_file { + int authenticated; + int minor; + pid_t pid; + uid_t uid; + drm_magic_t magic; + unsigned long ioctl_count; + struct drm_file *next; + struct drm_file *prev; + struct drm_device *dev; +} drm_file_t; + + +typedef struct drm_queue { + atomic_t use_count; /* Outstanding uses (+1) */ + atomic_t finalization; /* Finalization in progress */ + atomic_t block_count; /* Count of processes waiting */ + atomic_t block_read; /* Queue blocked for reads */ + wait_queue_head_t read_queue; /* Processes waiting on block_read */ + atomic_t block_write; /* Queue blocked for writes */ + wait_queue_head_t write_queue; /* Processes waiting on block_write */ + atomic_t total_queued; /* Total queued statistic */ + atomic_t total_flushed;/* Total flushes statistic */ + atomic_t total_locks; /* Total locks statistics */ + drm_ctx_flags_t flags; /* Context preserving and 2D-only */ + drm_waitlist_t waitlist; /* Pending buffers */ + wait_queue_head_t flush_queue; /* Processes waiting until flush */ +} drm_queue_t; + +typedef struct drm_lock_data { + drm_hw_lock_t *hw_lock; /* Hardware lock */ + pid_t pid; /* PID of lock holder (0=kernel) */ + wait_queue_head_t lock_queue; /* Queue of blocked processes */ + unsigned long lock_time; /* Time of last lock in jiffies */ +} drm_lock_data_t; + +typedef struct drm_device_dma { + /* Performance Counters */ + atomic_t total_prio; /* Total DRM_DMA_PRIORITY */ + atomic_t total_bytes; /* Total bytes DMA'd */ + atomic_t total_dmas; /* Total DMA buffers dispatched */ + + atomic_t total_missed_dma; /* Missed drm_do_dma */ + atomic_t total_missed_lock; /* Missed lock in drm_do_dma */ + atomic_t total_missed_free; /* Missed drm_free_this_buffer */ + atomic_t total_missed_sched;/* Missed drm_dma_schedule */ + + atomic_t total_tried; /* Tried next_buffer */ + atomic_t total_hit; /* Sent next_buffer */ + atomic_t total_lost; /* Lost interrupt */ + + drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; + int buf_count; + drm_buf_t **buflist; /* Vector of pointers info bufs */ + int seg_count; + int page_count; + unsigned long *pagelist; + unsigned long byte_count; + + /* DMA support */ + drm_buf_t *this_buffer; /* Buffer being sent */ + drm_buf_t *next_buffer; /* Selected buffer to send */ + drm_queue_t *next_queue; /* Queue from which buffer selected*/ + wait_queue_head_t waiting; /* Processes waiting on free bufs */ +} drm_device_dma_t; + +typedef struct drm_device { + const char *name; /* Simple driver name */ + char *unique; /* Unique identifier: e.g., busid */ + int unique_len; /* Length of unique field */ + dev_t device; /* Device number for mknod */ + char *devname; /* For /proc/interrupts */ + + int blocked; /* Blocked due to VC switch? */ + struct proc_dir_entry *root; /* Root for this device's entries */ + + /* Locks */ + spinlock_t count_lock; /* For inuse, open_count, buf_use */ + struct semaphore struct_sem; /* For others */ + + /* Usage Counters */ + int open_count; /* Outstanding files open */ + atomic_t ioctl_count; /* Outstanding IOCTLs pending */ + atomic_t vma_count; /* Outstanding vma areas open */ + int buf_use; /* Buffers in use -- cannot alloc */ + atomic_t buf_alloc; /* Buffer allocation in progress */ + + /* Performance Counters */ + atomic_t total_open; + atomic_t total_close; + atomic_t total_ioctl; + atomic_t total_irq; /* Total interruptions */ + atomic_t total_ctx; /* Total context switches */ + + atomic_t total_locks; + atomic_t total_unlocks; + atomic_t total_contends; + atomic_t total_sleeps; + + /* Authentication */ + drm_file_t *file_first; + drm_file_t *file_last; + drm_magic_head_t magiclist[DRM_HASH_SIZE]; + + /* Memory management */ + drm_map_t **maplist; /* Vector of pointers to regions */ + int map_count; /* Number of mappable regions */ + + drm_vma_entry_t *vmalist; /* List of vmas (for debugging) */ + drm_lock_data_t lock; /* Information on hardware lock */ + + /* DMA queues (contexts) */ + int queue_count; /* Number of active DMA queues */ + int queue_reserved; /* Number of reserved DMA queues */ + int queue_slots; /* Actual length of queuelist */ + drm_queue_t **queuelist; /* Vector of pointers to DMA queues */ + drm_device_dma_t *dma; /* Optional pointer for DMA support */ + + /* Context support */ + int irq; /* Interrupt used by board */ + __volatile__ int context_flag; /* Context swapping flag */ + __volatile__ int interrupt_flag;/* Interruption handler flag */ + __volatile__ int dma_flag; /* DMA dispatch flag */ + struct timer_list timer; /* Timer for delaying ctx switch */ + wait_queue_head_t context_wait; /* Processes waiting on ctx switch */ + int last_checked; /* Last context checked for DMA */ + int last_context; /* Last current context */ + unsigned long last_switch; /* jiffies at last context switch */ + struct tq_struct tq; + cycles_t ctx_start; + cycles_t lck_start; +#if DRM_DMA_HISTOGRAM + drm_histogram_t histo; +#endif + + /* Callback to X server for context switch + and for heavy-handed reset. */ + char buf[DRM_BSZ]; /* Output buffer */ + char *buf_rp; /* Read pointer */ + char *buf_wp; /* Write pointer */ + char *buf_end; /* End pointer */ + struct fasync_struct *buf_async;/* Processes waiting for SIGIO */ + wait_queue_head_t buf_readers; /* Processes waiting to read */ + wait_queue_head_t buf_writers; /* Processes waiting to ctx switch */ +} drm_device_t; + + + /* Internal function definitions */ + + /* Misc. support (init.c) */ +extern int drm_flags; +extern void drm_parse_options(char *s); + + + /* Device support (fops.c) */ +extern int drm_open_helper(struct inode *inode, struct file *filp, + drm_device_t *dev); +extern int drm_flush(struct file *filp); +extern int drm_release(struct inode *inode, struct file *filp); +extern int drm_fasync(int fd, struct file *filp, int on); +extern ssize_t drm_read(struct file *filp, char *buf, size_t count, + loff_t *off); +extern int drm_write_string(drm_device_t *dev, const char *s); + + /* Mapping support (vm.c) */ +extern unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern void drm_vm_open(struct vm_area_struct *vma); +extern void drm_vm_close(struct vm_area_struct *vma); +extern int drm_mmap_dma(struct file *filp, + struct vm_area_struct *vma); +extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); + + + /* Proc support (proc.c) */ +extern int drm_proc_init(drm_device_t *dev); +extern int drm_proc_cleanup(void); + + /* Memory management support (memory.c) */ +extern void drm_mem_init(void); +extern int drm_mem_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +extern void *drm_alloc(size_t size, int area); +extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, + int area); +extern char *drm_strdup(const char *s, int area); +extern void drm_strfree(const char *s, int area); +extern void drm_free(void *pt, size_t size, int area); +extern unsigned long drm_alloc_pages(int order, int area); +extern void drm_free_pages(unsigned long address, int order, + int area); +extern void *drm_ioremap(unsigned long offset, unsigned long size); +extern void drm_ioremapfree(void *pt, unsigned long size); + + /* Buffer management support (bufs.c) */ +extern int drm_order(unsigned long size); +extern int drm_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Buffer list management support (lists.c) */ +extern int drm_waitlist_create(drm_waitlist_t *bl, int count); +extern int drm_waitlist_destroy(drm_waitlist_t *bl); +extern int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf); +extern drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl); + +extern int drm_freelist_create(drm_freelist_t *bl, int count); +extern int drm_freelist_destroy(drm_freelist_t *bl); +extern int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, + drm_buf_t *buf); +extern drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block); + + /* DMA support (gen_dma.c) */ +extern void drm_dma_setup(drm_device_t *dev); +extern void drm_dma_takedown(drm_device_t *dev); +extern void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf); +extern void drm_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int drm_context_switch(drm_device_t *dev, int old, int new); +extern int drm_context_switch_complete(drm_device_t *dev, int new); +extern void drm_wakeup(drm_device_t *dev, drm_buf_t *buf); +extern void drm_clear_next_buffer(drm_device_t *dev); +extern int drm_select_queue(drm_device_t *dev, + void (*wrapper)(unsigned long)); +extern int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma); +extern int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma); +#if DRM_DMA_HISTOGRAM +extern int drm_histogram_slot(unsigned long count); +extern void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf); +#endif + + + /* Misc. IOCTL support (ioctl.c) */ +extern int drm_irq_busid(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_setunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Context IOCTL support (context.c) */ +extern int drm_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Drawable IOCTL support (drawable.c) */ +extern int drm_adddraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmdraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Authentication IOCTL support (auth.c) */ +extern int drm_add_magic(drm_device_t *dev, drm_file_t *priv, + drm_magic_t magic); +extern int drm_remove_magic(drm_device_t *dev, drm_magic_t magic); +extern int drm_getmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_authmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Locking IOCTL support (lock.c) */ +extern int drm_block(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_unblock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_lock_take(__volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_transfer(__volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, + unsigned int context); +extern int drm_finish(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_flush_unblock(drm_device_t *dev, int context, + drm_lock_flags_t flags); +extern int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags); +#endif +#endif diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c new file mode 100644 index 000000000..12ab4d628 --- /dev/null +++ b/drivers/char/drm/fops.c @@ -0,0 +1,204 @@ +/* fops.c -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 11:31:46 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +/* drm_open is called whenever a process opens /dev/drm. */ + +int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); + memset(priv, 0, sizeof(*priv)); + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_flush(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d, f_count = %d\n", + current->pid, dev->device, dev->open_count, atomic_read(&filp->f_count)); + return 0; +} + +/* drm_release is called whenever a process closes /dev/drm*. Linux calls + this only if any mappings have been closed. */ + +int drm_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + drm_reclaim_buffers(dev, priv->pid); + + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); + + return 0; +} + +int drm_fasync(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + copy_to_user_ret(buf, dev->buf_rp, cur, -EINVAL); + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int drm_write_string(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c new file mode 100644 index 000000000..66d828a71 --- /dev/null +++ b/drivers/char/drm/gamma_dma.c @@ -0,0 +1,802 @@ +/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 11:31:45 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/gamma_dma.c,v 1.8 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "gamma_drv.h" + +#include <linux/interrupt.h> /* For task queue support */ + + +/* WARNING!!! MAGIC NUMBER!!! The number of regions already added to the + kernel must be specified here. Currently, the number is 2. This must + match the order the X server uses for instantiating register regions , + or must be passed in a new ioctl. */ +#define GAMMA_REG(reg) \ + (2 \ + + ((reg < 0x1000) \ + ? 0 \ + : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3)))) + +#define GAMMA_OFF(reg) \ + ((reg < 0x1000) \ + ? reg \ + : ((reg < 0x10000) \ + ? (reg - 0x1000) \ + : ((reg < 0x11000) \ + ? (reg - 0x10000) \ + : (reg - 0x11000)))) + +#define GAMMA_BASE(reg) ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle) +#define GAMMA_ADDR(reg) (GAMMA_BASE(reg) + GAMMA_OFF(reg)) +#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg) +#define GAMMA_READ(reg) GAMMA_DEREF(reg) +#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0) + +#define GAMMA_BROADCASTMASK 0x9378 +#define GAMMA_COMMANDINTENABLE 0x0c48 +#define GAMMA_DMAADDRESS 0x0028 +#define GAMMA_DMACOUNT 0x0030 +#define GAMMA_FILTERMODE 0x8c00 +#define GAMMA_GCOMMANDINTFLAGS 0x0c50 +#define GAMMA_GCOMMANDMODE 0x0c40 +#define GAMMA_GCOMMANDSTATUS 0x0c60 +#define GAMMA_GDELAYTIMER 0x0c38 +#define GAMMA_GDMACONTROL 0x0060 +#define GAMMA_GINTENABLE 0x0808 +#define GAMMA_GINTFLAGS 0x0810 +#define GAMMA_INFIFOSPACE 0x0018 +#define GAMMA_OUTFIFOWORDS 0x0020 +#define GAMMA_OUTPUTFIFO 0x2000 +#define GAMMA_SYNC 0x8c40 +#define GAMMA_SYNC_TAG 0x0188 + +static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address, + unsigned long length) +{ + GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address)); + while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4) + ; + GAMMA_WRITE(GAMMA_DMACOUNT, length / 4); +} + +static inline void gamma_dma_quiescent(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) + ; + GAMMA_WRITE(GAMMA_BROADCASTMASK, 3); + GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); + GAMMA_WRITE(GAMMA_SYNC, 0); + + /* Read from first MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); + + + /* Read from second MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG); +} + +static inline void gamma_dma_ready(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; +} + +static inline int gamma_dma_is_ready(drm_device_t *dev) +{ + return !GAMMA_READ(GAMMA_DMACOUNT); +} + +static void gamma_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_device_dma_t *dma = dev->dma; + + atomic_inc(&dev->total_irq); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */ + GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8); + GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001); + if (gamma_dma_is_ready(dev)) { + /* Free previous buffer */ + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_free); + return; + } + if (dma->this_buffer) { + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = NULL; + } + clear_bit(0, &dev->dma_flag); + + /* Dispatch new buffer */ + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +/* Only called by gamma_dma_schedule. */ +static int gamma_do_dma(drm_device_t *dev, int locked) +{ + unsigned long address; + unsigned long length; + drm_buf_t *buf; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t dma_start, dma_stop; +#endif + + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_dma); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dma_start = get_cycles(); +#endif + + if (!dma->next_buffer) { + DRM_ERROR("No next_buffer\n"); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + buf = dma->next_buffer; + address = (unsigned long)buf->address; + length = buf->used; + + DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", + buf->context, buf->idx, length); + + if (buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + if (!length) { + DRM_ERROR("0 length buffer\n"); + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return 0; + } + + if (!gamma_dma_is_ready(dev)) { + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + + if (buf->while_locked) { + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Dispatching buffer %d from pid %d" + " \"while locked\", but no lock held\n", + buf->idx, buf->pid); + } + } else { + if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + atomic_inc(&dma->total_missed_lock); + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + } + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + /* PRE: dev->last_context != buf->context */ + if (drm_context_switch(dev, dev->last_context, buf->context)) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + retcode = -EBUSY; + goto cleanup; + + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + } + + drm_clear_next_buffer(dev); + buf->pending = 1; + buf->waiting = 0; + buf->list = DRM_LIST_PEND; +#if DRM_DMA_HISTOGRAM + buf->time_dispatched = get_cycles(); +#endif + + gamma_dma_dispatch(dev, address, length); + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = buf; + + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (!buf->while_locked && !dev->context_flag && !locked) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +cleanup: + + clear_bit(0, &dev->dma_flag); + +#if DRM_DMA_HISTOGRAM + dma_stop = get_cycles(); + atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]); +#endif + + return retcode; +} + +static void gamma_dma_schedule_timer_wrapper(unsigned long dev) +{ + gamma_dma_schedule((drm_device_t *)dev, 0); +} + +static void gamma_dma_schedule_tq_wrapper(void *dev) +{ + gamma_dma_schedule(dev, 0); +} + +int gamma_dma_schedule(drm_device_t *dev, int locked) +{ + int next; + drm_queue_t *q; + drm_buf_t *buf; + int retcode = 0; + int processed = 0; + int missed; + int expire = 20; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t schedule_start; +#endif + + if (test_and_set_bit(0, &dev->interrupt_flag)) { + /* Not reentrant */ + atomic_inc(&dma->total_missed_sched); + return -EBUSY; + } + missed = atomic_read(&dma->total_missed_sched); + +#if DRM_DMA_HISTOGRAM + schedule_start = get_cycles(); +#endif + +again: + if (dev->context_flag) { + clear_bit(0, &dev->interrupt_flag); + return -EBUSY; + } + if (dma->next_buffer) { + /* Unsent buffer that was previously + selected, but that couldn't be sent + because the lock could not be obtained + or the DMA engine wasn't ready. Try + again. */ + atomic_inc(&dma->total_tried); + if (!(retcode = gamma_do_dma(dev, locked))) { + atomic_inc(&dma->total_hit); + ++processed; + } + } else { + do { + next = drm_select_queue(dev, + gamma_dma_schedule_timer_wrapper); + if (next >= 0) { + q = dev->queuelist[next]; + buf = drm_waitlist_get(&q->waitlist); + dma->next_buffer = buf; + dma->next_queue = q; + if (buf && buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + } + } while (next >= 0 && !dma->next_buffer); + if (dma->next_buffer) { + if (!(retcode = gamma_do_dma(dev, locked))) { + ++processed; + } + } + } + + if (--expire) { + if (missed != atomic_read(&dma->total_missed_sched)) { + atomic_inc(&dma->total_lost); + if (gamma_dma_is_ready(dev)) goto again; + } + if (processed && gamma_dma_is_ready(dev)) { + atomic_inc(&dma->total_lost); + processed = 0; + goto again; + } + } + + clear_bit(0, &dev->interrupt_flag); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles() + - schedule_start)]); +#endif + return retcode; +} + +static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d) +{ + unsigned long address; + unsigned long length; + int must_free = 0; + int retcode = 0; + int i; + int idx; + drm_buf_t *buf; + drm_buf_t *last_buf = NULL; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + /* Turn off interrupt handling */ + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) return -EINTR; + } + if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) { + while (!drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + ++must_free; + } + atomic_inc(&dma->total_prio); + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + continue; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + retcode = -EINVAL; + goto cleanup; + } + if (buf->list != DRM_LIST_NONE) { + DRM_ERROR("Process %d using %d's buffer on list %d\n", + current->pid, buf->pid, buf->list); + retcode = -EINVAL; + goto cleanup; + } + /* This isn't a race condition on + buf->list, since our concern is the + buffer reclaim during the time the + process closes the /dev/drm? handle, so + it can't also be doing DMA. */ + buf->list = DRM_LIST_PRIO; + buf->used = d->send_sizes[i]; + buf->context = d->context; + buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED; + address = (unsigned long)buf->address; + length = buf->used; + if (!length) { + DRM_ERROR("0 length buffer\n"); + } + if (buf->pending) { + DRM_ERROR("Sending pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + if (buf->waiting) { + DRM_ERROR("Sending waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + buf->pending = 1; + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != buf->context */ + drm_context_switch(dev, dev->last_context, + buf->context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + retcode = -EINTR; + goto cleanup; + } + if (dev->last_context != buf->context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, + buf->context); + } + } + +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); + buf->time_dispatched = buf->time_queued; +#endif + gamma_dma_dispatch(dev, address, length); + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (last_buf) { + drm_free_buffer(dev, last_buf); + } + last_buf = buf; + } + + +cleanup: + if (last_buf) { + gamma_dma_ready(dev); + drm_free_buffer(dev, last_buf); + } + + if (must_free && !dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + clear_bit(0, &dev->interrupt_flag); + return retcode; +} + +static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d) +{ + DECLARE_WAITQUEUE(entry, current); + drm_buf_t *last_buf = NULL; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; + + if (d->flags & _DRM_DMA_BLOCK) { + last_buf = dma->buflist[d->send_indices[d->send_count-1]]; + add_wait_queue(&last_buf->dma_wait, &entry); + } + + if ((retcode = drm_dma_enqueue(dev, d))) { + if (d->flags & _DRM_DMA_BLOCK) + remove_wait_queue(&last_buf->dma_wait, &entry); + return retcode; + } + + gamma_dma_schedule(dev, 0); + + if (d->flags & _DRM_DMA_BLOCK) { + DRM_DEBUG("%d waiting\n", current->pid); + current->state = TASK_INTERRUPTIBLE; + for (;;) { + if (!last_buf->waiting + && !last_buf->pending) + break; /* finished */ + schedule(); + if (signal_pending(current)) { + retcode = -EINTR; /* Can't restart */ + break; + } + } + current->state = TASK_RUNNING; + DRM_DEBUG("%d running\n", current->pid); + remove_wait_queue(&last_buf->dma_wait, &entry); + if (!retcode + || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) { + if (!waitqueue_active(&last_buf->dma_wait)) { + drm_free_buffer(dev, last_buf); + } + } + if (retcode) { + DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", + d->context, + last_buf->waiting, + last_buf->pending, + DRM_WAITCOUNT(dev, d->context), + last_buf->idx, + last_buf->list, + last_buf->pid, + current->pid); + } + } + return retcode; +} + +int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_dma_t d; + + copy_from_user_ret(&d, (drm_dma_t *)arg, sizeof(d), -EFAULT); + DRM_DEBUG("%d %d: %d send, %d req\n", + current->pid, d.context, d.send_count, d.request_count); + + if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) { + DRM_ERROR("Process %d using context %d\n", + current->pid, d.context); + return -EINVAL; + } + if (d.send_count < 0 || d.send_count > dma->buf_count) { + DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", + current->pid, d.send_count, dma->buf_count); + return -EINVAL; + } + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count); + return -EINVAL; + } + + if (d.send_count) { + if (d.flags & _DRM_DMA_PRIORITY) + retcode = gamma_dma_priority(dev, &d); + else + retcode = gamma_dma_send_buffers(dev, &d); + } + + d.granted_count = 0; + + if (!retcode && d.request_count) { + retcode = drm_dma_get_buffers(dev, &d); + } + + DRM_DEBUG("%d returning, granted = %d\n", + current->pid, d.granted_count); + copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT); + + return retcode; +} + +int gamma_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG("%d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + + dev->tq.next = NULL; + dev->tq.sync = 0; + dev->tq.routine = gamma_dma_schedule_tq_wrapper; + dev->tq.data = dev; + + + /* Before installing handler */ + GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0); + GAMMA_WRITE(GAMMA_GDMACONTROL, 0); + + /* Install handler */ + if ((retcode = request_irq(dev->irq, + gamma_dma_service, + 0, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + + /* After installing handler */ + GAMMA_WRITE(GAMMA_GINTENABLE, 0x2001); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0x39090); + + return 0; +} + +int gamma_irq_uninstall(drm_device_t *dev) +{ + int irq; + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + + DRM_DEBUG("%d\n", irq); + + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0); + GAMMA_WRITE(GAMMA_GINTENABLE, 0); + free_irq(irq, dev); + + return 0; +} + + +int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + int retcode; + + copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); + + switch (ctl.func) { + case DRM_INST_HANDLER: + if ((retcode = gamma_irq_install(dev, ctl.irq))) + return retcode; + break; + case DRM_UNINST_HANDLER: + if ((retcode = gamma_irq_uninstall(dev))) + return retcode; + break; + default: + return -EINVAL; + } + return 0; +} + +int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + drm_queue_t *q; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; + q = dev->queuelist[lock.context]; + + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + + if (!ret) { + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (j > 0 && j <= DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(j); + } + } + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + atomic_inc(&q->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */ + + if (!ret) { + if (lock.flags & _DRM_LOCK_READY) + gamma_dma_ready(dev); + if (lock.flags & _DRM_LOCK_QUIESCENT) + gamma_dma_quiescent(dev); + } + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c new file mode 100644 index 000000000..d83c98ef9 --- /dev/null +++ b/drivers/char/drm/gamma_drv.c @@ -0,0 +1,525 @@ +/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 22:48:11 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define EXPORT_SYMTAB +#include "drmP.h" +#include "gamma_drv.h" +EXPORT_SYMBOL(gamma_init); +EXPORT_SYMBOL(gamma_cleanup); + +#define GAMMA_NAME "gamma" +#define GAMMA_DESC "3dlabs GMX 2000" +#define GAMMA_DATE "19990830" +#define GAMMA_MAJOR 0 +#define GAMMA_MINOR 0 +#define GAMMA_PATCHLEVEL 5 + +static drm_device_t gamma_device; + +static struct file_operations gamma_fops = { + open: gamma_open, + flush: drm_flush, + release: gamma_release, + ioctl: gamma_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, +}; + +static struct miscdevice gamma_misc = { + minor: MISC_DYNAMIC_MINOR, + name: GAMMA_NAME, + fops: &gamma_fops, +}; + +static drm_ioctl_desc_t gamma_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { gamma_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { gamma_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { drm_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { drm_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { drm_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { drm_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { drm_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { drm_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { drm_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { drm_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { gamma_dma, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { gamma_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { gamma_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls) + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +static char *gamma = NULL; + +MODULE_AUTHOR("Precision Insight, Inc., Cedar Park, Texas."); +MODULE_DESCRIPTION("3dlabs GMX 2000"); +MODULE_PARM(gamma, "s"); + +/* init_module is called when insmod is used to load the module */ + +int init_module(void) +{ + return gamma_init(); +} + +/* cleanup_module is called when rmmod is used to unload the module */ + +void cleanup_module(void) +{ + gamma_cleanup(); +} +#endif + +#ifndef MODULE +/* gamma_setup is called by the kernel to parse command-line options passed + * via the boot-loader (e.g., LILO). It calls the insmod option routine, + * drm_parse_drm. + * + * This is not currently supported, since it requires changes to + * linux/init/main.c. */ + + +void __init gamma_setup(char *str, int *ints) +{ + if (ints[0] != 0) { + DRM_ERROR("Illegal command line format, ignored\n"); + return; + } + drm_parse_options(str); +} +#endif + +static int gamma_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); +#if DRM_DMA_HISTO + memset(&dev->histo, 0, sizeof(dev->histo)); +#endif + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int gamma_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) gamma_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* gamma_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +int gamma_init(void) +{ + int retcode; + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(gamma); +#endif + + if ((retcode = misc_register(&gamma_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor); + dev->name = GAMMA_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + GAMMA_NAME, + GAMMA_MAJOR, + GAMMA_MINOR, + GAMMA_PATCHLEVEL, + GAMMA_DATE, + gamma_misc.minor); + + return 0; +} + +/* gamma_cleanup is called via cleanup_module at module unload time. */ + +void gamma_cleanup(void) +{ + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&gamma_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + gamma_takedown(dev); +} + +int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = GAMMA_MAJOR; + version.version_minor = GAMMA_MINOR; + version.version_patchlevel = GAMMA_PATCHLEVEL; + + DRM_COPY(version.name, GAMMA_NAME); + DRM_COPY(version.date, GAMMA_DATE); + DRM_COPY(version.desc, GAMMA_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int gamma_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &gamma_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { + MOD_INC_USE_COUNT; + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return gamma_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int gamma_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { + MOD_DEC_USE_COUNT; + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + return gamma_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= GAMMA_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &gamma_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + + +int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + gamma_dma_schedule(dev, 1); + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles() + - dev->lck_start)]); +#endif + + return 0; +} diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h new file mode 100644 index 000000000..d91526a98 --- /dev/null +++ b/drivers/char/drm/gamma_drv.h @@ -0,0 +1,58 @@ +/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#ifndef _GAMMA_DRV_H_ +#define _GAMMA_DRV_H_ + + /* gamma_drv.c */ +extern int gamma_init(void); +extern void gamma_cleanup(void); +extern int gamma_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_open(struct inode *inode, struct file *filp); +extern int gamma_release(struct inode *inode, struct file *filp); +extern int gamma_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* gamma_dma.c */ +extern int gamma_dma_schedule(drm_device_t *dev, int locked); +extern int gamma_dma(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_irq_install(drm_device_t *dev, int irq); +extern int gamma_irq_uninstall(drm_device_t *dev); +extern int gamma_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +#endif diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c new file mode 100644 index 000000000..ecdf62e7c --- /dev/null +++ b/drivers/char/drm/init.c @@ -0,0 +1,99 @@ +/* init.c -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_flags = 0; + +/* drm_parse_option parses a single option. See description for + drm_parse_drm for details. */ + +static void drm_parse_option(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + drm_flags |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + drm_flags |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void drm_parse_options(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + drm_parse_option(h); /* parse */ + } +} + diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c new file mode 100644 index 000000000..e5bfd6953 --- /dev/null +++ b/drivers/char/drm/ioctl.c @@ -0,0 +1,91 @@ +/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + copy_from_user_ret(&p, (drm_irq_busid_t *)arg, sizeof(p), -EFAULT); + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + copy_to_user_ret((drm_irq_busid_t *)arg, &p, sizeof(p), -EFAULT); + return 0; +} + +int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT); + if (u.unique_len >= dev->unique_len) { + copy_to_user_ret(u.unique, dev->unique, dev->unique_len, + -EFAULT); + } + u.unique_len = dev->unique_len; + copy_to_user_ret((drm_unique_t *)arg, &u, sizeof(u), -EFAULT); + return 0; +} + +int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) return -EBUSY; + + copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT); + if (!u.unique_len) return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER); + copy_from_user_ret(dev->unique, u.unique, dev->unique_len, + -EFAULT); + dev->unique[dev->unique_len] = '\0'; + + dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + + return 0; +} diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c new file mode 100644 index 000000000..062631f98 --- /dev/null +++ b/drivers/char/drm/lists.c @@ -0,0 +1,252 @@ +/* lists.c -- Buffer list handling routines -*- linux-c -*- + * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:27:01 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_waitlist_create(drm_waitlist_t *bl, int count) +{ + DRM_DEBUG("%d\n", count); + if (bl->count) return -EINVAL; + + bl->count = count; + bl->bufs = drm_alloc((bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->rp = bl->bufs; + bl->wp = bl->bufs; + bl->end = &bl->bufs[bl->count+1]; + bl->write_lock = SPIN_LOCK_UNLOCKED; + bl->read_lock = SPIN_LOCK_UNLOCKED; + return 0; +} + +int drm_waitlist_destroy(drm_waitlist_t *bl) +{ + DRM_DEBUG("\n"); + if (bl->rp != bl->wp) return -EINVAL; + if (bl->bufs) drm_free(bl->bufs, + (bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->count = 0; + bl->bufs = NULL; + bl->rp = NULL; + bl->wp = NULL; + bl->end = NULL; + return 0; +} + +int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf) +{ + int left; + unsigned long flags; + + left = DRM_LEFTCOUNT(bl); + DRM_DEBUG("put %d (%d left, rp = %p, wp = %p)\n", + buf->idx, left, bl->rp, bl->wp); + if (!left) { + DRM_ERROR("Overflow while adding buffer %d from pid %d\n", + buf->idx, buf->pid); + return -EINVAL; + } +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); +#endif + buf->list = DRM_LIST_WAIT; + + spin_lock_irqsave(&bl->write_lock, flags); + *bl->wp = buf; + if (++bl->wp >= bl->end) bl->wp = bl->bufs; + spin_unlock_irqrestore(&bl->write_lock, flags); + + return 0; +} + +drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl) +{ + drm_buf_t *buf; + unsigned long flags; + + spin_lock_irqsave(&bl->read_lock, flags); + buf = *bl->rp; + if (bl->rp == bl->wp) { + spin_unlock_irqrestore(&bl->read_lock, flags); + return NULL; + } + if (++bl->rp >= bl->end) bl->rp = bl->bufs; + spin_unlock_irqrestore(&bl->read_lock, flags); + + DRM_DEBUG("get %d\n", buf->idx); + return buf; +} + +int drm_freelist_create(drm_freelist_t *bl, int count) +{ + DRM_DEBUG("\n"); + atomic_set(&bl->count, 0); + bl->next = NULL; + init_waitqueue_head(&bl->waiting); + bl->low_mark = 0; + bl->high_mark = 0; + atomic_set(&bl->wfh, 0); + ++bl->initialized; + return 0; +} + +int drm_freelist_destroy(drm_freelist_t *bl) +{ + DRM_DEBUG("\n"); + atomic_set(&bl->count, 0); + bl->next = NULL; + return 0; +} + +int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf) +{ + unsigned int old; + unsigned int new; + char failed; + int count = 0; + drm_device_dma_t *dma = dev->dma; + + if (!dma) { + DRM_ERROR("No DMA support\n"); + return 1; + } + + if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) { + DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n", + buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh), + buf->waiting, buf->pending); + if (!bl) return 1; +#if DRM_DMA_HISTOGRAM + buf->time_freed = get_cycles(); + drm_histogram_compute(dev, buf); +#endif + buf->list = DRM_LIST_FREE; + do { + old = (unsigned long)bl->next; + buf->next = (void *)old; + new = (unsigned long)buf; + _DRM_CAS(&bl->next, old, new, failed); + if (++count > DRM_LOOPING_LIMIT) { + DRM_ERROR("Looping\n"); + return 1; + } + } while (failed); + atomic_inc(&bl->count); + if (atomic_read(&bl->count) > dma->buf_count) { + DRM_ERROR("%d of %d buffers free after addition of %d\n", + atomic_read(&bl->count), dma->buf_count, buf->idx); + return 1; + } + /* Check for high water mark */ + if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) { + atomic_set(&bl->wfh, 0); + wake_up_interruptible(&bl->waiting); + } + return 0; +} + +static drm_buf_t *drm_freelist_try(drm_freelist_t *bl) +{ + unsigned int old; + unsigned int new; + char failed; + drm_buf_t *buf; + int count = 0; + + if (!bl) return NULL; + + /* Get buffer */ + do { + old = (unsigned int)bl->next; + if (!old) { + return NULL; + } + new = (unsigned long)bl->next->next; + _DRM_CAS(&bl->next, old, new, failed); + if (++count > DRM_LOOPING_LIMIT) { + DRM_ERROR("Looping\n"); + return NULL; + } + } while (failed); + atomic_dec(&bl->count); + + buf = (drm_buf_t *)old; + buf->next = NULL; + buf->list = DRM_LIST_NONE; + DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n", + buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh), + buf->waiting, buf->pending); + if (buf->waiting || buf->pending) { + DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + + return buf; +} + +drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block) +{ + drm_buf_t *buf = NULL; + DECLARE_WAITQUEUE(entry, current); + + if (!bl || !bl->initialized) return NULL; + + /* Check for low water mark */ + if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */ + atomic_set(&bl->wfh, 1); + if (atomic_read(&bl->wfh)) { + DRM_DEBUG("Block = %d, count = %d, wfh = %d\n", + block, atomic_read(&bl->count), + atomic_read(&bl->wfh)); + if (block) { + add_wait_queue(&bl->waiting, &entry); + current->state = TASK_INTERRUPTIBLE; + for (;;) { + if (!atomic_read(&bl->wfh) + && (buf = drm_freelist_try(bl))) break; + schedule(); + if (signal_pending(current)) break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&bl->waiting, &entry); + } + return buf; + } + + DRM_DEBUG("Count = %d, wfh = %d\n", + atomic_read(&bl->count), atomic_read(&bl->wfh)); + return drm_freelist_try(bl); +} diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c new file mode 100644 index 000000000..03931acc3 --- /dev/null +++ b/drivers/char/drm/lock.c @@ -0,0 +1,227 @@ +/* lock.c -- IOCTLs for locking -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 09:27:01 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_block(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old; + unsigned int new; + char failed; + + DRM_DEBUG("%d attempts\n", context); + do { + old = *lock; + if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; + else new = context | _DRM_LOCK_HELD; + _DRM_CAS(lock, old, new, failed); + } while (failed); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + if (new == (context | _DRM_LOCK_HELD)) { + /* Have lock */ + DRM_DEBUG("%d\n", context); + return 1; + } + DRM_DEBUG("%d unable to get lock held by %d\n", + context, _DRM_LOCKING_CONTEXT(old)); + return 0; +} + +/* This takes a lock forcibly and hands it to context. Should ONLY be used + inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +int drm_lock_transfer(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old; + unsigned int new; + char failed; + + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + _DRM_CAS(lock, old, new, failed); + } while (failed); + DRM_DEBUG("%d => %d\n", _DRM_LOCKING_CONTEXT(old), context); + return 1; +} + +int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old; + unsigned int new; + char failed; + + DRM_DEBUG("%d\n", context); + do { + old = *lock; + new = 0; + _DRM_CAS(lock, old, new, failed); + } while (failed); + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d\n", + context, + _DRM_LOCKING_CONTEXT(old)); + return 1; + } + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + return 0; +} + +static int drm_flush_queue(drm_device_t *dev, int context) +{ + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + atomic_inc(&q->block_write); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&q->flush_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + if (!DRM_BUFCOUNT(&q->waitlist)) break; + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->flush_queue, &entry); + } + atomic_dec(&q->use_count); + atomic_inc(&q->total_flushed); + + /* NOTE: block_write is still incremented! + Use drm_flush_unlock_queue to decrement. */ + return ret; +} + +static int drm_flush_unblock_queue(drm_device_t *dev, int context) +{ + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + if (atomic_read(&q->block_write)) { + atomic_dec(&q->block_write); + wake_up_interruptible(&q->write_queue); + } + } + atomic_dec(&q->use_count); + return 0; +} + +int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_queue(dev, i); + } + } + return ret; +} + +int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_unblock_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_unblock_queue(dev, i); + } + } + + return ret; +} + +int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("\n"); + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + drm_flush_unblock(dev, lock.context, lock.flags); + return ret; +} diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c new file mode 100644 index 000000000..f7b5335a5 --- /dev/null +++ b/drivers/char/drm/memory.c @@ -0,0 +1,320 @@ +/* memory.c -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 13:04:33 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED; +static unsigned long drm_ram_available = 0; +static unsigned long drm_ram_used = 0; +static drm_mem_stats_t drm_mem_stats[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + { NULL, 0, } /* Last entry must be null */ +}; + +void drm_mem_init(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = drm_mem_stats; mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); + drm_ram_available = si.totalram; + drm_ram_used = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int _drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_mem_stats_t *pt; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu |\n", + "system", 0, 0, 0, drm_ram_available); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu |\n", + "locked", 0, 0, 0, drm_ram_used); + DRM_PROC_PRINT("\n"); + for (pt = drm_mem_stats; pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + return len; +} + +int drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + int ret; + + spin_lock(&drm_mem_lock); + ret = _drm_mem_info(buf, start, offset, len, eof, data); + spin_unlock(&drm_mem_lock); + return ret; +} + +void *drm_alloc(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = drm_alloc(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + drm_free(oldpt, oldsize, area); + } + return pt; +} + +char *drm_strdup(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = drm_alloc(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void drm_strfree(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + drm_free((void *)s, size, area); +} + +void drm_free(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree_s(pt, size); + spin_lock(&drm_mem_lock); + drm_mem_stats[area].bytes_freed += size; + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long drm_alloc_pages(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&drm_mem_lock); + if (drm_ram_used > +(DRM_RAM_PERCENT * drm_ram_available) / 100) { + spin_unlock(&drm_mem_lock); + return 0; + } + spin_unlock(&drm_mem_lock); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return 0; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += bytes; + drm_ram_used += bytes; + spin_unlock(&drm_mem_lock); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { + mem_map_reserve(MAP_NR(addr)); + } + + return address; +} + +void drm_free_pages(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { + mem_map_unreserve(MAP_NR(addr)); + } + free_pages(address, order); + } + + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_freed += bytes; + drm_ram_used -= bytes; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *drm_ioremap(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void drm_ioremapfree(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&drm_mem_lock); + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count; + alloc_count = drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c new file mode 100644 index 000000000..7db90aea8 --- /dev/null +++ b/drivers/char/drm/proc.c @@ -0,0 +1,568 @@ +/* proc.c -- /proc support for DRM -*- linux-c -*- + * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static struct proc_dir_entry *drm_root = NULL; +static struct proc_dir_entry *drm_dev_root = NULL; +static char drm_slot_name[64]; + +static int drm_name_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_vm_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_clients_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_queues_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_bufs_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#if DRM_DEBUG_CODE +static int drm_vma_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif +#if DRM_DMA_HISTOGRAM +static int drm_histo_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif + +struct drm_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} drm_proc_list[] = { + { "name", drm_name_info }, + { "mem", drm_mem_info }, + { "vm", drm_vm_info }, + { "clients", drm_clients_info }, + { "queues", drm_queues_info }, + { "bufs", drm_bufs_info }, +#if DRM_DEBUG_CODE + { "vma", drm_vma_info }, +#endif +#if DRM_DMA_HISTOGRAM + { "histo", drm_histo_info }, +#endif +}; +#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0])) + +int drm_proc_init(drm_device_t *dev) +{ + struct proc_dir_entry *ent; + int i, j; + + drm_root = create_proc_entry("video", S_IFDIR, NULL); + if (!drm_root) { + DRM_ERROR("Cannot create /proc/video\n"); + return -1; + } + + /* Instead of doing this search, we should + add some global support for /proc/video. */ + for (i = 0; i < 8; i++) { + sprintf(drm_slot_name, "video/%d", i); + drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL); + if (!drm_dev_root) { + DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name); + remove_proc_entry("video", NULL); + } + if (drm_dev_root->nlink == 2) break; + drm_dev_root = NULL; + } + if (!drm_dev_root) { + DRM_ERROR("Cannot find slot in /proc/video\n"); + return -1; + } + + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + ent = create_proc_entry(drm_proc_list[i].name, + S_IFREG|S_IRUGO, drm_dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/%s/%s\n", + drm_slot_name, drm_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + remove_proc_entry(drm_slot_name, NULL); + remove_proc_entry("video", NULL); + return -1; + } + ent->read_proc = drm_proc_list[i].f; + ent->data = dev; + } + + return 0; +} + + +int drm_proc_cleanup(void) +{ + int i; + + if (drm_root) { + if (drm_dev_root) { + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + } + remove_proc_entry(drm_slot_name, NULL); + } + remove_proc_entry("video", NULL); + remove_proc_entry(DRM_NAME, NULL); + } + drm_root = drm_dev_root = NULL; + return 0; +} + +static int drm_name_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + if (dev->unique) { + DRM_PROC_PRINT("%s 0x%x %s\n", + dev->name, dev->device, dev->unique); + } else { + DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device); + } + return len; +} + +static int _drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_map_t *map; + const char *types[] = { "FB", "REG", "SHM" }; + const char *type; + int i; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("slot offset size type flags " + "address mtrr\n\n"); + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->type < 0 || map->type > 2) type = "??"; + else type = types[map->type]; + DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ", + i, + map->offset, + map->size, + type, + map->flags, + (unsigned long)map->handle); + if (map->mtrr < 0) { + DRM_PROC_PRINT("none\n"); + } else { + DRM_PROC_PRINT("%4d\n", map->mtrr); + } + } + + return len; +} + +static int drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vm_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int i; + drm_queue_t *q; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" ctx/flags use fin" + " blk/rw/rwf wait flushed queued" + " locks\n\n"); + for (i = 0; i < dev->queue_count; i++) { + q = dev->queuelist[i]; + atomic_inc(&q->use_count); + DRM_PROC_PRINT_RET(atomic_dec(&q->use_count), + "%5d/0x%03x %5d %5d" + " %5d/%c%c/%c%c%c %5d %10d %10d %10d\n", + i, + q->flags, + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count), + atomic_read(&q->block_read) ? 'r' : '-', + atomic_read(&q->block_write) ? 'w' : '-', + waitqueue_active(&q->read_queue) ? 'r':'-', + waitqueue_active(&q->write_queue) ? 'w':'-', + waitqueue_active(&q->flush_queue) ? 'f':'-', + DRM_BUFCOUNT(&q->waitlist), + atomic_read(&q->total_flushed), + atomic_read(&q->total_queued), + atomic_read(&q->total_locks)); + atomic_dec(&q->use_count); + } + + return len; +} + +static int drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_queues_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +/* drm_bufs_info is called whenever a process reads + /dev/drm/<dev>/bufs. */ + +static int _drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return 0; + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" o size count free segs pages kB\n\n"); + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].buf_count) + DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n", + i, + dma->bufs[i].buf_size, + dma->bufs[i].buf_count, + atomic_read(&dma->bufs[i] + .freelist.count), + dma->bufs[i].seg_count, + dma->bufs[i].seg_count + *(1 << dma->bufs[i].page_order), + (dma->bufs[i].seg_count + * (1 << dma->bufs[i].page_order)) + * PAGE_SIZE / 1024); + } + DRM_PROC_PRINT("\n"); + for (i = 0; i < dma->buf_count; i++) { + if (i && !(i%32)) DRM_PROC_PRINT("\n"); + DRM_PROC_PRINT(" %d", dma->buflist[i]->list); + } + DRM_PROC_PRINT("\n"); + + return len; +} + +static int drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_bufs_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_file_t *priv; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("a dev pid uid magic ioctls\n\n"); + for (priv = dev->file_first; priv; priv = priv->next) { + DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n", + priv->authenticated ? 'y' : 'n', + priv->minor, + priv->pid, + priv->uid, + priv->magic, + priv->ioctl_count); + } + + return len; +} + +static int drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_clients_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +#if DRM_DEBUG_CODE + +static int _drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_vma_entry_t *pt; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long i; + struct vm_area_struct *vma; + unsigned long address; +#if defined(__i386__) + unsigned int pgprot; +#endif + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n", + atomic_read(&dev->vma_count), + high_memory, virt_to_phys(high_memory)); + for (pt = dev->vmalist; pt; pt = pt->next) { + if (!(vma = pt->vma)) continue; + DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx", + pt->pid, + vma->vm_start, + vma->vm_end, + vma->vm_flags & VM_READ ? 'r' : '-', + vma->vm_flags & VM_WRITE ? 'w' : '-', + vma->vm_flags & VM_EXEC ? 'x' : '-', + vma->vm_flags & VM_MAYSHARE ? 's' : 'p', + vma->vm_flags & VM_LOCKED ? 'l' : '-', + vma->vm_flags & VM_IO ? 'i' : '-', + vma->vm_offset ); +#if defined(__i386__) + pgprot = pgprot_val(vma->vm_page_prot); + DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c", + pgprot & _PAGE_PRESENT ? 'p' : '-', + pgprot & _PAGE_RW ? 'w' : 'r', + pgprot & _PAGE_USER ? 'u' : 's', + pgprot & _PAGE_PWT ? 't' : 'b', + pgprot & _PAGE_PCD ? 'u' : 'c', + pgprot & _PAGE_ACCESSED ? 'a' : '-', + pgprot & _PAGE_DIRTY ? 'd' : '-', + pgprot & _PAGE_4M ? 'm' : 'k', + pgprot & _PAGE_GLOBAL ? 'g' : 'l' ); +#endif + DRM_PROC_PRINT("\n"); + for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) { + pgd = pgd_offset(vma->vm_mm, i); + pmd = pmd_offset(pgd, i); + pte = pte_offset(pmd, i); + if (pte_present(*pte)) { + address = __pa(pte_page(*pte)) + + (i & (PAGE_SIZE-1)); + DRM_PROC_PRINT(" 0x%08lx -> 0x%08lx" + " %c%c%c%c%c\n", + i, + address, + pte_read(*pte) ? 'r' : '-', + pte_write(*pte) ? 'w' : '-', + pte_exec(*pte) ? 'x' : '-', + pte_dirty(*pte) ? 'd' : '-', + pte_young(*pte) ? 'a' : '-' ); + } else { + DRM_PROC_PRINT(" 0x%08lx\n", i); + } + } + } + + return len; +} + +static int drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vma_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif + + +#if DRM_DMA_HISTOGRAM +static int _drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + unsigned long slot_value = DRM_DMA_HISTOGRAM_INITIAL; + unsigned long prev_value = 0; + drm_buf_t *buffer; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + DRM_PROC_PRINT("general statistics:\n"); + DRM_PROC_PRINT("total %10u\n", atomic_read(&dev->histo.total)); + DRM_PROC_PRINT("open %10u\n", atomic_read(&dev->total_open)); + DRM_PROC_PRINT("close %10u\n", atomic_read(&dev->total_close)); + DRM_PROC_PRINT("ioctl %10u\n", atomic_read(&dev->total_ioctl)); + DRM_PROC_PRINT("irq %10u\n", atomic_read(&dev->total_irq)); + DRM_PROC_PRINT("ctx %10u\n", atomic_read(&dev->total_ctx)); + + DRM_PROC_PRINT("\nlock statistics:\n"); + DRM_PROC_PRINT("locks %10u\n", atomic_read(&dev->total_locks)); + DRM_PROC_PRINT("unlocks %10u\n", atomic_read(&dev->total_unlocks)); + DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends)); + DRM_PROC_PRINT("sleeps %10u\n", atomic_read(&dev->total_sleeps)); + + + if (dma) { + DRM_PROC_PRINT("\ndma statistics:\n"); + DRM_PROC_PRINT("prio %10u\n", + atomic_read(&dma->total_prio)); + DRM_PROC_PRINT("bytes %10u\n", + atomic_read(&dma->total_bytes)); + DRM_PROC_PRINT("dmas %10u\n", + atomic_read(&dma->total_dmas)); + DRM_PROC_PRINT("missed:\n"); + DRM_PROC_PRINT(" dma %10u\n", + atomic_read(&dma->total_missed_dma)); + DRM_PROC_PRINT(" lock %10u\n", + atomic_read(&dma->total_missed_lock)); + DRM_PROC_PRINT(" free %10u\n", + atomic_read(&dma->total_missed_free)); + DRM_PROC_PRINT(" sched %10u\n", + atomic_read(&dma->total_missed_sched)); + DRM_PROC_PRINT("tried %10u\n", + atomic_read(&dma->total_tried)); + DRM_PROC_PRINT("hit %10u\n", + atomic_read(&dma->total_hit)); + DRM_PROC_PRINT("lost %10u\n", + atomic_read(&dma->total_lost)); + + buffer = dma->next_buffer; + if (buffer) { + DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("next_buffer none\n"); + } + buffer = dma->this_buffer; + if (buffer) { + DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("this_buffer none\n"); + } + } + + + DRM_PROC_PRINT("\nvalues:\n"); + if (dev->lock.hw_lock) { + DRM_PROC_PRINT("lock 0x%08x\n", + dev->lock.hw_lock->lock); + } else { + DRM_PROC_PRINT("lock none\n"); + } + DRM_PROC_PRINT("context_flag 0x%08x\n", dev->context_flag); + DRM_PROC_PRINT("interrupt_flag 0x%08x\n", dev->interrupt_flag); + DRM_PROC_PRINT("dma_flag 0x%08x\n", dev->dma_flag); + + DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count); + DRM_PROC_PRINT("last_context %10d\n", dev->last_context); + DRM_PROC_PRINT("last_switch %10lu\n", dev->last_switch); + DRM_PROC_PRINT("last_checked %10d\n", dev->last_checked); + + + DRM_PROC_PRINT("\n q2d d2c c2f" + " q2c q2f dma sch" + " ctx lacq lhld\n\n"); + for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) { + DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u" + " %10u %10u %10u %10u %10u\n", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 + ? prev_value : slot_value , + + atomic_read(&dev->histo + .queued_to_dispatched[i]), + atomic_read(&dev->histo + .dispatched_to_completed[i]), + atomic_read(&dev->histo + .completed_to_freed[i]), + + atomic_read(&dev->histo + .queued_to_completed[i]), + atomic_read(&dev->histo + .queued_to_freed[i]), + atomic_read(&dev->histo.dma[i]), + atomic_read(&dev->histo.schedule[i]), + atomic_read(&dev->histo.ctx[i]), + atomic_read(&dev->histo.lacq[i]), + atomic_read(&dev->histo.lhld[i])); + prev_value = slot_value; + slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value); + } + return len; +} + +static int drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_histo_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif diff --git a/drivers/char/drm/sigio.c b/drivers/char/drm/sigio.c new file mode 100644 index 000000000..bb75087df --- /dev/null +++ b/drivers/char/drm/sigio.c @@ -0,0 +1,82 @@ +/* sigio.c -- Support for SIGIO handler -*- linux-c -*- + * Created: Thu Jun 3 15:39:18 1999 by faith@precisioninsight.com + * Revised: Thu Jun 3 16:16:35 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/shared/sigio.c,v 1.2 1999/06/14 21:11:29 faith Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/sigio.c,v 1.2 1999/06/14 12:02:11 dawes Exp $ + * + */ + + +#ifdef XFree86Server +# include "X.h" +# include "xf86.h" +# include "xf86drm.h" +# include "xf86_OSlib.h" +# include "xf86drm.h" +#else +# include <unistd.h> +# include <signal.h> +# include <fcntl.h> +#endif + +/* + * Linux libc5 defines FASYNC, but not O_ASYNC. Don't know if it is + * functional or not. + */ +#if defined(FASYNC) && !defined(O_ASYNC) +# define O_ASYNC FASYNC +#endif + +int +xf86InstallSIGIOHandler(int fd, void (*f)(int)) +{ + struct sigaction sa; + struct sigaction osa; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGIO); + sa.sa_flags = 0; + sa.sa_handler = f; + sigaction(SIGIO, &sa, &osa); + fcntl(fd, F_SETOWN, getpid()); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); + return 0; +} + +int +xf86RemoveSIGIOHandler(int fd) +{ + struct sigaction sa; + struct sigaction osa; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGIO); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_ASYNC); + sigaction(SIGIO, &sa, &osa); + return 0; +} diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c new file mode 100644 index 000000000..55bfc4c45 --- /dev/null +++ b/drivers/char/drm/vm.c @@ -0,0 +1,264 @@ +/* vm.c -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * Revised: Fri Aug 20 22:48:11 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/generic/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $ + * $XFree86$ + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: drm_vm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: drm_vm_shm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: drm_vm_dma_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +{ + DRM_DEBUG("0x%08lx, %d\n", address, write_access); + + return 0; /* Disallow mremap */ +} + +unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (address > vma->vm_end) return 0; /* Disallow mremap */ + if (!dev->lock.hw_lock) return 0; /* Nothing allocated */ + + offset = address - vma->vm_start; + page = offset >> PAGE_SHIFT; + physical = (unsigned long)dev->lock.hw_lock + (offset & (~PAGE_MASK)); + atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); + return physical; +} + +unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return 0; /* Error */ + if (address > vma->vm_end) return 0; /* Disallow mremap */ + if (!dma->pagelist) return 0; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_offset should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); + return physical; +} + +void drm_vm_open(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *vma_entry; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); + MOD_INC_USE_COUNT; + +#if DRM_DEBUG_CODE + vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +#endif +} + +void drm_vm_close(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *pt, *prev; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + MOD_DEC_USE_COUNT; + atomic_dec(&dev->vma_count); + +#if DRM_DEBUG_CODE + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + drm_free(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +#endif +} + +int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long length = vma->vm_end - vma->vm_start; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, vma->vm_offset); + + /* Length must match exact page count */ + if ((length >> PAGE_SHIFT) != dma->page_count) return -EINVAL; + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +int drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + int i; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, vma->vm_offset); + + if (!vma->vm_offset) return drm_mmap_dma(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->offset == vma->vm_offset) break; + } + + if (i >= dev->map_count) return -EINVAL; + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + if (vma->vm_offset >= __pa(high_memory)) { +#if defined(__i386__) + if (boot_cpu_data.x86 > 3) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } + if (remap_page_range(vma->vm_start, + vma->vm_offset, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + vma->vm_ops = &drm_vm_shm_ops; + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + if (map->flags & _DRM_READ_ONLY) { + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; + } + + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index 4df4a1e2b..30a76a04e 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -534,6 +534,8 @@ int __init dsp56k_init(void) dsp56k.in_use = 0; printk("DSP56k driver installed\n"); + + return 0; } #ifdef MODULE diff --git a/drivers/char/epca.c b/drivers/char/epca.c index abb8c5f35..8261e4ef2 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -1367,7 +1367,7 @@ static int block_til_ready(struct tty_struct *tty, while(1) { /* Begin forever while */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(ch->asyncflags & ASYNC_INITIALIZED)) @@ -3233,7 +3233,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, if (error) return error; - putUser(mflag, (unsigned long *) arg); + putUser(mflag, (unsigned int *) arg); break; @@ -4049,12 +4049,12 @@ int get_PCI_configuration(unsigned char bus, unsigned char device_fn, if (!dev) return(0); - *base_addr0 = dev->base_address[0]; - *base_addr1 = dev->base_address[1]; - *base_addr2 = dev->base_address[2]; - *base_addr3 = dev->base_address[3]; - *base_addr4 = dev->base_address[4]; - *base_addr5 = dev->base_address[5]; + *base_addr0 = dev->resource[0].start; + *base_addr1 = dev->resource[1].start; + *base_addr2 = dev->resource[2].start; + *base_addr3 = dev->resource[3].start; + *base_addr4 = dev->resource[4].start; + *base_addr5 = dev->resource[5].start; /* ------------------------------------------------------------------------ NOTE - The code below mask out either the 2 or 4 bits dependent on the @@ -4064,36 +4064,6 @@ int get_PCI_configuration(unsigned char bus, unsigned char device_fn, be 0 when used as an address. ---------------------------------------------------------------------------- */ - if ((*base_addr0) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr0) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr0) &= PCI_BASE_ADDRESS_MEM_MASK; - - if ((*base_addr1) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr1) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr1) &= PCI_BASE_ADDRESS_MEM_MASK; - - if ((*base_addr2) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr2) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr2) &= PCI_BASE_ADDRESS_MEM_MASK; - - if ((*base_addr3) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr3) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr3) &= PCI_BASE_ADDRESS_MEM_MASK; - - if ((*base_addr4) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr4) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr4) &= PCI_BASE_ADDRESS_MEM_MASK; - - if ((*base_addr5) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ - (*base_addr5) &= PCI_BASE_ADDRESS_IO_MASK; - else - (*base_addr5) &= PCI_BASE_ADDRESS_MEM_MASK; - return(1); } /* End get_PCI_configuration */ diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 511794642..ff800e6c4 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -2317,7 +2317,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, scratch | UART_MCR_DTR | UART_MCR_RTS); } sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c index 2fda1578b..464b792d3 100644 --- a/drivers/char/ftape/lowlevel/ftape-io.c +++ b/drivers/char/ftape/lowlevel/ftape-io.c @@ -95,13 +95,10 @@ void ftape_sleep(unsigned int time) TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks); timeout = ticks; - current->state = TASK_INTERRUPTIBLE; save_flags(flags); sti(); + set_current_state(TASK_INTERRUPTIBLE); do { - while (current->state != TASK_RUNNING) { - timeout = schedule_timeout(timeout); - } /* Mmm. Isn't current->blocked == 0xffffffff ? */ if (signal_pending(current)) { @@ -109,6 +106,9 @@ void ftape_sleep(unsigned int time) "awoken by non-blocked signal :-("); break; /* exit on signal */ } + while (current->state != TASK_RUNNING) { + timeout = schedule_timeout(timeout); + } } while (timeout); restore_flags(flags); } diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c index 01c231bb0..1c590fde3 100644 --- a/drivers/char/ftape/lowlevel/ftape-setup.c +++ b/drivers/char/ftape/lowlevel/ftape-setup.c @@ -60,12 +60,15 @@ static struct param_table { { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1} }; -void __init ftape_setup(char *str, int *ints) +static int __init ftape_setup(char *str) { int i; int param; + int ints[2]; + TRACE_FUN(ft_t_flow); + str = get_options(str, ARRAY_SIZE(ints), ints); if (str) { for (i=0; i < NR_ITEMS(config_params); i++) { if (strcmp(str,config_params[i].name) == 0){ @@ -81,13 +84,13 @@ void __init ftape_setup(char *str, int *ints) config_params[i].name, config_params[i].min, config_params[i].max); - TRACE_EXIT; + goto out; } if(config_params[i].var) { TRACE(ft_t_info, "%s=%d", str, param); *config_params[i].var = param; } - TRACE_EXIT; + goto out; } } } @@ -101,5 +104,8 @@ void __init ftape_setup(char *str, int *ints) } else { TRACE(ft_t_err, "botched ftape option"); } - TRACE_EXIT; + out: + TRACE_EXIT 1; } + +__setup("ftape=", ftape_setup); diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c new file mode 100644 index 000000000..f4ecc75a5 --- /dev/null +++ b/drivers/char/generic_serial.c @@ -0,0 +1,1074 @@ +/* + * generic_serial.c + * + * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl + * + * written for the SX serial driver. + * Contains the code that should be shared over all the serial drivers. + * + * Credit for the idea to do it this way might go to Alan Cox. + * + * + * Version 0.1 -- December, 1998. Initial version. + * Version 0.2 -- March, 1999. Some more routines. Bugfixes. Etc. + * Version 0.5 -- August, 1999. Some more fixes. Reformat for Linus. + */ + +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/mm.h> +#include <asm/semaphore.h> +#include <linux/version.h> + + +#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */ +#warning "Please use a 2.2.x kernel. " +#else +#if LINUX_VERSION_CODE < 0x020300 /* less than 2.2.x */ +#define TWO_TWO +#else +#define TWO_THREE +#endif +#endif +#endif + +#ifdef TWO_ZERO + +/* Here is the section that makes the 2.2 compatible driver source + work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, + and provide for compatibility stuff here if possible. */ + +/* Some 200 days (on intel) */ +#define MAX_SCHEDULE_TIMEOUT ((long)(~0UL>>1)) + + +#ifndef MODULE + +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) + +static inline int copy_from_user(void *to,const void *from, int c) +{ + memcpy_fromfs(to, from, c); + return 0; +} + + +#define capable(x) suser() + +#define queue_task queue_task_irq_off +#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer) +#define signal_pending(current) (current->signal & ~current->blocked) +#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +#define time_after(t1,t2) (((long)t1-t2) > 0) + +#define test_and_set_bit(nr, addr) set_bit(nr, addr) +#define test_and_clear_bit(nr, addr) clear_bit(nr, addr) + +/* Not yet implemented on 2.0 */ +#define ASYNC_SPD_SHI -1 +#define ASYNC_SPD_WARP -1 + + + +/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it + to the "name" field that does exist. As long as the assignments are + done in the right order, there is nothing to worry about. */ +#define driver_name name + + +/* Should be in a header somewhere. */ +#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */ +#define TTY_HW_COOK_IN 15 /* in hardware - output and input */ +#endif + +#endif + +#ifndef TWO_ZERO +/* This include is new with 2.2 (and required!) */ +#include <asm/uaccess.h> +#endif + +#ifndef TWO_THREE +/* These are new in 2.3. The source now uses 2.3 syntax, and here is + the compatibility define... */ +#define wait_queue_head_t struct wait_queue * +#define DECLARE_MUTEX(name) struct semaphore name = MUTEX +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } + +#endif + +#include "generic_serial.h" + + +#ifndef MODULE +extern void my_hd (unsigned char *ptr, int n); +#endif + +static char * tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); + +int gs_debug = 0; + + +#ifdef DEBUG +#define gs_dprintk(f, str...) if (gs_debug & f) printk (str) +#else +#define gs_dprintk(f, str...) /* nothing */ +#endif + +#define func_enter() gs_dprintk (SX_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n") +#define func_exit() gs_dprintk (SX_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n") + + + +#if NEW_WRITE_LOCKING +#define DECL /* Nothing */ +#define LOCKIT down (& port->port_write_sem); +#define RELEASEIT up (&port->port_write_sem); +#else +#define DECL unsigned long flags; +#define LOCKIT save_flags (flags);cli () +#define RELEASEIT restore_flags (flags) +#endif + + + +void gs_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct gs_port *port = tty->driver_data; + DECL + + /* func_enter (); */ + + /* Take a lock on the serial tranmit buffer! */ + LOCKIT; + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + /* Sorry, buffer is full, drop character. Update statistics???? -- REW */ + RELEASEIT; + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; /* Characters in buffer */ + + RELEASEIT; + /* func_exit ();*/ +} + + +#ifdef NEW_WRITE_LOCKING + +/* +> Problems to take into account are: +> -1- Interrupts that empty part of the buffer. +> -2- page faults on the access to userspace. +> -3- Other processes that are also trying to do a "write". +*/ + +int gs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct gs_port *port = tty->driver_data; + int c, total = 0; + int t; + + /* func_enter (); */ + + if (! (port->flags & ASYNC_INITIALIZED)) + return 0; + + /* get exclusive "write" access to this port (problem 3) */ + /* This is not a spinlock because we can have a disk access (page + fault) in copy_from_user */ + down (& port->port_write_sem); + + while (1) { + + c = count; + + /* This is safe because we "OWN" the "head". Noone else can + change the "head": we own the port_write_sem. */ + /* Don't overrun the end of the buffer */ + t = SERIAL_XMIT_SIZE - port->xmit_head; + if (t < c) c = t; + + /* This is safe because the xmit_cnt can only decrease. This + would increase "t", so we might copy too little chars. */ + /* Don't copy past the "head" of the buffer */ + t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; + if (t < c) c = t; + + /* Can't copy more? break out! */ + if (c <= 0) break; + if (from_user) + copy_from_user (port->xmit_buf + port->xmit_head, buf, c); + else + memcpy (port->xmit_buf + port->xmit_head, buf, c); + + port -> xmit_cnt += c; + port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1); + buf += c; + count -= c; + total += c; + } + up (& port->port_write_sem); + + gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", + (port->flags & GS_TX_INTEN)?"enabled": "disabled"); + + if (port->xmit_cnt && + !tty->stopped && + !tty->hw_stopped && + !(port->flags & GS_TX_INTEN)) { + port->flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + } + /* func_exit (); */ + return total; +} +#else +/* +> Problems to take into account are: +> -1- Interrupts that empty part of the buffer. +> -2- page faults on the access to userspace. +> -3- Other processes that are also trying to do a "write". +*/ + +int gs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct gs_port *port; + int c, total = 0; + int t; + unsigned long flags; + + func_enter (); + + /* The standard serial driver returns 0 in this case. + That sounds to me as "No error, I just didn't get to writing any + bytes. Feel free to try again." + The "official" way to write n bytes from buf is: + + for (nwritten = 0;nwritten < n;nwritten += rv) { + rv = write (fd, buf+nwritten, n-nwritten); + if (rv < 0) break; // Error: bail out. // + } + + which will loop endlessly in this case. The manual page for write + agrees with me. In practise almost everybody writes + "write (fd, buf,n);" but some people might have had to deal with + incomplete writes in the past and correctly implemented it by now... + */ + + if (!tty) return -EIO; + + port = tty->driver_data; + if (!port || !port->xmit_buf || !tmp_buf) + return -EIO; + + /* printk ("from_user = %d.\n", from_user); */ + save_flags(flags); + if (from_user) { + /* printk ("Going into the semaphore\n"); */ + down(&tmp_buf_sem); + /* printk ("got out of the semaphore\n"); */ + while (1) { + c = count; + + /* This is safe because we "OWN" the "head". Noone else can + change the "head": we own the port_write_sem. */ + /* Don't overrun the end of the buffer */ + t = SERIAL_XMIT_SIZE - port->xmit_head; + if (t < c) c = t; + + /* This is safe because the xmit_cnt can only decrease. This + would increase "t", so we might copy too little chars. */ + /* Don't copy past the "head" of the buffer */ + t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; + if (t < c) c = t; + + /* Can't copy more? break out! */ + if (c <= 0) break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!total) + total = -EFAULT; + break; + } + cli(); + t = SERIAL_XMIT_SIZE - port->xmit_head; + if (t < c) c = t; + t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; + if (t < c) c = t; + + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + port->xmit_head = ((port->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + cli(); + c = count; + + /* This is safe because we "OWN" the "head". Noone else can + change the "head": we own the port_write_sem. */ + /* Don't overrun the end of the buffer */ + t = SERIAL_XMIT_SIZE - port->xmit_head; + if (t < c) c = t; + + /* This is safe because the xmit_cnt can only decrease. This + would increase "t", so we might copy too little chars. */ + /* Don't copy past the "head" of the buffer */ + t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt; + if (t < c) c = t; + + /* Can't copy more? break out! */ + if (c <= 0) { + restore_flags(flags); + break; + } + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = ((port->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + } + + if (port->xmit_cnt && + !tty->stopped && + !tty->hw_stopped && + !(port->flags & GS_TX_INTEN)) { + port->flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + } + func_exit (); + return total; +} + +#endif + + + +int gs_write_room(struct tty_struct * tty) +{ + struct gs_port *port = tty->driver_data; + int ret; + + /* func_enter (); */ + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + /* func_exit (); */ + return ret; +} + + +int gs_chars_in_buffer(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + func_enter (); + + func_exit (); + return port->xmit_cnt; +} + + +int gs_real_chars_in_buffer(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + func_enter (); + + if (!tty) return 0; + port = tty->driver_data; + + func_exit (); + return port->xmit_cnt + port->rd->chars_in_buffer (port); +} + + +static void gs_wait_tx_flushed (void * ptr, int timeout) +{ + struct gs_port *port = ptr; + long end_jiffies; + int jiffies_to_transmit, charsleft; + int to, rcib; + + func_enter(); + + gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port); + if (port) { + gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n", + port->xmit_cnt, port->xmit_buf, port->tty); + } + + if (!port || port->xmit_cnt < 0 || !port->xmit_buf) { + gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n"); + func_exit(); + return; /* This is an error which we don't know how to handle. */ + } + gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 1\n"); + + rcib = gs_real_chars_in_buffer(port->tty); + + gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 2\n"); + + if(rcib <= 0) { + gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n"); + func_exit(); + return; + } + gs_dprintk (GS_DEBUG_FLUSH, "checkpoint 3\n"); + + /* stop trying: now + twice the time it would normally take + seconds */ + end_jiffies = jiffies; + if (timeout != MAX_SCHEDULE_TIMEOUT) + end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0; + end_jiffies += timeout; + + gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n", + jiffies, end_jiffies, end_jiffies-jiffies); + + to = 100; + /* the expression is actually jiffies < end_jiffies, but that won't + work around the wraparound. Tricky eh? */ + while (to-- && + (charsleft = gs_real_chars_in_buffer (port->tty)) && + time_after (end_jiffies, jiffies)) { + /* Units check: + chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies! + check! */ + + charsleft += 16; /* Allow 16 chars more to be transmitted ... */ + jiffies_to_transmit = port->baud?(1 + charsleft * 10 * HZ / port->baud):0; + /* ^^^ Round up.... */ + if (jiffies_to_transmit <= 0) jiffies_to_transmit = 1; + + gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies " + "(%d chars).\n", jiffies_to_transmit, charsleft); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(jiffies_to_transmit); + if (signal_pending (current)) + break; + } + + gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft); + current->state = TASK_RUNNING; + + func_exit(); +} + + + +void gs_flush_buffer(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + + func_enter (); + /* XXX Would the write semaphore do? */ + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 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); + func_exit (); +} + + +void gs_flush_chars(struct tty_struct * tty) +{ + struct gs_port *port = tty->driver_data; + + func_enter (); + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) { + func_exit (); + return; + } + + /* Beats me -- REW */ + port->flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + func_exit (); +} + + +void gs_stop(struct tty_struct * tty) +{ + struct gs_port *port = tty->driver_data; + + func_enter (); + if (port->xmit_cnt && + port->xmit_buf && + (port->flags & GS_TX_INTEN) ) { + port->flags &= ~GS_TX_INTEN; + port->rd->disable_tx_interrupts (port); + } + func_exit (); +} + + +void gs_start(struct tty_struct * tty) +{ + struct gs_port *port = tty->driver_data; + + if (port->xmit_cnt && + port->xmit_buf && + !(port->flags & GS_TX_INTEN) ) { + port->flags |= GS_TX_INTEN; + port->rd->enable_tx_interrupts (port); + } + func_exit (); +} + + +void gs_shutdown_port (struct gs_port *port) +{ + long flags; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + + save_flags (flags); + cli (); + + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = 0; + } + + if (port->tty) + set_bit(TTY_IO_ERROR, &port->tty->flags); + + port->rd->shutdown_port (port); + + port->flags &= ~ASYNC_INITIALIZED; + restore_flags (flags); +} + + +void gs_hangup(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + + func_enter (); + + tty = port->tty; + if (!tty) return; + + gs_shutdown_port (port); + + /* gs_flush_buffer (tty); */ + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE |GS_ACTIVE); + port->tty = NULL; + port->count = 0; + + wake_up_interruptible(&port->open_wait); + func_exit (); +} + + +void gs_do_softint(void *private_) +{ + struct gs_port *port = private_; + struct tty_struct *tty; + + tty = port->tty; + if(!tty) return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } + func_exit (); +} + + +int block_til_ready(void *port_, struct file * filp) +{ + struct gs_port *port = port_; + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + int CD; + struct tty_struct *tty; + + func_enter (); + tty = port->tty; + + gs_dprintk (GS_DEBUG_BTR, "Entering block_till_ready.\n"); + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + gs_dprintk (GS_DEBUG_BTR, "after hung up\n"); + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == GS_TYPE_CALLOUT) { + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + gs_dprintk (GS_DEBUG_BTR, "after subtype\n"); + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + gs_dprintk (GS_DEBUG_BTR, "after nonblock\n"); + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } + + gs_dprintk (GS_DEBUG_BTR, "after clocal check.\n"); + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); + + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; + while (1) { + CD = port->rd->get_CD (port); + gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n", + (int)signal_pending (current), *(long*)(¤t->blocked)); + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n", + port->blocked_open); + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + + port->flags |= ASYNC_NORMAL_ACTIVE; + func_exit (); + return 0; +} + + +void gs_close(struct tty_struct * tty, struct file * filp) +{ + unsigned long flags; + struct gs_port *port; + + func_enter (); + port = (struct gs_port *) tty->driver_data; + + gs_dprintk (GS_DEBUG_CLOSE, "tty=%p, port=%p port->tty=%p\n", + tty, port, port->tty); + + if(! port) { + func_exit(); + return; + } + if (!port->tty) { + printk (KERN_WARNING "gs: Odd: port->tty is NULL\n"); + port->tty = tty; + } + + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + port->rd->hungup (port); + func_exit (); + return; + } + + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_ERR "gs: gs_close: bad port count;" + " tty->count is 1, port count is %d\n", port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "gs: gs_close: bad port count: %d\n", port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + func_exit (); + return; + } + port->flags |= ASYNC_CLOSING; + + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + /* if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); */ + + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + + port->rd->disable_rx_interrupts (port); + + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + gs_wait_tx_flushed (port, port->closing_wait); + + port->flags &= ~GS_ACTIVE; + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(port->close_delay); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING | ASYNC_INITIALIZED); + wake_up_interruptible(&port->close_wait); + + port->rd->close (port); + port->rd->shutdown_port (port); + restore_flags(flags); + func_exit (); +} + + +static unsigned int gs_baudrates[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 +}; + + +void gs_set_termios (struct tty_struct * tty, + struct termios * old_termios) +{ + struct gs_port *port = tty->driver_data; + int baudrate, tmp; + struct termios *tiosp; + + func_enter(); + + tiosp = tty->termios; + + + if (gs_debug & GS_DEBUG_TERMIOS) { + gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); + my_hd ((unsigned char *)tiosp, sizeof (struct termios)); + } + +#if 0 + /* This is an optimization that is only allowed for dumb cards */ + /* Smart cards require knowledge of iflags and oflags too: that + might change hardware cooking mode.... */ +#endif + if (old_termios) { + if( (tiosp->c_iflag == old_termios->c_iflag) + && (tiosp->c_oflag == old_termios->c_oflag) + && (tiosp->c_cflag == old_termios->c_cflag) + && (tiosp->c_lflag == old_termios->c_lflag) + && (tiosp->c_line == old_termios->c_line) + && (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) { + gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n"); + return; + } + } else + gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: " + "no optimization\n"); + + if(old_termios && (gs_debug & GS_DEBUG_TERMIOS)) { + if(tiosp->c_iflag != old_termios->c_iflag) printk("c_iflag changed\n"); + if(tiosp->c_oflag != old_termios->c_oflag) printk("c_oflag changed\n"); + if(tiosp->c_cflag != old_termios->c_cflag) printk("c_cflag changed\n"); + if(tiosp->c_lflag != old_termios->c_lflag) printk("c_lflag changed\n"); + if(tiosp->c_line != old_termios->c_line) printk("c_line changed\n"); + if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); + } + + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; + if ((baudrate < 1) || (baudrate > 4)) + tiosp->c_cflag &= ~CBAUDEX; + else + baudrate += 15; + } + + baudrate = gs_baudrates[baudrate]; + if ((tiosp->c_cflag & CBAUD) == B38400) { + if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baudrate = 57600; + else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baudrate = 115200; + else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; + else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + baudrate = (port->baud_base / port->custom_divisor); + } + + /* I recommend using THIS instead of the mess in termios (and + duplicating the above code). Next we should create a clean + interface towards this variable. If your card supports arbitrary + baud rates, (e.g. CD1400 or 16550 based cards) then everything + will be very easy..... */ + port->baud = baudrate; + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + /* Baudrate/10 is cps. Divide by HZ to get chars per tick. */ + tmp = (baudrate / 10 / HZ) * 2; + + if (tmp < 0) tmp = 0; + if (tmp >= SERIAL_XMIT_SIZE) tmp = SERIAL_XMIT_SIZE-1; + + port->wakeup_chars = tmp; + + /* We should really wait for the characters to be all sent before + changing the settings. -- CAL */ + gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT); + + port->rd->set_real_termios(port); + + if ((!old_termios || + (old_termios->c_cflag & CRTSCTS)) && + !( tiosp->c_cflag & CRTSCTS)) { + tty->stopped = 0; + gs_start(tty); + } + +#ifdef tytso_patch_94Nov25_1726 + /* This "makes sense", Why is it commented out? */ + + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif + + func_exit(); + return; +} + + + +/* Must be called with interrupts enabled */ +int gs_init_port(struct gs_port *port) +{ + unsigned long flags; + unsigned long page; + + save_flags (flags); + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + + cli (); /* Don't expect this to make a difference. */ + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + restore_flags (flags); + + if (!tmp_buf) { + return -ENOMEM; + } + } + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + tmp = get_free_page(GFP_KERNEL); + + /* Spinlock? */ + cli (); + if (port->xmit_buf) + free_page (tmp); + else + port->xmit_buf = (unsigned char *) tmp; + restore_flags (flags); + + if (!port->xmit_buf) + return -ENOMEM; + } + + cli(); + + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + + gs_set_termios(port->tty, NULL); + + port->flags |= ASYNC_INITIALIZED; + port->flags &= ~GS_TX_INTEN; + + restore_flags(flags); + return 0; +} + + +int gs_setserial(struct gs_port *port, struct serial_struct *sp) +{ + struct serial_struct sio; + + copy_from_user(&sio, sp, sizeof(struct serial_struct)); + + if (!capable(CAP_SYS_ADMIN)) { + if ((sio.baud_base != port->baud_base) || + (sio.close_delay != port->close_delay) || + ((sio.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return(-EPERM); + } + + port->flags = (port->flags & ~ASYNC_USR_MASK) | + (sio.flags & ASYNC_USR_MASK); + + port->baud_base = sio.baud_base; + port->close_delay = sio.close_delay; + port->closing_wait = sio.closing_wait; + port->custom_divisor = sio.custom_divisor; + + gs_set_termios (port->tty, NULL); + + return 0; +} + + +/*****************************************************************************/ + +/* + * Generate the serial struct info. + */ + +void gs_getserial(struct gs_port *port, struct serial_struct *sp) +{ + struct serial_struct sio; + + memset(&sio, 0, sizeof(struct serial_struct)); + sio.flags = port->flags; + sio.baud_base = port->baud_base; + sio.close_delay = port->close_delay; + sio.closing_wait = port->closing_wait; + sio.custom_divisor = port->custom_divisor; + sio.hub6 = 0; + + /* If you want you can override these. */ + sio.type = PORT_UNKNOWN; + sio.xmit_fifo_size = -1; + sio.line = -1; + sio.port = -1; + sio.irq = -1; + + if (port->rd->getserial) + port->rd->getserial (port, &sio); + + copy_to_user(sp, &sio, sizeof(struct serial_struct)); +} + diff --git a/drivers/char/generic_serial.h b/drivers/char/generic_serial.h new file mode 100644 index 000000000..ca321af9b --- /dev/null +++ b/drivers/char/generic_serial.h @@ -0,0 +1,101 @@ +/* + * generic_serial.h + * + * Copyright (C) 1998 R.E.Wolff@BitWizard.nl + * + * written for the SX serial driver. + * Contains the code that should be shared over all the serial drivers. + * + * Version 0.1 -- December, 1998. + */ + +#ifndef GENERIC_SERIAL_H +#define GENERIC_SERIAL_H + +struct real_driver { + void (*disable_tx_interrupts) (void *); + void (*enable_tx_interrupts) (void *); + void (*disable_rx_interrupts) (void *); + void (*enable_rx_interrupts) (void *); + int (*get_CD) (void *); + void (*shutdown_port) (void*); + void (*set_real_termios) (void*); + int (*chars_in_buffer) (void*); + void (*close) (void*); + void (*hungup) (void*); + void (*getserial) (void*, struct serial_struct *sp); +}; + + + +struct gs_port { + int magic; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + /* struct semaphore port_write_sem; */ + int flags; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + long session; + long pgrp; + int count; + int blocked_open; + struct tty_struct *tty; + int event; + unsigned short closing_wait; + int close_delay; + struct real_driver *rd; + int wakeup_chars; + int baud_base; + int baud; + int custom_divisor; +}; + + +/* Flags */ +/* Warning: serial.h defines some ASYNC_ flags, they say they are "only" + used in serial.c, but they are also used in all other serial drivers. + Make sure they don't clash with these here... */ +#define GS_TX_INTEN 0x00800000 +#define GS_RX_INTEN 0x00400000 +#define GS_ACTIVE 0x00200000 + + + +#define GS_TYPE_NORMAL 1 +#define GS_TYPE_CALLOUT 2 + + +#define GS_DEBUG_FLUSH 0x00000001 +#define GS_DEBUG_BTR 0x00000002 +#define GS_DEBUG_TERMIOS 0x00000004 +#define GS_DEBUG_STUFF 0x00000008 +#define GS_DEBUG_CLOSE 0x00000010 + + +void gs_put_char(struct tty_struct *tty, unsigned char ch); +int gs_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count); +int gs_write_room(struct tty_struct *tty); +int gs_chars_in_buffer(struct tty_struct *tty); +void gs_flush_buffer(struct tty_struct *tty); +void gs_flush_chars(struct tty_struct *tty); +void gs_stop(struct tty_struct *tty); +void gs_start(struct tty_struct *tty); +void gs_hangup(struct tty_struct *tty); +void gs_do_softint(void *private_); +int block_til_ready(void *port, struct file *filp); +void gs_close(struct tty_struct *tty, struct file *filp); +void gs_set_termios (struct tty_struct * tty, + struct termios * old_termios); +int gs_init_port(struct gs_port *port); +int gs_setserial(struct gs_port *port, struct serial_struct *sp); +void gs_getserial(struct gs_port *port, struct serial_struct *sp); + +extern int gs_debug; + +#endif diff --git a/drivers/char/hfmodem/Config.in b/drivers/char/hfmodem/Config.in deleted file mode 100644 index 9d2799adb..000000000 --- a/drivers/char/hfmodem/Config.in +++ /dev/null @@ -1,6 +0,0 @@ -comment 'Misc. hamradio protocols' -dep_tristate 'Shortwave radio modem driver' CONFIG_HFMODEM $CONFIG_PARPORT -if [ "$CONFIG_HFMODEM" != "n" ]; then - bool ' HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC - bool ' HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS -fi diff --git a/drivers/char/hfmodem/Makefile b/drivers/char/hfmodem/Makefile deleted file mode 100644 index 2219cd52e..000000000 --- a/drivers/char/hfmodem/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# -# Makefile for the hfmodem device driver. -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# - -O_TARGET := hfmodem.o - -O_OBJS := refclock.o modem.o main.o -ifeq ($(CONFIG_HFMODEM_SBC),y) -O_OBJS += sbc.o -endif -ifeq ($(CONFIG_HFMODEM_WSS),y) -O_OBJS += wss.o -endif - -M_OBJS := $(O_TARGET) - -all: all_targets -.PHONY: all - -gentbl: gentbl.c - $(HOSTCC) $(HOSTCFLAGS) $< -o $@ -lm - -TBLHDR := tables.h - -tables.h: gentbl - ./gentbl > $@ - -fastdep: $(TBLHDR) - -include $(TOPDIR)/Rules.make diff --git a/drivers/char/hfmodem/gentbl.c b/drivers/char/hfmodem/gentbl.c deleted file mode 100644 index d60651b1b..000000000 --- a/drivers/char/hfmodem/gentbl.c +++ /dev/null @@ -1,69 +0,0 @@ -/*****************************************************************************/ - -/* - * gentbl.c -- Linux soundcard HF FSK driver, - * Table generator. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * 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. - * - * - */ - -/*****************************************************************************/ - -/* This is compiled with HOSTCC - do not include any <linux/foo.h> headers. */ -#include <math.h> -#include <stdio.h> - -/* --------------------------------------------------------------------- */ - -#define SINTABBITS 9 -#define SINTABSIZE (1<<SINTABBITS) - -/* --------------------------------------------------------------------- */ - -static void gensintbl(void) -{ - int i; - - printf("#define SINTABBITS %d\n#define SINTABSIZE (1<<SINTABBITS)\n" - "\nstatic short isintab[SINTABSIZE+SINTABSIZE/4] = {\n\t", SINTABBITS); - for (i = 0; i < (SINTABSIZE+SINTABSIZE/4); i++) { - printf("%6d", (int)(32767.0 * sin(2.0 * M_PI / SINTABSIZE * i))); - if (i < (SINTABSIZE+SINTABSIZE/4)-1) { - if ((i & 7) == 7) - printf(",\n\t"); - else - printf(","); - } - } - printf("\n};\n\n"); -} - -/* --------------------------------------------------------------------- */ - -int main(int argc, char *argv[]) -{ - printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n", - argv[0]); - gensintbl(); - exit(0); -} - -/* --------------------------------------------------------------------- */ - diff --git a/drivers/char/hfmodem/main.c b/drivers/char/hfmodem/main.c deleted file mode 100644 index a865689c1..000000000 --- a/drivers/char/hfmodem/main.c +++ /dev/null @@ -1,734 +0,0 @@ -/*****************************************************************************/ - -/* - * main.c -- Linux soundcard HF FSK driver. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * 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. - * - * Command line options (insmod command line) - * - * History: - * 0.1 15.04.97 Adapted from baycom.c and made network driver interface - * 0.2 05.07.97 All floating point stuff thrown out due to Linus' rantings :) - * - */ - -/*****************************************************************************/ - - -#include <linux/config.h> /* for CONFIG_HFMODEM_WSS and CONFIG_HFMODEM_SBC */ -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/malloc.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/ioport.h> -#include <linux/hfmodem.h> - -#include <asm/io.h> -#include <asm/segment.h> -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/dma.h> - -/* --------------------------------------------------------------------- */ - -/* - * currently this module is supposed to support both module styles, i.e. - * the old one present up to about 2.1.9, and the new one functioning - * starting with 2.1.21. The reason is I have a kit allowing to compile - * this module also under 2.0.x which was requested by several people. - * This will go in 2.2 - */ -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= 0x20100 -#include <asm/uaccess.h> -#else -#include <linux/mm.h> - -#undef put_user -#undef get_user - -#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) -#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) - -extern inline int copy_from_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_READ, from, n); - if (i) - return i; - memcpy_fromfs(to, from, n); - return 0; -} - -extern inline int copy_to_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_WRITE, to, n); - if (i) - return i; - memcpy_tofs(to, from, n); - return 0; -} -#endif - -#if LINUX_VERSION_CODE >= 0x20123 -#include <linux/init.h> -#else -#define __init -#define __initdata -#define __initfunc(x) x -#endif - -/* --------------------------------------------------------------------- */ - -/*static*/ const char hfmodem_drvname[] = "hfmodem"; -static const char hfmodem_drvinfo[] = KERN_INFO "hfmodem: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "hfmodem: version 0.2 compiled " __TIME__ " " __DATE__ "\n"; - -/* --------------------------------------------------------------------- */ -/* - * currently we support only one device - */ - -struct hfmodem_state hfmodem_state[NR_DEVICE]; - -/* --------------------------------------------------------------------- */ -/* - * ===================== port checking routines ======================== - */ - - -#define UART_RBR(iobase) (iobase+0) -#define UART_THR(iobase) (iobase+0) -#define UART_IER(iobase) (iobase+1) -#define UART_IIR(iobase) (iobase+2) -#define UART_FCR(iobase) (iobase+2) -#define UART_LCR(iobase) (iobase+3) -#define UART_MCR(iobase) (iobase+4) -#define UART_LSR(iobase) (iobase+5) -#define UART_MSR(iobase) (iobase+6) -#define UART_SCR(iobase) (iobase+7) -#define UART_DLL(iobase) (iobase+0) -#define UART_DLM(iobase) (iobase+1) - -#define SER_EXTENT 8 - -#define LPT_DATA(iobase) (iobase+0) -#define LPT_STATUS(iobase) (iobase+1) -#define LPT_CONTROL(iobase) (iobase+2) -#define LPT_IRQ_ENABLE 0x10 - -#define MIDI_DATA(iobase) (iobase) -#define MIDI_STATUS(iobase) (iobase+1) -#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */ -#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */ - -#define MIDI_EXTENT 2 - -#define SP_SER 1 -#define SP_PAR 2 -#define SP_MIDI 4 - -/* --------------------------------------------------------------------- */ - -static void parptt_wakeup(void *handle) -{ - struct hfmodem_state *dev = (struct hfmodem_state *)handle; - - printk(KERN_DEBUG "%s: parptt: why am I being woken up?\n", hfmodem_drvname); - if (!parport_claim(dev->ptt_out.pardev)) - printk(KERN_DEBUG "%s: parptt: I'm broken.\n", hfmodem_drvname); -} - -/* --------------------------------------------------------------------- */ -static int __init check_lpt(struct hfmodem_state *dev, unsigned int iobase) -{ - struct parport *pp = parport_enumerate(); - - while (pp && pp->base != iobase) - pp = pp->next; - if (!pp) - return 0; - if (!(dev->ptt_out.pardev = parport_register_device(pp, hfmodem_drvname, NULL, parptt_wakeup, - NULL, PARPORT_DEV_EXCL, dev))) - return 0; - return 1; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, c_uart_16450, c_uart_16550, c_uart_16550A }; -static const char *uart_str[] __initdata = { "unknown", "8250", "16450", "16550", "16550A" }; - -static enum uart __init check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - if (iobase <= 0 || iobase > 0x1000-SER_EXTENT) - return c_uart_unknown; - if (check_region(iobase, SER_EXTENT)) - return c_uart_unknown; - b1 = inb(UART_MCR(iobase)); - outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */ - b2 = inb(UART_MSR(iobase)); - outb(0x1a, UART_MCR(iobase)); - b3 = inb(UART_MSR(iobase)) & 0xf0; - outb(b1, UART_MCR(iobase)); /* restore old values */ - outb(b2, UART_MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(UART_RBR(iobase)); - inb(UART_RBR(iobase)); - outb(0x01, UART_FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, UART_SCR(iobase)); - b1 = inb(UART_SCR(iobase)); - outb(0xa5, UART_SCR(iobase)); - b2 = inb(UART_SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int __init check_midi(unsigned int iobase) -{ - unsigned long timeout; - unsigned long flags; - unsigned char b; - - if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT) - return 0; - if (check_region(iobase, MIDI_EXTENT)) - return 0; - timeout = jiffies + (HZ / 100); - while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) - if ((signed)(jiffies - timeout) > 0) - return 0; - save_flags(flags); - cli(); - outb(0xff, MIDI_DATA(iobase)); - b = inb(MIDI_STATUS(iobase)); - restore_flags(flags); - if (!(b & MIDI_WRITE_EMPTY)) - return 0; - while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) - if ((signed)(jiffies - timeout) > 0) - return 0; - return 1; -} - -/* --------------------------------------------------------------------- */ - -static void output_status(struct hfmodem_state *dev, int ptt) -{ - int dcd = 0; - - ptt = !!ptt; - if (dev->ptt_out.flags & SP_SER) { - outb(dcd | (ptt << 1), UART_MCR(dev->ptt_out.seriobase)); - outb(0x40 & (-ptt), UART_LCR(dev->ptt_out.seriobase)); - } - if (dev->ptt_out.flags & SP_PAR) { - outb(ptt | (dcd << 1), LPT_DATA(dev->ptt_out.pariobase)); - } - if (dev->ptt_out.flags & SP_MIDI && ptt) { - outb(0, MIDI_DATA(dev->ptt_out.midiiobase)); - } -} - -/* --------------------------------------------------------------------- */ - -static void __init output_check(struct hfmodem_state *dev) -{ - enum uart u = c_uart_unknown; - - if (((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown) - printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n", - hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]); - else { - if (dev->ptt_out.seriobase > 0) - printk(KERN_WARNING "%s: PTT output: no uart found at address 0x%x\n", - hfmodem_drvname, dev->ptt_out.seriobase); - dev->ptt_out.seriobase = 0; - } - if (check_lpt(dev, dev->ptt_out.pariobase)) - printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n", - hfmodem_drvname, dev->ptt_out.pariobase); - else { - if (dev->ptt_out.pariobase > 0) - printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n", - hfmodem_drvname, dev->ptt_out.pariobase); - dev->ptt_out.pariobase = 0; - dev->ptt_out.pardev = NULL; - } - if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && - check_midi(dev->ptt_out.midiiobase)) - printk(KERN_INFO "%s: PTT output: midi port found at address 0x%x\n", - hfmodem_drvname, dev->ptt_out.midiiobase); - else { - if (dev->ptt_out.midiiobase > 0) - printk(KERN_WARNING "%s: PTT output: no midi port found at address 0x%x\n", - hfmodem_drvname, dev->ptt_out.midiiobase); - dev->ptt_out.midiiobase = 0; - } -} - -/* --------------------------------------------------------------------- */ - -static void output_open(struct hfmodem_state *dev) -{ - dev->ptt_out.flags = 0; - if (dev->ptt_out.seriobase > 0) { - if (!check_region(dev->ptt_out.seriobase, SER_EXTENT)) { - request_region(dev->ptt_out.seriobase, SER_EXTENT, "hfmodem ser ptt"); - dev->ptt_out.flags |= SP_SER; - outb(0, UART_IER(dev->ptt_out.seriobase)); - /* 5 bits, 1 stop, no parity, no break, Div latch access */ - outb(0x80, UART_LCR(dev->ptt_out.seriobase)); - outb(0, UART_DLM(dev->ptt_out.seriobase)); - outb(1, UART_DLL(dev->ptt_out.seriobase)); /* as fast as possible */ - /* LCR and MCR set by output_status */ - } else - printk(KERN_WARNING "%s: PTT output: serial port at 0x%x busy\n", - hfmodem_drvname, dev->ptt_out.seriobase); - } - if (dev->ptt_out.pariobase > 0) { - if (parport_claim(dev->ptt_out.pardev)) - printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n", - hfmodem_drvname, dev->ptt_out.pariobase); - else - dev->ptt_out.flags |= SP_PAR; - } - if (dev->ptt_out.midiiobase > 0) { - if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) { - request_region(dev->ptt_out.midiiobase, MIDI_EXTENT, "hfmodem midi ptt"); - dev->ptt_out.flags |= SP_MIDI; - } else - printk(KERN_WARNING "%s: PTT output: midi port at 0x%x busy\n", - hfmodem_drvname, dev->ptt_out.midiiobase); - } - output_status(dev, 0); - printk(KERN_INFO "%s: PTT output:", hfmodem_drvname); - if (dev->ptt_out.flags & SP_SER) - printk(" serial interface at 0x%x", dev->ptt_out.seriobase); - if (dev->ptt_out.flags & SP_PAR) - printk(" parallel interface at 0x%x", dev->ptt_out.pariobase); - if (dev->ptt_out.flags & SP_MIDI) - printk(" mpu401 (midi) interface at 0x%x", dev->ptt_out.midiiobase); - if (!dev->ptt_out.flags) - printk(" none"); - printk("\n"); -} - -/* --------------------------------------------------------------------- */ - -static void output_close(struct hfmodem_state *dev) -{ - /* release regions used for PTT output */ - output_status(dev, 0); - if (dev->ptt_out.flags & SP_SER) - release_region(dev->ptt_out.seriobase, SER_EXTENT); - if (dev->ptt_out.flags & SP_PAR) - parport_release(dev->ptt_out.pardev); - if (dev->ptt_out.flags & SP_MIDI) - release_region(dev->ptt_out.midiiobase, MIDI_EXTENT); - dev->ptt_out.flags = 0; -} - -/* --------------------------------------------------------------------- */ - -#define INC_SAMPLE (1000000/HFMODEM_SRATE) -#define INC_FRAGMENT (HFMODEM_FRAGSAMPLES*1000000/HFMODEM_SRATE) -#define SIZE (HFMODEM_FRAGSAMPLES*HFMODEM_NUMFRAGS) - -static void hfmodem_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct hfmodem_state *dev = (struct hfmodem_state *)dev_id; - unsigned int dmaptr; - __s16 *s; - unsigned int curfrag, nfrags; - int i; - hfmodem_time_t l1time; - - dmaptr = dev->scops->intack(dev); - l1time = hfmodem_refclock_current(dev, ((SIZE+dmaptr-dev->dma.last_dmaptr) % SIZE) * - INC_SAMPLE, 1); - curfrag = (dev->dma.last_dmaptr = dmaptr) / HFMODEM_FRAGSAMPLES; - l1time -= INC_SAMPLE * (SIZE+dmaptr-dev->dma.fragptr*HFMODEM_FRAGSAMPLES) % SIZE; - sti(); - /* - * handle receiving - */ - if (dev->dma.ptt_frames <= 0) { - while (dev->dma.fragptr != curfrag) { - if (dev->dma.fragptr < HFMODEM_EXCESSFRAGS) { - s = dev->dma.buf + SIZE + HFMODEM_FRAGSAMPLES * dev->dma.fragptr; - memcpy(s, s - SIZE, HFMODEM_FRAGSIZE); - } else - s = dev->dma.buf + HFMODEM_FRAGSAMPLES * dev->dma.fragptr; - if (dev->sbuf.kbuf && dev->sbuf.kptr && dev->sbuf.rem > 0) { - i = HFMODEM_FRAGSAMPLES; - if (i > dev->sbuf.rem) - i = dev->sbuf.rem; - memcpy(dev->sbuf.kptr, s, i * sizeof(s[0])); - dev->sbuf.rem -= i; - dev->sbuf.kptr += i; - } - hfmodem_input_samples(dev, l1time, INC_SAMPLE, s); - l1time += INC_FRAGMENT; - dev->dma.fragptr++; - if (dev->dma.fragptr >= HFMODEM_NUMFRAGS) - dev->dma.fragptr = 0; - } - /* - * check for output - */ - if (hfmodem_next_tx_event(dev, l1time) > (long)INC_FRAGMENT/2) - goto int_return; - /* - * start output - */ - output_status(dev, 1); - dev->scops->prepare_output(dev); - dev->dma.last_dmaptr = 0; - /* - * clock adjust - */ - l1time = hfmodem_refclock_current(dev, 0, 0); - /* - * fill first two fragments - */ - dev->dma.ptt_frames = 1; - for (i = 0; i < 2 && i < HFMODEM_NUMFRAGS; i++) - if (hfmodem_output_samples(dev, l1time+i*INC_FRAGMENT, INC_SAMPLE, - dev->dma.buf+i*HFMODEM_FRAGSAMPLES)) - dev->dma.ptt_frames = i + 1; - dev->dma.lastfrag = 0; - dev->scops->trigger_output(dev); - /* - * finish already pending rx requests - */ - hfmodem_finish_pending_rx_requests(dev); - goto int_return; - } - /* - * handle transmitting - */ - nfrags = HFMODEM_NUMFRAGS + curfrag - dev->dma.lastfrag; - dev->dma.lastfrag = curfrag; - if (nfrags >= HFMODEM_NUMFRAGS) - nfrags -= HFMODEM_NUMFRAGS; - dev->dma.ptt_frames -= nfrags; - if (dev->dma.ptt_frames < 0) - dev->dma.ptt_frames = 0; - while (dev->dma.ptt_frames < HFMODEM_NUMFRAGS && dev->dma.ptt_frames < 4 && - hfmodem_output_samples(dev, l1time+dev->dma.ptt_frames*INC_FRAGMENT, - INC_SAMPLE, dev->dma.buf + HFMODEM_FRAGSAMPLES * - ((curfrag + dev->dma.ptt_frames) % HFMODEM_NUMFRAGS))) - dev->dma.ptt_frames++; - if (dev->dma.ptt_frames > 0) - goto int_return; - /* - * start receiving - */ - output_status(dev, 0); - dev->dma.last_dmaptr = 0; - dev->dma.lastfrag = 0; - dev->dma.fragptr = 0; - dev->dma.ptt_frames = 0; - dev->scops->prepare_input(dev); - dev->scops->trigger_input(dev); - hfmodem_refclock_current(dev, 0, 0); /* needed to reset the time difference */ -int_return: - hfmodem_wakeup(dev); -} - -/* --------------------------------------------------------------------- */ - -static int hfmodem_close(struct inode *inode, struct file *file) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - - if (!dev->active) - return -EPERM; - dev->active = 0; - dev->scops->stop(dev); - free_irq(dev->io.irq, dev); - disable_dma(dev->io.dma); - free_dma(dev->io.dma); - release_region(dev->io.base_addr, dev->scops->extent); - kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); - hfmodem_clear_rq(dev); - if (dev->sbuf.kbuf) { - kfree_s(dev->sbuf.kbuf, dev->sbuf.size); - dev->sbuf.kbuf = dev->sbuf.kptr = NULL; - dev->sbuf.size = dev->sbuf.rem = 0; - } - output_close(dev); - MOD_DEC_USE_COUNT; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int hfmodem_open(struct inode *inode, struct file *file) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - - if (dev->active) - return -EBUSY; - if (!dev->scops) - return -EPERM; - /* - * clear vars - */ - memset(&dev->l1, 0, sizeof(dev->l1)); - dev->dma.last_dmaptr = 0; - dev->dma.lastfrag = 0; - dev->dma.fragptr = 0; - dev->dma.ptt_frames = 0; - /* - * allocate memory - */ - if (!(dev->dma.buf = kmalloc(HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS), GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - /* - * allocate resources - */ - if (request_dma(dev->io.dma, hfmodem_drvname)) { - kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); - return -EBUSY; - } - if (request_irq(dev->io.irq, hfmodem_interrupt, SA_INTERRUPT, hfmodem_drvname, dev)) { - free_dma(dev->io.dma); - kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS)); - return -EBUSY; - } - request_region(dev->io.base_addr, dev->scops->extent, hfmodem_drvname); - - /* clear requests */ - dev->active++; - MOD_INC_USE_COUNT; - hfmodem_refclock_init(dev); - output_open(dev); - dev->scops->init(dev); - dev->scops->prepare_input(dev); - dev->scops->trigger_input(dev); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static struct file_operations hfmodem_fops = { - NULL, /* hfmodem_seek */ - NULL, /* hfmodem_read */ - NULL, /* hfmodem_write */ - NULL, /* hfmodem_readdir */ -#if LINUX_VERSION_CODE >= 0x20100 - hfmodem_poll, /* hfmodem_poll */ -#else - hfmodem_select, /* hfmodem_select */ -#endif - hfmodem_ioctl, /* hfmodem_ioctl */ - NULL, /* hfmodem_mmap */ - hfmodem_open, /* hfmodem_open */ - NULL, /* flush */ - hfmodem_close, /* hfmodem_close */ - NULL, /* hfmodem_fsync */ - NULL, /* hfmodem_fasync */ - NULL, /* hfmodem_check_media_change */ - NULL /* hfmodem_revalidate */ -}; - -/* --------------------------------------------------------------------- */ - -static struct miscdevice hfmodem_device = { - HFMODEM_MINOR, hfmodem_drvname, &hfmodem_fops -}; - -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -/* - * Command line parameters - */ - -static int hw = 0; -static unsigned int iobase = 0x220; -static unsigned int irq = 7; -static unsigned int dma = 1; - -static unsigned int serio = 0; -static unsigned int pario = 0; -static unsigned int midiio = 0; - -#if LINUX_VERSION_CODE >= 0x20115 - -MODULE_PARM(hw, "i"); -MODULE_PARM_DESC(hw, "hardware type: 0=SBC, 1=WSS"); -MODULE_PARM(iobase, "i"); -MODULE_PARM_DESC(iobase, "io base address"); -MODULE_PARM(irq, "i"); -MODULE_PARM_DESC(irq, "interrupt number"); -MODULE_PARM(dma, "i"); -MODULE_PARM_DESC(dma, "dma number (>=4 for SB16/32/64/etc, <=3 for the rest)"); -MODULE_PARM(serio, "i"); -MODULE_PARM_DESC(serio, "address of serial port to output PTT"); -MODULE_PARM(pario, "i"); -MODULE_PARM_DESC(pario, "address of parallel port to output PTT"); -MODULE_PARM(midiio, "i"); -MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("HF FSK modem code"); - -/* these are the module parameters from refclock.c */ - -MODULE_PARM(scale_tvusec, "i"); -MODULE_PARM_DESC(scale_tvusec, "Scaling value for the tv_usec field (can be obta -ined by refclock)"); - -#ifdef __i386__ -MODULE_PARM(scale_rdtsc, "i"); -MODULE_PARM_DESC(scale_rdtsc, "Scaling value for the rdtsc counter (can be obtai -ned by refclock)"); -MODULE_PARM(rdtsc_ok, "i"); -MODULE_PARM_DESC(rdtsc_ok, "Set to 0 to disable the use of the rdtsc instruction -"); -#endif /* __i386__ */ - -#endif - -int __init init_module(void) -{ - int i; - - printk(hfmodem_drvinfo); - memset(hfmodem_state, 0, sizeof(hfmodem_state)); - memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache)); - hfmodem_state[0].io.base_addr = iobase; - hfmodem_state[0].io.irq = irq; - hfmodem_state[0].io.dma = dma; - hfmodem_state[0].ptt_out.seriobase = serio; - hfmodem_state[0].ptt_out.pariobase = pario; - hfmodem_state[0].ptt_out.midiiobase = midiio; - init_waitqueue_head(&hfmodem_state[0].wait); - hfmodem_refclock_probe(); - output_check(&hfmodem_state[0]); -#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC) - if (hw) - i = hfmodem_wssprobe(&hfmodem_state[0]); - else - i = hfmodem_sbcprobe(&hfmodem_state[0]); -#else - i = -EINVAL; -#ifdef CONFIG_HFMODEM_WSS - i = hfmodem_wssprobe(&hfmodem_state[0]); -#endif -#ifdef CONFIG_HFMODEM_SBC - i = hfmodem_sbcprobe(&hfmodem_state[0]); -#endif -#endif - if (i) - return i; - if ((i = misc_register(&hfmodem_device))) { - printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname); - return i; - } - return 0; -} - -void cleanup_module(void) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - - if (dev->ptt_out.pariobase > 0) - parport_unregister_device(dev->ptt_out.pardev); - misc_deregister(&hfmodem_device); -} - -#else /* MODULE */ -/* --------------------------------------------------------------------- */ - -static int hw = 0; - -void __init hfmodem_setup(char *str, int *ints) -{ - if (ints[0] < 7) { - printk(KERN_WARNING "%s: setup: too few parameters\n", hfmodem_drvname); - return; - } - memset(hfmodem_state, 0, sizeof(hfmodem_state)); - memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache)); - hw = ints[1]; - hfmodem_state[0].io.base_addr = ints[2]; - hfmodem_state[0].io.irq = ints[3]; - hfmodem_state[0].io.dma = ints[4]; - if (ints[0] >= 8) - hfmodem_state[0].ptt_out.seriobase = ints[5]; - if (ints[0] >= 9) - hfmodem_state[0].ptt_out.pariobase = ints[6]; - if (ints[0] >= 10) - hfmodem_state[0].ptt_out.midiiobase = ints[7]; - hfmodem_refclock_setscale(ints[ints[0]-2], ints[ints[0]-1], ints[ints[0]]); -} - -void __init hfmodem_init(void) -{ - int i; - - printk(hfmodem_drvinfo); - init_waitqueue_head(&hfmode_state[0].wait); - hfmodem_refclock_probe(); - output_check(&hfmodem_state[0]); -#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC) - if (hw) - i = hfmodem_wssprobe(&hfmodem_state[0]); - else - i = hfmodem_sbcprobe(&hfmodem_state[0]); -#else - i = -EINVAL; -#ifdef CONFIG_HFMODEM_WSS - i = hfmodem_wssprobe(&hfmodem_state[0]); -#endif -#ifdef CONFIG_HFMODEM_SBC - i = hfmodem_sbcprobe(&hfmodem_state[0]); -#endif -#endif - if (i) { - printk(KERN_ERR "%s: soundcard probe failed\n", hfmodem_drvname); - return; - } - if ((i = misc_register(&hfmodem_device))) { - printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname); - return; - } -} - -/* --------------------------------------------------------------------- */ -#endif /* MODULE */ diff --git a/drivers/char/hfmodem/modem.c b/drivers/char/hfmodem/modem.c deleted file mode 100644 index 02dcad5cc..000000000 --- a/drivers/char/hfmodem/modem.c +++ /dev/null @@ -1,792 +0,0 @@ -/*****************************************************************************/ - -/* - * modem.c -- Linux soundcard HF FSK driver, - * Modem code. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - */ - -/*****************************************************************************/ - - -#include <linux/wait.h> -#include <linux/malloc.h> -#include <linux/hfmodem.h> - -/* --------------------------------------------------------------------- */ - -/* - * currently this module is supposed to support both module styles, i.e. - * the old one present up to about 2.1.9, and the new one functioning - * starting with 2.1.21. The reason is I have a kit allowing to compile - * this module also under 2.0.x which was requested by several people. - * This will go in 2.2 - */ -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= 0x20100 -#include <asm/uaccess.h> -#else -#include <asm/segment.h> -#include <linux/mm.h> - -#undef put_user -#undef get_user - -#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) -#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) - -extern inline int copy_from_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_READ, from, n); - if (i) - return i; - memcpy_fromfs(to, from, n); - return 0; -} - -extern inline int copy_to_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_WRITE, to, n); - if (i) - return i; - memcpy_tofs(to, from, n); - return 0; -} -#endif - -#if LINUX_VERSION_CODE >= 0x20123 -#include <linux/init.h> -#else -#define __init -#define __initdata -#define __initfunc(x) x -#endif - -/* --------------------------------------------------------------------- */ - -struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE]; - -/* --------------------------------------------------------------------- */ - -#include "tables.h" - -#define M_PI 3.14159265358979323846 /* pi */ - -/* --------------------------------------------------------------------- */ - -extern __inline__ int isimplecos(unsigned int arg) -{ - return isintab[((arg+0x4000) >> (16-SINTABBITS)) & (SINTABSIZE-1)]; -} - -extern __inline__ int isimplesin(unsigned int arg) -{ - return isintab[(arg >> (16-SINTABBITS)) & (SINTABSIZE-1)]; -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int itblcos(unsigned int arg) -{ - unsigned int x; - int dx; - int s, c; - - x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS))); - dx = arg - x; - x >>= (16-SINTABBITS); - c = isintab[x+(0x4000 >> (16-SINTABBITS))]; - s = isintab[x]; - return c - ((s * dx * (int)(M_PI*64.0)) >> 21); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void itblcossin(unsigned int arg, int *cos, int *sin) -{ - unsigned int x; - int dx; - int s, c; - - x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS))); - dx = arg - x; - x >>= (16-SINTABBITS); - c = isintab[x+(0x4000 >> (16-SINTABBITS))]; - s = isintab[x]; - *cos = c - ((s * dx * (int)(M_PI*64.0)) >> 21); - *sin = s + ((c * dx * (int)(M_PI*64.0)) >> 21); -} - -/* --------------------------------------------------------------------- */ - -static unsigned short random_seed; - -extern __inline__ unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - -/* --------------------------------------------------------------------- */ -/* - * correlator cache routines - */ - -extern __inline__ void cc_lock(unsigned int u) -{ - if (u >= HFMODEM_CORRELATOR_CACHE) - return; - hfmodem_correlator_cache[u].refcnt++; -} - -extern __inline__ void cc_unlock(unsigned int u) -{ - if (u >= HFMODEM_CORRELATOR_CACHE) - return; - if ((--hfmodem_correlator_cache[u].refcnt) <= 0) { - unsigned int i; - - for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++) - if (hfmodem_correlator_cache[i].lru < 32767) - hfmodem_correlator_cache[i].lru++; - hfmodem_correlator_cache[u].lru = 0; - hfmodem_correlator_cache[u].refcnt = 0; - } -} - - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int cc_lookup(unsigned short phinc0, unsigned short phinc1) -{ - unsigned int j; - - /* find correlator cache entry */ - for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++) - if (hfmodem_correlator_cache[j].phase_incs[0] == phinc0 && - hfmodem_correlator_cache[j].phase_incs[1] == phinc1) - return j; - return ~0; -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int cc_replace(void) -{ - unsigned int j, k = HFMODEM_CORRELATOR_CACHE; - int l = -1; - - for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++) - if (hfmodem_correlator_cache[j].refcnt <= 0 && hfmodem_correlator_cache[j].lru > l) { - k = j; - l = hfmodem_correlator_cache[j].lru; - } - if (k < HFMODEM_CORRELATOR_CACHE) - return k; - printk(KERN_ERR "%s: modem: out of filter coefficient cache entries\n", hfmodem_drvname); - return random_num() % HFMODEM_CORRELATOR_CACHE; -} - -/* --------------------------------------------------------------------- */ - -#define SH1 8 /* min. ceil(log2(L1CORR_LEN)) - (31-(2*15-1)) */ -#define SH2 (3*15-2*SH1) - -#ifdef __i386__ - -extern __inline__ int icorr(int n, const int *coeff, const short *inp) -{ - int ret, rethi, tmp1 = 0, tmp2 = 0; - - __asm__("\n1:\n\t" - "movswl (%0),%%eax\n\t" - "imull (%1)\n\t" - "subl $2,%0\n\t" - "addl $4,%1\n\t" - "addl %%eax,%3\n\t" - "adcl %%edx,%4\n\t" - "decl %2\n\t" - "jne 1b\n\t" - : "=&S" (inp), "=&D" (coeff), "=&c" (n), "=m" (tmp1), "=m" (tmp2) - : "0" (inp), "1" (coeff), "2" (n) - : "ax", "dx"); - __asm__("shrdl %2,%1,%0\n\t" - "# sarl %2,%1\n\t" - : "=&r" (ret), "=&r" (rethi) - : "i" (SH1), "0" (tmp1), "1" (tmp2)); - - - return ret; -} - -#else /* __i386__ */ - -extern __inline__ int icorr(int n, const int *coeff, const short *inp) -{ - long long sum = 0; - int i; - - for (i = n; i > 0; i--, coeff++, inp--) - sum += (*coeff) * (*inp); - sum >>= SH1; - return sum; -} - -#endif /* __i386__ */ - -/* --------------------------------------------------------------------- */ - -extern __inline__ long long isqr(int x) __attribute__ ((const)); - -extern __inline__ long long isqr(int x) -{ - return ((long long)x) * ((long long)x); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ hfmodem_soft_t do_filter(struct hfmodem_l1_rxslot *slot, short *s) -{ - unsigned int cc = slot->corr_cache; - long long ll; - - if (cc >= HFMODEM_CORRELATOR_CACHE) { - printk(KERN_ERR "do_filter: correlator cache index overrange\n"); - return 0; - } - ll = isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][0], s)) + - isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][1], s)) - - isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][0], s)) - - isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][1], s)); - ll >>= SH2; - return (ll * slot->scale) >> 23; -} - -/* --------------------------------------------------------------------- */ - -static void cc_prepare(struct hfmodem_l1_rxslot *slot, unsigned short phinc0, unsigned short phinc1) -{ - unsigned int j, k, l, ph, phinc; - - slot->scale = (1<<23) / (slot->corrlen*slot->corrlen); - - j = cc_lookup(phinc0, phinc1); - if (j >= HFMODEM_CORRELATOR_CACHE) { - j = cc_replace(); - /* calculate the correlator values */ - printk(KERN_DEBUG "%s: corr cache calc: %u phases: 0x%04x 0x%04x\n", - hfmodem_drvname, j, phinc0, phinc1); - hfmodem_correlator_cache[j].phase_incs[0] = phinc0; - hfmodem_correlator_cache[j].phase_incs[1] = phinc1; - for (k = 0; k < 2; k++) { - phinc = hfmodem_correlator_cache[j].phase_incs[k]; - for (ph = l = 0; l < HFMODEM_MAXCORRLEN; l++, ph = (ph + phinc) & 0xffff) - itblcossin(ph, &hfmodem_correlator_cache[j].correlator[k][0][l], - &hfmodem_correlator_cache[j].correlator[k][1][l]); - } - hfmodem_correlator_cache[j].refcnt = 0; - -#if 0 - printk(KERN_DEBUG "%s: corr: %u ph: 0x%04x 0x%04x\n", hfmodem_drvname, j, - hfmodem_correlator_cache[j].phase_incs[0], - hfmodem_correlator_cache[j].phase_incs[1]); - for (l = 0; l < HFMODEM_MAXCORRLEN; l++) - printk(KERN_DEBUG "%s: corr: %6d %6d %6d %6d\n", hfmodem_drvname, - hfmodem_correlator_cache[j].correlator[0][0][l], - hfmodem_correlator_cache[j].correlator[0][1][l], - hfmodem_correlator_cache[j].correlator[1][0][l], - hfmodem_correlator_cache[j].correlator[1][1][l]); -#endif - } - slot->corr_cache = j; - cc_lock(j); -} - -/* --------------------------------------------------------------------- */ - -void hfmodem_clear_rq(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned int i; - - save_flags(flags); - cli(); - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state == ss_unused) - continue; - dev->l1.rxslots[i].state = ss_unused; - kfree_s(dev->l1.rxslots[i].data, dev->l1.rxslots[i].nbits * sizeof(hfmodem_soft_t)); - } - for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_unused) - continue; - dev->l1.txslots[i].state = ss_unused; - kfree_s(dev->l1.txslots[i].data, (dev->l1.txslots[i].nbits + 7) >> 3); - } - for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++) - hfmodem_correlator_cache[i].refcnt = 0; - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - struct hfmodem_ioctl_fsk_tx_request txrq; - struct hfmodem_ioctl_fsk_rx_request rxrq; - struct hfmodem_ioctl_mixer_params mix; - struct hfmodem_ioctl_sample_params spar; - unsigned long flags; - unsigned int len; - int ret, i, idx; - void *data, *userdata; - hfmodem_id_t id; - hfmodem_time_t tm = 0; - - if (!dev->active) - return -EBUSY; - switch(cmd) { - default: - return -EINVAL; - - case HFMODEM_IOCTL_FSKTXREQUEST: - if ((ret = copy_from_user(&txrq, (void *)arg, sizeof(txrq)))) - return ret; - if (txrq.nbits > HFMODEM_MAXBITS) - return -EINVAL; - len = (txrq.nbits + 7) >> 3; - if (!(data = kmalloc(len, GFP_KERNEL))) - return -ENOMEM; - if (copy_from_user(data, txrq.data, len)) { - kfree_s(data, len); - return -EFAULT; - } - save_flags(flags); - cli(); - for (i = 0; i < HFMODEM_NUMTXSLOTS && dev->l1.txslots[i].state != ss_unused; i++); - if (i >= HFMODEM_NUMTXSLOTS) { - restore_flags(flags); - kfree_s(data, len); - return -EBUSY; - } - dev->l1.txslots[i].state = ss_ready; - dev->l1.txslots[i].tstart = txrq.tstart; - dev->l1.txslots[i].tinc = txrq.tinc; - dev->l1.txslots[i].data = data; - dev->l1.txslots[i].nbits = txrq.nbits; - dev->l1.txslots[i].cntbits = 0; - dev->l1.txslots[i].inv = txrq.inv ? 0xff : 0; - dev->l1.txslots[i].id = txrq.id; - dev->l1.txslots[i].phase_incs[0] = ((txrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) - & 0xffff; - dev->l1.txslots[i].phase_incs[1] = ((txrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) - & 0xffff; - restore_flags(flags); - return 0; - - case HFMODEM_IOCTL_FSKRXREQUEST: - if ((ret = copy_from_user(&rxrq, (void *)arg, sizeof(rxrq)))) - return ret; - if (rxrq.nbits > HFMODEM_MAXBITS) - return -EINVAL; - if (rxrq.baud < HFMODEM_MINBAUD || rxrq.baud > HFMODEM_MAXBAUD) - return -EINVAL; - len = rxrq.nbits * sizeof(hfmodem_soft_t); - if (verify_area(VERIFY_WRITE, rxrq.data, len)) - return -EFAULT; - if (!(data = kmalloc(len, GFP_KERNEL))) - return -ENOMEM; - save_flags(flags); - cli(); - for (i = 0; i < HFMODEM_NUMRXSLOTS && dev->l1.rxslots[i].state != ss_unused; i++); - if (i >= HFMODEM_NUMRXSLOTS) { - restore_flags(flags); - kfree_s(data, len); - return -EBUSY; - } - dev->l1.rxslots[i].state = ss_ready; - dev->l1.rxslots[i].tstart = rxrq.tstart; - dev->l1.rxslots[i].tinc = rxrq.tinc; - dev->l1.rxslots[i].data = data; - dev->l1.rxslots[i].userdata = rxrq.data; - dev->l1.rxslots[i].nbits = rxrq.nbits; - dev->l1.rxslots[i].cntbits = 0; - dev->l1.rxslots[i].id = rxrq.id; - dev->l1.rxslots[i].corrlen = HFMODEM_SRATE/rxrq.baud; - cc_prepare(dev->l1.rxslots+i, - ((rxrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff, - ((rxrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff); - restore_flags(flags); - return 0; - - case HFMODEM_IOCTL_CLEARRQ: - hfmodem_clear_rq(dev); - return 0; - - case HFMODEM_IOCTL_GETCURTIME: - return put_user(dev->l1.last_time + 20000L, (hfmodem_time_t *)arg); /* heuristic */ - - case HFMODEM_IOCTL_WAITRQ: - save_flags(flags); - cli(); - ret = 0; - for (idx = -1, i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state == ss_unused) - continue; - if (dev->l1.rxslots[i].state != ss_retired) { - ret++; - continue; - } - if (idx < 0 || (signed)(tm - dev->l1.rxslots[i].tstart) > 0) { - idx = i; - tm = dev->l1.rxslots[i].tstart; - } - } - if (idx >= 0) { - cc_unlock(dev->l1.rxslots[idx].corr_cache); - id = dev->l1.rxslots[idx].id; - data = dev->l1.rxslots[idx].data; - userdata = dev->l1.rxslots[idx].userdata; - len = dev->l1.rxslots[idx].nbits * sizeof(hfmodem_soft_t); - dev->l1.rxslots[idx].state = ss_unused; - restore_flags(flags); - ret = copy_to_user(userdata, data, len); - kfree_s(data, len); - return (put_user(id, (hfmodem_id_t *)arg)) ? -EFAULT : ret; - } - for (idx = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_unused) - continue; - if (dev->l1.txslots[i].state != ss_retired) { - ret++; - continue; - } - if (idx < 0 || (signed)(tm - dev->l1.txslots[i].tstart) > 0) { - idx = i; - tm = dev->l1.txslots[i].tstart; - } - } - if (idx >= 0) { - id = dev->l1.txslots[idx].id; - data = dev->l1.txslots[idx].data; - len = (dev->l1.txslots[idx].nbits + 7) >> 3; - dev->l1.txslots[idx].state = ss_unused; - restore_flags(flags); - kfree_s(data, len); - return put_user(id, (hfmodem_id_t *)arg); - } - restore_flags(flags); - return ret ? -EAGAIN : -EPIPE; - - case HFMODEM_IOCTL_MIXERPARAMS: - if ((ret = copy_from_user(&mix, (void *)arg, sizeof(mix)))) - return ret; - dev->scops->mixer(dev, mix.src, mix.igain, mix.ogain); - return 0; - - case HFMODEM_IOCTL_SAMPLESTART: - save_flags(flags); - cli(); - if (dev->sbuf.kbuf) - kfree_s(dev->sbuf.kbuf, dev->sbuf.size); - dev->sbuf.kbuf = dev->sbuf.kptr = NULL; - dev->sbuf.size = dev->sbuf.rem = 0; - restore_flags(flags); - if ((ret = copy_from_user(&spar, (void *)arg, sizeof(spar)))) - return ret; - if (spar.len == 0) - return 0; - if (spar.len < 2 || spar.len > 8192) - return -EINVAL; - if (verify_area(VERIFY_WRITE, spar.data, spar.len * sizeof(__s16))) - return -EFAULT; - if (!(dev->sbuf.kbuf = kmalloc(spar.len * sizeof(__s16), GFP_KERNEL))) - return -ENOMEM; - save_flags(flags); - cli(); - dev->sbuf.kptr = dev->sbuf.kbuf; - dev->sbuf.size = spar.len * sizeof(__s16); - dev->sbuf.rem = spar.len; - dev->sbuf.ubuf = spar.data; - restore_flags(flags); - return 0; - - case HFMODEM_IOCTL_SAMPLEFINISHED: - save_flags(flags); - cli(); - if (dev->sbuf.rem > 0) { - restore_flags(flags); - return -EAGAIN; - } - if (!dev->sbuf.kbuf || !dev->sbuf.size) { - restore_flags(flags); - return -EPIPE; - } - restore_flags(flags); - ret = copy_to_user(dev->sbuf.ubuf, dev->sbuf.kbuf, dev->sbuf.size); - kfree_s(dev->sbuf.kbuf, dev->sbuf.size); - dev->sbuf.kbuf = dev->sbuf.kptr = NULL; - dev->sbuf.size = dev->sbuf.rem = 0; - return ret; - } -} - -/* --------------------------------------------------------------------- */ - -#if LINUX_VERSION_CODE >= 0x20100 - -unsigned int hfmodem_poll(struct file *file, poll_table *wait) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - unsigned long flags; - int i, cnt1, cnt2; - - poll_wait(file, &dev->wait, wait); - save_flags(flags); - cli(); - for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_retired) - cnt1++; - if (dev->l1.txslots[i].state != ss_unused) - cnt2++; - } - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state == ss_retired) - cnt1++; - if (dev->l1.rxslots[i].state != ss_unused) - cnt2++; - } - restore_flags(flags); - if (cnt1 || !cnt2) - return POLLIN | POLLRDNORM; - return 0; -} - -#else - -int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) -{ - struct hfmodem_state *dev = &hfmodem_state[0]; - unsigned long flags; - int i, cnt1, cnt2; - - if (sel_type == SEL_IN) { - save_flags(flags); - cli(); - for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_retired) - cnt1++; - if (dev->l1.txslots[i].state != ss_unused) - cnt2++; - } - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state == ss_retired) - cnt1++; - if (dev->l1.rxslots[i].state != ss_unused) - cnt2++; - } - restore_flags(flags); - if (cnt1 || !cnt2) - return 1; - select_wait(&dev->wait, wait); - } - return 0; -} - -#endif - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int l1fsk_phinc(struct hfmodem_l1_txslot *txs, unsigned int nbit) -{ - return txs->phase_incs[!!((txs->data[nbit >> 3] ^ txs->inv) & (1 << (nbit & 7)))]; -} - -/* --------------------------------------------------------------------- */ - -void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, - hfmodem_time_t tinc, __s16 *samples) -{ - hfmodem_time_t tst, tend; - __s16 *s; - int i, j; - hfmodem_soft_t sample; - - dev->l1.last_time = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc; - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - struct hfmodem_l1_rxslot *rxs = dev->l1.rxslots + i; - - if (rxs->state == ss_unused || rxs->state == ss_retired) - continue; - tst = tstart - (rxs->corrlen-1) * tinc; - tend = tst + (HFMODEM_FRAGSAMPLES-1) * tinc; - if (rxs->state == ss_ready) { - if ((signed)(rxs->tstart - tend) > 0) - continue; - rxs->state = ss_oper; - } - for (s = samples, j = 0; j < HFMODEM_FRAGSAMPLES; j++, s++, tst += tinc) - if ((signed)(rxs->tstart - tst) <= 0) { - sample = do_filter(rxs, s); - while ((signed)(rxs->tstart - tst) <= 0 && - rxs->cntbits < rxs->nbits) { - rxs->data[rxs->cntbits] = sample; - rxs->cntbits++; - rxs->tstart += rxs->tinc; - } - if (rxs->cntbits >= rxs->nbits) { - rxs->state = ss_retired; - break; - } - } - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int output_one_sample(struct hfmodem_state *dev, hfmodem_time_t tm) -{ - int i, j, k; - struct hfmodem_l1_txslot *txs; - /* - * first activate new output slots - */ - for (j = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) { - txs = dev->l1.txslots + i; - if (txs->state == ss_ready && (signed)(txs->tstart - tm) <= 0) { - for (k = 0; k < HFMODEM_NUMTXSLOTS; k++) { - if (dev->l1.txslots[k].state != ss_oper) - continue; - dev->l1.txslots[k].state = ss_retired; - } - txs->state = ss_oper; - txs->tstart += txs->tinc; - txs->phinc = l1fsk_phinc(txs, 0); - txs->cntbits = 1; - }; - if (txs->state != ss_oper) - continue; - j = i; - } - if (j < 0 || j >= HFMODEM_NUMTXSLOTS) - return 0; - /* - * calculate the current slot - */ - txs = dev->l1.txslots + j; - while ((signed)(txs->tstart - tm) <= 0) { - if (txs->cntbits >= txs->nbits) { - txs->state = ss_retired; - return 0; - } - txs->tstart += txs->tinc; - txs->phinc = l1fsk_phinc(txs, txs->cntbits); - txs->cntbits++; - } - return txs->phinc; -} - -/* --------------------------------------------------------------------- */ - -int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, - hfmodem_time_t tinc, __s16 *samples) -{ - int i, j; - hfmodem_time_t tend = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc; - - for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_oper) - break; - if (dev->l1.txslots[i].state == ss_ready && - (signed)(dev->l1.txslots[i].tstart - tend) <= 0) - break; - } - if (i >= HFMODEM_NUMTXSLOTS) - return 0; - for (j = 0; j < HFMODEM_FRAGSAMPLES; j++, tstart += tinc, samples++) { - *samples = isimplecos(dev->l1.tx_phase); - dev->l1.tx_phase += output_one_sample(dev, tstart); - } - return 1; -} - -/* --------------------------------------------------------------------- */ - -long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr) -{ - long diff = LONG_MAX, t; - int i; - - for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_oper) - if (diff > 0) - diff = 0; - if (dev->l1.txslots[i].state == ss_ready) { - t = dev->l1.txslots[i].tstart - curr; - if (t < diff) - diff = t; - } - } - return diff; -} - -/* --------------------------------------------------------------------- */ - -void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev) -{ - int i; - - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state != ss_oper) - continue; - while (dev->l1.rxslots[i].cntbits < dev->l1.rxslots[i].nbits) { - dev->l1.rxslots[i].data[dev->l1.rxslots[i].cntbits] = 0; - dev->l1.rxslots[i].cntbits++; - } - dev->l1.rxslots[i].state = ss_retired; - } -} - -/* --------------------------------------------------------------------- */ - -void hfmodem_wakeup(struct hfmodem_state *dev) -{ - int i, cnt1, cnt2; - - for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) { - if (dev->l1.txslots[i].state == ss_retired) - cnt1++; - if (dev->l1.txslots[i].state != ss_unused) - cnt2++; - } - for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) { - if (dev->l1.rxslots[i].state == ss_retired) - cnt1++; - if (dev->l1.rxslots[i].state != ss_unused) - cnt2++; - } - if (cnt1 || !cnt2) - wake_up_interruptible(&dev->wait); -} - -/* --------------------------------------------------------------------- */ diff --git a/drivers/char/hfmodem/refclock.c b/drivers/char/hfmodem/refclock.c deleted file mode 100644 index 20153c02b..000000000 --- a/drivers/char/hfmodem/refclock.c +++ /dev/null @@ -1,154 +0,0 @@ -/*****************************************************************************/ - -/* - * refclock.c -- Linux soundcard HF FSK driver, - * Reference clock routines. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - */ - -/*****************************************************************************/ - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/time.h> -#include <linux/hfmodem.h> -#include <asm/processor.h> - -/* --------------------------------------------------------------------- */ - -/* - * currently this module is supposed to support both module styles, i.e. - * the old one present up to about 2.1.9, and the new one functioning - * starting with 2.1.21. The reason is I have a kit allowing to compile - * this module also under 2.0.x which was requested by several people. - * This will go in 2.2 - */ -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= 0x20123 -#include <linux/init.h> -#else -#define __init -#define __initdata -#define __initfunc(x) x -#endif - -/* --------------------------------------------------------------------- */ -/* - * command line params - */ - -static unsigned int scale_tvusec = 1UL<<24; - -#ifdef __i386__ -static unsigned int scale_rdtsc = 0; -static int rdtsc_ok = 1; -#endif /* __i386__ */ - -/* --------------------------------------------------------------------- */ - -#ifdef __i386__ -static void __init i386_capability(void) -{ - if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) - rdtsc_ok = 1; - else - printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname); -} -#endif /* __i386__ */ - -/* --------------------------------------------------------------------- */ - -void __init hfmodem_refclock_probe(void) -{ -#ifdef __i386__ - if (rdtsc_ok) { - rdtsc_ok = 0; - i386_capability(); - if (rdtsc_ok) { - unsigned int tmp0, tmp1, tmp2, tmp3; - __asm__("rdtsc" : "=a" (tmp0), "=d" (tmp1)); - __asm__("rdtsc" : "=a" (tmp2), "=d" (tmp3)); - if (tmp0 == tmp2 && tmp1 == tmp3) { - rdtsc_ok = 0; - printk(KERN_WARNING "%s: rdtsc unusable, does not change\n", - hfmodem_drvname); - } - } - } - printk(KERN_INFO "%s: using %s as timing source\n", hfmodem_drvname, - rdtsc_ok ? "rdtsc" : "gettimeofday"); -#endif /* __i386__ */ -} - -/* --------------------------------------------------------------------- */ - -void hfmodem_refclock_init(struct hfmodem_state *dev) -{ - struct timeval tv; - - dev->clk.lasttime = 0; -#ifdef __i386__ - if (rdtsc_ok) { - __asm__("rdtsc;" : "=&d" (dev->clk.starttime_hi), "=&a" (dev->clk.starttime_lo)); - return; - } -#endif /* __i386__ */ - do_gettimeofday(&tv); - dev->clk.last_tvusec = tv.tv_usec; - dev->clk.time_cnt = 0; -} - -/* --------------------------------------------------------------------- */ - -hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid) -{ - struct timeval tv; - hfmodem_time_t curtime; - long diff; - -#ifdef __i386__ - if (rdtsc_ok) { - unsigned int tmp0, tmp1; - unsigned int tmp2, tmp3; - - __asm__("rdtsc;\n\t" - "subl %2,%%eax\n\t" - "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) - : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx"); - __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax"); - __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx"); - curtime = tmp2 + tmp3; - goto time_known; - } -#endif /* __i386__ */ - do_gettimeofday(&tv); - dev->clk.time_cnt += (unsigned)(1000000 + tv.tv_usec - dev->clk.last_tvusec) % 1000000; - dev->clk.last_tvusec = tv.tv_usec; - curtime = (dev->clk.time_cnt * scale_tvusec) >> 24; - time_known: - if (exp_valid && abs(diff = (curtime - dev->clk.lasttime - expected)) >= 1000) - printk(KERN_DEBUG "%s: refclock adjustment %ld more than 1ms\n", - hfmodem_drvname, diff); - return (dev->clk.lasttime = curtime); -} - -/* --------------------------------------------------------------------- */ diff --git a/drivers/char/hfmodem/sbc.c b/drivers/char/hfmodem/sbc.c deleted file mode 100644 index 8e4010619..000000000 --- a/drivers/char/hfmodem/sbc.c +++ /dev/null @@ -1,741 +0,0 @@ -/*****************************************************************************/ - -/* - * sbc.c -- Linux soundcard HF FSK driver, - * Soundblaster specific functions. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - */ - -/*****************************************************************************/ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/ioport.h> -#include <linux/hfmodem.h> - -#include <asm/io.h> -#include <asm/dma.h> - -/* --------------------------------------------------------------------- */ -/* - * the sbc converter's registers - */ -#define DSP_RESET(iobase) (iobase+0x6) -#define DSP_READ_DATA(iobase) (iobase+0xa) -#define DSP_WRITE_DATA(iobase) (iobase+0xc) -#define DSP_WRITE_STATUS(iobase) (iobase+0xc) -#define DSP_DATA_AVAIL(iobase) (iobase+0xe) -#define DSP_MIXER_ADDR(iobase) (iobase+0x4) -#define DSP_MIXER_DATA(iobase) (iobase+0x5) -#define DSP_INTACK_16BIT(iobase) (iobase+0xf) -#define SBC_EXTENT 16 - -/* --------------------------------------------------------------------- */ -/* - * SBC commands - */ - -#define SBC_OUTPUT 0x14 -#define SBC_INPUT 0x24 -#define SBC_BLOCKSIZE 0x48 -#define SBC_HI_OUTPUT 0x91 -#define SBC_HI_INPUT 0x99 -#define SBC_LO_OUTPUT_AUTOINIT 0x1c -#define SBC_LO_INPUT_AUTOINIT 0x2c -#define SBC_HI_OUTPUT_AUTOINIT 0x90 -#define SBC_HI_INPUT_AUTOINIT 0x98 -#define SBC_IMMED_INT 0xf2 -#define SBC_GET_REVISION 0xe1 -#define ESS_GET_REVISION 0xe7 -#define ESS_EXTENDED_MODE 0xc6 -#define SBC_SPEAKER_ON 0xd1 -#define SBC_SPEAKER_OFF 0xd3 -#define SBC_DMA_ON 0xd0 -#define SBC_DMA_OFF 0xd4 -#define SBC_SAMPLE_RATE 0x40 -#define SBC_SAMPLE_RATE_OUT 0x41 -#define SBC_SAMPLE_RATE_IN 0x42 -#define SBC_MONO_8BIT 0xa0 -#define SBC_MONO_16BIT 0xa4 -#define SBC_STEREO_8BIT 0xa8 -#define SBC_STEREO_16BIT 0xac - -#define SBC4_OUT8_AI 0xc6 -#define SBC4_IN8_AI 0xce -#define SBC4_MODE_UNS_MONO 0x00 -#define SBC4_MODE_SIGN_MONO 0x10 - -#define SBC4_OUT16_AI 0xb6 -#define SBC4_IN16_AI 0xbe -#define SBC4_OUT16_AI_NO_FIFO 0xb4 -#define SBC4_IN16_AI_NO_FIFO 0xbc - -/* --------------------------------------------------------------------- */ - -extern const struct hfmodem_scops sbc4_scops; -extern const struct hfmodem_scops ess_scops; - -/* --------------------------------------------------------------------- */ - -static int reset_dsp(struct hfmodem_state *dev) -{ - int i; - - outb(1, DSP_RESET(dev->io.base_addr)); - udelay(3); - outb(0, DSP_RESET(dev->io.base_addr)); - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) - if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) - return 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void write_dsp(struct hfmodem_state *dev, unsigned char data) -{ - int i; - - for (i = 0; i < 0xffff; i++) - if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) { - outb(data, DSP_WRITE_DATA(dev->io.base_addr)); - return; - } -} - -/* --------------------------------------------------------------------- */ - -static int read_dsp(struct hfmodem_state *dev, unsigned char *data) -{ - int i; - - if (!data) - return 0; - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) { - *data = inb(DSP_READ_DATA(dev->io.base_addr)); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data) -{ - write_dsp(dev, reg); - write_dsp(dev, data); -} - -/* --------------------------------------------------------------------- */ - -static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data) -{ - write_dsp(dev, 0xc0); - write_dsp(dev, reg); - return read_dsp(dev, data); -} - -/* --------------------------------------------------------------------- */ - -static int reset_ess(struct hfmodem_state *dev) -{ - int i; - - outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */ - udelay(3); - outb(0, DSP_RESET(dev->io.base_addr)); - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) - if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) { - write_dsp(dev, ESS_EXTENDED_MODE); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int config_resources(struct hfmodem_state *dev) -{ - unsigned char irqreg = 0, dmareg = 0, realirq, realdma; - unsigned long flags; - - switch (dev->io.irq) { - case 2: - case 9: - irqreg |= 0x01; - break; - - case 5: - irqreg |= 0x02; - break; - - case 7: - irqreg |= 0x04; - break; - - case 10: - irqreg |= 0x08; - break; - - default: - return -ENODEV; - } - - switch (dev->io.dma) { - case 0: - dmareg |= 0x01; - break; - - case 1: - dmareg |= 0x02; - break; - - case 3: - dmareg |= 0x08; - break; - - case 5: - dmareg |= 0x20; - break; - - case 6: - dmareg |= 0x40; - break; - - case 7: - dmareg |= 0x80; - break; - - default: - return -ENODEV; - } - save_flags(flags); - cli(); - outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr)); - outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr)); - realirq = inb(DSP_MIXER_DATA(dev->io.base_addr)); - outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr)); - outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr)); - realdma = inb(DSP_MIXER_DATA(dev->io.base_addr)); - restore_flags(flags); - if ((~realirq) & irqreg || (~realdma) & dmareg) { - printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " - "and IRQ/DMA specified wrongly?\n", hfmodem_drvname); - return -EINVAL; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev) -{ - inb(DSP_DATA_AVAIL(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev) -{ - inb(DSP_INTACK_16BIT(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data) -{ - outb(reg, DSP_MIXER_ADDR(dev->io.base_addr)); - outb(data, DSP_MIXER_DATA(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -int hfmodem_sbcprobe(struct hfmodem_state *dev) -{ - unsigned char revhi, revlo, essrevhi, essrevlo, tmp; - int ret; - - if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT || - dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2) - return -ENXIO; - if (check_region(dev->io.base_addr, SBC_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", - hfmodem_drvname, dev->io.base_addr); - return -ENODEV; - } - set_mixer(dev, 0, 0); /* reset mixer */ - write_dsp(dev, SBC_GET_REVISION); - if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo)) - return -ENODEV; - printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo); - if (revhi == 3 && revlo == 1) { - write_dsp(dev, ESS_GET_REVISION); - if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo)) - return -ENODEV; - if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) { - printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n", - hfmodem_drvname, essrevlo & 0x0f); - return -ENODEV; - } - if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) { - printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n", - hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f); - if (dev->io.dma > 3) { - printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname); - return -ENXIO; - } - printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname); - read_ess(dev, 0xb1, &tmp); - switch (tmp & 0xf) { - case 0: - printk("2, 9, \"all others\""); - break; - - case 5: - printk("5"); - break; - - case 10: - printk("7"); - break; - - case 15: - printk("10"); - break; - - default: - printk("unknown (%d)", tmp & 0xf); - break; - } - printk(" dma: "); - read_ess(dev, 0xb2, &tmp); - switch (tmp & 0xf) { - case 0: - printk("\"all others\""); - break; - - case 5: - printk("0"); - break; - - case 10: - printk("1"); - break; - - case 15: - printk("3"); - break; - - default: - printk("unknown (%d)", tmp & 0xf); - break; - } - printk("\n"); - dev->scops = &ess_scops; - return 0; - } - } - if (revhi < 4) { - printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname); - return -ENODEV; - } - if (dev->io.dma < 4) { - printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname); - return -ENXIO; - } - if ((ret = config_resources(dev))) - return ret; - dev->scops = &sbc4_scops; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_init(struct hfmodem_state *dev) -{ -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_prepare_input(struct hfmodem_state *dev) -{ - unsigned long flags; - - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname); - return; - } - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC_SAMPLE_RATE_IN); /* set sampling rate */ - write_dsp(dev, HFMODEM_SRATE >> 8); - write_dsp(dev, HFMODEM_SRATE & 0xff); - write_dsp(dev, SBC_SPEAKER_OFF); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_trigger_input(struct hfmodem_state *dev) -{ - unsigned long flags; - - save_flags(flags); - cli(); - write_dsp(dev, SBC4_IN16_AI_NO_FIFO); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff); - write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_prepare_output(struct hfmodem_state *dev) -{ - unsigned long flags; - - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname); - return; - } - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC_SAMPLE_RATE_OUT); /* set sampling rate */ - write_dsp(dev, HFMODEM_SRATE >> 8); - write_dsp(dev, HFMODEM_SRATE & 0xff); - write_dsp(dev, SBC_SPEAKER_ON); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_trigger_output(struct hfmodem_state *dev) -{ - unsigned long flags; - - save_flags(flags); - cli(); - write_dsp(dev, SBC4_OUT16_AI_NO_FIFO); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff); - write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_stop(struct hfmodem_state *dev) -{ - reset_dsp(dev); -} - -/* --------------------------------------------------------------------- */ - -static unsigned int sbc4_intack(struct hfmodem_state *dev) -{ - unsigned int dmaptr; - unsigned long flags; - unsigned char intsrc; - - save_flags(flags); - cli(); - outb(0x82, DSP_MIXER_ADDR(dev->io.base_addr)); - intsrc = inb(DSP_MIXER_DATA(dev->io.base_addr)); - if (intsrc & 0x01) - sbc_int_ack_8bit(dev); - if (intsrc & 0x02) - sbc_int_ack_16bit(dev); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - dmaptr = get_dma_residue(dev->io.dma); - enable_dma(dev->io.dma); - restore_flags(flags); - if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) - dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; - return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; -} - -/* --------------------------------------------------------------------- */ - -static void sbc4_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) -{ - unsigned long flags; - static const unsigned char srcbits[3] = { 0x18, 0x01, 0x06 }; - - save_flags(flags); - cli(); - if (src >= 0 && src <= 2) { - set_mixer(dev, 0x3d, srcbits[src]); - set_mixer(dev, 0x3e, srcbits[src]); - } - if (ogain >= 0 && ogain <= 255) { - set_mixer(dev, 0x30, ogain); - set_mixer(dev, 0x31, ogain); - } - if (igain >= 0 && igain <= 255) { - set_mixer(dev, 0x36, igain); - set_mixer(dev, 0x37, igain); - set_mixer(dev, 0x38, igain); - set_mixer(dev, 0x39, igain); - set_mixer(dev, 0x3a, igain); - } - set_mixer(dev, 0x32, 0xff); - set_mixer(dev, 0x33, 0xff); - set_mixer(dev, 0x34, 0); - set_mixer(dev, 0x35, 0); - set_mixer(dev, 0x3b, 0); /* pc spkr vol */ - set_mixer(dev, 0x3c, 0); /* output src */ - set_mixer(dev, 0x3f, 0); /* inp gain */ - set_mixer(dev, 0x40, 0); - set_mixer(dev, 0x41, 0); /* outp gain */ - set_mixer(dev, 0x42, 0); - set_mixer(dev, 0x43, 1); /* mic agc off */ - set_mixer(dev, 0x44, 8<<4); /* treble */ - set_mixer(dev, 0x45, 8<<4); - set_mixer(dev, 0x46, 8<<4); /* bass */ - set_mixer(dev, 0x47, 8<<4); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void ess_prepare_input(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned char tmp; - - if (!reset_ess(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname); - return; - } - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - sbc_int_ack_8bit(dev); - write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE); - /* - * Set filter divider register - * Rolloff at 90% of the half sampling rate - */ - write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20)))); - write_dsp(dev, SBC_SPEAKER_OFF); - write_ess(dev, 0xb8, 0x0e); /* Auto init DMA mode */ - read_ess(dev, 0xa8, &tmp); - write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */ - write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ - /* 16 bit mono */ - write_ess(dev, 0xb7, 0x71); - write_ess(dev, 0xb7, 0xf4); - - read_ess(dev, 0xb1, &tmp); - write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50); - read_ess(dev, 0xb2, &tmp); - write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50); - - write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff)); - write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff)); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void ess_trigger_input(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned char tmp; - - save_flags(flags); - cli(); - read_ess(dev, 0xb8, &tmp); - write_ess(dev, 0xb8, tmp | 0x0f); /* Go */ - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -void ess_prepare_output(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned char tmp; - - if (!reset_ess(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname); - return; - } - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - sbc_int_ack_8bit(dev); - write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE); - /* - * Set filter divider register - * Rolloff at 90% of the half sampling rate - */ - write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20)))); - write_ess(dev, 0xb8, 0x04); /* Auto init DMA mode */ - read_ess(dev, 0xa8, &tmp); - write_ess(dev, 0xa8, (tmp & ~0x03) | 2); /* Mono */ - write_ess(dev, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ - /* 16 bit mono */ - write_ess(dev, 0xb6, 0x00); - write_ess(dev, 0xb7, 0x71); - write_ess(dev, 0xb7, 0xf4); - - read_ess(dev, 0xb1, &tmp); - write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50); - read_ess(dev, 0xb2, &tmp); - write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50); - - write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff)); - write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff)); - - write_dsp(dev, SBC_SPEAKER_ON); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -void ess_trigger_output(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned char tmp; - - save_flags(flags); - cli(); - read_ess(dev, 0xb8, &tmp); - write_ess(dev, 0xb8, tmp | 0x05); /* Go */ - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -unsigned int ess_intack(struct hfmodem_state *dev) -{ - unsigned int dmaptr; - unsigned long flags; - unsigned char st; -#if 0 - static unsigned int cnt = 0; -#endif - - save_flags(flags); - cli(); - st = inb(DSP_WRITE_STATUS(dev->io.base_addr)); - sbc_int_ack_8bit(dev); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - dmaptr = get_dma_residue(dev->io.dma); - enable_dma(dev->io.dma); - restore_flags(flags); -#if 0 - cnt = (cnt + 1) & 0x3f; - if (!cnt) - printk(KERN_DEBUG "%s: ess: FIFO: full:%c empty:%c half empty:%c IRQ: cpu:%c half empty:%c DMA:%c\n", - hfmodem_drvname, '1'-!(st&0x20), '1'-!(st&0x10), '1'-!(st&0x8), - '1'-!(st&0x4), '1'-!(st&0x2), '1'-!(st&0x1)); -#endif - if (st & 0x20) /* FIFO full, 256 bytes */ - dmaptr += 256; - else if (!(st & 0x10)) /* FIFO not empty, assume half full 128 bytes */ - dmaptr += 128; - if (dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) - dmaptr -= HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; - if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) - dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; - return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; -} - -/* --------------------------------------------------------------------- */ - -static void ess_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) -{ - unsigned long flags; - - save_flags(flags); - cli(); - if (src >= 0 && src <= 2) - set_mixer(dev, 0x0c, ((src+3) & 3) << 1); - if (ogain >= 0 && ogain <= 255) - set_mixer(dev, 0x22, (ogain & 0xf0) | ((ogain >> 4) & 0xf)); - if (igain >= 0 && igain <= 255) { - set_mixer(dev, 0x36, igain); - set_mixer(dev, 0x37, igain); - set_mixer(dev, 0x38, igain); - set_mixer(dev, 0x39, igain); - set_mixer(dev, 0x3a, igain); - } - set_mixer(dev, 0x4, 0xff); - set_mixer(dev, 0xe, 0x0); - set_mixer(dev, 0x26, 0); - set_mixer(dev, 0x28, 0); - set_mixer(dev, 0x2e, 0); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static const struct hfmodem_scops sbc4_scops = { - SBC_EXTENT, sbc4_init, sbc4_prepare_input, sbc4_trigger_input, - sbc4_prepare_output, sbc4_trigger_output, sbc4_stop, sbc4_intack, sbc4_mixer -}; - -static const struct hfmodem_scops ess_scops = { - SBC_EXTENT, sbc4_init, ess_prepare_input, ess_trigger_input, - ess_prepare_output, ess_trigger_output, sbc4_stop, ess_intack, ess_mixer -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/char/hfmodem/wss.c b/drivers/char/hfmodem/wss.c deleted file mode 100644 index c54aeadee..000000000 --- a/drivers/char/hfmodem/wss.c +++ /dev/null @@ -1,437 +0,0 @@ -/*****************************************************************************/ - -/* - * wss.c -- Linux soundcard HF FSK driver, - * WindowsSoundSystem specific functions. - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Swiss Federal Institute of Technology (ETH), Electronics Lab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - */ - -/*****************************************************************************/ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/ioport.h> - -#include <asm/io.h> -#include <asm/dma.h> - -#include <linux/hfmodem.h> - -/* --------------------------------------------------------------------- */ - -#define WSS_CONFIG(iobase) (iobase+0) -#define WSS_STATUS(iobase) (iobase+3) -#define WSS_CODEC_IA(iobase) (iobase+4) -#define WSS_CODEC_ID(iobase) (iobase+5) -#define WSS_CODEC_STATUS(iobase) (iobase+6) -#define WSS_CODEC_DATA(iobase) (iobase+7) - -#define WSS_EXTENT 8 - -/* --------------------------------------------------------------------- */ - -extern const struct hfmodem_scops wss_scops; - -/* --------------------------------------------------------------------- */ - -static void write_codec(struct hfmodem_state *dev, unsigned char idx, - unsigned char data) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80) - timeout--; - outb(idx, WSS_CODEC_IA(dev->io.base_addr)); - outb(data, WSS_CODEC_ID(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static unsigned char read_codec(struct hfmodem_state *dev, unsigned char idx) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80) - timeout--; - outb(idx & 0x1f, WSS_CODEC_IA(dev->io.base_addr)); - return inb(WSS_CODEC_ID(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void wss_ack_int(struct hfmodem_state *dev) -{ - outb(0, WSS_CODEC_STATUS(dev->io.base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static int wss_srate_tab[16] = { - 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6620 -}; - -static int wss_srate_index(int srate) -{ - int i; - - for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++) - if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0) - return i; - return -1; -} - -/* --------------------------------------------------------------------- */ - -static int wss_set_codec_fmt(struct hfmodem_state *dev, unsigned char fmt) -{ - unsigned long time; - unsigned long flags; - - save_flags(flags); - cli(); - /* Clock and data format register */ - write_codec(dev, 0x48, fmt); - /* MCE and interface config reg */ - write_codec(dev, 0x49, 0xc); - outb(0xb, WSS_CODEC_IA(dev->io.base_addr)); /* leave MCE */ - /* - * wait for ACI start - */ - time = 1000; - while (!(read_codec(dev, 0x0b) & 0x20)) - if (!(--time)) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", - hfmodem_drvname); - restore_flags(flags); - return -1; - } - /* - * wait for ACI end - */ - sti(); - time = jiffies + HZ/4; - while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0)); - restore_flags(flags); - if ((signed)(jiffies - time) >= 0) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", - hfmodem_drvname); - return -1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_init_codec(struct hfmodem_state *dev) -{ - unsigned char tmp, revwss, revid; - static const signed char irqtab[16] = { - -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, -1, -1 - }; - static const signed char dmatab[4] = { 1, 2, -1, 3 }; - int fmt; - - if ((fmt = wss_srate_index(HFMODEM_SRATE)) < 0) { - printk(KERN_ERR "%s: WSS: sampling rate not supported\n", hfmodem_drvname); - return -1; - } - fmt &= 0x0f; -#ifdef __BIG_ENDIAN - fmt |= 0xc0; -#else /* __BIG_ENDIAN */ - fmt |= 0x40; -#endif /* __BIG_ENDIAN */ - tmp = inb(WSS_STATUS(dev->io.base_addr)); - if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && - (tmp & 0x3f) != 0x0f) { - printk(KERN_WARNING "%s: WSS card id register not found, " - "address 0x%x, ID register 0x%02x\n", hfmodem_drvname, - dev->io.base_addr, (int)tmp); - /* return -1; */ - revwss = 0; - } else { - if ((tmp & 0x80) && ((dev->io.dma == 0) || ((dev->io.irq >= 8) && (dev->io.irq != 9)))) { - printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 " - "(except IRQ9) cannot be used on an 8bit " - "card\n", hfmodem_drvname); - return -1; - } - if (dev->io.irq > 15 || irqtab[dev->io.irq] == -1) { - printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", - hfmodem_drvname, (int)dev->io.irq); - return -1; - } - if (dev->io.dma > 3 || dmatab[dev->io.dma] == -1) { - printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", - hfmodem_drvname, (int)dev->io.dma); - return -1; - } - tmp = irqtab[dev->io.irq] | dmatab[dev->io.dma]; - /* irq probe */ - outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->io.base_addr)); - if (!(inb(WSS_STATUS(dev->io.base_addr)) & 0x40)) { - outb(0, WSS_CONFIG(dev->io.base_addr)); - printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", - hfmodem_drvname, dev->io.irq); - } - outb(tmp, WSS_CONFIG(dev->io.base_addr)); - revwss = inb(WSS_STATUS(dev->io.base_addr)) & 0x3f; - } - /* - * initialize the codec - */ - write_codec(dev, 9, 0); - write_codec(dev, 12, 0); - write_codec(dev, 0, 0x45); - if (read_codec(dev, 0) != 0x45) - goto codec_err; - write_codec(dev, 0, 0xaa); - if (read_codec(dev, 0) != 0xaa) - goto codec_err; - if (wss_set_codec_fmt(dev, fmt)) - goto codec_err; - write_codec(dev, 0, 0x40); /* left input control */ - write_codec(dev, 1, 0x40); /* right input control */ - write_codec(dev, 2, 0x80); /* left aux#1 input control */ - write_codec(dev, 3, 0x80); /* right aux#1 input control */ - write_codec(dev, 4, 0x80); /* left aux#2 input control */ - write_codec(dev, 5, 0x80); /* right aux#2 input control */ - write_codec(dev, 6, 0x80); /* left dac control */ - write_codec(dev, 7, 0x80); /* right dac control */ - write_codec(dev, 0xa, 0x2); /* pin control register */ - write_codec(dev, 0xd, 0x0); /* digital mix control */ - revid = read_codec(dev, 0xc) & 0xf; - /* - * print revisions - */ - printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", - hfmodem_drvname, (int)revwss, (int)revid); - return 0; - codec_err: - outb(0, WSS_CONFIG(dev->io.base_addr)); - printk(KERN_ERR "%s: no WSS soundcard found at address 0x%x\n", - hfmodem_drvname, dev->io.base_addr); - return -1; -} - -/* --------------------------------------------------------------------- */ - -int hfmodem_wssprobe(struct hfmodem_state *dev) -{ - if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-WSS_EXTENT || - dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 3 || dev->io.dma == 2) - return -ENXIO; - if (check_region(dev->io.base_addr, WSS_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (wss_init_codec(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", - hfmodem_drvname, dev->io.base_addr); - return -ENODEV; - } - dev->scops = &wss_scops; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void wss_init(struct hfmodem_state *dev) -{ - wss_init_codec(dev); -} - -/* --------------------------------------------------------------------- */ - -static void wss_stop(struct hfmodem_state *dev) -{ - unsigned long flags; - unsigned char oldcodecmode; - long abrt; - - save_flags(flags); - cli(); - /* - * perform the final DMA sequence to disable the codec request - */ - oldcodecmode = read_codec(dev, 9); - write_codec(dev, 9, 0xc); /* disable codec */ - wss_ack_int(dev); - if (read_codec(dev, 11) & 0x10) { - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, (oldcodecmode & 1) ? - (DMA_MODE_WRITE | DMA_MODE_AUTOINIT) : (DMA_MODE_READ | DMA_MODE_AUTOINIT)); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - abrt = 0; - while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); - } - disable_dma(dev->io.dma); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_prepare_input(struct hfmodem_state *dev) -{ - unsigned long flags; - - wss_stop(dev); - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff); - write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_trigger_input(struct hfmodem_state *dev) -{ - unsigned long flags; - - save_flags(flags); - cli(); - write_codec(dev, 9, 0x0e); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_prepare_output(struct hfmodem_state *dev) -{ - unsigned long flags; - - wss_stop(dev); - save_flags(flags); - cli(); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf)); - set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE); - enable_dma(dev->io.dma); - write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff); - write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_trigger_output(struct hfmodem_state *dev) -{ - unsigned long flags; - - save_flags(flags); - cli(); - write_codec(dev, 9, 0x0d); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static unsigned int wss_intack(struct hfmodem_state *dev) -{ - unsigned int dmaptr, nums; - unsigned long flags; - - save_flags(flags); - cli(); - wss_ack_int(dev); - disable_dma(dev->io.dma); - clear_dma_ff(dev->io.dma); - dmaptr = get_dma_residue(dev->io.dma); - if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE) - dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE; - nums = (((dmaptr - 1) % HFMODEM_FRAGSIZE) - 1) / 2; - write_codec(dev, 15, nums & 0xff); - write_codec(dev, 14, nums >> 8); - enable_dma(dev->io.dma); - restore_flags(flags); - return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2; -} - -/* --------------------------------------------------------------------- */ - -static void wss_mixer(struct hfmodem_state *dev, int src, int igain, int ogain) -{ - unsigned long flags; - static const unsigned char srctoreg[3] = { 1, 2, 0 }; - static const unsigned char regtosrc[4] = { 2, 0, 1, 0 }; - unsigned char tmp; - - save_flags(flags); - cli(); - tmp = read_codec(dev, 0x00); - if (src < 0 || src > 2) - src = regtosrc[(tmp >> 6) & 3]; - if (igain < 0 || igain > 255) { - if (src == 1) - igain = ((tmp & 0xf) + ((tmp & 0x20) ? 13 : 0)) << 3; - else - igain = (tmp & 0xf) << 4; - } - if (src == 1) { - if (igain > (28<<3)) - tmp = 0x2f; - else if (igain >= (13<<3)) - tmp = 0x20 + (((igain >> 3) - 13) & 0xf); - else - tmp = (igain >> 3) & 0xf; - } else - tmp = (igain >> 4) & 0xf; - tmp |= srctoreg[src] << 6; - write_codec(dev, 0, tmp); - write_codec(dev, 1, tmp); - if (ogain > 0 && ogain <= 255) { - tmp = 63 - (ogain >> 2); - write_codec(dev, 6, tmp); - write_codec(dev, 7, tmp); - } else if (ogain == 0) { - write_codec(dev, 6, 0x80); - write_codec(dev, 7, 0x80); - } - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static const struct hfmodem_scops wss_scops = { - WSS_EXTENT, wss_init, wss_prepare_input, wss_trigger_input, - wss_prepare_output, wss_trigger_output, wss_stop, wss_intack, wss_mixer -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/char/i2c-parport.c b/drivers/char/i2c-parport.c index 29b5e16c9..a8f83ce8e 100644 --- a/drivers/char/i2c-parport.c +++ b/drivers/char/i2c-parport.c @@ -22,7 +22,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/init.h> -#include <asm/spinlock.h> +#include <linux/spinlock.h> #define I2C_DELAY 10 diff --git a/drivers/char/ip2.c b/drivers/char/ip2.c new file mode 100644 index 000000000..e9880146a --- /dev/null +++ b/drivers/char/ip2.c @@ -0,0 +1,72 @@ +// ip2.c +// This is a dummy module to make the firmware available when needed +// and allows it to be unloaded when not. Rumor is the __initdata +// macro doesn't always works on all platforms so we use this kludge. +// If not compiled as a module it just makes fip_firm avaliable then +// __initdata should work as advertized +// + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/wait.h> + +#include "./ip2/ip2types.h" +#include "./ip2/fip_firm.h" // the meat + +int +ip2_loadmain(int *, int *, unsigned char *, int ); // ref into ip2main.c + +#ifdef MODULE +static int io[IP2_MAX_BOARDS]= { 0,}; +static int irq[IP2_MAX_BOARDS] = { 0,}; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_PARM(irq,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); +MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); + + +//====================================================================== +int +init_module(void) +{ + int rc; + + MOD_INC_USE_COUNT; // hold till done + + rc = ip2_loadmain(io,irq,(unsigned char *)fip_firm,sizeof(fip_firm)); + // The call to lock and load main, create dep + + MOD_DEC_USE_COUNT; //done - kerneld now can unload us + return rc; +} + +//====================================================================== +int +ip2_init(void) +{ + // call to this is int tty_io.c so we need this + return 0; +} + +//====================================================================== +void +cleanup_module(void) +{ +} + +#else // !MODULE + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +int +ip2_init(void) { + return ip2_loadmain(NULL,NULL,(unsigned char *)fip_firm,sizeof(fip_firm)); +} + +#endif /* !MODULE */ diff --git a/drivers/char/ip2/.cvsignore b/drivers/char/ip2/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/char/ip2/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile new file mode 100644 index 000000000..26fb618ca --- /dev/null +++ b/drivers/char/ip2/Makefile @@ -0,0 +1,12 @@ + +all: ip2mkdev ip2trace ip2stat + +ip2mkdev: ip2mkdev.c + cc -o ip2mkdev ip2mkdev.c + +ip2trace: ip2trace.c + cc -o ip2trace ip2trace.c + +ip2stat: ip2stat.c + cc -o ip2stat ip2stat.c + diff --git a/drivers/char/ip2/fip_firm.h b/drivers/char/ip2/fip_firm.h new file mode 100644 index 000000000..d85c3fc31 --- /dev/null +++ b/drivers/char/ip2/fip_firm.h @@ -0,0 +1,2149 @@ +/* fip_firm.h - Intelliport II loadware */ +/* -31232 bytes read from ff.lod */ + +unsigned char fip_firm[] __initdata = { +0x3C,0x42,0x8A,0xE1,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x57,0x65,0x64,0x20,0x4A,0x75,0x6E,0x20,0x32,0x33,0x20,0x31,0x35,0x3A,0x33,0x30, +0x3A,0x31,0x33,0x20,0x31,0x39,0x39,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xE9,0x68,0x0F,0x42,0x65,0x47,0x69,0x4E,0x6E,0x49,0x6E,0x47,0x20,0x6F,0x46,0x20, +0x63,0x4F,0x64,0x45,0xCC,0x13,0x5A,0x15,0xE8,0x16,0x76,0x18,0x04,0x1A,0x92,0x1B, +0x20,0x1D,0xAE,0x1E,0x3C,0x20,0xCA,0x21,0x58,0x23,0xE6,0x24,0x74,0x26,0x02,0x28, +0x90,0x29,0x1E,0x2B,0xAC,0x2C,0x3A,0x2E,0xC8,0x2F,0x56,0x31,0xE4,0x32,0x72,0x34, +0x00,0x36,0x8E,0x37,0x1C,0x39,0xAA,0x3A,0x38,0x3C,0xC6,0x3D,0x54,0x3F,0xE2,0x40, +0x70,0x42,0xFE,0x43,0x8C,0x45,0x1A,0x47,0xA8,0x48,0x36,0x4A,0xC4,0x4B,0x52,0x4D, +0xE0,0x4E,0x6E,0x50,0xFC,0x51,0x8A,0x53,0x18,0x55,0xA6,0x56,0x34,0x58,0xC2,0x59, +0x50,0x5B,0xDE,0x5C,0x6C,0x5E,0xFA,0x5F,0x88,0x61,0x16,0x63,0xA4,0x64,0x32,0x66, +0xC0,0x67,0x4E,0x69,0xDC,0x6A,0x6A,0x6C,0xF8,0x6D,0x86,0x6F,0x14,0x71,0xA2,0x72, +0x30,0x74,0xBE,0x75,0x4C,0x77,0x6C,0x77,0x8C,0x77,0xAC,0x77,0x33,0xDB,0x8A,0xDC, +0x53,0x33,0xDB,0x25,0x07,0x00,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x83,0xE3,0x0C,0xEB, +0x20,0x90,0x3C,0x01,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x80,0xE3,0xC0,0xEB,0x12,0x90, +0x8A,0x1E,0x0D,0x01,0x3C,0x02,0x75,0x06,0x80,0xE3,0x0C,0xEB,0x04,0x90,0x80,0xE3, +0xC0,0x53,0x50,0x8B,0x1E,0xBA,0x13,0x8E,0xDB,0xE8,0x4C,0x65,0x55,0x8B,0xEC,0x53, +0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06,0xD1,0xE3, +0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x2A,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3,0x55,0x8B, +0xEC,0x53,0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06, +0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x34,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3, +0xFB,0x55,0x8B,0xEC,0x53,0x51,0x52,0x56,0x57,0x1E,0x06,0x1E,0x07,0x33,0xC0,0x8E, +0xD8,0x8B,0x5E,0x04,0x26,0x8A,0x47,0x59,0x25,0x03,0x00,0x8B,0xF0,0xD1,0xE6,0x2E, +0x8B,0xB4,0xC4,0x00,0xC1,0xE0,0x04,0x26,0x02,0x47,0x1A,0xD1,0xE0,0x8B,0xE8,0x2E, +0x8B,0xAE,0x44,0x00,0x89,0x2C,0x26,0x8A,0x47,0x1C,0x88,0x44,0x0F,0x26,0x8A,0x47, +0x1D,0x88,0x44,0x10,0x26,0x8A,0x47,0x1E,0x88,0x44,0x11,0x26,0x8A,0x47,0x1F,0x88, +0x44,0x12,0x26,0x8A,0x47,0x20,0x88,0x44,0x13,0x26,0x8A,0x47,0x23,0x88,0x44,0x14, +0x26,0x8A,0x47,0x24,0x88,0x44,0x15,0x26,0x8A,0x47,0x5A,0x88,0x44,0x0E,0x33,0xC0, +0x89,0x44,0x06,0x89,0x44,0x08,0x88,0x44,0x0B,0x88,0x44,0x0A,0xB0,0x21,0xB4,0x64, +0x89,0x44,0x04,0x89,0x44,0x02,0xB0,0x55,0x88,0x44,0x0D,0x88,0x44,0x0C,0xE8,0x6A, +0x00,0x72,0x5B,0xE8,0xC9,0x00,0xE8,0xBD,0x10,0x89,0x44,0x08,0x80,0x7C,0x0F,0x01, +0x74,0x29,0xE8,0x2B,0x02,0xE8,0x7F,0x02,0x80,0x7C,0x0F,0x03,0x74,0x1D,0xE8,0xA5, +0x10,0x8B,0xF8,0x2B,0x44,0x08,0x3D,0xA0,0x0F,0x72,0x10,0x89,0x7C,0x08,0x33,0xC0, +0x87,0x44,0x06,0x85,0xC0,0x75,0x04,0xC6,0x44,0x0A,0xFF,0x8A,0x44,0x0A,0x84,0xC0, +0x75,0x0B,0xB8,0x08,0x00,0xE8,0x4C,0x4A,0xE8,0xA9,0x01,0x73,0xBF,0xE8,0x4F,0x01, +0x81,0x66,0x48,0x7F,0xFF,0x83,0x66,0x7A,0xBF,0xB0,0x02,0xE8,0x00,0x0E,0x8A,0x44, +0x0A,0x98,0x07,0x1F,0x5F,0x5E,0x5A,0x59,0x5B,0x5D,0xC3,0x81,0x4E,0x48,0x80,0x00, +0xB0,0x40,0xE8,0x1F,0x4A,0xE8,0x6B,0x40,0x73,0x2A,0xE8,0x49,0x10,0x8B,0xD8,0xB0, +0x05,0xE8,0x10,0x4A,0xF6,0x46,0x27,0x02,0x75,0x1A,0xE8,0x39,0x10,0x2B,0xC3,0x3D, +0x58,0x1B,0x72,0xEB,0x81,0x66,0x48,0x7F,0xFF,0xB0,0x02,0xE8,0xC0,0x0D,0xC6,0x44, +0x0A,0x01,0xF9,0xC3,0x83,0x4E,0x7A,0x40,0xF8,0xC3,0xFB,0xB0,0x01,0xE8,0xE4,0x49, +0xFA,0xE8,0x95,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xB0,0x4E,0xE6,0x0A,0xFB,0xB0, +0x01,0xE8,0xD0,0x49,0xFA,0xE8,0x81,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xC3,0xFA, +0xE8,0x76,0x1E,0xE4,0xEC,0x88,0x44,0x16,0xE4,0xE4,0x88,0x44,0x17,0xE4,0xF8,0x88, +0x44,0x18,0xE4,0xF0,0x88,0x44,0x19,0xE4,0x10,0x88,0x44,0x1A,0xE4,0x12,0x88,0x44, +0x1B,0xE4,0x14,0x88,0x44,0x1C,0xE4,0x34,0x88,0x44,0x1D,0xE4,0x36,0x88,0x44,0x1E, +0xE4,0xD8,0x24,0x01,0x8A,0xE0,0xE4,0xDA,0x24,0x02,0x0A,0xC4,0x88,0x44,0x1F,0x8A, +0x44,0x10,0xE8,0xC9,0x1F,0x8A,0x44,0x11,0xE8,0x31,0x21,0x8A,0x44,0x12,0xE8,0x85, +0x21,0x8A,0x44,0x13,0xE8,0x3F,0x21,0xC6,0x86,0xA1,0x00,0x00,0xE4,0x14,0x24,0x10, +0xE6,0x14,0xE4,0x12,0x24,0x3D,0xE6,0x12,0x8A,0x44,0x15,0x3C,0x01,0x72,0x1E,0x77, +0x16,0xB0,0x11,0xE6,0x34,0xB0,0x13,0xE6,0x36,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4, +0x12,0x0C,0x40,0xE6,0x12,0xEB,0x06,0xE4,0x12,0x0C,0x02,0xE6,0x12,0x8A,0x44,0x0F, +0x3C,0x01,0x74,0x06,0x3C,0x02,0x74,0x0A,0xEB,0x0E,0xE4,0x12,0x0C,0x08,0xE6,0x12, +0xEB,0x06,0xE4,0x12,0x0C,0x10,0xE6,0x12,0xE8,0x2F,0xFF,0x8A,0x44,0x14,0x3C,0x02, +0x75,0x08,0xB0,0x55,0x88,0x44,0x0C,0x88,0x44,0x0D,0xB0,0x21,0xB4,0x64,0x89,0x44, +0x04,0x89,0x44,0x02,0xE4,0x0C,0x0C,0x10,0xE6,0x0C,0xE8,0xCF,0x39,0xFB,0xC3,0xE8, +0x41,0x3F,0x73,0x08,0xFB,0xB0,0x0A,0xE8,0xEA,0x48,0xEB,0xF3,0xFA,0xE8,0x99,0x1D, +0x8A,0x64,0x16,0x8A,0x44,0x17,0x89,0x86,0x94,0x00,0xE6,0xE4,0x8A,0xC4,0xE6,0xEC, +0x8A,0x64,0x18,0x8A,0x44,0x19,0x89,0x86,0x96,0x00,0xE6,0xF0,0x8A,0xC4,0xE6,0xF8, +0x8A,0x44,0x1A,0xE6,0x10,0x8A,0x44,0x1B,0xE6,0x12,0x8A,0x44,0x1C,0xE6,0x14,0x8A, +0x44,0x1D,0xE6,0x34,0x8A,0x44,0x1E,0xE6,0x36,0x8A,0x44,0x1F,0xE6,0xD8,0xE6,0xDA, +0xE9,0xB7,0xFE,0x90,0xFA,0x8A,0x44,0x0E,0xE6,0xFE,0xE4,0x02,0xA8,0x01,0x75,0x05, +0x33,0xC0,0xFB,0xF8,0xC3,0x33,0xC0,0xE4,0x00,0xFB,0xF9,0xC3,0x8A,0x64,0x14,0x80, +0xFC,0x02,0x74,0x2B,0xFE,0xC0,0xFE,0xC7,0x80,0xFF,0x4E,0x72,0x1C,0x74,0x09,0x80, +0xFF,0x50,0x73,0x08,0xB0,0x0A,0xEB,0x17,0xB0,0x0D,0xEB,0x13,0x02,0xDC,0x32,0xFF, +0x80,0xFB,0x7F,0x7C,0x02,0xB3,0x21,0x8A,0xC3,0x3C,0x7F,0x7C,0x02,0xB0,0x21,0xC3, +0xFA,0x80,0x7C,0x0B,0x04,0x76,0x02,0xFB,0xC3,0x8B,0x46,0x24,0x3D,0x08,0x00,0x72, +0xF6,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0x44,0x0C,0x8B,0x5C,0x02,0xAA,0xE8,0xAB, +0xFF,0xAA,0xE8,0xA7,0xFF,0xAA,0xE8,0xA3,0xFF,0xAA,0xE8,0x9F,0xFF,0x88,0x44,0x0C, +0x89,0x5C,0x02,0x80,0x44,0x0B,0x04,0x89,0x7E,0x22,0x83,0x6E,0x24,0x04,0x83,0x46, +0x1A,0x04,0x80,0x7E,0x26,0x02,0x74,0x06,0x80,0x66,0x26,0xFD,0xFB,0xC3,0x60,0xB0, +0xFD,0xE8,0xE4,0x3E,0x61,0xFB,0xC3,0xFA,0x80,0x7C,0x0F,0x03,0x75,0x09,0xC6,0x44, +0x0B,0x00,0xE8,0xC7,0x38,0xFB,0xC3,0xC4,0x7E,0x14,0x8B,0x4E,0x3A,0x85,0xC9,0x75, +0x35,0x26,0x8B,0x0D,0x47,0x47,0xE3,0xEA,0x3B,0x7E,0x04,0x76,0x22,0xB8,0x02,0x00, +0x39,0x46,0x2E,0x77,0x07,0xC7,0x46,0x2E,0x00,0x00,0xEB,0x13,0x8B,0x5E,0x2C,0x89, +0x5E,0x04,0x26,0xC7,0x07,0x00,0x00,0x43,0x43,0x89,0x5E,0x2C,0x29,0x46,0x2E,0x85, +0xC9,0x78,0xCE,0x89,0x4E,0x3A,0x8A,0x44,0x0D,0x8B,0x5C,0x04,0x26,0x8A,0x25,0x47, +0x3A,0xC4,0x75,0x16,0xFE,0x4C,0x0B,0xFF,0x44,0x06,0xE8,0x0F,0xFF,0xE2,0xED,0x88, +0x44,0x0D,0x89,0x5C,0x04,0x89,0x4E,0x3A,0xEB,0xA7,0xC6,0x44,0x0A,0xFE,0xE8,0x5B, +0x38,0xFB,0xC3,0x90,0xE8,0xAF,0x0D,0x8A,0xE8,0x8A,0x0E,0xCB,0x13,0xB3,0x07,0x8A, +0xC1,0xEE,0xEB,0x00,0xEC,0x3A,0xC1,0x75,0x09,0x02,0xCD,0xFE,0xCB,0x75,0xF0,0xEB, +0x0C,0x90,0x88,0x0E,0xCB,0x13,0x8A,0xE8,0xBB,0xFF,0xFF,0xF9,0xC3,0x88,0x0E,0xCB, +0x13,0xF8,0xC3,0x90,0xBB,0x3F,0x3F,0x8A,0x8E,0x9E,0x00,0xBA,0xFE,0x00,0xEC,0x8A, +0xE8,0x32,0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0xE8,0xE5,0xFF,0x73, +0x01,0xC3,0xBA,0xD0,0x00,0xBB,0x03,0x03,0x8A,0x8E,0x9F,0x00,0xEC,0x8A,0xE8,0x32, +0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0, +0x80,0x3E,0xC8,0x13,0x00,0x75,0x07,0xB0,0x0A,0xE8,0x08,0x47,0xEB,0xF2,0xFB,0x33, +0xDB,0x8A,0x1E,0xC9,0x13,0x43,0x43,0x83,0xFB,0x7E,0x76,0x07,0x33,0xDB,0xB0,0x02, +0xE8,0xF1,0x46,0x2E,0x8B,0xAF,0x44,0x00,0x83,0x7E,0x08,0x00,0x74,0xE7,0x88,0x1E, +0xC9,0x13,0xB0,0x02,0xE8,0xDD,0x46,0xFA,0xF7,0x46,0x38,0x40,0x00,0x74,0x14,0xE8, +0x92,0x1B,0xE8,0x7F,0xFF,0x72,0x1C,0x33,0xD2,0x8A,0x96,0x9F,0x00,0x83,0xC2,0x0E, +0xEB,0x0C,0x90,0xE8,0x73,0x1B,0xE8,0x83,0xFF,0x72,0x08,0xBA,0x48,0x00,0xE8,0x33, +0xFF,0x73,0xAB,0x23,0xCB,0x89,0x8E,0x9A,0x00,0x89,0x96,0x9C,0x00,0xFE,0x86,0xB5, +0x00,0xC6,0x06,0xC8,0x13,0x00,0xB0,0x0A,0xE8,0x63,0x0A,0xFB,0xEB,0x89,0x10,0x18, +0x08,0x28,0x33,0xC0,0xA0,0x05,0x01,0x8A,0xC8,0x24,0x40,0x75,0x24,0xC7,0x06,0x7C, +0x12,0x70,0x45,0xC7,0x06,0x42,0x12,0x01,0x00,0xC6,0x06,0x54,0x12,0x02,0xB0,0x08, +0xF6,0xC1,0x01,0x74,0x02,0xB0,0x04,0xA3,0x46,0x12,0xA2,0x4C,0x12,0xA2,0x94,0x12, +0xC3,0xC7,0x06,0x7C,0x12,0x98,0x45,0xA0,0x0F,0x01,0x84,0xC0,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xAD,0x0C,0x90,0xC7,0x06,0x44,0x12, +0x01,0x00,0xA3,0x42,0x12,0x8B,0xD8,0xC1,0xE3,0x04,0x88,0x1E,0x94,0x12,0xBE,0xE2, +0x05,0x2B,0xF0,0x8B,0xC8,0x33,0xDB,0x8B,0xFB,0x2E,0xAC,0x88,0x85,0x48,0x12,0x8A, +0xD8,0x0C,0x05,0xE6,0xFE,0x8A,0xE0,0xEB,0x00,0xE4,0xFE,0x32,0xC4,0xA8,0x3F,0x74, +0x03,0xE9,0x9A,0x00,0xE4,0x00,0x88,0x85,0x50,0x12,0x8A,0xE0,0x24,0x30,0xBA,0x10, +0xFF,0x3C,0x30,0x74,0x12,0x80,0xFC,0x04,0x74,0x0A,0xBA,0x04,0x03,0xF6,0x06,0x08, +0x01,0xFE,0x74,0x03,0xBA,0x08,0x0F,0x88,0x95,0x4C,0x12,0x02,0xFA,0x32,0xC0,0xF6, +0xC4,0x08,0x74,0x02,0xB0,0x01,0x88,0x85,0x58,0x12,0x8A,0xC4,0x3C,0x35,0x74,0x57, +0x3C,0x34,0x74,0x53,0x3C,0x04,0x74,0x4F,0x3C,0x14,0x74,0x4B,0x3C,0x15,0x74,0x47, +0xA8,0x40,0x74,0x25,0xC6,0x85,0x54,0x12,0x04,0xD1,0xE7,0xB4,0x03,0x8A,0xC3,0x89, +0x85,0x5C,0x12,0x8A,0xC3,0x8A,0xE3,0x80,0xCC,0x01,0x89,0x85,0x64,0x12,0xD1,0xEF, +0x47,0xE2,0x03,0xEB,0x1A,0x90,0xE9,0x70,0xFF,0xC6,0x85,0x54,0x12,0x02,0xD1,0xE7, +0x8A,0xE6,0x8A,0xC3,0x0C,0x04,0x89,0x85,0x5C,0x12,0xD1,0xEF,0x47,0xE2,0xE7,0x33, +0xC0,0x8A,0xC7,0xA3,0x46,0x12,0xC3,0xC6,0x85,0x54,0x12,0x06,0xEB,0xBB,0xC6,0x85, +0x54,0x12,0x00,0x33,0xC0,0x88,0x85,0x50,0x12,0x88,0x85,0x4C,0x12,0x88,0x85,0x58, +0x12,0xEB,0xA6,0xC7,0x46,0x26,0x02,0x12,0x8B,0x46,0x1E,0x89,0x46,0x00,0x89,0x46, +0x22,0x8B,0x46,0x20,0x89,0x46,0x24,0xC7,0x46,0x1A,0x00,0x00,0xC3,0xC7,0x46,0x3C, +0x80,0x00,0xC7,0x46,0x38,0x01,0x00,0x1E,0x56,0x8B,0x76,0x30,0x89,0x76,0x04,0x89, +0x76,0x14,0x8E,0x5E,0x06,0x33,0xC0,0x89,0x04,0x46,0x46,0x89,0x76,0x2C,0x89,0x46, +0x3A,0x8B,0x46,0x32,0x48,0x48,0x89,0x46,0x2E,0x5E,0x1F,0xC3,0x33,0xC0,0x89,0x46, +0x48,0x89,0x46,0x4A,0xC7,0x46,0x46,0xAE,0x01,0x89,0x46,0x4E,0x8B,0x46,0x44,0x89, +0x46,0x50,0x8B,0x46,0x42,0x89,0x46,0x40,0x89,0x46,0x08,0xC3,0x33,0xC0,0x89,0x46, +0x76,0x89,0x46,0x78,0xC7,0x46,0x7A,0x10,0x00,0x56,0x1E,0x8B,0x76,0x70,0x89,0x76, +0x10,0x89,0x76,0x0C,0x8E,0x5E,0x12,0xC7,0x04,0x00,0x00,0x8B,0x46,0x72,0x89,0x46, +0x74,0x1F,0x5E,0xC3,0x89,0x56,0x18,0x89,0x56,0x02,0x89,0x56,0x06,0x89,0x56,0x0A, +0x89,0x56,0x0E,0x89,0x56,0x12,0x89,0x56,0x16,0x8B,0xD8,0x4B,0x4B,0xC1,0xE3,0x02, +0xBF,0x02,0x00,0x89,0x7E,0x1E,0x03,0xFB,0x89,0x7E,0x30,0x03,0xFB,0x89,0x7E,0x42, +0x03,0xFB,0x89,0x7E,0x70,0x83,0xEB,0x08,0x89,0x5E,0x20,0x89,0x5E,0x32,0x89,0x5E, +0x44,0x89,0x5E,0x72,0x50,0xE8,0x2B,0xFF,0xE8,0x71,0xFF,0xE8,0x3F,0xFF,0xE8,0x8B, +0xFF,0x58,0xC3,0xB8,0x10,0x75,0xC1,0xE8,0x04,0x0E,0x5B,0x03,0xC3,0xA3,0xBA,0x13, +0x83,0x3E,0x42,0x12,0x00,0x74,0x07,0x80,0x3E,0x94,0x12,0x00,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xBD,0x0A,0x90,0xB8,0x30,0x7A,0xC1, +0xE8,0x04,0x40,0xA3,0xC0,0x13,0x2B,0x06,0x12,0x01,0xF7,0xD8,0x33,0xD2,0x8B,0xCA, +0x8A,0x0E,0x94,0x12,0xF7,0xF1,0x3D,0x80,0x00,0x77,0x0E,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x25,0x9C,0x0E,0xE8,0x90,0x0A,0x90,0x48,0x3D,0xFF,0x07,0x72,0x03,0xB8, +0xFF,0x07,0xA3,0xC2,0x13,0x33,0xC9,0x8A,0x0E,0x94,0x12,0x33,0xF6,0xB8,0x00,0x09, +0x2E,0x8B,0xAC,0x44,0x00,0x89,0x46,0x4C,0x40,0x46,0x46,0xE2,0xF3,0x8A,0x0E,0x94, +0x12,0x33,0xF6,0x8B,0x16,0xC0,0x13,0xA1,0xC2,0x13,0x2E,0x8B,0xAC,0x44,0x00,0xE8, +0x22,0xFF,0x03,0xD0,0x46,0x46,0xE2,0xF2,0xC3,0x33,0xC0,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x46,0x08,0x47,0x47,0xE2,0xF4,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD, +0x44,0x00,0x89,0x86,0x9E,0x00,0x81,0x4E,0x38,0x00,0x20,0x47,0x47,0xFE,0xC4,0x80, +0xFC,0x04,0x72,0x04,0x32,0xE4,0xFE,0xC0,0xE2,0xE3,0x59,0x83,0xE9,0x10,0x74,0x05, +0xF7,0xD9,0xE8,0xC4,0xFF,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x86,0x9E,0x00,0x83,0x4E,0x38,0x40,0x47,0x47,0x80,0xC4,0x10,0x79,0x04,0x32, +0xE4,0xFE,0xC0,0xE2,0xE6,0x59,0x83,0xE9,0x10,0x74,0x05,0xF7,0xD9,0xE8,0x99,0xFF, +0xC3,0xE8,0xD2,0xFF,0xC3,0x89,0x08,0x98,0x08,0xC6,0x08,0xF1,0x08,0x8B,0x0E,0x42, +0x12,0x33,0xF6,0x51,0x56,0x33,0xDB,0x8B,0xCB,0x8A,0x94,0x48,0x12,0x8A,0x8C,0x4C, +0x12,0x8A,0x9C,0x54,0x12,0x8B,0xFE,0xC1,0xE7,0x05,0x85,0xDB,0x75,0x02,0xB1,0x10, +0x2E,0xFF,0x97,0xF5,0x08,0x5E,0x59,0x46,0xE2,0xD9,0xC3,0x01,0xCC,0x03,0xD0,0x00, +0xE8,0x02,0xD0,0x00,0xE8,0x01,0xD0,0x00,0xE8,0x00,0xD0,0x00,0xE8,0x04,0xD0,0xA8, +0xDA,0x00,0xDC,0x00,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0xA8, +0xDA,0x20,0xDC,0x00,0xDE,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00,0xD8,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0x00, +0xDA,0x20,0xDC,0x03,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00, +0xD8,0x00,0xCC,0x00,0xD0,0x00,0x00,0x56,0x52,0x1E,0x0E,0x1F,0xBE,0x2B,0x09,0x33, +0xD2,0xFC,0xAD,0x85,0xC0,0x74,0x0D,0x8A,0xD4,0xEE,0xAD,0x85,0xC0,0x74,0x05,0x8A, +0xD4,0xEE,0xEB,0xEE,0x1F,0x5A,0x5E,0xC3,0xE4,0x80,0x84,0xC0,0x74,0x16,0x78,0x14, +0xB0,0x27,0xE6,0xFC,0xB0,0x11,0xE6,0x34,0xE4,0xFC,0x3C,0x27,0x75,0x06,0xE4,0x11, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xB0, +0x10,0xEE,0x88,0x86,0xAF,0x00,0xB0,0x11,0x83,0xC2,0x04,0xEE,0x83,0xC2,0x02,0xEE, +0xB0,0x13,0x83,0xC2,0x02,0xEE,0x83,0xC2,0x02,0xEE,0x2E,0xA1,0x2E,0x2D,0x89,0x86, +0x94,0x00,0x83,0xEA,0x0E,0xEE,0x83,0xC2,0x02,0x8A,0xC4,0xEE,0x83,0xC2,0x04,0xB0, +0x03,0xEE,0x88,0x86,0xA8,0x00,0x83,0xEA,0x04,0x32,0xC0,0xEE,0x83,0xC2,0x02,0xB0, +0x89,0xEE,0x88,0x86,0xA6,0x00,0x0C,0x06,0xEE,0xB0,0x40,0xB4,0x38,0x89,0x46,0x1C, +0xC7,0x46,0x36,0x38,0x00,0x83,0xC2,0x04,0x32,0xC0,0xEE,0x88,0x86,0xA7,0x00,0xC3, +0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xEC,0x3A,0x86,0xAF,0x00,0x75,0x24, +0x83,0xC2,0x04,0xEC,0x3C,0x11,0x75,0x1C,0x83,0xC2,0x06,0xEC,0x3C,0x13,0x75,0x14, +0x83,0xEA,0x08,0x8A,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x02,0xEC,0x24,0xC0,0x3C,0xC0, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x33,0xC9,0x8B,0xD1,0x8B,0xF1,0x8A,0x0E,0x94,0x12, +0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x0E,0x8A, +0x86,0x9E,0x00,0xE6,0xFE,0x32,0xC0,0xE6,0x80,0x42,0xE8,0xFA,0xFE,0x83,0xC6,0x08, +0xE2,0xE1,0x85,0xD2,0x74,0x03,0xE8,0x05,0x08,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E, +0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x73, +0x16,0xE8,0x12,0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94, +0x12,0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x16, +0xE8,0x46,0x16,0xE8,0xD2,0xFE,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xE3,0x07,0x90,0x83,0xC6,0x08,0xE2,0xD9,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x16, +0xE8,0x21,0x16,0xE8,0x2A,0xFF,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xB3,0x07,0x90,0x46,0x46,0xE2,0xDA,0xC3,0x0C,0x00,0x00,0x10,0x00, +0x13,0x12,0x00,0x00,0x14,0x00,0x28,0x3C,0x00,0x1B,0x3E,0x00,0x00,0x2A,0x00,0x00, +0x2C,0x00,0x00,0x42,0x00,0x14,0xD8,0x00,0x00,0xDA,0x00,0x00,0x34,0x00,0x11,0x36, +0x00,0x13,0x38,0x00,0x11,0x3A,0x00,0x13,0x00,0x00,0x56,0x50,0x52,0xBE,0x2B,0x0B, +0x2E,0xAD,0x85,0xC0,0x74,0x06,0x92,0x2E,0xAC,0xEE,0xEB,0xF4,0x5A,0x58,0x5E,0xC3, +0x53,0x2E,0xA1,0x5C,0x22,0xE6,0xE4,0xE6,0xF0,0x8A,0xC4,0xE6,0xEC,0xE6,0xF8,0xE8, +0xD8,0xFF,0xB0,0x4B,0xE6,0x10,0xB0,0x50,0xE6,0x12,0xB0,0x38,0xE6,0x14,0xE8,0xAE, +0x15,0xB0,0x46,0xE6,0x0A,0xE8,0xA7,0x15,0xB0,0x1A,0xE6,0x0A,0xE8,0xA0,0x15,0xB0, +0x22,0xE6,0x0A,0xE8,0x99,0x15,0xE8,0xFD,0x06,0x8B,0xD8,0xE4,0x16,0xA8,0x04,0x75, +0x18,0xE8,0xF2,0x06,0x2B,0xC3,0x3D,0x32,0x00,0x72,0xF0,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x23,0x9C,0x0E,0xE8,0x10,0x07,0x90,0xE8,0xDA,0x06,0x2B,0xC3,0x3D,0x24, +0x00,0x77,0x1B,0xB0,0x31,0xE6,0xFC,0x56,0x51,0x55,0xB9,0x10,0x00,0x2E,0x8B,0xAC, +0x44,0x00,0x81,0x4E,0x38,0x80,0x00,0x46,0x46,0xE2,0xF2,0x5D,0x59,0x5E,0xE8,0x69, +0xFF,0xE8,0x4B,0x15,0xB0,0x46,0xE6,0x0A,0xE8,0x44,0x15,0x5B,0xC3,0x33,0xF6,0x8B, +0x0E,0x42,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8, +0x17,0x15,0xE8,0x5B,0xFF,0x83,0xC6,0x20,0xE2,0xE9,0xC3,0x8B,0xC2,0x05,0x04,0x00, +0x89,0x46,0x28,0x2E,0xA1,0x2E,0x2D,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x89, +0x86,0x92,0x00,0xC6,0x86,0xA3,0x00,0x0A,0xC6,0x86,0xC3,0x00,0x03,0x52,0x83,0xC2, +0x04,0x8A,0x86,0xA6,0x00,0x0C,0x06,0xEE,0x5A,0x83,0xC2,0x02,0xB0,0x05,0xEE,0x88, +0x86,0xA5,0x00,0xC3,0xE8,0x03,0xFF,0xE8,0xE5,0x14,0xB0,0x42,0xE6,0x0A,0xF7,0x46, +0x38,0x80,0x00,0x74,0x06,0x2E,0xA1,0x98,0x22,0xEB,0x04,0x2E,0xA1,0x68,0x22,0xC7, +0x46,0x1C,0x0C,0x00,0x89,0x86,0x94,0x00,0x89,0x86,0x96,0x00,0x89,0x86,0x8E,0x00, +0x89,0x86,0x90,0x00,0x89,0x86,0x92,0x00,0xE6,0xF0,0xE6,0xE4,0x8A,0xC4,0xE6,0xF8, +0xE6,0xEC,0xC6,0x86,0xC3,0x00,0x03,0xE8,0xA5,0x14,0xB0,0x1A,0xE6,0x0A,0xB0,0x10, +0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E, +0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x76,0x14,0xE8,0x5A, +0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E,0x8B, +0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8,0x4C,0x14,0xE8,0x74,0xFF, +0x46,0x46,0xE2,0xEA,0xC3,0x90,0x83,0x3E,0x44,0x12,0x00,0x75,0x14,0xB0,0x01,0xBA, +0x06,0x01,0xEE,0x2A,0xC0,0xEE,0xB0,0x02,0xEE,0xB0,0x04,0xEE,0xB8,0x00,0x02,0xEB, +0x0F,0xBA,0x06,0x01,0xB0,0x40,0xEE,0xB8,0x01,0x00,0x8A,0x0E,0x0E,0x01,0xD3,0xE0, +0xA3,0x88,0x12,0xC3,0xA1,0x88,0x12,0xA3,0x84,0x12,0x2D,0x20,0x00,0xA3,0x8A,0x12, +0x2D,0x20,0x00,0xA3,0x82,0x12,0xC7,0x06,0x86,0x12,0x20,0x00,0xC7,0x06,0x80,0x12, +0x32,0x00,0xC3,0x83,0x3E,0x44,0x12,0x00,0x74,0x76,0x8B,0x0E,0x42,0x12,0x33,0xF6, +0x8A,0xA4,0x54,0x12,0x84,0xE4,0x74,0x5F,0x8A,0x84,0x48,0x12,0x0C,0x04,0xE6,0xFE, +0xF6,0xC4,0x04,0x74,0x25,0xB0,0x1B,0xBA,0x00,0x00,0xEE,0xEB,0x00,0x2A,0xC0,0xBA, +0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0x32,0xC0,0xBA,0x02,0x00,0xEE, +0xEB,0x00,0xBA,0x00,0x00,0xB0,0x00,0xEE,0xEB,0x2D,0xB0,0x1F,0xBA,0x00,0x00,0xEE, +0xEB,0x00,0x2A,0xC0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0xD1, +0xE6,0x8A,0x84,0x5D,0x12,0xD1,0xEE,0xF6,0xD0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xBA, +0x00,0x00,0xB0,0x0A,0xEE,0xEB,0x00,0xE4,0x04,0xEB,0x00,0xE4,0x04,0x46,0xE2,0x90, +0xC3,0x90,0xB8,0x14,0x00,0xBA,0x3E,0xFF,0xEF,0xB8,0x06,0x00,0xBA,0x32,0xFF,0xEF, +0xB8,0x0F,0x00,0xBA,0x34,0xFF,0xEF,0xBA,0x36,0xFF,0xEF,0x83,0x3E,0x44,0x12,0x00, +0x75,0x16,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8,0x12,0x00,0xBA,0x3A,0xFF,0xEF, +0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8, +0x12,0x00,0xBA,0x3A,0xFF,0xEF,0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0xFC, +0x00,0xBA,0x28,0xFF,0xEF,0xFB,0x83,0x3E,0x44,0x12,0x00,0x74,0x07,0xB8,0xCC,0x00, +0xBA,0x28,0xFF,0xEF,0xC3,0x00,0xFF,0xFF,0x20,0x24,0x28,0xFF,0x2C,0xFF,0xFF,0x30, +0x34,0x38,0xFF,0xFF,0x3C,0x90,0x3C,0x0F,0x77,0x0E,0xBB,0x15,0x0E,0x2E,0xD7,0x3C, +0xFF,0x74,0x05,0x8A,0xD8,0xF8,0xC3,0x90,0x2A,0xDB,0xF9,0xC3,0x83,0x3E,0x44,0x12, +0x00,0x74,0x27,0xA0,0x06,0x01,0x80,0x26,0x06,0x01,0x30,0x80,0x3E,0x06,0x01,0x30, +0x75,0x18,0xB9,0x02,0x00,0xBF,0xC4,0x13,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8, +0xBA,0x04,0x01,0xED,0xAB,0xE2,0xF1,0xEB,0x16,0x90,0xB9,0x04,0x00,0xBF,0xC4,0x13, +0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8,0xBA,0x04,0x01,0xEC,0xAA,0xE2,0xF1,0xFA, +0x90,0xBE,0xC4,0x13,0xAD,0x80,0xE4,0x3F,0x80,0xFC,0x02,0x74,0x0E,0x6A,0x00,0x1F, +0xC6,0x06,0x93,0x12,0x0A,0x9C,0x0E,0xE8,0x3E,0x04,0x90,0xAD,0x3C,0x0F,0x75,0xED, +0x8A,0xC4,0xE8,0x81,0xFF,0x72,0xE6,0x88,0x1E,0x1A,0x01,0xC6,0x06,0x8E,0x12,0x00, +0xB0,0x00,0x0A,0x06,0x1A,0x01,0xBA,0x00,0x01,0xEE,0xC6,0x06,0x8F,0x12,0x40,0x83, +0x3E,0x44,0x12,0x00,0x75,0x06,0xB8,0x0C,0x00,0xEB,0x04,0x90,0xB8,0x4C,0x00,0xBA, +0x28,0xFF,0xEF,0xC3,0x83,0x3E,0x44,0x12,0x00,0x75,0x01,0xC3,0xA1,0x50,0x12,0x0B, +0x06,0x52,0x12,0x0A,0xC4,0xA8,0x08,0x74,0xF2,0xA0,0x0F,0x01,0x2A,0xE4,0x50,0xFF, +0x36,0xBA,0x13,0x1F,0xE8,0x36,0x56,0x83,0xC4,0x02,0x6A,0x00,0x1F,0x33,0xC0,0xA3, +0xBC,0x13,0xA0,0x0F,0x01,0xA3,0xBE,0x13,0x8B,0x1E,0xBC,0x13,0x8A,0x87,0x50,0x12, +0xF6,0x87,0x50,0x12,0x08,0x74,0x0D,0x24,0x07,0x8A,0xE0,0xBE,0xCC,0x00,0xA0,0xBC, +0x13,0xE8,0x7A,0x3D,0xFF,0x06,0xBC,0x13,0xFF,0x0E,0xBE,0x13,0x75,0xDA,0xC3,0x90, +0x1E,0x33,0xC0,0x8E,0xD8,0xB0,0x01,0xE8,0x3A,0x3D,0x1F,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xC7,0x46,0x62,0x1A,0x44,0xC7,0x46, +0x7C,0xDE,0x3B,0xC7,0x46,0x7E,0xC4,0x3B,0xC7,0x86,0x80,0x00,0xCE,0x3C,0xE8,0xAB, +0x16,0xC6,0x86,0xC0,0x00,0x11,0x83,0x7E,0x08,0x00,0x74,0x07,0x51,0x56,0xE8,0x19, +0x33,0x5E,0x59,0x46,0x46,0xE2,0xCD,0xC3,0x33,0xC9,0x8B,0xF1,0x8B,0xF9,0x8A,0x0E, +0x94,0x12,0xC1,0xE9,0x02,0xE3,0x13,0x2E,0x8B,0xAC,0x44,0x00,0x8A,0x86,0x9E,0x00, +0x88,0x85,0x6C,0x12,0x83,0xC6,0x08,0x47,0xE2,0xED,0xC3,0xFA,0xFC,0xB0,0xC0,0xBA, +0x00,0x01,0xEE,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0,0xBF,0x16,0x01,0xB9,0xCC, +0x77,0x2B,0xCF,0xD1,0xE9,0xF3,0xAB,0xBC,0x40,0x12,0xE8,0xD9,0x02,0xE8,0x56,0x3C, +0xBE,0xC8,0x0F,0xE8,0xD8,0x3C,0xF4,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0, +0xF6,0x06,0x0A,0x01,0x80,0x74,0x0B,0xBE,0x17,0x55,0xE8,0xC1,0x3C,0xB0,0x01,0xE8, +0x92,0x3C,0xE8,0xB3,0x00,0xE8,0xFA,0xF5,0xE8,0x08,0xF8,0xE8,0x0F,0xF9,0xE8,0x85, +0xFA,0xE8,0xB6,0xFA,0xE8,0xEF,0xFC,0xE8,0xC2,0x10,0xE8,0xE9,0x3B,0xE8,0xB2,0xFD, +0xE8,0x30,0xFD,0xE8,0x54,0x02,0xC6,0x06,0x8F,0x12,0xC0,0xE8,0xBB,0xFA,0xE8,0xEB, +0xFA,0xE8,0xE9,0xFB,0xE8,0xAF,0xFC,0xE8,0x8D,0xFC,0xE8,0x1F,0xFF,0xE8,0x58,0xFF, +0xE8,0xDB,0xFD,0xE8,0x16,0xFE,0x33,0xC0,0xBE,0x5A,0x05,0xE8,0x70,0x3C,0xE8,0xA3, +0xFE,0xE8,0xE0,0xFC,0xFB,0xBE,0x86,0x44,0xE8,0x63,0x3C,0xE9,0xB0,0x2D,0x56,0x98, +0x8B,0xF0,0x8B,0x42,0x52,0x85,0xC0,0x75,0x27,0xC7,0x42,0x52,0x01,0x00,0x53,0x36, +0x8B,0x9C,0x2C,0x01,0xF6,0xC3,0x01,0x75,0x0C,0x36,0x89,0x68,0x52,0x36,0x89,0xAC, +0x2C,0x01,0x5B,0x5E,0xC3,0x36,0x89,0xAC,0x2C,0x01,0x36,0x89,0xAC,0x1C,0x01,0x5B, +0x5E,0xC3,0x56,0x98,0x8B,0xF0,0x33,0xED,0x36,0x8B,0x84,0x1C,0x01,0xA8,0x01,0x75, +0x15,0x8B,0xE8,0x33,0xC0,0x87,0x42,0x52,0x36,0x89,0x84,0x1C,0x01,0xA8,0x01,0x74, +0x05,0x36,0x89,0x84,0x2C,0x01,0x5E,0xC3,0x56,0x51,0x33,0xF6,0xB8,0x01,0x00,0xB9, +0x08,0x00,0x89,0x84,0x1C,0x01,0x89,0x84,0x2C,0x01,0x46,0x46,0xE2,0xF4,0x59,0x5E, +0xC3,0x90,0xBB,0x01,0x00,0x8B,0xE8,0xFF,0x4E,0x6E,0x74,0x0A,0x8B,0xDD,0x8B,0x46, +0x58,0xA8,0x01,0x74,0xF0,0xC3,0x8B,0x46,0x48,0xA9,0x08,0x00,0x74,0x45,0xF7,0x46, +0x38,0x40,0x00,0x74,0x27,0xE8,0x5C,0x10,0x80,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0x88,0x86,0xA8,0x00,0xEE,0x60,0xB0,0xFE,0xE8,0x6C,0x32,0x61,0xB0,0x02,0xE8, +0x4C,0xFF,0x8B,0x46,0x48,0x24,0xF7,0x89,0x46,0x48,0xEB,0x17,0xE8,0x2A,0x10,0x81, +0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x8B,0x46,0x48,0xA9,0x04,0x00,0x74,0x14,0xB0,0x02,0xE8,0x21,0xFF,0x8B,0x46,0x48, +0x24,0xFB,0x89,0x46,0x48,0x60,0xB0,0xDF,0xE8,0x2D,0x32,0x61,0x33,0xC0,0x87,0x46, +0x58,0xF6,0xC3,0x01,0x75,0x0B,0x36,0x89,0x47,0x58,0xA8,0x01,0x75,0x0D,0xE9,0x74, +0xFF,0xA3,0x22,0x01,0xA8,0x01,0x75,0x03,0xE9,0x6A,0xFF,0x89,0x1E,0x32,0x01,0xC3, +0xBB,0x01,0x00,0x8B,0xE8,0xF7,0x46,0x38,0x40,0x00,0x74,0x15,0xE8,0xD5,0x0F,0x80, +0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x0A,0x8B,0xDD,0x8B,0x46,0x56,0xA8,0x01,0x74,0xE3, +0xC3,0x8B,0x46,0x26,0x80,0xE4,0xFE,0x80,0xCC,0x02,0x89,0x46,0x26,0xB0,0x02,0xE8, +0xBC,0xFE,0x33,0xC0,0x87,0x46,0x56,0xF6,0xC3,0x01,0x75,0x0A,0x36,0x89,0x47,0x56, +0xA8,0x01,0x75,0x0B,0xEB,0xBD,0xA3,0x20,0x01,0xA8,0x01,0x75,0x02,0xEB,0xB4,0x89, +0x1E,0x30,0x01,0xC3,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA0,0x90,0x12,0x84,0xC0, +0x75,0x49,0xA1,0x22,0x01,0xA8,0x01,0x75,0x03,0xE8,0xF6,0xFE,0xA1,0x20,0x01,0xA8, +0x01,0x75,0x03,0xE8,0x8A,0xFF,0xA1,0xAC,0x13,0x48,0x78,0x05,0x74,0x45,0xA3,0xAC, +0x13,0xA1,0xAE,0x13,0x48,0x78,0x05,0x74,0x51,0xA3,0xAE,0x13,0xA1,0xB0,0x13,0x48, +0x78,0x05,0x74,0x63,0xA3,0xB0,0x13,0xA1,0x7E,0x12,0x40,0x78,0x03,0xA3,0x7E,0x12, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0xA0,0x91,0x12,0x40,0x3C, +0x02,0x72,0x0B,0x33,0xC0,0xA2,0x91,0x12,0xFF,0x16,0x7C,0x12,0xEB,0xA4,0xA2,0x91, +0x12,0xEB,0x9F,0xA0,0x8E,0x12,0x32,0x06,0x8F,0x12,0xA2,0x8E,0x12,0x0A,0x06,0x1A, +0x01,0xBA,0x00,0x01,0xEE,0xB8,0x2C,0x01,0xEB,0xA4,0x83,0x3E,0x84,0x12,0x10,0x72, +0x11,0xBA,0x28,0xFF,0xED,0x0C,0x81,0xEF,0xE8,0x39,0x37,0xBA,0x28,0xFF,0xED,0x24, +0x7E,0xEF,0xB8,0x04,0x00,0xEB,0x92,0xC6,0x06,0x8D,0x12,0x01,0xE8,0x25,0x37,0xC6, +0x06,0x8D,0x12,0x00,0xA1,0xB2,0x13,0xEB,0x8B,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF, +0x6B,0xC3,0x19,0xBA,0x62,0xFF,0xEF,0xB8,0x0A,0x00,0xBA,0x60,0xFF,0xEF,0xB8,0x01, +0xE0,0xBA,0x66,0xFF,0xEF,0xB8,0xFF,0xFF,0xBA,0x52,0xFF,0xEF,0xB8,0x09,0xC0,0xBA, +0x56,0xFF,0xEF,0xC7,0x06,0xAC,0x13,0x2C,0x01,0xC7,0x06,0xAE,0x13,0x04,0x00,0xC6, +0x06,0x91,0x12,0x00,0xC3,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF,0x6B,0xC3,0x05,0xD1, +0xE8,0xA3,0x18,0x01,0xC3,0x90,0x52,0xBA,0x50,0xFF,0xED,0x5A,0xC3,0x90,0x53,0x51, +0x8B,0x1E,0x18,0x01,0xB9,0x32,0x05,0x90,0xE2,0xFE,0x4B,0x75,0xF7,0x59,0x5B,0xC3, +0xB0,0x80,0xBA,0x00,0x01,0x0A,0x06,0x1A,0x01,0xEE,0xC3,0x90,0xB0,0x40,0xEB,0xF2, +0xB0,0xC0,0xEB,0xEE,0xB0,0x00,0xEB,0xEA,0xFA,0x60,0x06,0x1E,0x16,0x2B,0xDB,0x8E, +0xDB,0x2E,0xA1,0x9C,0x4C,0x2E,0xA3,0x74,0x4C,0xA0,0x93,0x12,0x98,0x8B,0xE8,0x89, +0x26,0x2D,0x7A,0x80,0x3E,0xCA,0x13,0x00,0x74,0x03,0xE9,0x51,0x42,0xE8,0xC0,0xFF, +0xE8,0xAB,0xFF,0xE8,0xA8,0xFF,0xB0,0x20,0xC6,0x06,0x90,0x12,0x00,0xFF,0x16,0x7C, +0x12,0x8B,0xFD,0x83,0xFF,0x0A,0x72,0x11,0xE8,0xB9,0xFF,0xE8,0x90,0xFF,0xE8,0xAB, +0xFF,0xE8,0x8A,0xFF,0x83,0xEF,0x0A,0xEB,0xEA,0x0B,0xFF,0x74,0x0F,0xE8,0xA4,0xFF, +0xE8,0x7B,0xFF,0xE8,0x9A,0xFF,0xE8,0x75,0xFF,0x4F,0x75,0xF1,0xE8,0x95,0xFF,0xE8, +0x6C,0xFF,0xEB,0xB9,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE,0x88,0x86,0xA5,0x00,0xC3, +0x8A,0x86,0xA6,0x00,0x0C,0x02,0xEE,0xC3,0x8B,0x76,0x38,0xF7,0xC6,0x01,0x00,0x74, +0xEF,0x8B,0x4E,0x36,0x8B,0x46,0x2E,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89, +0x46,0x2E,0x01,0x4E,0x34,0xC4,0x7E,0x04,0x26,0x01,0x0D,0x8B,0x7E,0x2C,0x83,0xEA, +0x04,0xF3,0x6C,0x8E,0xC1,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72,0x12,0xF7,0xC6,0x20, +0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8,0xA0,0xFC,0xC3,0xF7, +0xC6,0x04,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xCE,0x10,0x89,0x76,0x38,0x8A,0x86,0xA7, +0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xC2,0x08,0xEE,0x83,0xEA,0x08,0x8B,0xC3, +0x3D,0x40,0x00,0x72,0x01,0xC3,0x81,0x4E,0x38,0x00,0x04,0x83,0xC2,0x02,0x8A,0x86, +0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0xC3,0x8A,0x86,0xA6,0x00,0x0C,0x02, +0xEE,0xC3,0xF7,0x46,0x38,0x01,0x00,0x74,0xF1,0x8B,0x4E,0x2E,0x32,0xDB,0x8A,0xBE, +0xA3,0x00,0x83,0xC2,0x06,0xC4,0x76,0x04,0x8B,0x7E,0x2C,0x83,0xF9,0x08,0x72,0x2C, +0xEC,0xA8,0x01,0x74,0x16,0x8A,0xE0,0x83,0xEA,0x0A,0xEC,0x83,0xC2,0x0A,0x84,0xE7, +0x75,0x51,0xAA,0xFE,0xC3,0x49,0x83,0xF9,0x08,0x73,0xE5,0x32,0xFF,0x26,0x01,0x1C, +0x01,0x5E,0x34,0x89,0x76,0x04,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x3B,0x4E,0x3C,0x72, +0x11,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xFD, +0xFB,0xC3,0xF6,0x46,0x38,0x04,0x74,0x15,0x83,0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00, +0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xEA,0x02,0xEE,0x83,0xC2,0x02,0x3D,0x40,0x00, +0x72,0x5D,0xC3,0x32,0xFF,0x26,0x03,0x1C,0x85,0xDB,0x74,0x09,0x26,0x89,0x1C,0x8B, +0xF7,0x47,0x47,0x49,0x49,0x80,0xE4,0x1E,0x80,0xCC,0xC0,0x26,0x89,0x04,0xF6,0xC4, +0x10,0x74,0x27,0x8B,0x76,0x38,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE,0x86,0xB2, +0x00,0xB0,0x0A,0xE8,0xA8,0xFB,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8,0x68,0x26, +0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x8B,0xF7,0x33,0xC0,0xAB,0x32, +0xDB,0x8A,0xBE,0xA3,0x00,0x49,0x49,0x83,0xF9,0x08,0x72,0x17,0xE9,0x41,0xFF,0x81, +0x4E,0x38,0x00,0x04,0x83,0xC2,0xF8,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5, +0x00,0xEE,0xC3,0xE9,0x45,0xFF,0x83,0xC2,0x08,0xEC,0x88,0x86,0xAA,0x00,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0x8B,0x5E,0x3E,0x84,0xE3, +0x74,0x4F,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74,0x0C,0xA8,0x08,0x74,0x05, +0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08,0x74,0x0C,0xA8,0x02,0x74, +0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E,0x26,0x8B,0xF0,0x8A,0x86, +0xA5,0x00,0x84,0xC9,0x74,0x08,0xA8,0x02,0x74,0x15,0x24,0xFD,0xEB,0x06,0xA8,0x02, +0x75,0x0D,0x0C,0x02,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x83,0xC2,0x0A,0x8B, +0xC6,0x84,0xE7,0x75,0x01,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8,0xEE,0xFA, +0xF7,0x46,0x38,0x00,0x02,0x74,0xEE,0x83,0x7E,0x2E,0x06,0x72,0xE8,0x8A,0xA6,0xAA, +0x00,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA,0xB0,0x02,0xAB,0x26,0x83,0x07, +0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83, +0x4E,0x38,0x20,0xB0,0x00,0xE8,0xB6,0xFA,0xC3,0x90,0x83,0xEA,0x08,0xE9,0xB4,0xFD, +0x83,0xC2,0x06,0x8B,0x5E,0x26,0xF6,0xC3,0xC0,0x75,0xEF,0x8B,0x4E,0x1C,0xEC,0x88, +0x86,0xA4,0x00,0x83,0xEA,0x0A,0xA8,0x20,0x75,0x02,0x8A,0xCD,0x32,0xED,0x8B,0x46, +0x1A,0x3B,0xC8,0x73,0x18,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0xC5,0x76,0x00, +0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x30,0xC3,0x85,0xC0,0x74, +0x31,0x8B,0xC8,0x01,0x46,0x2A,0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x32,0xF1,0xF6,0xC7,0x01,0x75,0x16,0x83,0xC2,0x02,0xE8,0x53, +0xFD,0xF6,0xC7,0x10,0x75,0x0B,0xB0,0x02,0xE8,0x43,0xFA,0xC3,0xF6,0xC7,0x01,0x74, +0xF0,0xC3,0x80,0xCB,0x02,0x89,0x5E,0x26,0xF6,0xC7,0x01,0x74,0xDE,0x83,0xC2,0x02, +0xE8,0x31,0xFD,0xF6,0x86,0xA4,0x00,0x40,0x74,0x0B,0x80,0xE7,0xFE,0x80,0xCF,0x02, +0x89,0x5E,0x26,0xEB,0xCC,0xB0,0x04,0xE8,0x14,0xFA,0xC3,0xC0,0xC2,0xC8,0xCA,0xC4, +0xC6,0xCC,0xCE,0xD0,0xD2,0xD8,0xDA,0xD4,0xD6,0xDC,0xDE,0x90,0xE9,0x0E,0x01,0xE4, +0xC4,0x8A,0xE0,0xE4,0xC4,0x8B,0xD0,0x83,0xF9,0x08,0x72,0xF0,0x26,0x83,0x3F,0x00, +0x74,0x04,0x8B,0xDF,0x49,0x49,0x8B,0xFB,0x8A,0xDE,0x83,0xE3,0x0F,0x2E,0x8A,0xA7, +0x2B,0x16,0xAB,0xF6,0xC4,0x10,0x74,0x24,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE, +0x86,0xB2,0x00,0xB0,0x0A,0xE8,0xC6,0xF9,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8, +0x86,0x24,0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x89,0x7E,0x04,0x33, +0xC0,0xAB,0x49,0x49,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x8B,0xC1,0xEB,0x4E,0x90,0xEB, +0x9E,0x90,0xE4,0xD6,0x84,0xC0,0x79,0x63,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03, +0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0x8B,0x4E,0x2E,0xC4, +0x5E,0x04,0x8B,0x7E,0x2C,0x8B,0x76,0x38,0xE4,0x86,0x24,0x07,0x3C,0x03,0x75,0xCF, +0xE4,0x1C,0x91,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89,0x46,0x2E,0x01,0x4E, +0x34,0x26,0x01,0x0F,0xBA,0xC4,0x00,0xF3,0x6C,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72, +0x1C,0xF7,0xC6,0x20,0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8, +0x3C,0xF9,0x8A,0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD6,0xC3,0xF9,0xC3,0xF7,0xC6,0x0A, +0x00,0x74,0x35,0xF7,0xC6,0x10,0x00,0x75,0x2F,0x83,0xCE,0x10,0x89,0x76,0x38,0xF7, +0xC6,0x02,0x00,0x74,0x0E,0x50,0xE4,0xD8,0x24,0xFE,0xE6,0xD8,0x58,0xF7,0xC6,0x08, +0x00,0x74,0x15,0x50,0x51,0xB9,0xE8,0x03,0xE4,0x0A,0x84,0xC0,0xE0,0xFA,0x84,0xC0, +0x75,0x04,0xB0,0x24,0xE6,0x0A,0x59,0x58,0x3D,0x40,0x00,0x73,0xB5,0x8A,0x86,0xA5, +0x00,0x24,0xEF,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0xCE,0x10,0x04,0x89,0x76,0x38, +0xEB,0xA0,0x00,0x08,0x04,0x0C,0x01,0x09,0x05,0x0D,0x02,0x0A,0x06,0x0E,0x03,0x0B, +0x07,0x0F,0x00,0x40,0x80,0xC0,0x20,0x60,0xA0,0xE0,0x10,0x50,0x90,0xD0,0x30,0x70, +0xB0,0xF0,0xE4,0xD2,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E, +0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0xE4,0xD8,0xC0,0xE8,0x04,0x8B,0xD8,0x2E, +0x8A,0x87,0x62,0x17,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0xE4,0x98, +0x8B,0x5E,0x3E,0x84,0xE3,0x74,0x54,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74, +0x0C,0xA8,0x08,0x74,0x05,0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08, +0x74,0x0C,0xA8,0x02,0x74,0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E, +0x26,0x8B,0xF0,0x8A,0x86,0xA5,0x00,0xF6,0xC1,0xFD,0x74,0x08,0xA8,0x06,0x74,0x19, +0x24,0xF9,0xEB,0x0F,0xA8,0x06,0x75,0x11,0xF6,0xC5,0x01,0x75,0x04,0x0C,0x04,0xEB, +0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x8B,0xC6,0x84,0xE7,0x75,0x09,0x8A, +0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD2,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8, +0x1C,0xF8,0xF7,0x46,0x38,0x00,0x02,0x74,0xE6,0x83,0x7E,0x2E,0x06,0x72,0xE0,0x8A, +0x86,0xA9,0x00,0x8A,0xE0,0x86,0x86,0xAA,0x00,0x8A,0xC8,0x32,0xC4,0x80,0xC9,0x0B, +0x22,0xC1,0xC0,0xE4,0x04,0x0A,0xE0,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA, +0xB0,0x02,0xAB,0x26,0x83,0x07,0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46, +0x38,0x20,0x75,0xAB,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xD1,0xF7,0xEB,0xA0,0x90, +0xE4,0x12,0x24,0xDF,0xE6,0x12,0x81,0xE3,0xFE,0x9F,0x89,0x5E,0x26,0x83,0x66,0x48, +0xF7,0xEB,0x73,0x90,0xF6,0xC7,0x20,0x75,0xE7,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32, +0xC0,0xE6,0xC6,0xB0,0x83,0xE6,0xC6,0x80,0xCF,0x20,0x89,0x5E,0x26,0x8A,0x86,0xA5, +0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xEB,0x74,0x90,0xF6,0xC7,0x40,0x75, +0xD3,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32,0xC0,0xE6,0xC6,0xB0,0x81,0xE6,0xC6,0x80, +0xE7,0xDF,0x80,0xCB,0x01,0x89,0x5E,0x26,0xB0,0x06,0xE8,0x71,0xF7,0x90,0x8A,0x86, +0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x43,0xE4,0xD4,0xE6,0xD0, +0x8B,0xF8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x8B,0x5E, +0x26,0xF6,0xC7,0x60,0x75,0xB6,0xF6,0xC3,0xC0,0x75,0xD3,0xBA,0xC6,0x00,0x8B,0x4E, +0x1C,0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1E,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A, +0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x3D,0x8B, +0xC7,0x24,0x3F,0xE6,0xD4,0xC3,0x85,0xC0,0x74,0x39,0x8B,0xC8,0x01,0x46,0x2A,0xC5, +0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x83,0xCB,0x02,0x89,0x5E,0x26,0xE8,0xD9,0xED,0xF6, +0xC7,0x01,0x75,0x39,0x8A,0x86,0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00, +0xF6,0xC7,0x10,0x75,0xCA,0xB0,0x02,0xE8,0xE4,0xF6,0xEB,0xC3,0xF6,0xC7,0x01,0x74, +0xEF,0xEB,0xBC,0xF6,0xC7,0x01,0x74,0xDC,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x11, +0x81,0xE3,0xFF,0xFE,0x81,0xCB,0x00,0x02,0x89,0x5E,0x26,0xEB,0xC7,0x8A,0x86,0xA5, +0x00,0x24,0xFB,0x0C,0x02,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x92,0x90,0xFD,0xF7, +0xDF,0x7F,0xFE,0xFB,0xEF,0xBF,0x00,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x03,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE, +0xC1,0xE3,0x02,0xE4,0xCE,0xA8,0x04,0x75,0x09,0xA8,0x02,0x74,0x03,0xE9,0x2C,0xFE, +0xF9,0xC3,0x50,0x53,0xE8,0xCB,0xFC,0x5B,0x58,0xA8,0x02,0x74,0x03,0xE9,0x1C,0xFE, +0xF8,0xC3,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE,0xC1,0xE3,0x02,0xE9, +0xD0,0xFB,0x96,0x1A,0xC2,0x1A,0x00,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0E,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0xC3,0x90,0xD6,0x14,0x90,0x15,0x58,0x13,0xE2,0x13, +0xD6,0x1B,0xD6,0x1B,0xE2,0x13,0xD6,0x1B,0x8B,0x94,0x64,0x12,0xC1,0xE6,0x04,0xA8, +0x01,0x74,0x35,0x50,0x33,0xC0,0x8A,0xC2,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27, +0x8B,0xD8,0x2E,0x8A,0x9F,0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56, +0x28,0xEC,0xA8,0x01,0x75,0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF, +0x97,0xD8,0x1B,0x5E,0x5A,0xEB,0xCD,0x58,0xA8,0x02,0x74,0x36,0x83,0xC6,0x10,0x33, +0xC0,0x8A,0xC6,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27,0x8B,0xD8,0x2E,0x8A,0x9F, +0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56,0x28,0xEC,0xA8,0x01,0x75, +0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF,0x97,0xD8,0x1B,0x5E,0x5A, +0xEB,0xCD,0xC3,0x90,0x32,0xE4,0x8B,0xD8,0x8B,0xD0,0x2E,0x8A,0x9F,0x96,0x19,0x2E, +0x22,0x97,0x8E,0x19,0x56,0x52,0x8A,0xC3,0x24,0x03,0x03,0xC6,0x80,0xE3,0x04,0xD0, +0xEB,0x2E,0xFF,0x97,0xD2,0x1A,0x58,0x5E,0xA9,0x55,0x00,0x75,0xD9,0xC3,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0xBF,0xFF,0xEB,0xEE,0x90,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1, +0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8,0x94,0xFF, +0xEB,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61, +0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x6B,0xFF,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8,0x5A,0xFF,0xEB,0xDD,0xA1,0x60,0x12,0xE6, +0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5C,0x12,0xE6, +0xFE,0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0x1C,0xFF,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22, +0xC4,0x74,0xE4,0xBE,0x0C,0x00,0xE8,0x0B,0xFF,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0x53,0xFE,0xEB,0xEE,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x02,0x00,0xE8,0x2C,0xFE,0xEB,0xED,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8, +0x06,0xFE,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4, +0x74,0x08,0xBE,0x06,0x00,0xE8,0xE0,0xFD,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12, +0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x37,0xFE,0xA1,0x60,0x12, +0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0xAA,0xFD,0xEB,0xDD, +0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0xEC,0xFD,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84, +0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x5F,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x04,0x07,0xE4,0x04, +0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x27, +0xFD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8, +0x92,0xFD,0xEB,0xDD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4, +0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x19, +0xBE,0x02,0x00,0xE8,0xE2,0xFC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74, +0xE4,0xBE,0x0C,0x00,0xE8,0x4D,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x9D,0xFC,0xA1,0x60,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0x8C,0xFC,0xEB,0xDD,0xA1,0x60, +0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA,0x22, +0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x19,0xBE,0x02,0x00,0xE8,0x5C,0xFC,0xA1,0x62,0x12,0xE6, +0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x4B,0xFC,0xEB,0xDC,0xA1, +0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0x2A,0xC0, +0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0x14,0x33,0xDB,0xE8,0xD5,0xF6,0xEB,0xEF,0x90, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xF6,0x06,0x05,0x01, +0x01,0x75,0xED,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0xE3,0xBB,0x04,0x00, +0xE8,0xAF,0xF6,0xEB,0xC9,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0xFB,0x90, +0xFA,0x2A,0xC0,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x13,0x33,0xDB,0xE8,0xCC,0xF8, +0xEB,0xEC,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xA8,0x04, +0x74,0xF0,0x33,0xDB,0xE8,0x5B,0xF7,0xEB,0xD5,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0x90,0xFB,0x90,0xFA,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x15,0xBB, +0x04,0x00,0xE8,0x97,0xF8,0xEB,0xEB,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0xA8,0x04,0x74,0xF0,0xBB,0x04,0x00,0xE8,0x24,0xF7,0xEB,0xD2, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x09,0x9C,0x0E,0xE8,0x6B,0xF2,0x90,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x29,0x9C,0x0E,0xE8,0x5D,0xF2,0x90,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xCA,0x1D,0x8E,0x1C,0xE2,0x1C,0x16,0x1E,0x6E,0x20,0x7E,0x1D,0xAA,0x1E, +0x34,0x1F,0x6E,0x20,0x7E,0x1D,0x6E,0x20,0x6E,0x20,0x34,0x1F,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xF0,0x1D,0xB8,0x1C,0x30,0x1D,0x60,0x1E,0x6E,0x20,0xA4,0x1D,0xEE,0x1E, +0x74,0x1F,0x6E,0x20,0xA4,0x1D,0x6E,0x20,0x6E,0x20,0x74,0x1F,0xFC,0xB9,0x40,0x00, +0x8C,0xCB,0xB8,0x60,0x20,0x2B,0xFF,0xAB,0x93,0xAB,0x93,0xE2,0xFA,0xC7,0x06,0x4C, +0x00,0xA4,0x11,0x83,0x3E,0x44,0x12,0x00,0x75,0x20,0xC7,0x06,0x3C,0x00,0xEA,0x4A, +0xC7,0x06,0x30,0x00,0xB6,0x1F,0xC7,0x06,0x34,0x00,0xF6,0x1F,0xF6,0x06,0x05,0x01, +0x01,0x75,0x06,0xC7,0x06,0x38,0x00,0x2A,0x20,0xC3,0xC7,0x06,0x3C,0x00,0x38,0x4B, +0x33,0xDB,0x8A,0x1E,0x54,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x56,0x12,0x2E,0x8B,0x87, +0x7C,0x20,0xA3,0x30,0x00,0x8A,0x1E,0x55,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x57,0x12, +0x2E,0x8B,0x87,0x9C,0x20,0xA3,0x34,0x00,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x86, +0xC4,0xE6,0xD0,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x33,0xD2,0x8A,0xD4,0xC3,0x51, +0xB9,0x10,0x27,0xE4,0x0A,0x90,0x90,0x84,0xC0,0x74,0x05,0xE2,0xF6,0x59,0xF9,0xC3, +0x59,0xF8,0xC3,0x84,0xC0,0x78,0x1E,0x51,0x8A,0xE8,0x8A,0xC8,0xB8,0x01,0x00,0xD3, +0xE0,0x09,0x86,0x98,0x00,0x3A,0xAE,0xA0,0x00,0x59,0x75,0x10,0xE8,0xA9,0xE5,0x83, +0x4E,0x26,0x02,0xF9,0xC3,0x98,0x89,0x86,0x98,0x00,0xEB,0xF0,0xF8,0xC3,0x84,0xC0, +0x78,0x12,0x51,0x8A,0xE0,0x8A,0xC8,0xB8,0x01,0x00,0xD3,0xE0,0x59,0xF7,0xD0,0x21, +0x86,0x98,0x00,0xC3,0xC7,0x86,0x98,0x00,0x00,0x00,0xC3,0x83,0xC2,0x04,0x8A,0x86, +0xA6,0x00,0x0C,0x04,0xEE,0x83,0xEA,0x04,0xC3,0xE8,0x93,0xFF,0x72,0x04,0xB0,0x82, +0xE6,0x0A,0xC3,0x8B,0x46,0x26,0xA8,0xFD,0x74,0x11,0x8A,0x86,0xA5,0x00,0xA8,0x06, +0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC4,0x01,0x74,0x0A, +0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0xEB,0x0C,0xA8,0x02,0x75,0x0F,0x8A,0x86, +0xA5,0x00,0x24,0xFD,0x0C,0x04,0x3A,0x86,0xA5,0x00,0x75,0xD8,0xC3,0x8A,0x86,0xA5, +0x00,0xEB,0xCF,0xE4,0xD8,0x33,0xDB,0x8A,0xD8,0xC0,0xEB,0x04,0x2E,0x8A,0x9F,0x62, +0x17,0x88,0x9E,0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07, +0xA8,0x10,0x75,0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x80,0x75,0x03, +0x80,0xCB,0x40,0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0xFD,0x74,0x0D,0xA8, +0x06,0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC7,0x01,0x74, +0x04,0x0C,0x02,0xEB,0xF0,0xF6,0xC3,0x02,0x75,0xE9,0x0C,0x04,0xEB,0xE7,0xC4,0x04, +0xC4,0x04,0x85,0x04,0x59,0x04,0x48,0x04,0x41,0x04,0xC3,0x03,0x82,0x03,0x41,0x03, +0x82,0x02,0x57,0x02,0x41,0x02,0x82,0x01,0x41,0x01,0x82,0x00,0x41,0x00,0x4E,0x02, +0xAD,0x01,0x57,0x01,0x2D,0x00,0x2B,0x00,0x27,0x00,0x21,0x00,0x16,0x00,0xF4,0x04, +0xF4,0x04,0xA3,0x04,0x6F,0x04,0x5B,0x04,0x51,0x04,0xF4,0x03,0xA3,0x03,0x51,0x03, +0xA3,0x02,0x6D,0x02,0x51,0x02,0xA3,0x01,0x51,0x01,0xA3,0x00,0x51,0x00,0x62,0x02, +0xD9,0x01,0x6D,0x01,0x38,0x00,0x36,0x00,0x31,0x00,0x29,0x00,0x1B,0x00,0x51,0x57, +0xBF,0x02,0x00,0xEB,0x0F,0x90,0x51,0x56,0xBF,0x01,0x00,0xEB,0x07,0x90,0x51,0x56, +0xBF,0x03,0x00,0x90,0x3C,0x19,0x76,0x02,0xB0,0x17,0x98,0x8B,0xF0,0x8A,0x82,0xC4, +0x00,0x2A,0xE4,0x8B,0xF0,0x83,0xFE,0x18,0x73,0x46,0xD1,0xE6,0x2E,0x8B,0x8C,0x4E, +0x22,0xF7,0x46,0x38,0x80,0x00,0x74,0x05,0x2E,0x8B,0x8C,0x7E,0x22,0xF7,0xC7,0x02, +0x00,0x74,0x12,0x3B,0x8E,0x94,0x00,0x74,0x0C,0x89,0x8E,0x94,0x00,0x8A,0xC5,0xE6, +0xEC,0x8A,0xC1,0xE6,0xE4,0xF7,0xC7,0x01,0x00,0x74,0x12,0x3B,0x8E,0x96,0x00,0x74, +0x0C,0x89,0x8E,0x96,0x00,0x8A,0xC5,0xE6,0xF8,0x8A,0xC1,0xE6,0xF0,0x5E,0x59,0xC3, +0x77,0x06,0x8B,0x8E,0x8E,0x00,0xEB,0xC5,0x8B,0x8E,0x90,0x00,0xEB,0xBF,0xD5,0x03, +0xF6,0x00,0x3E,0x00,0x10,0x00,0x04,0x00,0xCA,0x04,0x33,0x01,0x4D,0x00,0x14,0x00, +0x05,0x00,0x01,0x03,0x05,0x07,0x09,0x00,0x01,0x02,0x03,0x04,0x80,0x84,0x1E,0x00, +0xA0,0x25,0x26,0x00,0x00,0x00,0x60,0x8B,0xF0,0x33,0xFF,0x2E,0xA1,0x4C,0x23,0x2E, +0x8B,0x16,0x4E,0x23,0xBB,0x2E,0x23,0xF7,0x46,0x38,0x80,0x00,0x74,0x0C,0x2E,0xA1, +0x50,0x23,0x2E,0x8B,0x16,0x52,0x23,0xBB,0x38,0x23,0xB9,0x05,0x00,0x2E,0x3B,0x31, +0x73,0x0A,0x47,0x47,0xE2,0xF7,0xB8,0xFF,0xFF,0xEB,0x1D,0x90,0xD1,0xEF,0x2E,0x8A, +0x8D,0x42,0x23,0x2A,0xED,0xD1,0xEA,0xD1,0xD8,0xE2,0xFA,0xF7,0xF6,0x05,0x02,0x00, +0xC1,0xE8,0x02,0x2E,0x8A,0xA5,0x47,0x23,0x2E,0xA3,0x54,0x23,0x61,0x2E,0xA1,0x54, +0x23,0xC3,0x08,0x00,0x20,0x00,0x80,0x00,0x00,0x02,0x60,0x09,0x08,0x00,0x20,0x00, +0x80,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00, +0x52,0x56,0x57,0x85,0xC0,0x74,0x05,0x3D,0x01,0x09,0x76,0x03,0xB8,0x01,0x09,0xBF, +0x5B,0x01,0xF7,0x46,0x38,0x80,0x00,0x74,0x03,0xBF,0xB2,0x01,0x33,0xF6,0x2E,0x3B, +0x84,0xB2,0x23,0x76,0x04,0x46,0x46,0xEB,0xF5,0xF7,0xE7,0x2E,0x8B,0xBC,0xBC,0x23, +0x03,0xC7,0x83,0xD2,0x00,0xD1,0xE7,0xF7,0xF7,0x2E,0x8A,0xA4,0xC6,0x23,0x5F,0x5E, +0x5A,0xC3,0xE4,0x3E,0x80,0xBE,0xC3,0x00,0x03,0x75,0x0C,0xF7,0x46,0x7A,0x20,0x00, +0x74,0x05,0x0C,0x80,0xE6,0x3E,0xC3,0x24,0x7F,0xE6,0x3E,0xC3,0x24,0x03,0x88,0x86, +0xC3,0x00,0x8A,0xE0,0xE4,0x10,0x24,0xFC,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00, +0x42,0xE8,0xCE,0xFF,0xC3,0x90,0x56,0x8B,0xF0,0x83,0xE6,0x07,0xD1,0xE6,0x2E,0xFF, +0xA4,0x54,0x24,0x90,0x64,0x24,0x68,0x24,0x6C,0x24,0x70,0x24,0x74,0x24,0x83,0x24, +0x83,0x24,0x83,0x24,0xB4,0x00,0xEB,0x0E,0xB4,0xC0,0xEB,0x0A,0xB4,0x40,0xEB,0x06, +0xB4,0x20,0xEB,0x02,0xB4,0xA0,0xE4,0x10,0x24,0x1F,0x0A,0xC4,0xE6,0x10,0x80,0x8E, +0xA1,0x00,0x42,0x5E,0xC3,0x90,0x3C,0x02,0x77,0x12,0x8A,0xE0,0xE4,0x10,0x24,0xF3, +0xC0,0xE4,0x02,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xC3,0x90,0x8B,0x5E, +0x38,0x84,0xC0,0x74,0x1F,0x3C,0x02,0x74,0x20,0x83,0xCB,0x08,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x0C,0xE8,0x88,0xFC,0x72,0x07,0xB0,0x24,0xE6,0x0A,0x83,0xCB,0x10, +0x89,0x5E,0x38,0xC3,0x83,0xE3,0xF7,0xEB,0xF7,0xF7,0xC3,0x10,0x00,0x74,0xF5,0xE8, +0x6D,0xFC,0x72,0xEC,0x8A,0x86,0xC0,0x00,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xEB,0xE0, +0x8B,0x5E,0x38,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0xE4,0xD8,0x77,0x0B,0x24,0xFE,0x80, +0xCB,0x12,0xE6,0xD8,0x89,0x5E,0x38,0xC3,0x0C,0x01,0x80,0xCB,0x02,0xEB,0xF3,0x50, +0x33,0xDB,0xC1,0xE8,0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A, +0xDC,0x2E,0x8A,0xA7,0x62,0x17,0x09,0x46,0x3E,0x58,0xC3,0x50,0x33,0xDB,0xC1,0xE8, +0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A,0xDC,0x2E,0x8A,0xA7, +0x62,0x17,0xF7,0xD0,0x21,0x46,0x3E,0x58,0xC3,0x8B,0x46,0x3E,0x33,0xDB,0x8A,0xD8, +0x0A,0xDC,0x2E,0x8A,0x87,0x72,0x17,0xE6,0x2C,0x8A,0xE0,0xE4,0x2A,0x24,0x0F,0x0A, +0xC4,0xE6,0x2A,0x8A,0x86,0xA5,0x00,0x84,0xE4,0x75,0x0D,0xA8,0x80,0x74,0x11,0x24, +0x7F,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x80,0x75,0x04,0x0C,0x80,0xEB,0xF1, +0xC3,0x1E,0x60,0x33,0xC9,0x33,0xD2,0x33,0xF6,0x8E,0xD9,0x8D,0xBE,0xFD,0x00,0x57, +0x8B,0x05,0x84,0xC0,0x74,0x16,0x8B,0xD1,0x42,0x8B,0xFE,0x4F,0x78,0x09,0x38,0xA3, +0xE4,0x00,0x74,0x08,0x4F,0x79,0xF7,0x88,0xA2,0xE4,0x00,0x46,0x5F,0x83,0xC7,0x09, +0x41,0x83,0xF9,0x10,0x72,0xD9,0x89,0xB6,0x86,0x00,0x89,0x96,0x84,0x00,0x61,0x1F, +0xC3,0x53,0xC7,0x46,0x66,0x00,0x00,0x8B,0x46,0x64,0xA9,0x40,0x00,0x74,0x0D,0xB3, +0x00,0xA9,0x80,0x00,0x74,0x02,0xB3,0x7F,0x88,0x9E,0xC1,0x00,0x32,0xDB,0xA9,0x02, +0x00,0x74,0x03,0x80,0xCB,0x40,0xA9,0x00,0x40,0x74,0x03,0x80,0xCB,0x02,0xA9,0x00, +0x80,0x74,0x03,0x80,0xCB,0x01,0xA9,0x30,0x1E,0x74,0x03,0x80,0xCB,0xBC,0xA9,0x00, +0x20,0x74,0x03,0x80,0xCB,0x08,0xA9,0x04,0x01,0x74,0x03,0x80,0xCB,0x10,0xA9,0x08, +0x00,0x74,0x03,0x80,0xCB,0x20,0x88,0x9E,0xC2,0x00,0x5B,0xC3,0x06,0x51,0x57,0x50, +0x16,0x07,0x8D,0xBE,0xC4,0x00,0xB9,0x1F,0x00,0x33,0xC0,0xAA,0x40,0xE2,0xFC,0x8B, +0x86,0x92,0x00,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x58,0x5F,0x59,0x07,0xC3, +0xE4,0xD8,0xC0,0xE8,0x04,0x53,0x25,0x0F,0x00,0x8B,0xD8,0x2E,0x8A,0x87,0x62,0x17, +0x88,0x86,0xA9,0x00,0x5A,0xC3,0x08,0x86,0xAC,0x00,0xC6,0x86,0xBA,0x00,0x01,0xB0, +0x0E,0xE8,0xEA,0xE9,0xC3,0xAD,0x36,0xA3,0xB4,0x13,0xAD,0x36,0xA3,0xB6,0x13,0xAD, +0x36,0xA3,0xB8,0x13,0x83,0xE9,0x06,0x36,0xF7,0x06,0xB6,0x13,0x0F,0x00,0xC3,0x8A, +0x46,0x26,0xF7,0x46,0x48,0x80,0x00,0x74,0x02,0x0C,0x10,0x88,0x86,0xBD,0x00,0x32, +0xC0,0x83,0x7E,0x1A,0x00,0x75,0x0E,0x8B,0x5E,0x40,0x43,0x80,0xE3,0xFE,0x3B,0x5E, +0x08,0x75,0x02,0x0C,0x01,0x83,0x7E,0x3A,0x00,0x75,0x0D,0x1E,0xC5,0x5E,0x14,0x8B, +0x1F,0x1F,0x85,0xDB,0x75,0x02,0x0C,0x02,0xF7,0x46,0x38,0x10,0x00,0x74,0x02,0x0C, +0x04,0xF7,0x46,0x7A,0x02,0x00,0x74,0x02,0x0C,0x08,0x88,0x86,0xBF,0x00,0xC3,0x90, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x0D,0x9C,0x0E,0xE8,0x0B,0xEC,0x90,0xB0,0x02, +0xE6,0xDA,0xF8,0xC3,0x33,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x01,0xE6,0xD8,0xF8,0xC3, +0x33,0xC0,0xE6,0xD8,0xF8,0xC3,0xB0,0xFF,0xE8,0x68,0xFA,0xE8,0xBB,0xFA,0xF8,0xC3, +0xAC,0x49,0xE8,0xC9,0xFB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x2F,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0x81,0xFD,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x39,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE6,0x34,0xF8,0xC3,0xAC,0x49,0xE6,0x36,0xF8,0xC3,0xAC,0x49,0x3C,0x02, +0x77,0x1F,0x84,0xC0,0x75,0x1D,0xE4,0x14,0x24,0xEF,0xE6,0x14,0xE4,0x12,0x24,0x3F, +0xE6,0x12,0xE4,0x16,0xA8,0x04,0x74,0x09,0xE8,0x04,0xFA,0x72,0x04,0xB0,0x18,0xE6, +0x0A,0xF8,0xC3,0x8A,0xE0,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4,0x12,0x0C,0xC0,0xF6, +0xC4,0x01,0x74,0x02,0x24,0x7F,0xE6,0x12,0xF8,0xC3,0xAC,0x49,0xE8,0x3F,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x40,0xE8,0x97,0xFD,0xE8,0xCE,0xFD,0xE8,0xC2,0xFE,0xB0,0x01, +0xE8,0xD3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x9F,0xFD,0xE8,0xBA,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x10,0xE8,0x77,0xFD,0xE8,0xAE,0xFD,0xE8,0xA2,0xFE,0xB0,0x08, +0xE8,0xB3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x7F,0xFD,0xE8,0x9A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x80,0xE8,0x57,0xFD,0xE8,0x8E,0xFD,0xE8,0x82,0xFE,0xB0,0x02, +0xE8,0x93,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x5F,0xFD,0xE8,0x7A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x20,0xE8,0x37,0xFD,0xE8,0x6E,0xFD,0xE8,0x62,0xFE,0xB0,0x04, +0xE8,0x73,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x3F,0xFD,0xE8,0x5A,0xFD,0xF8, +0xC3,0x90,0xAC,0x49,0xE8,0x48,0x14,0xE4,0x3C,0x24,0xE7,0x0A,0xC4,0xE6,0x3C,0xF8, +0xC3,0x90,0xB8,0xDE,0x3B,0x89,0x46,0x7C,0xE4,0x3C,0x0C,0x18,0xE6,0x3C,0xF8,0xC3, +0xE4,0x12,0x0C,0x02,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFD,0xEB,0xF6,0xE8,0xCF, +0xFC,0xF8,0xC3,0x90,0x83,0x66,0x38,0xFD,0xF8,0xC3,0xAC,0x49,0xA8,0x01,0x74,0x06, +0x83,0x4E,0x7A,0x20,0xEB,0x04,0x83,0x66,0x7A,0xDF,0xE8,0xE5,0xFB,0xF8,0xC3,0x90, +0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0x4E, +0x26,0x01,0x20,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x48,0x08,0x49,0x46, +0xF9,0xC3,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x81,0x4E,0x26,0x01,0x20,0xAC,0xB4,0x0A,0xF6,0xE4,0xEB,0xD8,0xE8,0xFA,0x13,0xE4, +0x3C,0x24,0xF8,0x0A,0xC4,0xE6,0x3C,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89,0x46,0x64, +0xA9,0x01,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x1A,0xA9,0x04,0x00,0x74, +0x0F,0xE4,0x3E,0x0C,0x02,0xE6,0x3E,0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x90, +0xE4,0x3E,0x24,0xFC,0xEB,0xEF,0xE4,0x3E,0x24,0xFC,0xE6,0x3E,0xE8,0x02,0xFD,0xB8, +0x8C,0x40,0xEB,0xE6,0xE8,0x88,0xF8,0x72,0x05,0xB0,0x18,0xE6,0x0A,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90, +0xE8,0x82,0xFD,0x75,0x06,0x32,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x02,0xE6,0xDA,0x36, +0xA0,0xB4,0x13,0x24,0x10,0x34,0x10,0xE8,0x16,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01, +0x00,0x74,0x05,0xE8,0xFC,0xFE,0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB, +0x02,0xB0,0x01,0xE8,0xDE,0xFE,0x36,0xA1,0xB4,0x13,0xE8,0xB5,0x13,0xE4,0x3C,0x24, +0xF8,0x0A,0xC4,0xE6,0x3C,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01,0x00,0xE8, +0xFA,0xFE,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x73,0xFB,0x32,0xC0,0x36,0x8A,0x26, +0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02,0xFE,0xC0, +0xE8,0xDB,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x71,0xF9,0x36,0xA1,0xB6, +0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0xD2,0xFA,0x36,0xA1,0xB6,0x13,0xC1,0xE8, +0x05,0x25,0x02,0x00,0xE8,0x1F,0xFB,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01,0x75,0x04, +0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8,0xC6,0xFA, +0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x83,0xFE,0xEB,0x03,0xE8,0x84,0xFE, +0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x65,0xFE,0xEB,0x03,0xE8,0x68,0xFE, +0xF8,0xC3,0xE4,0x12,0x0C,0x01,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFE,0xEB,0xF6, +0xE4,0x14,0x24,0xF0,0x0C,0x05,0xE6,0x14,0xE4,0x2A,0x24,0xF0,0x0C,0x06,0xE6,0x2A, +0xF8,0xC3,0xE4,0x2A,0x24,0xF0,0xE6,0x2A,0xE4,0x14,0x24,0xF0,0x0C,0x07,0xE6,0x14, +0xF8,0xC3,0xAD,0x49,0x49,0xE8,0x7E,0xF9,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49, +0x49,0xE8,0x72,0xF9,0x89,0x86,0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0xC2, +0xF7,0xF8,0xC3,0x90,0x83,0x66,0x26,0xFB,0xE8,0xB8,0xF7,0xF8,0xC3,0x90,0xAC,0x49, +0x84,0xC0,0x75,0x0D,0xE4,0x10,0x24,0xEF,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xF8, +0xC3,0xE4,0x10,0x0C,0x10,0xEB,0xF1,0x90,0xAC,0x49,0x3C,0x02,0x76,0x02,0x32,0xC0, +0xC0,0xE0,0x04,0xA8,0x20,0x74,0x02,0x0C,0x08,0x24,0x18,0x8A,0xE0,0xE4,0x12,0x24, +0xE7,0x0A,0xC4,0xE6,0x12,0x80,0x8E,0xA1,0x00,0x44,0xF8,0xC3,0xAC,0x49,0x88,0x86, +0xC0,0x00,0xF8,0xC3,0xAC,0x49,0xE6,0x3A,0xF8,0xC3,0xAC,0x49,0x84,0xC0,0x74,0x08, +0xE4,0x12,0x0C,0x04,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFB,0xEB,0xF6,0xAC,0x49, +0xE8,0xF0,0xF6,0x73,0x03,0xE8,0x41,0xF7,0xF8,0xC3,0xE4,0x12,0xA8,0x02,0x74,0x04, +0x24,0xFD,0xE6,0x12,0xB8,0xF0,0x00,0xE8,0xA1,0xFA,0x81,0x66,0x26,0xFF,0xF3,0xE8, +0x71,0xF7,0xE8,0xB4,0xFA,0xF8,0xC3,0x90,0xB8,0x80,0x00,0xE8,0x71,0xFA,0x80,0x4E, +0x27,0x08,0xE8,0x5E,0xF7,0xE8,0xA1,0xFA,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x7B,0xFA, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0x4B,0xF7,0xE8,0x8E,0xFA,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x4B,0xFA,0x80,0x4E,0x27,0x04,0xE8,0x38,0xF7,0xE8,0x7B,0xFA,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x55,0xFA,0x81,0x66,0x26,0xFF,0xFB,0xE8,0x25,0xF7,0xE8,0x68, +0xFA,0xF8,0xC3,0x90,0x33,0xC0,0xAC,0x49,0x3C,0x01,0x73,0x04,0xB0,0x01,0xEB,0x06, +0x3C,0x0C,0x76,0x02,0xB0,0x0C,0x89,0x46,0x1C,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00, +0x20,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x83, +0x4E,0x26,0x01,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8,0xC3,0x90,0xAC,0x49,0x50,0xE8,0x1F,0xF6, +0x58,0x72,0x08,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF8,0xC3,0xF9,0xC3,0x90,0xAC,0x50, +0xAD,0xE8,0x9C,0xF8,0x5A,0xF6,0xC2,0x01,0x74,0x12,0x39,0x86,0x96,0x00,0x74,0x0C, +0x89,0x86,0x96,0x00,0xE6,0xF0,0x86,0xE0,0xE6,0xF8,0x86,0xE0,0xF6,0xC2,0x02,0x74, +0x10,0x39,0x86,0x94,0x00,0x74,0x0A,0x89,0x86,0x94,0x00,0xE6,0xE4,0x86,0xE0,0xE6, +0xEC,0x83,0xE9,0x03,0xC3,0x90,0xE4,0x16,0x88,0x86,0xBC,0x00,0xE8,0x00,0xFB,0x33, +0xDB,0xE4,0x0C,0xA8,0x06,0x74,0x03,0x80,0xCB,0x01,0xA8,0x10,0x74,0x03,0x80,0xCB, +0x02,0xA8,0x80,0x74,0x03,0x80,0xCB,0x04,0xE4,0x12,0x8A,0xE0,0x24,0x18,0x0A,0xD8, +0xE4,0xDA,0xF6,0xC4,0x02,0x74,0x07,0xA8,0x40,0x75,0x03,0x80,0xCB,0x20,0xA8,0x02, +0x75,0x09,0xE4,0x2A,0xA8,0x0F,0x74,0x03,0x80,0xCB,0x40,0xF7,0x46,0x38,0x02,0x00, +0x74,0x09,0xE4,0xD8,0xA8,0x01,0x75,0x03,0x80,0xCB,0x80,0x88,0x9E,0xBE,0x00,0xFE, +0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x76,0xE4,0xF8,0xC3,0xAC,0x49,0x3C,0x02,0x74,0x41, +0x77,0x1F,0x50,0xE8,0x69,0xF5,0x58,0x72,0x0C,0x84,0xC0,0x74,0x0A,0xB0,0x12,0xE6, +0x0A,0x80,0x4E,0x38,0x01,0xF8,0xC3,0xB0,0x11,0xE6,0x0A,0x80,0x66,0x38,0xFE,0xF8, +0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9,0x00,0x04,0x75,0xE6,0x8A, +0x86,0xA5,0x00,0xA8,0x10,0x75,0xDE,0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8, +0xC3,0x81,0x4E,0x38,0x00,0x08,0x8A,0x86,0xA5,0x00,0xA8,0x10,0x74,0xC7,0x24,0xEF, +0xEB,0xE7,0xAD,0x49,0x49,0x3C,0x01,0x72,0x11,0x3C,0x0C,0x77,0x0D,0x50,0x8A,0xE0, +0xE4,0x14,0x25,0xF0,0x0F,0x0A,0xC4,0xE6,0x14,0x58,0x8A,0xC4,0x84,0xC0,0x74,0x02, +0xE6,0x42,0xF8,0xC3,0xE8,0xE9,0xF9,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xEE,0xE3, +0xF8,0xC3,0x3A,0x86,0xAF,0x00,0x74,0x1F,0x88,0x86,0xAF,0x00,0x8A,0xE0,0x80,0xC2, +0x06,0xB0,0xBF,0xEE,0x80,0xEA,0x02,0x8A,0xC4,0xEE,0x8A,0x86,0xA8,0x00,0x80,0xC2, +0x02,0xEE,0x80,0xEA,0x06,0x8A,0xC4,0xC3,0x8B,0x46,0x3E,0x85,0xC0,0x8A,0x86,0xA5, +0x00,0x74,0x12,0xA8,0x08,0x75,0x0D,0x0C,0x08,0x88,0x86,0xA5,0x00,0x80,0xC2,0x02, +0xEE,0x80,0xEA,0x02,0xC3,0xA8,0x08,0x74,0xFB,0x24,0xF7,0xEB,0xEC,0x8B,0x46,0x26, +0x84,0xC0,0x74,0x16,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x0D,0x24,0xFD,0x88,0x86, +0xA5,0x00,0x83,0xC2,0x02,0xEE,0x83,0xEA,0x02,0xC3,0x8A,0x86,0xA5,0x00,0xA8,0x02, +0x75,0xF7,0x0C,0x02,0xEB,0xE8,0x52,0x83,0xC2,0x0C,0xEC,0xC0,0xE8,0x04,0x88,0x86, +0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07,0xA8,0x08,0x75, +0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x02,0x75,0x03,0x80,0xCB,0x80, +0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0x84,0xDB,0x74,0x10,0xA8,0x02,0x74,0x0A,0x24, +0xFD,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x5A,0xC3,0xA8,0x02,0x75,0xFA,0x0C, +0x02,0xEB,0xEE,0x90,0xFF,0xFF,0x00,0x48,0x00,0x30,0xBA,0x20,0xC4,0x1A,0x00,0x18, +0x00,0x12,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x02,0x80,0x01,0xC0,0x00,0x60,0x00, +0x30,0x00,0x18,0x00,0xCD,0x01,0x00,0x01,0x80,0x00,0x10,0x00,0x10,0x00,0x0E,0x00, +0x0C,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x03,0x00,0x02,0x00, +0x01,0x00,0x52,0x51,0x56,0x3C,0x1E,0x77,0x47,0x98,0x8B,0xF0,0x8A,0x82,0xC4,0x00, +0x32,0xE4,0x83,0xFE,0x18,0x74,0x3D,0x83,0xFE,0x19,0x74,0x3E,0x83,0xFE,0x1E,0x77, +0x2F,0xD1,0xE6,0x2E,0x8B,0x8C,0x14,0x2D,0x3B,0x8E,0x94,0x00,0x74,0x22,0x89,0x8E, +0x94,0x00,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA, +0x06,0x8A,0xC1,0xEE,0x83,0xC2,0x02,0x8A,0xC5,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE, +0x5E,0x59,0x5A,0xC3,0x8B,0x8E,0x8E,0x00,0xEB,0xCE,0x8B,0x8E,0x90,0x00,0xEB,0xC8, +0x52,0x51,0x3D,0x05,0x00,0x77,0x03,0xB8,0x05,0x00,0x8B,0xC8,0xBA,0x02,0x00,0xB8, +0x00,0xD0,0xF7,0xF1,0x05,0x01,0x00,0xD1,0xE8,0x59,0x5A,0xC3,0x8B,0x46,0x7A,0xA8, +0x20,0x74,0x0B,0x80,0xBE,0xC3,0x00,0x03,0x75,0x04,0x0C,0x01,0xEB,0x02,0x24,0xFE, +0x89,0x46,0x7A,0xC3,0x24,0x03,0x88,0x86,0xC3,0x00,0x8A,0xA6,0xA8,0x00,0x8A,0xDC, +0x80,0xE4,0xFC,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2,0x06, +0xEE,0x83,0xEA,0x06,0xE8,0xC5,0xFF,0xC3,0x00,0x08,0x18,0x38,0x28,0x90,0x3C,0x04, +0x77,0x23,0x32,0xE4,0x8B,0xD8,0x2E,0x8A,0x87,0x08,0x2E,0x8A,0xA6,0xA8,0x00,0x8A, +0xDC,0x80,0xE4,0xC7,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2, +0x06,0xEE,0x83,0xEA,0x06,0xC3,0x84,0xC0,0x74,0x02,0xB0,0x04,0x8A,0xA6,0xA8,0x00, +0x8A,0xDC,0x80,0xE4,0xFB,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83, +0xC2,0x06,0xEE,0x83,0xEA,0x06,0xC3,0x90,0x8B,0x5E,0x38,0x84,0xC0,0x74,0x34,0x3C, +0x02,0x74,0x3B,0x8A,0x86,0xAF,0x00,0x0C,0x04,0xE8,0xE6,0xFD,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x1B,0xF7,0xC3,0x00,0x04,0x75,0x15,0x81,0xCB,0x00,0x04,0x83,0xC2, +0x02,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0x89, +0x5E,0x38,0xC3,0x8A,0x86,0xAF,0x00,0x24,0xFB,0xE8,0xB6,0xFD,0xEB,0xF1,0xF7,0xC3, +0x10,0x00,0x74,0xEF,0xEB,0xED,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8,0x04, +0x88,0x86,0xA9,0x00,0xC3,0x90,0x8A,0x86,0xA7,0x00,0x0C,0x01,0x88,0x86,0xA7,0x00, +0x8B,0xDA,0x80,0xC2,0x08,0xEE,0x8B,0xD3,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x24,0xFE, +0xEB,0xEA,0x8A,0x86,0xA7,0x00,0x0C,0x02,0xEB,0xE2,0x8A,0x86,0xA7,0x00,0x24,0xFD, +0xEB,0xDA,0xB0,0xFF,0xE8,0x6C,0xF2,0xE8,0xB1,0xF2,0xF8,0xC3,0xAC,0x49,0xE8,0x61, +0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xEB,0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x35, +0xFF,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x05,0xFF,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06, +0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x02,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE, +0x5A,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x06,0xEB, +0xE6,0x90,0xAC,0x49,0x3C,0x02,0x77,0x0D,0x84,0xC0,0x75,0x0B,0x8A,0x86,0xAF,0x00, +0x24,0xFD,0xE8,0x0D,0xFD,0xF8,0xC3,0x50,0x8A,0x86,0xAF,0x00,0x0C,0x02,0xE8,0x01, +0xFD,0x5B,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0xF6,0xC3,0x01,0x74,0x0C,0x24,0xDF, +0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x0C,0x20,0xEB,0xF2,0xAC,0x49, +0xE8,0xE5,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x83,0xF5,0xE8,0xF9,0xFC,0xE8, +0x24,0xFF,0xB0,0x01,0xE8,0xBF,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x8B,0xF5, +0xE8,0xE5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x63,0xF5,0xE8,0xD9,0xFC,0xE8, +0x04,0xFF,0xB0,0x08,0xE8,0x9F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x6B,0xF5, +0xE8,0xC5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x43,0xF5,0xE8,0xB9,0xFC,0xE8, +0xE4,0xFE,0xB0,0x02,0xE8,0x7F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x4B,0xF5, +0xE8,0xA5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x23,0xF5,0xE8,0x99,0xFC,0xE8, +0xC4,0xFE,0xB0,0x04,0xE8,0x5F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x2B,0xF5, +0xE8,0x85,0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x34,0x0C,0xF8,0xC3,0x90,0xB8,0xDE, +0x3B,0x89,0x46,0x7C,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0x0C,0x80,0xE8,0x43,0xFC,0xF8, +0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0x7F,0xEB,0xF2,0x8A,0x86,0xAF,0x00,0x0C,0x40, +0xE8,0x2F,0xFC,0xF8,0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0xBF,0xEB,0xF2,0xAC,0x49, +0xA8,0x01,0x74,0x07,0x83,0x4E,0x7A,0x20,0xEB,0x05,0x90,0x83,0x66,0x7A,0xDF,0xE8, +0x8A,0xFD,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x26,0x01, +0x83,0x4E,0x48,0x08,0xB0,0x06,0xE8,0xD5,0xDF,0x49,0x46,0xF9,0xC3,0x90,0x83,0xC2, +0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x06,0xAC, +0xB4,0x0A,0xF6,0xE4,0xEB,0xD0,0xE8,0xE0,0x0B,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89, +0x46,0x64,0xA9,0x01,0x00,0x74,0x19,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x0A,0xA9,0x04, +0x00,0x74,0x0D,0xB8,0xC4,0x3F,0xEB,0x0B,0xE8,0x06,0xF5,0xB8,0x8C,0x40,0xEB,0x03, +0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0xA8,0x02,0x74,0x0A, +0x24,0xFD,0xE8,0x8D,0xFB,0x0C,0x02,0xE8,0x88,0xFB,0xF8,0xC3,0xAC,0x49,0xE8,0x81, +0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x79,0xFC,0xF8,0xC3,0x90,0xE8,0x76,0xF5,0x75, +0x05,0xE8,0xE6,0xFD,0xF8,0xC3,0xE8,0xCD,0xFD,0x36,0xA0,0xB4,0x13,0x24,0x10,0x34, +0x10,0xE8,0x26,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01,0x00,0x74,0x05,0xE8,0xFE,0xFE, +0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB,0x02,0xB0,0x01,0xE8,0xE8,0xFE, +0x36,0xA1,0xB4,0x13,0xE8,0xAB,0x0B,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01, +0x00,0xE8,0x0C,0xFF,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x2B,0xFD,0x32,0xC0,0x36, +0x8A,0x26,0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02, +0xFE,0xC0,0xE8,0xEF,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x03,0xFC,0x36, +0xA1,0xB6,0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0x88,0xFC,0x36,0xA1,0xB6,0x13, +0xC1,0xE8,0x05,0x25,0x02,0x00,0xE8,0xCD,0xFC,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01, +0x75,0x04,0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8, +0x8C,0xFC,0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x8D,0xFE,0xEB,0x03,0xE8, +0x94,0xFE,0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x69,0xFE,0xEB,0x03,0xE8, +0x70,0xFE,0xF8,0xC3,0xF8,0xC3,0x8B,0x46,0x38,0xA9,0x04,0x00,0x75,0x23,0x0D,0x04, +0x00,0x89,0x46,0x38,0x83,0xC2,0x08,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0x73,0x14,0x83, +0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA, +0x08,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEB,0xEE,0x90,0x8B,0x46,0x38,0xA9, +0x04,0x00,0x74,0x06,0x25,0xFB,0xFF,0x89,0x46,0x38,0xF8,0xC3,0xAD,0x49,0x49,0xE8, +0xBE,0xFB,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49,0x49,0xE8,0xB2,0xFB,0x89,0x86, +0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0x92,0xFA,0xF8,0xC3,0x90,0x83,0x66, +0x26,0xFB,0xE8,0x88,0xFA,0xF8,0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x80,0x8E, +0xA3,0x00,0x04,0xF8,0xC3,0x80,0xA6,0xA3,0x00,0xFB,0xF8,0xC3,0xAC,0x49,0x83,0xC2, +0x08,0x3C,0x02,0x76,0x02,0x32,0xC0,0x3C,0x01,0x74,0x12,0x77,0x0B,0x8A,0x86,0xA7, +0x00,0x24,0xEF,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x8A,0x86,0xA7, +0x00,0x0C,0x10,0xEB,0xEE,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2, +0x04,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE,0x5A,0xF8,0xC3,0x90,0x52,0x83, +0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x08,0xEB,0xE6,0x90,0xAC,0x49,0xF8,0xC3, +0xAC,0x49,0xE8,0xCE,0xEE,0x73,0x03,0xE8,0x11,0xEF,0xF8,0xC3,0x8A,0x86,0xAF,0x00, +0x24,0x7F,0xE8,0xBD,0xF9,0xB8,0xF0,0x00,0xE8,0x80,0xF2,0x81,0x66,0x26,0xFF,0xF3, +0xE8,0x23,0xFA,0xE8,0xD2,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x51,0xF2,0x80,0x4E, +0x27,0x08,0xE8,0x11,0xFA,0xE8,0xC0,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x5B,0xF2, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0xFE,0xF9,0xE8,0xAD,0xF9,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x2B,0xF2,0x80,0x4E,0x27,0x04,0xE8,0xEB,0xF9,0xE8,0x9A,0xF9,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x19,0xF2,0x81,0x66,0x26,0xFF,0xFB,0xE8,0xD8,0xF9,0xF8,0xC3, +0xAC,0x49,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xF8,0xC3,0x90,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0xEB,0xEA,0x90,0xAC,0x49,0x8A,0xE0,0x80,0xC2,0x0A,0xEC,0x80,0xEA,0x0A,0xA8, +0x20,0x74,0x05,0x8A,0xC4,0xEE,0xF8,0xC3,0x06,0x51,0x57,0x8B,0x4E,0x24,0xE3,0x34, +0x49,0x89,0x4E,0x24,0xFF,0x46,0x1A,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0xC4,0xAA, +0x89,0x7E,0x22,0x8B,0x46,0x26,0x24,0xFD,0x89,0x46,0x26,0x75,0x29,0x8A,0x86,0xA5, +0x00,0xA8,0x02,0x75,0x21,0x80,0xC2,0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x80, +0xEA,0x02,0xEB,0x12,0xC4,0x7E,0x00,0x3B,0x7E,0x1E,0x76,0x0A,0x4F,0x26,0x88,0x25, +0x89,0x7E,0x00,0xFF,0x46,0x1A,0x5F,0x59,0x07,0xF8,0xC3,0x90,0xAC,0xAD,0x83,0xE9, +0x03,0x85,0xC0,0x74,0x05,0x3D,0x00,0x20,0x72,0x05,0xB8,0xFF,0xFF,0xEB,0x03,0xC1, +0xE0,0x03,0x3B,0x86,0x94,0x00,0x74,0x26,0x89,0x86,0x94,0x00,0x8B,0xD8,0x52,0x83, +0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA,0x06,0x8A,0xC3, +0xEE,0x83,0xC2,0x02,0x8A,0xC7,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE,0x5A,0xF8,0xC3, +0xB0,0x88,0x88,0x86,0xBC,0x00,0xE8,0xA6,0xF2,0x33,0xDB,0x8A,0x86,0xA5,0x00,0xA8, +0x02,0x74,0x03,0x80,0xCB,0x01,0xA8,0x05,0x74,0x03,0x80,0xCB,0x02,0xA8,0x08,0x74, +0x03,0x80,0xCB,0x04,0xF6,0x86,0xA7,0x00,0x10,0x74,0x03,0x80,0xCB,0x10,0x8A,0x86, +0xA9,0x00,0xF6,0xC3,0x04,0x75,0x0A,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0x86,0xAF,0x00,0xA8,0x80,0x74,0x08,0xF6,0xC4,0x01,0x75,0x03, +0x80,0xCB,0x20,0xF6,0x86,0xA7,0x00,0x02,0x75,0x0A,0xF7,0x46,0x38,0x04,0x00,0x74, +0x03,0x80,0xCB,0x40,0x88,0x9E,0xBE,0x00,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x0D, +0xDC,0xF8,0xC3,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x02,0xDC,0xF8,0xC3,0xAC,0x49, +0x3C,0x02,0x74,0x37,0x77,0x10,0x84,0xC0,0x74,0x06,0x80,0x4E,0x38,0x01,0xF8,0xC3, +0x80,0x66,0x38,0xFE,0xF8,0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9, +0x00,0x04,0x75,0xEA,0x8A,0x86,0xA5,0x00,0xA8,0x01,0x75,0xE2,0x0C,0x05,0x83,0xC2, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0xF8,0xC3,0x81,0x4E,0x38,0x00,0x08, +0x8A,0x86,0xA5,0x00,0xA8,0x01,0x74,0xC6,0x24,0xFA,0xEB,0xE2,0xAD,0x49,0x49,0xF8, +0xC3,0x90,0xE8,0x11,0xFA,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xA0,0xDB,0xF8,0xC3, +0xB0,0xFF,0xE8,0xD9,0xEC,0xF8,0xC3,0x90,0x83,0x66,0x7A,0xFB,0xB0,0x00,0xE8,0x8D, +0xDB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x6D,0xD9,0x72,0x11,0x36,0x88,0x1E,0x1A,0x01, +0x36,0xA0,0x8E,0x12,0x0A,0xC3,0x52,0xBA,0x00,0x01,0xEE,0x5A,0xF8,0xC3,0xAC,0x49, +0x32,0xE4,0x36,0xA3,0x86,0x12,0x05,0x06,0x00,0x36,0x8B,0x1E,0x88,0x12,0x2B,0xD8, +0x36,0x89,0x1E,0x8A,0x12,0xF8,0xC3,0x90,0xAD,0x8B,0xD8,0xAD,0x83,0xE9,0x04,0x03, +0xC3,0x2B,0x46,0x76,0x89,0x46,0x78,0xF7,0x46,0x7A,0x02,0x00,0x74,0x0A,0x83,0x66, +0x7A,0xFD,0xB8,0x00,0x00,0xE8,0x36,0xDB,0xF8,0xC3,0x06,0x16,0x07,0xAC,0x49,0x25, +0x0F,0x00,0x6B,0xC0,0x09,0x8D,0xBE,0xFD,0x00,0x03,0xF8,0xAC,0x49,0x25,0x0F,0x00, +0xAA,0x85,0xC0,0x74,0x08,0x2B,0xC8,0x51,0x8B,0xC8,0xF3,0xA4,0x59,0xE8,0x41,0xF0, +0xE8,0x44,0x03,0x07,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0xB2,0x13,0x36,0xA3, +0xB0,0x13,0xF8,0xC3,0x83,0x66,0x7A,0xEF,0xE8,0x2C,0x03,0xF8,0xC3,0x90,0x83,0x4E, +0x7A,0x10,0xEB,0xF4,0xE8,0xB5,0xF0,0xF8,0xC3,0x90,0xAD,0x3C,0x19,0x77,0x0E,0x3C, +0x19,0x77,0x0A,0x8B,0xF8,0x81,0xE7,0xFF,0x00,0x88,0xA6,0xC4,0x00,0xF8,0xC3,0x90, +0x83,0x4E,0x26,0x20,0xAC,0x49,0x32,0xE4,0xD1,0xE0,0x8B,0xD8,0xC1,0xE3,0x02,0x03, +0xC3,0x89,0x46,0x6E,0x83,0x4E,0x48,0x04,0xB0,0x06,0xE8,0xB1,0xDA,0x49,0x46,0xF9, +0xC3,0x90,0xFE,0x86,0xB3,0x00,0xB0,0x0A,0xE8,0xA3,0xDA,0xF8,0xC3,0x90,0x33,0xC0, +0xAC,0x49,0x6B,0xC0,0x0A,0x89,0x86,0x8A,0x00,0xF8,0xC3,0x90,0xAC,0x49,0x32,0xE4, +0x3D,0x0A,0x00,0x77,0x05,0xB8,0x0A,0x00,0xEB,0x08,0x3D,0x5A,0x00,0x72,0x03,0xB8, +0x5A,0x00,0x51,0xF7,0xD8,0x05,0x64,0x00,0x8B,0xC8,0x8B,0x46,0x44,0xF7,0xE1,0xB9, +0x64,0x00,0xF7,0xF1,0x89,0x46,0x46,0x59,0xF8,0xC3,0xAC,0x49,0xE8,0x9F,0xEB,0xF8, +0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x81,0x66,0x38,0xFF,0xFD,0xF8,0xC3,0x81, +0x4E,0x38,0x00,0x02,0xF7,0x46,0x38,0x40,0x00,0x75,0x08,0x8A,0x86,0xA9,0x00,0x88, +0x86,0xAA,0x00,0xF8,0xC3,0x90,0x51,0x56,0xE8,0x7F,0x0C,0x5E,0x59,0xF8,0xC3,0x90, +0xFE,0x86,0xB6,0x00,0xB0,0x0A,0xE8,0x25,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB7,0x00, +0xB0,0x0A,0xE8,0x19,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB8,0x00,0xB0,0x0A,0xE8,0x0D, +0xDA,0xF8,0xC3,0x90,0x00,0x90,0x51,0x55,0xAC,0x2E,0xA2,0x34,0x36,0x33,0xC9,0xAD, +0x8B,0xF9,0xC1,0xE7,0x05,0xA9,0x01,0x00,0x74,0x23,0x2E,0x8B,0xAD,0x44,0x00,0x83, +0x7E,0x08,0x00,0x74,0x18,0x2E,0x80,0x3E,0x34,0x36,0x01,0x74,0x09,0x60,0xB0,0x04, +0xE8,0xBB,0x0C,0x61,0xEB,0x07,0x60,0xB0,0xFB,0xE8,0xEC,0x0C,0x61,0x47,0x47,0xD1, +0xE8,0x75,0xD2,0x41,0x83,0xF9,0x04,0x72,0xC6,0x5D,0x59,0x83,0xE9,0x05,0xF7,0x46, +0x38,0x40,0x00,0x74,0x05,0xE8,0xA1,0xEA,0xF8,0xC3,0xE8,0xA7,0xEA,0xF8,0xC3,0x90, +0x36,0xC6,0x06,0xC8,0x13,0x01,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0x80,0x12, +0xAC,0x49,0x36,0x2B,0x06,0x88,0x12,0xF7,0xD8,0x36,0xA3,0x82,0x12,0xF8,0xC3,0x90, +0xC0,0x26,0xC0,0x26,0xCE,0x26,0xD4,0x26,0xDA,0x26,0xE0,0x26,0xE6,0x26,0xF0,0x26, +0xF8,0x26,0x00,0x27,0x08,0x27,0x10,0x27,0x16,0x27,0xA0,0x34,0xA8,0x34,0xB4,0x34, +0x1C,0x27,0x5A,0x27,0x62,0x27,0x76,0x27,0x82,0x27,0x96,0x27,0xA2,0x27,0xB6,0x27, +0xC2,0x27,0xD6,0x27,0xE2,0x27,0xF2,0x27,0xCE,0x34,0xC0,0x26,0x00,0x28,0x08,0x28, +0x0E,0x28,0x14,0x28,0x1A,0x28,0x30,0x28,0x6C,0x28,0xE8,0x34,0x0A,0x35,0x7A,0x28, +0xA0,0x28,0xB4,0x28,0xC0,0x28,0xC8,0x28,0x36,0x35,0x44,0x35,0x4E,0x35,0xD0,0x28, +0xA2,0x29,0xAA,0x29,0xB0,0x29,0xC2,0x29,0x54,0x35,0x5A,0x35,0xD2,0x29,0xDE,0x29, +0x70,0x35,0xEA,0x29,0xF4,0x29,0xFE,0x29,0x92,0x35,0x18,0x2A,0x9E,0x35,0x3C,0x2A, +0x44,0x2A,0x4A,0x2A,0xAC,0x35,0x5E,0x2A,0xDA,0x35,0x6A,0x2A,0x88,0x2A,0x9A,0x2A, +0xAE,0x2A,0xC0,0x2A,0xD4,0x2A,0xE2,0x35,0xEC,0x2A,0x06,0x2B,0x06,0x36,0x1A,0x2B, +0x2E,0x2B,0x66,0x2B,0x10,0x36,0x1C,0x36,0x28,0x36,0x36,0x36,0xCA,0x2B,0x90,0x36, +0x22,0x2C,0x44,0x2C,0x98,0x36,0x52,0x28,0xC0,0x26,0xC0,0x26,0xB6,0x2E,0xCA,0x2E, +0xD2,0x2E,0xDA,0x2E,0xE2,0x2E,0xEC,0x2E,0xF4,0x2E,0xFC,0x2E,0x04,0x2F,0x0C,0x2F, +0x24,0x2F,0xA0,0x34,0xA8,0x34,0xB4,0x34,0x32,0x2F,0x6E,0x2F,0x76,0x2F,0x8A,0x2F, +0x96,0x2F,0xAA,0x2F,0xB6,0x2F,0xCA,0x2F,0xD6,0x2F,0xEA,0x2F,0xF6,0x2F,0xFE,0x2F, +0xCE,0x34,0xC0,0x26,0x06,0x30,0x12,0x30,0x1A,0x30,0x26,0x30,0x2E,0x30,0x44,0x30, +0x86,0x30,0xE8,0x34,0x0A,0x35,0x8C,0x30,0xB0,0x30,0xB8,0x30,0xCC,0x30,0xD4,0x30, +0x36,0x35,0x44,0x35,0x4E,0x35,0xDC,0x30,0xA4,0x31,0xA4,0x31,0xA6,0x31,0xDC,0x31, +0x54,0x35,0x5A,0x35,0xEC,0x31,0xF8,0x31,0x70,0x35,0x04,0x32,0x0E,0x32,0x18,0x32, +0x92,0x35,0x2C,0x32,0x9E,0x35,0x56,0x32,0x6E,0x32,0x7C,0x32,0xAC,0x35,0x80,0x32, +0xDA,0x35,0x8C,0x32,0xA8,0x32,0xBA,0x32,0xCE,0x32,0xE0,0x32,0xF0,0x32,0xE2,0x35, +0xF4,0x32,0x08,0x33,0x06,0x36,0x14,0x33,0x7C,0x33,0xC0,0x33,0x10,0x36,0x1C,0x36, +0x28,0x36,0x36,0x36,0x3E,0x34,0x90,0x36,0x8C,0x34,0x92,0x34,0x98,0x36,0x6E,0x30, +0xE3,0x28,0xF7,0x46,0x38,0x40,0x00,0x75,0x32,0xE8,0xFD,0xE8,0x33,0xC0,0xAC,0x49, +0x3D,0x5B,0x00,0x77,0x19,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0xB0,0x36,0x72,0x0B, +0x85,0xC9,0x75,0xE8,0x8B,0x46,0x48,0xE8,0x1A,0x0C,0xC3,0x4E,0x41,0xC3,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x0C,0x9C,0x0E,0xE8,0x7D,0xDA,0xE8,0xD6,0xE8,0x33,0xC0, +0xAC,0x49,0x3D,0x5B,0x00,0x77,0xE7,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0x68,0x37, +0x72,0xD9,0x85,0xC9,0x75,0xE8,0xC3,0xF7,0x46,0x7A,0x10,0x00,0x75,0x0F,0x83,0xBE, +0x84,0x00,0x00,0x74,0x08,0xB8,0x2A,0x3A,0x89,0x86,0x80,0x00,0xC3,0x81,0xBE,0x80, +0x00,0xCE,0x3C,0x74,0xF7,0x83,0xBE,0x88,0x00,0x00,0x75,0x05,0xB8,0xCE,0x3C,0xEB, +0xE7,0xF7,0x46,0x7A,0x08,0x00,0x75,0x40,0x1E,0x60,0x8B,0x8E,0x88,0x00,0x3B,0x4E, +0x74,0x77,0x33,0x3B,0x4E,0x78,0x77,0x2E,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D, +0x47,0x47,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xF4,0x00,0x8B,0xC1,0xF7,0x46,0x7A,0x01, +0x00,0x75,0x1D,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46, +0x74,0xB0,0x0C,0xE8,0x58,0xD7,0x61,0x1F,0xC7,0x86,0x88,0x00,0x00,0x00,0xEB,0xAC, +0xE3,0xE3,0x50,0x90,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB,0xD8,0x90,0x8B,0x8E, +0x88,0x00,0xE3,0x46,0x8B,0x9E,0x8A,0x00,0x85,0xDB,0x74,0x3E,0xBA,0x50,0xFF,0xED, +0x2B,0x86,0x82,0x00,0x3B,0xC3,0x72,0x37,0x8D,0xB6,0xF4,0x00,0xC4,0x7E,0x10,0x8B, +0xDF,0x26,0x03,0x3D,0x47,0x47,0x8B,0xC1,0x16,0x1F,0xF7,0x46,0x7A,0x01,0x00,0x75, +0x24,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC7, +0x86,0x88,0x00,0x00,0x00,0xB0,0x0C,0xE8,0xF4,0xD6,0x83,0x66,0x7A,0xF7,0xC3,0xB0, +0x00,0xE8,0xEA,0xD6,0xC3,0xE3,0xDC,0x50,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB, +0xD2,0x90,0x1E,0x60,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xFD,0x00,0x8B,0x86,0x88,0x00, +0x8B,0x96,0x84,0x00,0x3A,0x04,0x75,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D,0xBE,0xF4, +0x00,0xF3,0xA6,0x74,0x66,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6,0x8D,0xB6, +0xFD,0x00,0x8B,0x96,0x84,0x00,0x3A,0x04,0x73,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D, +0xBE,0xF4,0x00,0xF3,0xA6,0x74,0x76,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6, +0x8D,0xB6,0xF4,0x00,0xAC,0xF7,0x46,0x7A,0x01,0x00,0x74,0x02,0x24,0x7F,0x1E,0xC5, +0x5E,0x10,0x8B,0x37,0x88,0x40,0x02,0x46,0x89,0x37,0xFF,0x4E,0x78,0xFF,0x46,0x76, +0xFF,0x4E,0x74,0x1F,0x8B,0x8E,0x88,0x00,0x49,0x89,0x8E,0x88,0x00,0xE3,0x43,0x8D, +0xB6,0xF4,0x00,0x8B,0xFE,0x46,0xF3,0xA4,0xE9,0x7D,0xFF,0xC5,0x76,0x10,0x8B,0x1C, +0x85,0xDB,0x74,0x08,0x03,0xF3,0x83,0xC6,0x03,0x83,0xE6,0xFE,0x8B,0x86,0x84,0x00, +0x2B,0xC2,0xB4,0x80,0x89,0x04,0x46,0x46,0xC7,0x04,0x00,0x00,0x89,0x76,0x10,0x83, +0x4E,0x7A,0x04,0xC7,0x86,0x88,0x00,0x00,0x00,0x61,0x1F,0xF9,0xC3,0x33,0xC0,0x61, +0x1F,0xC3,0xB0,0x80,0x84,0xC0,0x61,0x1F,0xC3,0x90,0x8B,0x4E,0x78,0x2B,0x8E,0x88, +0x00,0x76,0x27,0x89,0xB6,0x8C,0x00,0x8B,0x5E,0x74,0x3B,0xCB,0x72,0x02,0x8B,0xCB, +0x3B,0xC8,0x72,0x02,0x8B,0xC8,0x8B,0xC1,0xE3,0x44,0x33,0xD2,0x8E,0xC2,0x8B,0xD1, +0x83,0xBE,0x88,0x00,0x00,0x74,0x06,0xE9,0x8E,0x00,0x33,0xC0,0xC3,0x8B,0x5E,0x10, +0x03,0x1F,0x43,0x43,0x52,0xF7,0x46,0x7A,0x01,0x00,0x75,0x2A,0xAC,0x8D,0xBE,0xE4, +0x00,0x8B,0x8E,0x86,0x00,0xF2,0xAE,0x74,0x34,0x88,0x07,0x43,0x4A,0x75,0xED,0x58, +0x8B,0x5E,0x10,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x8B,0xC6, +0x2B,0x86,0x8C,0x00,0xC3,0x90,0xAC,0x8D,0xBE,0xE4,0x00,0x8B,0x8E,0x86,0x00,0xF2, +0xAE,0x74,0x0A,0x24,0x7F,0x88,0x07,0x43,0x4A,0x75,0xEB,0xEB,0xD2,0x88,0x86,0xF4, +0x00,0xC7,0x86,0x88,0x00,0x01,0x00,0x58,0x2B,0xC2,0x74,0x0E,0x8B,0x5E,0x10,0x01, +0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x40,0xE8,0x94,0xFE,0x72,0xBE, +0x4A,0x75,0x15,0x83,0xBE,0x8A,0x00,0x00,0x74,0xB4,0xBA,0x50,0xFF,0xED,0x89,0x86, +0x82,0x00,0x83,0x4E,0x7A,0x08,0xEB,0xA6,0x8D,0xBE,0xF4,0x00,0x03,0xBE,0x88,0x00, +0xA4,0xFF,0x86,0x88,0x00,0xE8,0x6A,0xFE,0x72,0x94,0x79,0x06,0x4A,0x74,0x8F,0xE9, +0x5B,0xFF,0x4A,0x74,0xCE,0xEB,0xE1,0x90,0x50,0xE8,0x2B,0xCC,0x8B,0x46,0x74,0x39, +0x46,0x72,0x74,0x27,0x1E,0x56,0x51,0x33,0xC9,0xC5,0x76,0x0C,0xAD,0x74,0x10,0x78, +0x09,0x03,0xC8,0x05,0x01,0x00,0x24,0xFE,0x03,0xF0,0x3B,0x76,0x10,0x76,0xED,0x29, +0x4E,0x76,0x01,0x4E,0x78,0xE8,0x51,0xCC,0x59,0x5E,0x1F,0x58,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x83,0xC3,0x03,0x26,0x89,0x1D,0x4B,0x03,0xFB,0xAB,0x91,0xAA, +0xB8,0x03,0x00,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x43,0x26,0x89,0x1D,0x43,0x03,0xFB,0xAA,0xFF,0x4E,0x78,0xFF, +0x46,0x76,0xFF,0x4E,0x74,0xC3,0xE8,0xE5,0xFF,0xC3,0x80,0x81,0x84,0x85,0x82,0x83, +0x86,0x87,0x50,0x53,0x8A,0xDC,0x83,0xE3,0x0E,0xD1,0xEB,0x2E,0x8A,0x87,0x7A,0x3B, +0x08,0x86,0xB0,0x00,0xFE,0x86,0xB1,0x00,0xB0,0x0A,0xE8,0xA1,0xD4,0x5B,0x58,0xC3, +0x50,0x8A,0xC8,0xB8,0xFF,0x00,0xE8,0x95,0xFF,0x58,0xC3,0x90,0x8A,0x86,0xBB,0x00, +0xE8,0xAB,0xFF,0xC3,0xE8,0xCB,0xFF,0xE8,0xF2,0xFF,0xC3,0x90,0xE8,0xC3,0xFF,0xE8, +0xB4,0xFF,0xC3,0x90,0x33,0xC0,0xE8,0x95,0xFF,0xC3,0xB8,0xFF,0x00,0x33,0xC9,0xE8, +0x6C,0xFF,0xC3,0x90,0xB8,0xFF,0x01,0xB1,0x10,0xE8,0x62,0xFF,0xC3,0x90,0xC3,0xDE, +0x3B,0xC4,0x3B,0xD4,0x3B,0xD4,0x3B,0xDE,0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE, +0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE,0x3B,0xC4,0x3B,0xC4,0x3B,0xC4,0x3B,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x51, +0x53,0x8B,0x4E,0x38,0x81,0xE1,0xFF,0xEE,0xA8,0x04,0x74,0x04,0x81,0xC9,0x00,0x01, +0x8A,0xE0,0x80,0xE4,0x03,0x24,0x18,0xD0,0xE4,0x0A,0xC4,0x33,0xDB,0x8A,0xD8,0x2E, +0x8B,0x87,0xDF,0x3B,0x89,0x46,0x7C,0x2E,0x0B,0x8F,0xFF,0x3B,0x89,0x4E,0x38,0xD1, +0xEB,0x2E,0x8A,0xA7,0x1F,0x3C,0x5B,0x59,0xC3,0xAC,0x49,0x3C,0x01,0x72,0x1D,0x74, +0x20,0x3C,0x03,0x72,0x23,0x74,0x28,0x3C,0x08,0x72,0x2B,0x74,0x30,0x3C,0x20,0x72, +0x37,0x74,0x3A,0xBB,0xBC,0x3B,0x32,0xE4,0x89,0x5E,0x7E,0xC3,0xBB,0x82,0x3B,0xEB, +0xF5,0xBB,0x76,0x3B,0xB4,0x01,0xEB,0xF0,0xBB,0xDE,0x3B,0xB4,0x02,0xEB,0xE9,0xBB, +0xC4,0x3B,0xB4,0x03,0xEB,0xE2,0xBB,0xA0,0x3B,0xB4,0x04,0xEB,0xDB,0xBB,0xAC,0x3B, +0xAC,0x49,0x88,0x86,0xBB,0x00,0xEB,0xCE,0xBB,0xB4,0x3B,0xEB,0xF3,0xBB,0xDE,0x3B, +0xEB,0xC4,0xA9,0x04,0x00,0x75,0xD1,0xA9,0x08,0x00,0x75,0xDA,0xEB,0xD1,0x8B,0x5E, +0x74,0x8B,0x4E,0x78,0x3B,0xCB,0x72,0x02,0x8B,0xCB,0x3B,0xC8,0x72,0x02,0x8B,0xC8, +0x8B,0xC1,0xE3,0x2C,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D,0x47,0x47,0xF7,0x46, +0x7A,0x01,0x00,0x75,0x1C,0xF7,0xC7,0x01,0x00,0x74,0x02,0x49,0xA4,0xD1,0xE9,0xF3, +0xA5,0x73,0x01,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74, +0xC3,0x50,0x53,0xBB,0x7F,0x7F,0xF7,0xC7,0x01,0x00,0x74,0x05,0x49,0xAC,0x22,0xC3, +0xAA,0xD1,0xE9,0xE3,0x1D,0x9C,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x14,0xAD,0x23,0xC3, +0xAB,0x49,0x74,0x0D,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x06,0xAD,0x23,0xC3,0xAB,0xE2, +0xE5,0x9D,0x73,0x04,0xAC,0x22,0xC3,0xAB,0x5B,0x58,0xEB,0xB8,0xE8,0xE8,0xC9,0x8B, +0x5E,0x38,0xF7,0xC3,0x10,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x40,0x00,0x74,0x05,0xE8, +0xD2,0xE3,0xEB,0x03,0xE8,0xC2,0xE3,0x81,0x66,0x38,0xEF,0xFB,0xF6,0xC3,0x10,0x74, +0x3C,0xF6,0xC3,0x02,0x74,0x06,0xE4,0xD8,0x0C,0x01,0xE6,0xD8,0xF6,0xC3,0x04,0x74, +0x11,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEE,0x88,0x86,0xA7,0x00,0x83, +0xEA,0x08,0xF6,0xC3,0x08,0x74,0x0F,0xE8,0xA5,0xE3,0x72,0x0A,0x8A,0x86,0xC0,0x00, +0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF7,0xC3,0x00,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x00, +0x08,0x75,0xF9,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0x40,0x75,0x0D,0xA8,0x10,0x75,0xEC, +0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x01,0x75,0xDF,0x83,0xC2,0x02, +0x0C,0x05,0xEE,0x88,0x86,0xA5,0x00,0xC3,0xB0,0x00,0xE8,0x61,0xD2,0xEB,0x0F,0xB0, +0x02,0xE8,0x90,0x0E,0xEB,0x08,0x83,0x66,0x38,0xDF,0x83,0x4E,0x7A,0x02,0x33,0xC0, +0x8E,0xD8,0xFA,0xA0,0x92,0x12,0x40,0xA2,0x92,0x12,0x3C,0x05,0x72,0x1E,0xC6,0x06, +0x92,0x12,0x00,0xFB,0xB0,0x01,0xE8,0x6B,0x0E,0xFA,0xA1,0x26,0x01,0x23,0x06,0x2A, +0x01,0xA8,0x01,0x75,0x07,0xE8,0xE2,0x07,0xE8,0x61,0x09,0x90,0xB0,0x00,0xE8,0x51, +0xD2,0xFB,0x85,0xED,0x74,0xB9,0xFA,0xF7,0x46,0x7A,0x46,0x00,0x75,0xC0,0x8B,0x46, +0x78,0x3D,0x0A,0x00,0x72,0xB0,0x8B,0x4E,0x74,0x83,0xF9,0x50,0x72,0x9A,0x83,0x66, +0x38,0xDF,0xC5,0x76,0x14,0x8B,0x46,0x3A,0x85,0xC0,0x75,0x58,0xAD,0x85,0xC0,0x75, +0x0F,0xE8,0xF8,0xFE,0xF7,0x46,0x7A,0x08,0x00,0x74,0x93,0xE8,0xA0,0xFA,0xEB,0x8E, +0x3B,0x76,0x04,0x76,0x21,0xB9,0x02,0x00,0x39,0x4E,0x2E,0x77,0x05,0xC7,0x46,0x2E, +0x00,0x00,0x56,0x8B,0x76,0x2C,0x89,0x76,0x04,0xC7,0x04,0x00,0x00,0x46,0x46,0x89, +0x76,0x2C,0x29,0x4E,0x2E,0x5E,0x85,0xC0,0x79,0x17,0xF6,0xC4,0x10,0x74,0x05,0xFF, +0x56,0x7C,0xEB,0x03,0xFF,0x56,0x7E,0x89,0x76,0x14,0xB0,0x0C,0xE8,0x9F,0xD1,0xEB, +0x86,0x89,0x46,0x3A,0xFF,0x96,0x80,0x00,0x29,0x46,0x3A,0x89,0x76,0x14,0xB0,0x0C, +0xE8,0x8B,0xD1,0xE9,0x71,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x04, +0x10,0x02,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0x80,0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x30,0x41,0x5A,0x41,0xB2,0x41,0xD6,0x41,0xE8,0x41, +0xFA,0x41,0xC3,0x90,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x80,0x66,0x27, +0xFD,0x8B,0x56,0x24,0x83,0xFA,0x04,0x72,0xE9,0x83,0xEA,0x02,0x8B,0xD9,0x3B,0xCA, +0x76,0x02,0x8B,0xCA,0xB0,0x0A,0x57,0x51,0x8B,0xFE,0xF2,0xAE,0x8B,0xC1,0x59,0x5F, +0x75,0x1E,0x50,0x40,0x2B,0xC8,0x74,0x06,0x2B,0xD1,0x2B,0xD9,0xF3,0xA4,0x59,0x4B, +0x4A,0x4A,0xB0,0x0D,0xAA,0xA4,0x3B,0xCA,0x76,0x02,0x8B,0xCA,0xE3,0x13,0xEB,0xD4, +0x2B,0xD9,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1,0xE9,0xF3,0xA5,0x73,0x01, +0xA4,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x29,0x7E,0x24,0x01,0x7E,0x1A,0x8B,0xCB,0x80, +0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8,0x18,0x03, +0x61,0xC3,0xC3,0x90,0xE8,0x7C,0x02,0x72,0xF9,0x90,0x83,0x4E,0x26,0x20,0x8B,0x46, +0x6A,0x89,0x46,0x6E,0x8B,0x46,0x48,0x0D,0x04,0x00,0x25,0xBF,0xFF,0x89,0x46,0x48, +0xB0,0x06,0xE8,0xD9,0xCF,0xC3,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A,0x29, +0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x05,0x83,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD, +0xE8,0xD5,0x02,0x61,0xC3,0x90,0x8A,0xBE,0xC2,0x00,0xEB,0x24,0xF7,0x46,0x48,0x40, +0x00,0x75,0xB1,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x8B,0x56,0x24,0x83, +0xEA,0x0A,0x78,0x9E,0x03,0xD7,0x80,0x66,0x27,0xFD,0x33,0xC0,0x8A,0xBE,0xC2,0x00, +0xE3,0xB4,0x3B,0xFA,0x77,0xB0,0xAC,0x49,0x93,0x2E,0x8A,0x87,0xB6,0x3E,0x93,0x22, +0xDF,0x75,0x17,0xAA,0xE3,0xA0,0x3B,0xFA,0x77,0x9C,0xAC,0x49,0x93,0x2E,0x8A,0x87, +0xB6,0x3E,0x93,0x22,0xDF,0x75,0x03,0xAA,0xEB,0xD6,0xF6,0xC3,0x7F,0x75,0x05,0xFF, +0x46,0x66,0xEB,0xDF,0xF6,0xC3,0x40,0x75,0x0C,0x8B,0xD8,0x83,0xEB,0x08,0xD1,0xE3, +0x2E,0xFF,0xA7,0xB6,0x3F,0xFF,0x46,0x66,0x2C,0x20,0xEB,0xC7,0x85,0xC0,0x74,0x2C, +0x89,0x46,0x6A,0x83,0x4E,0x48,0x40,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A, +0x29,0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x08,0x83,0x66,0x26,0xFD,0xE8,0xA3,0x01, +0xC3,0x60,0xB0,0xFD,0xE8,0x31,0x02,0x61,0xE8,0x98,0x01,0xC3,0xE9,0x57,0xFF,0x90, +0x8B,0x5E,0x66,0x4B,0x78,0x03,0x89,0x5E,0x66,0xAA,0x8B,0x5E,0x64,0xF7,0xC3,0x00, +0x20,0x75,0x03,0xE9,0x40,0xFF,0xF7,0xC3,0x40,0x00,0x74,0x08,0x8A,0x86,0xC1,0x00, +0xAA,0xE9,0x32,0xFF,0xB8,0x32,0x00,0xEB,0xA3,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x83,0xC3,0x08,0x80,0xE3,0xF8,0x89,0x5E,0x66,0x8B,0x5E,0x64,0x81,0xE3,0x00,0x18, +0x81,0xFB,0x00,0x18,0x74,0x2D,0xAA,0x85,0xDB,0x74,0x25,0xF7,0x46,0x64,0x40,0x00, +0x75,0x18,0x81,0xFB,0x00,0x10,0x74,0x0C,0x8B,0x46,0x66,0x2B,0x46,0x68,0xC1,0xE0, +0x04,0xE9,0x68,0xFF,0xB8,0x64,0x00,0xE9,0x62,0xFF,0x8A,0x86,0xC1,0x00,0xAA,0xAA, +0xE9,0xE3,0xFE,0x51,0x8B,0x4E,0x66,0x2B,0x4E,0x68,0xB0,0x20,0xF3,0xAA,0x59,0xE9, +0xD4,0xFE,0x8B,0x5E,0x66,0x89,0x5E,0x68,0x8B,0x5E,0x64,0xF7,0xC3,0x24,0x00,0x74, +0x10,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x04,0x00,0x74,0x05,0xB0,0x0D,0xAA,0xB0, +0x0A,0xAA,0xEB,0x48,0x90,0x90,0xAA,0xF7,0x46,0x64,0x00,0x40,0x74,0x06,0xB8,0xD0, +0x07,0xE9,0x18,0xFF,0xE9,0x9F,0xFE,0x90,0xAA,0xF7,0x46,0x64,0x00,0x80,0x74,0x06, +0xB8,0xD0,0x07,0xE9,0x06,0xFF,0xE9,0x8D,0xFE,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x85,0xDB,0x75,0x0C,0x8B,0x5E,0x64,0xF7,0xC3,0x10,0x00,0x74,0x06,0xE9,0x76,0xFE, +0x8B,0x5E,0x64,0xF7,0xC3,0x08,0x00,0x74,0x27,0xB0,0x0A,0xAA,0xF7,0xC3,0x20,0x00, +0x75,0x1F,0xF7,0xC3,0x00,0x01,0x75,0x03,0xE9,0x5B,0xFE,0xF7,0xC3,0x40,0x00,0x75, +0x06,0xB8,0x64,0x00,0xE9,0xC5,0xFE,0x8A,0x86,0xC1,0x00,0xAA,0xAA,0xE9,0x46,0xFE, +0xAA,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x00,0x06,0x74,0xF1,0xF7,0xC3,0x40,0x00, +0x74,0x19,0x8A,0x86,0xC1,0x00,0x81,0xE3,0x00,0x06,0x81,0xFB,0x00,0x04,0x72,0x06, +0x76,0x02,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xE9,0x1B,0xFE,0x81,0xE3,0x00,0x06,0x81, +0xFB,0x00,0x04,0x72,0x0E,0x76,0x06,0xB8,0x96,0x00,0xE9,0x7F,0xFE,0xB8,0x64,0x00, +0xE9,0x79,0xFE,0x8B,0x46,0x68,0xE9,0x73,0xFE,0x90,0x36,0x8B,0x0E,0xDA,0x12,0x83, +0xF9,0x32,0x73,0x1D,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8D,0x76,0x4C,0xBF, +0xDC,0x12,0x03,0xF9,0xA5,0xA5,0xA5,0x83,0xC1,0x06,0x89,0x0E,0xDA,0x12,0x07,0x1F, +0xC3,0xB0,0x08,0xE8,0x88,0xCD,0xC3,0x90,0x83,0x66,0x48,0xFE,0xE8,0xAD,0xC4,0xE8, +0xC8,0xFF,0xC3,0xF6,0x46,0x27,0x02,0x75,0x0F,0x9C,0xFA,0x83,0x7E,0x1A,0x00,0x74, +0x09,0x80,0x4E,0x27,0x01,0x9D,0xF9,0xC3,0xF8,0xC3,0x50,0x52,0xF7,0x46,0x38,0x40, +0x00,0x74,0x1D,0xE8,0x4E,0xDE,0x83,0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x27,0x83,0xEA, +0x08,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x5A,0x58,0xEB,0xD1, +0xE8,0x26,0xDE,0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6, +0x0C,0x5A,0x58,0xEB,0xBC,0x80,0x4E,0x27,0x02,0x5A,0x58,0x9D,0xF8,0xC3,0x08,0x46, +0x26,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38,0x40,0x00,0x75,0x14,0xF6,0xC1, +0x06,0x74,0x23,0xE8,0xF3,0xDD,0x8A,0xC1,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x9D,0xC3,0xF6,0xC1,0x02,0x74,0x0F,0xE8,0xEA,0xDD,0x83,0xC2,0x02,0x8A,0xC1,0x24, +0xFD,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x8B,0x5E,0x26,0x22,0xC3,0x88,0x46,0x26, +0x74,0x01,0xC3,0x80,0x66,0x27,0xFD,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38, +0x40,0x00,0x75,0x16,0xF6,0xC1,0x04,0x75,0x0F,0xE8,0xAD,0xDD,0x8A,0xC1,0x24,0xFD, +0x0C,0x04,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x9D,0xC3,0xF6,0xC1,0x02,0x75,0xF9,0xE8, +0xA2,0xDD,0x83,0xC2,0x0A,0xEC,0xA8,0x20,0x75,0x0E,0x83,0xEA,0x08,0x8A,0xC1,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x83,0xEA,0x0A,0x33,0xC9,0x8A,0x4E,0x1C, +0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1B,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0x1E, +0xC5,0x76,0x00,0xF3,0x6E,0x1F,0x89,0x76,0x00,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00, +0xEB,0xCD,0x85,0xC0,0x74,0x12,0x01,0x46,0x2A,0x8B,0xC8,0x1E,0xC5,0x76,0x00,0xF3, +0x6E,0x1F,0x89,0x76,0x00,0x89,0x4E,0x1A,0xF6,0xC7,0x01,0x75,0x23,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x22,0xC3,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE, +0x88,0x86,0xA5,0x00,0xF6,0xC7,0x10,0x75,0x05,0xB0,0x02,0xE8,0x30,0xCC,0x9D,0xC3, +0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0xEB,0x86,0x90,0x8B,0xD1,0x8B,0x46,0x24,0x3B, +0xC8,0x76,0x02,0x8B,0xC8,0x2B,0xD1,0x2B,0xC1,0x8B,0xD9,0xE3,0x22,0x80,0x66,0x27, +0xFD,0x8E,0x46,0x02,0x8B,0x7E,0x22,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1, +0xE9,0xF3,0xA5,0x73,0x01,0xA4,0x89,0x7E,0x22,0x89,0x46,0x24,0x01,0x5E,0x1A,0x8B, +0xCA,0x80,0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8, +0xF6,0xFE,0x61,0xC3,0x50,0xE4,0x0A,0x84,0xC0,0x75,0x0A,0x86,0x86,0xA1,0x00,0x84, +0xC0,0x74,0x0A,0xE6,0x0A,0x58,0x0C,0x20,0x89,0x46,0x48,0xF9,0xC3,0x58,0x24,0xDF, +0x89,0x46,0x48,0xF8,0xC3,0x90,0xFB,0xB0,0x02,0xE8,0xE8,0x07,0xFA,0xE8,0x2E,0x01, +0xFB,0xB0,0x01,0xE8,0xDE,0x07,0xFA,0xB0,0x02,0xE8,0xD6,0xCB,0xFB,0x85,0xED,0x74, +0xE5,0xFA,0x8E,0x5E,0x0A,0xFB,0x90,0xFA,0x8B,0x46,0x48,0x8B,0x76,0x40,0xA8,0x8C, +0x75,0xDE,0xA8,0x20,0x74,0x1A,0x50,0xE8,0x6F,0xDC,0x58,0xE8,0xA6,0xFF,0x73,0x10, +0xB0,0x02,0xE8,0x79,0xCB,0xEB,0xC9,0x90,0x25,0xFF,0x00,0x8B,0xC8,0xEB,0x36,0x90, +0xA8,0x01,0x75,0x22,0x46,0x83,0xE6,0xFE,0x3B,0x76,0x08,0x74,0x79,0xAD,0x8A,0xFC, +0xB3,0xF0,0x22,0xFB,0x3A,0xFB,0x74,0xE0,0x3A,0xBE,0xA0,0x00,0x74,0x2E,0xE8,0xD2, +0xFD,0x73,0x77,0xEB,0x9B,0x90,0x8A,0xE0,0x24,0xFC,0x88,0x46,0x48,0x8B,0x4E,0x4A, +0xF6,0xC4,0x02,0x74,0x1D,0xE8,0xBB,0xFD,0x72,0x86,0xE8,0x13,0xF3,0x89,0x76,0x40, +0xE3,0x93,0x83,0x4E,0x48,0x03,0x89,0x4E,0x4A,0xE9,0x74,0xFF,0x25,0xFF,0x0F,0x8B, +0xC8,0x90,0x8B,0x86,0x98,0x00,0x85,0xC0,0x74,0x1A,0x51,0x8A,0x8E,0xA0,0x00,0xC0, +0xE9,0x04,0xBA,0x01,0x00,0xD3,0xE2,0x59,0x23,0xC2,0x74,0x08,0x03,0xF1,0x89,0x76, +0x40,0xE9,0x61,0xFF,0xFF,0x56,0x62,0xE3,0xF5,0x83,0x4E,0x48,0x01,0x89,0x4E,0x4A, +0x89,0x76,0x40,0xE9,0x3A,0xFF,0x81,0x4E,0x26,0x00,0x10,0x8B,0x46,0x50,0x3B,0x46, +0x46,0x77,0x03,0xE8,0x52,0xFD,0xE9,0x27,0xFF,0x90,0x88,0xBE,0xA0,0x00,0xEB,0xAC, +0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06,0x01,0xB0,0x04,0xEE,0xEC,0x84,0xC0,0x75, +0x12,0xB0,0x04,0xEE,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80,0x74,0x06,0xC7,0x06,0x84, +0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06, +0x01,0xEC,0xA8,0x01,0x75,0xED,0xBA,0x08,0x01,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80, +0x74,0xE1,0xC7,0x06,0x84,0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x90,0x36,0xF7, +0x06,0x24,0x01,0x01,0x00,0x75,0x30,0x36,0x8B,0x0E,0xDA,0x12,0x80,0xF9,0x36,0x73, +0x26,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0xDC,0x12,0x03,0xF9,0xB0,0x08,0xE8,0x91, +0xCA,0x85,0xED,0x74,0x0E,0x8D,0x76,0x4C,0xA5,0xA5,0xA5,0x80,0xC1,0x06,0x80,0xF9, +0x36,0x72,0xE9,0x89,0x0E,0xDA,0x12,0xC3,0xC3,0x90,0xF7,0x06,0x26,0x01,0x01,0x00, +0x75,0xF6,0x8B,0x0E,0x20,0x13,0x85,0xC9,0x75,0xEE,0x33,0xC0,0x8E,0xC0,0x8E,0xD8, +0xBF,0x24,0x13,0xB9,0x36,0x00,0xB0,0x0A,0xE8,0x57,0xCA,0x85,0xED,0x75,0x06,0xE9, +0x12,0x01,0xE9,0x0A,0x01,0x33,0xDB,0x8A,0x46,0x4C,0x8A,0xA6,0xB3,0x00,0xFE,0xCC, +0x78,0x0E,0x88,0xA6,0xB3,0x00,0x0A,0xDC,0xB4,0x0A,0xAB,0x83,0xE9,0x02,0x76,0xE2, +0x8A,0xA6,0xB2,0x00,0xFE,0xCC,0x78,0x0E,0x88,0xA6,0xB2,0x00,0x0A,0xDC,0xB4,0x08, +0xAB,0x83,0xE9,0x02,0x76,0xCC,0x8A,0xA6,0xB1,0x00,0xFE,0xCC,0x78,0x18,0x8A,0xBE, +0xB0,0x00,0x75,0x04,0x88,0xA6,0xB0,0x00,0x88,0xA6,0xB1,0x00,0x0A,0xDC,0x8A,0xE7, +0xAB,0x83,0xE9,0x02,0x76,0xAC,0x8A,0xA6,0xB4,0x00,0xFE,0xCC,0x78,0x1F,0x88,0xA6, +0xB4,0x00,0x0A,0xDC,0xB4,0x0B,0xAB,0x8A,0x86,0xBC,0x00,0x8A,0xA6,0xBD,0x00,0xAB, +0x8B,0x86,0xBE,0x00,0xAB,0x83,0xE9,0x06,0x76,0x88,0x8A,0x46,0x4C,0x8A,0xA6,0xB6, +0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB6,0x00,0x0A,0xDC,0xB4,0x0C,0xAB,0xE8,0xF5, +0xCB,0xAB,0x8B,0x46,0x2A,0xAB,0x83,0xE9,0x06,0x76,0x74,0x8A,0x46,0x4C,0x8A,0xA6, +0xB7,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB7,0x00,0x0A,0xDC,0xB4,0x0D,0xAB,0xE8, +0xD4,0xCB,0xAB,0x8B,0x46,0x34,0xAB,0x83,0xE9,0x06,0x76,0x53,0x8A,0x46,0x4C,0x8A, +0xA6,0xB8,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB8,0x00,0x0A,0xDC,0xB4,0x0E,0xAB, +0xA1,0x50,0x12,0xAB,0xA1,0x52,0x12,0xAB,0x83,0xE9,0x06,0x76,0x32,0x8A,0x46,0x4C, +0x8A,0xA6,0xB5,0x00,0xFE,0xCC,0x78,0x18,0x88,0xA6,0xB5,0x00,0x0A,0xDC,0xB4,0x0F, +0xAB,0x8B,0x86,0x9A,0x00,0xAB,0x8B,0x86,0x9C,0x00,0xAB,0x83,0xE9,0x06,0x76,0x0F, +0x84,0xDB,0x75,0x03,0xE9,0xEF,0xFE,0xB0,0x0A,0xE8,0x12,0xC9,0xE9,0xE7,0xFE,0xB0, +0x0A,0xE8,0x0A,0xC9,0xF7,0xD9,0x83,0xC1,0x36,0x8B,0xC1,0x0D,0x80,0x00,0x86,0xC4, +0xA3,0x22,0x13,0x41,0x41,0x89,0x0E,0x20,0x13,0xC3,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x22,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x20,0x13, +0xF8,0xC3,0xF9,0xC3,0xC3,0x81,0xEF,0x6A,0x13,0x74,0xF9,0x8B,0xC7,0x0D,0x80,0x00, +0x86,0xC4,0xA3,0x68,0x13,0x47,0x47,0x89,0x3E,0x66,0x13,0xC3,0xF7,0x06,0x2A,0x01, +0x01,0x00,0x75,0xE0,0x8B,0x0E,0x66,0x13,0xE3,0x07,0x80,0xF9,0x20,0x77,0xD5,0x49, +0x49,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0x6A,0x13,0x8B,0xF7,0x03,0xF9,0x83,0xC6, +0x34,0x3B,0xFE,0x77,0xC0,0xB0,0x0E,0xE8,0xC8,0xC8,0x85,0xED,0x74,0xB7,0x8A,0x46, +0x4C,0x8A,0xB6,0xB9,0x00,0xFE,0xCE,0x78,0x15,0x88,0xB6,0xB9,0x00,0x8A,0xA6,0xA9, +0x00,0x80,0xCC,0xC0,0xAB,0x84,0xF6,0x74,0x05,0xB0,0x0E,0xE8,0x70,0xC8,0x8A,0xB6, +0xBA,0x00,0xFE,0xCE,0x78,0xCB,0x8A,0x9E,0xA9,0x00,0x8A,0xBE,0xAB,0x00,0x8A,0x56, +0x3F,0x8A,0xF3,0x32,0xF7,0x0A,0xB6,0xAC,0x00,0xC6,0x86,0xAC,0x00,0x00,0x22,0xF2, +0x74,0x4B,0xF6,0xC6,0x08,0x74,0x0F,0xB4,0x02,0xF6,0xC3,0x08,0x75,0x02,0xB4,0x03, +0xAB,0x80,0xE6,0xF7,0x74,0x37,0xF6,0xC6,0x01,0x74,0x0F,0xB4,0x00,0xF6,0xC3,0x01, +0x75,0x02,0xB4,0x01,0xAB,0x80,0xE6,0xFE,0x74,0x23,0xF6,0xC6,0x02,0x74,0x0F,0xB4, +0x04,0xF6,0xC3,0x02,0x75,0x02,0xB4,0x05,0xAB,0x80,0xE6,0xFD,0x74,0x0F,0xF6,0xC6, +0x04,0x74,0x0A,0xB4,0x06,0xF6,0xC3,0x04,0x75,0x02,0xB4,0x07,0xAB,0xC6,0x86,0xBA, +0x00,0x00,0x88,0x9E,0xAB,0x00,0xE9,0x58,0xFF,0x90,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x68,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x66,0x13, +0xF8,0xC3,0xF9,0xC3,0xA1,0x84,0x12,0x41,0x41,0x2B,0xC1,0x72,0x23,0xA3,0x84,0x12, +0x8B,0xC1,0x48,0x48,0x32,0xE4,0x0C,0x80,0x86,0xC4,0xEF,0x90,0x90,0x90,0x90,0x90, +0xBE,0xDC,0x12,0x49,0x49,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0xDA,0x12,0xF8,0xC3, +0xF9,0xC3,0x8A,0xC8,0x8A,0x46,0x4C,0xB4,0x01,0x83,0xEB,0x06,0xEF,0x90,0x90,0x90, +0x90,0x90,0xB8,0x01,0x00,0xEF,0x90,0x90,0x90,0x90,0x90,0x8A,0xC1,0xEF,0x90,0x90, +0x90,0x90,0x90,0xE9,0x97,0x00,0xE9,0xAC,0x00,0x33,0xC0,0x8E,0xD8,0x89,0x1E,0x84, +0x12,0xC3,0x36,0x8B,0x1E,0x84,0x12,0xFB,0x90,0xFA,0xB0,0x0C,0xE8,0xA3,0xC7,0x85, +0xED,0x74,0xE6,0xC5,0x76,0x0C,0x83,0xFB,0x14,0x72,0xDB,0xFB,0x90,0xFA,0xAD,0x85, +0xC0,0x78,0xAF,0x74,0xE2,0x8B,0xFE,0x03,0xF8,0x36,0x8B,0x0E,0x86,0x12,0x3B,0xC1, +0x77,0x02,0x8B,0xC8,0x83,0xEB,0x04,0x3B,0xD9,0x77,0x02,0x8B,0xCB,0x33,0xC0,0x8A, +0x46,0x4C,0xEF,0x90,0x90,0x90,0x90,0x90,0x8B,0xC1,0xEF,0x90,0x90,0x90,0x90,0x90, +0x41,0x80,0xE1,0xFE,0x2B,0xD9,0x51,0xD1,0xE9,0xF3,0x6F,0x90,0x59,0x8B,0xC7,0x40, +0x24,0xFE,0x3B,0xC6,0x74,0x27,0x2B,0xFE,0x4E,0x4E,0x53,0x8B,0x5E,0x10,0x3B,0xF3, +0x72,0x13,0x03,0x1F,0x83,0xC3,0x03,0x80,0xE3,0xFE,0xC7,0x07,0x00,0x00,0x83,0x6E, +0x74,0x02,0x89,0x5E,0x10,0x5B,0x89,0x3C,0x89,0x76,0x0C,0xEB,0x89,0x89,0x76,0x0C, +0x39,0x76,0x10,0x77,0x81,0x72,0x08,0x83,0x3C,0x00,0x74,0x03,0xE9,0x77,0xFF,0xE8, +0x27,0xBE,0xE9,0x62,0xFF,0x36,0x89,0x1E,0x84,0x12,0xB0,0x0C,0xE8,0xCF,0xC6,0x33, +0xC0,0x8E,0xD8,0xC3,0xA1,0x84,0x12,0x3D,0x10,0x00,0x72,0x77,0xBA,0x04,0x01,0x3B, +0x06,0x88,0x12,0x75,0x06,0xC7,0x06,0x7E,0x12,0x00,0x00,0x8B,0x0E,0xDA,0x12,0xE3, +0x0B,0xE8,0xD0,0xFE,0x72,0x57,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x20,0x13, +0xE3,0x0B,0xE8,0xA5,0xFD,0x72,0x46,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x66, +0x13,0xE3,0x0B,0xE8,0x94,0xFE,0x72,0x35,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0xA1,0x28, +0x01,0xA9,0x01,0x00,0x75,0x03,0xE8,0xF9,0xFE,0x80,0x3E,0x8D,0x12,0x00,0x75,0x1D, +0xA1,0x84,0x12,0x3D,0x20,0x00,0x76,0x15,0x3B,0x06,0x82,0x12,0x76,0x09,0xA1,0x7E, +0x12,0x3B,0x06,0x80,0x12,0x72,0x0C,0x80,0x0E,0x90,0x12,0x80,0xC3,0xB0,0x80,0xFF, +0x16,0x7C,0x12,0xC3,0x80,0x0E,0x90,0x12,0x40,0xC3,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x17,0x9C,0x0E,0xE8,0xD1,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x20,0x9C, +0x0E,0xE8,0xC4,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x16,0x9C,0x0E,0xE8,0xB7, +0xC8,0x90,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xCA,0xFB,0x90,0xFA,0xBA,0x04,0x01, +0xED,0x90,0x90,0x90,0x90,0x90,0x3A,0x06,0x94,0x12,0x77,0xBE,0x33,0xDB,0x8A,0xD8, +0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0xC4,0x7E,0x08,0x85,0xFF,0x74,0xB9,0xF6,0xC4, +0xC0,0x75,0x55,0x32,0xC0,0xC1,0xE0,0x02,0x80,0xE4,0xF0,0x8B,0xF0,0xED,0x90,0x90, +0x90,0x90,0x90,0x85,0xC0,0x74,0xBB,0x8B,0xC8,0x41,0x80,0xE1,0xFE,0x0B,0xC6,0x8B, +0x5E,0x50,0x4B,0x4B,0x2B,0xD9,0x78,0x9C,0xAB,0x8B,0xC1,0x40,0x40,0x01,0x46,0x4E, +0xD1,0xE9,0xF3,0x6D,0x90,0x89,0x5E,0x50,0x89,0x7E,0x08,0x8B,0x46,0x26,0x80,0xE4, +0xEF,0x89,0x46,0x26,0xF6,0xC4,0x01,0x75,0x0C,0xF7,0x46,0x48,0x0C,0x00,0x75,0x05, +0xB0,0x02,0xE8,0x99,0xC5,0xE9,0x7A,0xFF,0x86,0xC4,0x8B,0xC8,0x83,0xE1,0x3F,0x41, +0x80,0xE1,0xFE,0xE3,0x0A,0x3C,0x80,0x72,0x09,0x24,0x3F,0xB4,0xF0,0xEB,0xB0,0xE9, +0x60,0xFF,0x25,0x3F,0x00,0x33,0xFF,0x8E,0xC7,0xBF,0x96,0x12,0x8B,0xF7,0xD1,0xE9, +0xF3,0x6D,0x90,0x8B,0xC8,0xE8,0x48,0xED,0xE9,0x47,0xFF,0x90,0x6A,0x00,0x1F,0xC6, +0x06,0x93,0x12,0x1B,0x9C,0x0E,0xE8,0xEF,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E, +0xD8,0x8E,0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xB0,0x06,0xEE,0xEC,0xA2, +0x8C,0x12,0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12, +0x00,0xE8,0x60,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0x04,0xFF,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x1B,0x9C,0x0E,0xE8,0xA1,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E, +0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xBA,0x08,0x01,0xEC,0xA2,0x8C,0x12, +0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12,0x00,0xE8, +0x12,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0xB6,0xFE,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xEE,0x86,0xE0,0xEE,0x86,0xE0,0xEC,0x86, +0xE0,0xEC,0x86,0xE0,0x80,0xE1,0xFE,0xF3,0x6C,0x90,0x80,0xE1,0xFE,0xF3,0x6E,0x90, +0x05,0x00,0x57,0x47,0x8A,0x4B,0x05,0x00,0x57,0x48,0x8A,0x4B,0x05,0x00,0x85,0x48, +0x8A,0x4B,0x05,0x00,0x17,0x49,0x8A,0x4B,0x06,0x00,0x7A,0x48,0x78,0x4B,0x06,0x00, +0x9C,0x48,0x78,0x4B,0x06,0x00,0xA5,0x48,0x78,0x4B,0x06,0x00,0xAD,0x48,0x78,0x4B, +0x06,0x00,0x02,0x49,0x78,0x4B,0x06,0x00,0x0A,0x49,0x78,0x4B,0x06,0x00,0x30,0x4A, +0x7E,0x4B,0x06,0x00,0x5D,0x4A,0x7E,0x4B,0x05,0x00,0x80,0x4A,0x84,0x4B,0x05,0x00, +0xCE,0x4A,0x84,0x4B,0x00,0x00,0x1E,0x06,0x83,0x3E,0x44,0x12,0x00,0x74,0x09,0xA0, +0x06,0x01,0x24,0x30,0x3C,0x30,0x74,0x1A,0x8C,0xC8,0x8E,0xD8,0x8E,0xC0,0xBB,0x90, +0x4B,0x8B,0x0F,0xE3,0x0D,0x8B,0x7F,0x02,0x8B,0x77,0x04,0xF3,0xA4,0x83,0xC3,0x06, +0xEB,0xEF,0x07,0x1F,0xC3,0x90,0x33,0xC0,0xA3,0x3E,0x01,0xB9,0x0C,0x01,0xBE,0x40, +0x01,0x8B,0xFE,0x81,0xC6,0xB4,0x0F,0x89,0x04,0x8B,0xC6,0x2B,0xF1,0x3B,0xC7,0x77, +0xF6,0xA3,0x3C,0x01,0xC3,0x90,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x8B,0x5E, +0x00,0x3B,0xEB,0x74,0x2B,0x8B,0x76,0x02,0x89,0x1C,0x89,0x77,0x02,0x36,0xA1,0x3C, +0x01,0x89,0x46,0x00,0x36,0x89,0x2E,0x3C,0x01,0x8B,0xEB,0xFF,0x4E,0x06,0x74,0x08, +0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8,0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04, +0x61,0x07,0x1F,0xC3,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x98,0x89,0x46,0x06, +0x89,0x66,0x04,0x3B,0x6E,0x00,0x74,0x10,0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8, +0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04,0x61,0x07,0x1F,0xC3,0xC3,0x90,0x1E,0x06, +0x60,0x9C,0xFA,0x33,0xED,0x8E,0xDD,0x8B,0x2E,0x3C,0x01,0x85,0xED,0x74,0x3D,0x8B, +0x4E,0x00,0x89,0x0E,0x3C,0x01,0x8B,0xCC,0x8D,0xA6,0x0A,0x01,0x56,0x1E,0x06,0x60, +0x89,0x66,0x04,0xC7,0x46,0x08,0x0F,0x1A,0xC7,0x46,0x06,0x01,0x00,0x8B,0x1E,0x3E, +0x01,0x85,0xDB,0x74,0x1D,0x8B,0xC5,0x87,0x07,0x89,0x46,0x00,0x89,0x5E,0x02,0x8B, +0xD8,0x89,0x6F,0x02,0x8B,0xE1,0x9D,0x61,0x07,0x1F,0xF8,0xC3,0x9D,0x61,0x07,0x1F, +0xF9,0xC3,0x89,0x2E,0x3E,0x01,0x89,0x6E,0x00,0x89,0x6E,0x02,0x87,0xE1,0x9D,0x8B, +0xE1,0xEB,0xE4,0x00,0x0D,0x0A,0x54,0x65,0x72,0x6D,0x69,0x6E,0x61,0x6C,0x73,0x20, +0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x65,0x64,0x3A,0x0D,0x0A,0x31,0x29,0x20,0x41, +0x4E,0x53,0x49,0x20,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x6C,0x65,0x0D,0x0A, +0x32,0x29,0x20,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x0D,0x0A,0x50,0x6C,0x65,0x61, +0x73,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x3A,0x20,0x00,0x0D,0x0A,0x63,0x6F, +0x64,0x65,0x20,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x3D,0x00,0x0D,0x0A,0x4D,0x6F, +0x6E,0x69,0x74,0x6F,0x72,0x20,0x76,0x32,0x2E,0x35,0x0A,0x0D,0x0A,0x3E,0x00,0x0D, +0x0A,0x50,0x61,0x72,0x64,0x6F,0x6E,0x3F,0x00,0x0D,0x0A,0x4E,0x6F,0x20,0x61,0x64, +0x64,0x72,0x65,0x73,0x73,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x00, +0x0D,0x0A,0x3A,0x00,0x0D,0x0A,0x00,0x4C,0x6F,0x63,0x3D,0x00,0x0D,0x0A,0x46,0x41, +0x54,0x41,0x4C,0x20,0x45,0x52,0x52,0x4F,0x52,0x3D,0x00,0x0D,0x0A,0x4D,0x6F,0x6E, +0x69,0x74,0x6F,0x72,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x3A,0x2D,0x0D, +0x0A,0x20,0x20,0x20,0x44,0x2C,0x64,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78, +0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x6D,0x65,0x6D,0x6F, +0x72,0x79,0x0D,0x0A,0x20,0x20,0x20,0x4C,0x2C,0x6C,0x5B,0x5B,0x78,0x78,0x78,0x78, +0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x73, +0x69,0x6E,0x67,0x6C,0x65,0x20,0x6C,0x69,0x6E,0x65,0x0D,0x0A,0x20,0x20,0x20,0x45, +0x2C,0x65,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20, +0x2D,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x0D,0x0A,0x20, +0x20,0x20,0x46,0x2C,0x66,0x5B,0x5B,0x78,0x78,0x78,0x78,0x20,0x5D,0x78,0x78,0x78, +0x78,0x5D,0x20,0x2D,0x20,0x66,0x69,0x6C,0x6C,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79, +0x20,0x70,0x61,0x72,0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x0D,0x0A,0x20,0x20,0x20, +0x49,0x5B,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x2D,0x20,0x77,0x6F,0x72,0x64,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72, +0x6F,0x6D,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x69,0x5B,0x78,0x78, +0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62, +0x79,0x74,0x65,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,0x70, +0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x4F,0x78,0x78,0x78,0x78,0x20,0x78,0x78, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75, +0x74,0x20,0x77,0x6F,0x72,0x64,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A, +0x20,0x20,0x20,0x6F,0x78,0x78,0x78,0x78,0x20,0x78,0x78,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75,0x74,0x20,0x62,0x79,0x74, +0x65,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x47,0x5B, +0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x2D, +0x20,0x67,0x6F,0x74,0x6F,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x0D,0x0A,0x20, +0x20,0x20,0x57,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D, +0x20,0x20,0x20,0x2D,0x20,0x77,0x61,0x74,0x63,0x68,0x20,0x61,0x20,0x77,0x6F,0x72, +0x64,0x0D,0x0A,0x20,0x20,0x20,0x43,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69,0x6E,0x74,0x65,0x72,0x72,0x75, +0x70,0x74,0x73,0x20,0x6F,0x66,0x66,0x0D,0x0A,0x20,0x20,0x20,0x53,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69, +0x6E,0x74,0x65,0x72,0x72,0x75,0x70,0x74,0x73,0x20,0x6F,0x6E,0x0D,0x0A,0x20,0x20, +0x20,0x73,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x2D,0x20,0x73,0x69,0x6E,0x67,0x6C,0x65,0x20,0x73,0x74,0x65,0x70,0x0D, +0x0A,0x20,0x20,0x20,0x42,0x78,0x78,0x78,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65,0x61,0x6B,0x70,0x6F,0x69,0x6E, +0x74,0x20,0x73,0x65,0x74,0x0D,0x0A,0x20,0x20,0x20,0x62,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x20,0x63,0x6C,0x65,0x61,0x72,0x0D,0x0A,0x20, +0x20,0x20,0x52,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2D,0x20,0x72,0x65,0x73,0x74,0x61,0x72,0x74,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x0D,0x0A,0x20,0x20,0x20,0x72,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x72, +0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x20,0x61,0x74,0x20,0x62,0x72,0x6B,0x70, +0x74,0x0D,0x0A,0x20,0x20,0x20,0x58,0x2C,0x78,0x20,0x6E,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x65,0x78,0x61,0x6D,0x69,0x6E,0x65, +0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x6E,0x0D,0x0A,0x20,0x20,0x20,0x48, +0x2C,0x3F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2D,0x20,0x74,0x68,0x69,0x73,0x20,0x6D,0x65,0x73,0x73,0x61,0x67,0x65,0x00,0x1B, +0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x41,0x4E,0x53,0x49,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x0A,0x00,0x1B,0x5B,0x4B,0x00,0x1B,0x5B, +0x4A,0x00,0x1B,0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x00,0x1B,0x5B,0x44, +0x20,0x1B,0x5B,0x44,0x00,0x1B,0x5B,0x31,0x3B,0x37,0x32,0x48,0x00,0x1B,0x5B,0x00, +0x3B,0x00,0x48,0x00,0x1B,0x5B,0x73,0x00,0x1B,0x5B,0x75,0x00,0x1B,0x7A,0x2B,0x0B, +0x7F,0x1B,0x7A,0x2E,0x0C,0x7F,0x1B,0x7A,0x2D,0x08,0x7F,0x1B,0x7A,0x2C,0x0A,0x7F, +0x1B,0x7A,0x22,0x08,0x7F,0x1A,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x00,0x1B,0x54,0x00,0x1B,0x59,0x00,0x1A, +0x00,0x1E,0x00,0x08,0x20,0x08,0x00,0x00,0x1B,0x3D,0x00,0x00,0x00,0x1B,0x46,0x00, +0x0D,0x00,0x3F,0x44,0x64,0x45,0x65,0x46,0x66,0x47,0x67,0x48,0x68,0x49,0x69,0x4F, +0x6F,0x43,0x63,0x53,0x73,0x42,0x62,0x52,0x72,0x57,0x77,0x58,0x78,0x4C,0x6C,0x1E, +0x60,0xB6,0x57,0xB6,0x57,0x32,0x58,0x32,0x58,0xB8,0x59,0xB8,0x59,0x96,0x59,0x96, +0x59,0x1E,0x60,0x1E,0x60,0x4E,0x57,0x2A,0x57,0x08,0x57,0xE8,0x56,0x72,0x57,0x72, +0x57,0x7A,0x57,0x2A,0x5F,0xEE,0x5E,0x3A,0x5F,0x15,0x5F,0x22,0x5F,0x82,0x57,0x82, +0x57,0xE0,0x59,0xE0,0x59,0xBE,0x57,0xBE,0x57,0x6A,0x61,0x7A,0x61,0xA2,0x61,0xAE, +0x61,0xBA,0x61,0xD8,0x61,0xE4,0x61,0x04,0x62,0xDA,0x56,0x2C,0x62,0x3A,0x62,0x42, +0x59,0x20,0x20,0x66,0x6C,0x61,0x67,0x73,0x3D,0x00,0x20,0x20,0x61,0x78,0x3D,0x00, +0x20,0x20,0x62,0x78,0x3D,0x00,0x20,0x20,0x63,0x78,0x3D,0x00,0x20,0x20,0x64,0x78, +0x3D,0x00,0x20,0x20,0x63,0x73,0x3D,0x00,0x20,0x20,0x64,0x73,0x3D,0x00,0x20,0x20, +0x65,0x73,0x3D,0x00,0x20,0x20,0x73,0x73,0x3D,0x00,0x20,0x20,0x64,0x69,0x3D,0x00, +0x20,0x20,0x73,0x69,0x3D,0x00,0x20,0x20,0x62,0x70,0x3D,0x00,0x20,0x20,0x73,0x70, +0x3D,0x00,0x20,0x20,0x69,0x70,0x3D,0x00,0x20,0x63,0x68,0x61,0x6E,0x65,0x6C,0x3D, +0x00,0x20,0x20,0x20,0x20,0x73,0x65,0x67,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74, +0x72,0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x73,0x3D,0x00,0x20,0x74,0x69,0x5F, +0x6D,0x61,0x78,0x3D,0x00,0x20,0x74,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74, +0x69,0x5F,0x73,0x69,0x7A,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74,0x66,0x3D,0x00, +0x20,0x74,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x74,0x69,0x5F,0x66,0x6C,0x67, +0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x70, +0x63,0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x69, +0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x72,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20, +0x72,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x6D,0x69, +0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x66,0x6C,0x67,0x3D,0x00,0x20,0x72,0x69,0x5F, +0x74,0x6F,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x74,0x68,0x72,0x3D,0x00,0x20,0x74, +0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x74,0x72,0x3D,0x00, +0x20,0x74,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x69,0x7A, +0x3D,0x00,0x20,0x74,0x68,0x5F,0x74,0x72,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x66, +0x6C,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x63,0x6E,0x74,0x3D,0x00,0x20,0x72,0x68, +0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20, +0x72,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x68,0x5F,0x73,0x70,0x61,0x3D,0x00,0x20,0x72,0x68,0x5F,0x61,0x73, +0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F, +0x66,0x6C,0x67,0x3D,0x00,0x20,0x6D,0x5F,0x63,0x61,0x72,0x65,0x3D,0x00,0x20,0x70, +0x74,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x61,0x73,0x5F,0x66,0x6C,0x6F,0x3D,0x00, +0x20,0x72,0x6D,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x20,0x20,0x71,0x5F,0x69,0x6E, +0x3D,0x00,0x20,0x20,0x71,0x5F,0x6F,0x75,0x74,0x3D,0x00,0x20,0x71,0x5F,0x64,0x72, +0x61,0x6E,0x3D,0x00,0x20,0x20,0x71,0x5F,0x74,0x69,0x6D,0x3D,0x00,0x20,0x20,0x20, +0x71,0x5F,0x66,0x63,0x3D,0x00,0x20,0x71,0x5F,0x73,0x74,0x61,0x74,0x3D,0x00,0x20, +0x71,0x5F,0x64,0x61,0x74,0x61,0x3D,0x00,0x20,0x71,0x5F,0x6D,0x6F,0x64,0x6D,0x3D, +0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x6F,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F, +0x62,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x65,0x3D,0x00,0x20,0x68,0x61,0x6E, +0x64,0x5F,0x69,0x3D,0x00,0x20,0x20,0x6F,0x70,0x6F,0x73,0x74,0x3D,0x00,0x20,0x20, +0x74,0x69,0x6D,0x65,0x6F,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x31,0x3D,0x00, +0x20,0x63,0x75,0x73,0x74,0x6D,0x32,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x64, +0x3D,0x00,0x20,0x74,0x78,0x72,0x61,0x74,0x65,0x3D,0x00,0x20,0x72,0x78,0x72,0x61, +0x74,0x65,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x61,0x70,0x3D,0x00,0x20,0x63,0x5F, +0x61,0x64,0x64,0x72,0x3D,0x00,0x20,0x63,0x5F,0x61,0x69,0x73,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x78,0x74,0x61,0x67,0x3D,0x00,0x20,0x63,0x5F,0x64,0x65,0x66,0x72,0x3D, +0x00,0x20,0x63,0x5F,0x66,0x6C,0x73,0x68,0x3D,0x00,0x20,0x74,0x78,0x6D,0x61,0x78, +0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x65,0x6D,0x73,0x3D,0x00,0x20,0x20,0x63,0x5F, +0x6C,0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x65,0x72,0x3D,0x00,0x20,0x20, +0x63,0x5F,0x66,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x63,0x72,0x3D,0x00, +0x20,0x20,0x63,0x5F,0x6C,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x64,0x73,0x73, +0x3D,0x00,0x20,0x63,0x5F,0x64,0x73,0x73,0x69,0x3D,0x00,0x20,0x63,0x5F,0x64,0x73, +0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x73,0x72,0x3D,0x00,0x20,0x20,0x63, +0x5F,0x63,0x61,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x65,0x66,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x65,0x72,0x73,0x74,0x3D,0x00,0x20,0x63,0x5F,0x65,0x63,0x6E,0x74,0x3D, +0x00,0x20,0x63,0x5F,0x62,0x72,0x6B,0x63,0x3D,0x00,0x20,0x63,0x5F,0x62,0x6F,0x6B, +0x63,0x3D,0x00,0x20,0x63,0x5F,0x72,0x65,0x70,0x6C,0x3D,0x00,0x20,0x63,0x5F,0x63, +0x63,0x73,0x72,0x3D,0x00,0x20,0x63,0x5F,0x73,0x74,0x74,0x31,0x3D,0x00,0x20,0x63, +0x5F,0x73,0x74,0x74,0x32,0x3D,0x00,0x2B,0xC0,0x8E,0xD8,0x8E,0xC0,0xE8,0xC2,0x00, +0xE8,0xE5,0x00,0xFA,0xBF,0x84,0x00,0xC7,0x05,0xBE,0x56,0x8C,0x4D,0x02,0xBF,0x0C, +0x00,0xC7,0x05,0x50,0x5E,0x8C,0x4D,0x02,0xBF,0x04,0x00,0xC7,0x05,0x9C,0x5E,0x8C, +0x4D,0x02,0xE8,0xF1,0x00,0x90,0xE8,0x49,0x01,0xE8,0x16,0x00,0xF4,0x90,0xE8,0xE5, +0x00,0xBE,0x9C,0x4D,0xE8,0x09,0x0C,0xA0,0x93,0x12,0xE8,0x5D,0x0C,0xE8,0xC2,0x09, +0xEB,0xE4,0xE8,0xD5,0x0C,0xE8,0xC4,0x0C,0x0A,0xC0,0x74,0xF6,0x8B,0x1E,0xF8,0x79, +0x3C,0x0D,0x74,0x2E,0x3C,0x08,0x74,0x17,0x3C,0x7F,0x74,0x13,0x83,0xFB,0x20,0x7F, +0xE1,0x88,0x87,0xD6,0x79,0x43,0x89,0x1E,0xF8,0x79,0xE8,0x77,0x0C,0xEB,0xD3,0x0B, +0xDB,0x74,0xCF,0x4B,0x89,0x1E,0xF8,0x79,0x8B,0x36,0x16,0x7A,0xE8,0xC1,0x0B,0xEB, +0xC1,0x90,0xE8,0x02,0x00,0xEB,0xBB,0xC6,0x87,0xD6,0x79,0x00,0x0B,0xDB,0x74,0x1E, +0xA0,0xD6,0x79,0xBF,0x42,0x51,0xB9,0x1D,0x00,0x8B,0xD9,0x06,0x0E,0x07,0xF2,0xAE, +0x07,0x75,0x17,0x41,0x2B,0xD9,0xD1,0xE3,0x2E,0xFF,0x97,0x5F,0x51,0x90,0x33,0xC0, +0xA3,0xF8,0x79,0xBE,0x6B,0x4D,0xE8,0x87,0x0B,0xC3,0xBE,0x6F,0x4D,0xE8,0x80,0x0B, +0xEB,0xEC,0xBA,0x00,0x02,0xB0,0x93,0xEE,0xB0,0x55,0xEE,0xBA,0x10,0x02,0xB0,0x93, +0xEE,0xB0,0xAA,0xEE,0xBA,0x00,0x02,0xEC,0x3C,0x55,0x75,0x08,0xBA,0x10,0x02,0xEC, +0x3C,0xAA,0x74,0x03,0xE8,0x2F,0xF6,0xC3,0xBA,0x04,0x02,0xB0,0x1A,0xEE,0xB0,0x20, +0xEE,0xB0,0x30,0xEE,0xB0,0x40,0xEE,0xB0,0x80,0xEE,0xBA,0x00,0x02,0xB0,0x13,0xEE, +0xB0,0x07,0xEE,0xBA,0x08,0x02,0xB0,0x80,0xEE,0xBA,0x02,0x02,0xB0,0xBB,0xEE,0xBA, +0x04,0x02,0xB0,0x05,0xEE,0xC3,0xC6,0x06,0xCA,0x13,0x01,0xC7,0x06,0xF8,0x79,0x00, +0x00,0xC6,0x06,0xF6,0x79,0x01,0xC7,0x06,0xD0,0x79,0x00,0x00,0xC7,0x06,0xD2,0x79, +0x00,0x00,0xC7,0x06,0xD4,0x79,0x00,0x00,0xC7,0x06,0xFA,0x79,0x00,0x00,0xC7,0x06, +0xFC,0x79,0x00,0x00,0xC7,0x06,0xFE,0x79,0x00,0x00,0xC7,0x06,0x00,0x7A,0x00,0x00, +0xC7,0x06,0x02,0x7A,0xB0,0x59,0x8C,0x0E,0x04,0x7A,0xC7,0x06,0x06,0x7A,0x00,0x00, +0xC7,0x06,0x27,0x7A,0x00,0x00,0xC6,0x06,0x29,0x7A,0x00,0xC6,0x06,0x2A,0x7A,0x00, +0xC3,0x90,0xBE,0x04,0x4D,0xE8,0xC8,0x0A,0xE8,0x3F,0x00,0x2C,0x31,0x3C,0x01,0x77, +0xF7,0xE8,0x81,0x09,0x8B,0x36,0x0C,0x7A,0xE8,0xB5,0x0A,0xBE,0x4C,0x4D,0xE8,0xAF, +0x0A,0x0E,0x58,0xE8,0xF8,0x0A,0xBE,0x5C,0x4D,0xE8,0xA4,0x0A,0xC3,0x90,0x60,0xD1, +0xE3,0x83,0xFB,0x18,0x73,0x11,0x1E,0xBA,0x00,0x00,0x8E,0xDA,0x2E,0xFF,0x97,0x99, +0x51,0x8B,0xEC,0x89,0x46,0x10,0x1F,0x61,0xCF,0x90,0xE8,0x4F,0x0B,0x0A,0xC0,0x75, +0x05,0xE8,0x56,0x0B,0xEB,0xF4,0xC3,0x90,0x83,0x3E,0xF8,0x79,0x01,0x74,0x16,0xBE, +0xD7,0x79,0xE8,0x31,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x04,0x3C,0x20,0x75,0x05, +0xE8,0x23,0x0A,0xEE,0xC3,0xE9,0xD2,0xFE,0x83,0x3E,0xF8,0x79,0x01,0x74,0xF6,0xBE, +0xD7,0x79,0xE8,0x11,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x08,0x3C,0x20,0x74,0x04, +0xE9,0xB7,0xFE,0x90,0xE8,0xFF,0x09,0xEF,0xC3,0x90,0x8B,0x16,0x06,0x7A,0x83,0x3E, +0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xEB,0x09,0x8B,0xD0,0xA3,0x06,0x7A, +0xB0,0x20,0xE8,0x57,0x0B,0x8B,0x16,0x06,0x7A,0xEC,0xE8,0x6F,0x0B,0xC3,0x8B,0x16, +0x06,0x7A,0x83,0x3E,0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xC7,0x09,0x8B, +0xD0,0xA3,0x06,0x7A,0xB0,0x20,0xE8,0x33,0x0B,0x8B,0x16,0x06,0x7A,0xED,0xE8,0x67, +0x0B,0xC3,0xFA,0xC6,0x06,0xF6,0x79,0x00,0xC3,0x90,0xC6,0x06,0xF6,0x79,0x01,0xFB, +0xC3,0x90,0x06,0xE8,0x58,0x09,0xB0,0x20,0xE8,0x11,0x0B,0x26,0x8B,0x05,0xE8,0x47, +0x0B,0xB0,0x08,0xE8,0x06,0x0B,0xE8,0x03,0x0B,0xE8,0x00,0x0B,0xE8,0xFD,0x0A,0xB8, +0x01,0x00,0xE8,0xCF,0xF4,0xBA,0x02,0x02,0xEC,0x24,0x01,0x75,0x02,0xEB,0xDC,0xBA, +0x06,0x02,0xEC,0x07,0xC3,0x90,0xC7,0x06,0x08,0x7A,0x10,0x00,0xEB,0x06,0xC7,0x06, +0x08,0x7A,0x01,0x00,0x06,0x8E,0x06,0xFC,0x79,0x8B,0x3E,0xFA,0x79,0xE8,0x0E,0x09, +0xE8,0x0B,0x00,0x89,0x3E,0xFA,0x79,0x8C,0x06,0xFC,0x79,0x07,0xC3,0x90,0xBE,0x94, +0x4D,0xE8,0x7C,0x09,0x8B,0x16,0x08,0x7A,0x52,0xE8,0x2A,0x09,0xE8,0x0F,0x0A,0xE8, +0x0C,0x0A,0x33,0xDB,0xB9,0x10,0x00,0x90,0x26,0x8A,0x01,0xE8,0xBC,0x09,0xE8,0xFD, +0x09,0x43,0xE2,0xF4,0xE8,0xF7,0x09,0xE8,0xF4,0x09,0x33,0xDB,0xB9,0x10,0x00,0x90, +0x26,0x8A,0x01,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xE3, +0x09,0x43,0xE2,0xEC,0xBE,0x94,0x4D,0xE8,0x36,0x09,0x83,0xC7,0x10,0x5A,0x4A,0x75, +0xB7,0xC3,0x06,0x8E,0x06,0x00,0x7A,0x8B,0x3E,0xFE,0x79,0xE8,0xA0,0x08,0x89,0x3E, +0xFE,0x79,0x8C,0x06,0x00,0x7A,0x57,0x8B,0x36,0x0E,0x7A,0xE8,0x12,0x09,0xC7,0x06, +0x08,0x7A,0x10,0x00,0xBA,0x00,0x02,0xE8,0xE8,0x00,0xE8,0x81,0xFF,0x5F,0xBA,0x00, +0x00,0xE8,0xDE,0x00,0xBE,0x97,0x4D,0xE8,0xF6,0x08,0x8C,0xC0,0xE8,0x3F,0x09,0xB0, +0x3A,0xE8,0x90,0x09,0x8B,0xC7,0xE8,0x35,0x09,0xE8,0x7E,0x08,0xE8,0xC3,0x00,0x90, +0xE8,0xB7,0x09,0xE8,0xA6,0x09,0x0A,0xC0,0x74,0xF6,0x3C,0x0B,0x75,0x06,0x83,0xEF, +0x10,0xEB,0x19,0x90,0x3C,0x0A,0x75,0x06,0x83,0xC7,0x10,0xEB,0x0F,0x90,0x3C,0x0C, +0x75,0x04,0x47,0xEB,0x07,0x90,0x3C,0x08,0x75,0x24,0x4F,0x90,0x8B,0x36,0xFE,0x79, +0x8B,0xC7,0x2B,0xC6,0x3D,0x00,0x01,0x72,0xA5,0x3D,0x10,0x01,0x72,0x04,0x83,0xEE, +0x20,0x90,0x83,0xC6,0x10,0x89,0x36,0xFE,0x79,0x57,0x8B,0xFE,0xEB,0x80,0x3C,0x2E, +0x75,0x08,0xBA,0x01,0x13,0xE8,0x6A,0x00,0x07,0xC3,0xC6,0x06,0x0A,0x7A,0x02,0x32, +0xC9,0x90,0x3C,0x30,0x72,0x4C,0x3C,0x39,0x76,0x0C,0x24,0x5F,0x3C,0x41,0x72,0x42, +0x3C,0x46,0x77,0x3E,0x2C,0x07,0x2C,0x30,0x50,0xE8,0xCC,0x08,0x58,0x02,0xC8,0xFE, +0x0E,0x0A,0x7A,0x74,0x0F,0xC0,0xE1,0x04,0xE8,0x2F,0x09,0xE8,0x1E,0x09,0x0A,0xC0, +0x74,0xF6,0xEB,0xCE,0x26,0x88,0x0D,0xE8,0xE0,0x07,0x8A,0xD0,0xE8,0x23,0x00,0x8A, +0xC1,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xD5,0x08,0xE9, +0x70,0xFF,0xE8,0xC5,0x07,0xE8,0x0A,0x00,0x26,0x8A,0x05,0xE8,0x7C,0x08,0xE9,0x1D, +0xFF,0x90,0xF6,0x06,0x26,0x7A,0x02,0x75,0x02,0x86,0xF2,0x52,0x8B,0x36,0x1A,0x7A, +0xE8,0x0D,0x08,0x5A,0x52,0x8A,0xC6,0x02,0x06,0x24,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x9F,0x08,0xEB,0x0D,0x90,0x32,0xE4,0xE8,0x0D,0x08,0x8B,0x36,0x1C, +0x7A,0xE8,0xEC,0x07,0x5A,0x8A,0xC2,0x02,0x06,0x25,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x7F,0x08,0xEB,0x06,0x90,0x32,0xE4,0xE8,0xED,0x07,0x8B,0x36,0x1E, +0x7A,0xE8,0xCC,0x07,0xC3,0x90,0x06,0x8E,0x06,0x04,0x7A,0x8B,0x3E,0x02,0x7A,0xE8, +0x3C,0x07,0x89,0x3E,0x02,0x7A,0x8C,0x06,0x04,0x7A,0x07,0xFF,0x1E,0x02,0x7A,0xC3, +0xBE,0x79,0x4D,0xE8,0xAA,0x07,0xCB,0x90,0x06,0x57,0xBE,0xD7,0x79,0xE8,0x66,0x07, +0x8B,0xD8,0xE8,0x61,0x07,0x8B,0xC8,0x2B,0xCB,0x78,0x11,0x8E,0xC3,0xBF,0x00,0x00, +0xB8,0xFF,0xFF,0x51,0xB9,0x08,0x00,0xF3,0xAB,0x59,0xE2,0xF7,0x5F,0x07,0xC3,0x90, +0x06,0xBE,0xD7,0x79,0xE8,0x3F,0x07,0x8B,0xD8,0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00, +0xBE,0x08,0x52,0xE8,0xF1,0x08,0x8B,0xC3,0xE8,0xDD,0x08,0xB8,0x01,0x00,0xE8,0x73, +0xF2,0xE8,0xE0,0x08,0xBE,0x11,0x52,0xE8,0xDD,0x08,0x8B,0x47,0x18,0xE8,0xC8,0x08, +0xBE,0x59,0x52,0xE8,0xD1,0x08,0x8B,0x47,0x26,0xE8,0xBC,0x08,0xBE,0x35,0x52,0xE8, +0xC5,0x08,0x8B,0x47,0x1E,0xE8,0xB0,0x08,0xBE,0x3E,0x52,0xE8,0xB9,0x08,0x8B,0x47, +0x20,0xE8,0xA4,0x08,0xBE,0x50,0x52,0xE8,0xAD,0x08,0x8B,0x47,0x24,0xE8,0x98,0x08, +0xBE,0x62,0x52,0xE8,0xA1,0x08,0x8B,0x47,0x2A,0xE8,0x8C,0x08,0xE8,0x95,0x08,0xBE, +0x1A,0x52,0xE8,0x92,0x08,0x8B,0x07,0xE8,0x7E,0x08,0xBE,0x23,0x52,0xE8,0x87,0x08, +0x8B,0x47,0x1A,0xE8,0x72,0x08,0xBE,0x2C,0x52,0xE8,0x7B,0x08,0x8B,0x47,0x1C,0xE8, +0x66,0x08,0xBE,0x47,0x52,0xE8,0x6F,0x08,0x8B,0x47,0x22,0xE8,0x5A,0x08,0xE8,0x63, +0x08,0xBE,0xB3,0x52,0xE8,0x60,0x08,0x8B,0x47,0x38,0xE8,0x4B,0x08,0xBE,0x8F,0x52, +0xE8,0x54,0x08,0x8B,0x47,0x30,0xE8,0x3F,0x08,0xBE,0x98,0x52,0xE8,0x48,0x08,0x8B, +0x47,0x32,0xE8,0x33,0x08,0xBE,0x86,0x52,0xE8,0x3C,0x08,0x8B,0x47,0x2E,0xE8,0x27, +0x08,0xBE,0xA1,0x52,0xE8,0x30,0x08,0x8B,0x47,0x34,0xE8,0x1B,0x08,0xE8,0x24,0x08, +0xBE,0x6B,0x52,0xE8,0x21,0x08,0x8B,0x47,0x04,0xE8,0x0C,0x08,0xBE,0x74,0x52,0xE8, +0x15,0x08,0x8B,0x47,0x14,0xE8,0x00,0x08,0xBE,0x7D,0x52,0xE8,0x09,0x08,0x8B,0x47, +0x2C,0xE8,0xF4,0x07,0xBE,0xAA,0x52,0xE8,0xFD,0x07,0x8B,0x47,0x36,0xE8,0xE8,0x07, +0xBE,0xBC,0x52,0xE8,0xF1,0x07,0x8B,0x47,0x3A,0xE8,0xDC,0x07,0xBE,0xC5,0x52,0xE8, +0xE5,0x07,0x8B,0x47,0x3C,0xE8,0xD0,0x07,0xE8,0xD9,0x07,0xBE,0xFB,0x52,0xE8,0xD6, +0x07,0x8B,0x47,0x48,0xE8,0xC1,0x07,0xBE,0xE0,0x52,0xE8,0xCA,0x07,0x8B,0x47,0x42, +0xE8,0xB5,0x07,0xBE,0xE9,0x52,0xE8,0xBE,0x07,0x8B,0x47,0x44,0xE8,0xA9,0x07,0xBE, +0x5E,0x53,0xE8,0xB2,0x07,0x8B,0x47,0x4C,0xE8,0x9D,0x07,0xBE,0x67,0x53,0xE8,0xA6, +0x07,0x8B,0x47,0x4E,0xE8,0x91,0x07,0xBE,0x70,0x53,0xE8,0x9A,0x07,0x8B,0x47,0x50, +0xE8,0x85,0x07,0xE8,0x8E,0x07,0xBE,0x04,0x53,0xE8,0x8B,0x07,0x8B,0x47,0x4A,0xE8, +0x76,0x07,0xBE,0xCE,0x52,0xE8,0x7F,0x07,0x8B,0x47,0x08,0xE8,0x6A,0x07,0xBE,0xD7, +0x52,0xE8,0x73,0x07,0x8B,0x47,0x40,0xE8,0x5E,0x07,0xBE,0xF2,0x52,0xE8,0x67,0x07, +0x8B,0x47,0x46,0xE8,0x52,0x07,0xE8,0x5B,0x07,0xBE,0x4C,0x53,0xE8,0x58,0x07,0x8B, +0x47,0x7A,0xE8,0x43,0x07,0xBE,0x1F,0x53,0xE8,0x4C,0x07,0x8B,0x47,0x70,0xE8,0x37, +0x07,0xBE,0x28,0x53,0xE8,0x40,0x07,0x8B,0x47,0x72,0xE8,0x2B,0x07,0xBE,0x31,0x53, +0xE8,0x34,0x07,0x8B,0x47,0x74,0xE8,0x1F,0x07,0xE8,0x28,0x07,0xBE,0x0D,0x53,0xE8, +0x25,0x07,0x8B,0x47,0x0C,0xE8,0x10,0x07,0xBE,0x16,0x53,0xE8,0x19,0x07,0x8B,0x47, +0x10,0xE8,0x04,0x07,0xBE,0x3A,0x53,0xE8,0x0D,0x07,0x8B,0x47,0x76,0xE8,0xF8,0x06, +0xBE,0x43,0x53,0xE8,0x01,0x07,0x8B,0x47,0x78,0xE8,0xEC,0x06,0xBE,0x55,0x53,0xE8, +0xF5,0x06,0x8B,0x47,0x3E,0xE8,0xE0,0x06,0xE8,0xE9,0x06,0xBE,0x79,0x53,0xE8,0xE6, +0x06,0x8B,0x47,0x52,0xE8,0xD1,0x06,0xBE,0x82,0x53,0xE8,0xDA,0x06,0x8B,0x47,0x54, +0xE8,0xC5,0x06,0xBE,0x8B,0x53,0xE8,0xCE,0x06,0x8B,0x47,0x56,0xE8,0xB9,0x06,0xBE, +0x94,0x53,0xE8,0xC2,0x06,0x8B,0x47,0x58,0xE8,0xAD,0x06,0xBE,0x9D,0x53,0xE8,0xB6, +0x06,0x8B,0x47,0x5A,0xE8,0xA1,0x06,0xBE,0xA6,0x53,0xE8,0xAA,0x06,0x8B,0x47,0x5C, +0xE8,0x95,0x06,0xE8,0x9E,0x06,0xBE,0xAF,0x53,0xE8,0x9B,0x06,0x8B,0x47,0x5E,0xE8, +0x86,0x06,0xBE,0xB8,0x53,0xE8,0x8F,0x06,0x8B,0x47,0x60,0xE8,0x7A,0x06,0xBE,0xC1, +0x53,0xE8,0x83,0x06,0x8B,0x47,0x62,0xE8,0x6E,0x06,0xBE,0xCA,0x53,0xE8,0x77,0x06, +0x8B,0x47,0x7C,0xE8,0x62,0x06,0xBE,0xD3,0x53,0xE8,0x6B,0x06,0x8B,0x47,0x7E,0xE8, +0x56,0x06,0xBE,0xDC,0x53,0xE8,0x5F,0x06,0x8B,0x87,0x80,0x00,0xE8,0x49,0x06,0xE8, +0x52,0x06,0xBE,0x24,0x54,0xE8,0x4F,0x06,0x8B,0x87,0x9E,0x00,0xE8,0x39,0x06,0xBE, +0xE5,0x53,0xE8,0x42,0x06,0x8B,0x47,0x64,0xE8,0x2D,0x06,0xBE,0xEE,0x53,0xE8,0x36, +0x06,0x8B,0x47,0x6E,0xE8,0x21,0x06,0xBE,0xF7,0x53,0xE8,0x2A,0x06,0x8B,0x87,0x8E, +0x00,0xE8,0x14,0x06,0xBE,0x00,0x54,0xE8,0x1D,0x06,0x8B,0x87,0x90,0x00,0xE8,0x07, +0x06,0xBE,0x09,0x54,0xE8,0x10,0x06,0x8B,0x87,0x92,0x00,0xE8,0xFA,0x05,0xE8,0x03, +0x06,0xBE,0x12,0x54,0xE8,0x00,0x06,0x8B,0x87,0x94,0x00,0xE8,0xEA,0x05,0xBE,0x1B, +0x54,0xE8,0xF3,0x05,0x8B,0x87,0x96,0x00,0xE8,0xDD,0x05,0xBE,0x51,0x54,0xE8,0xE6, +0x05,0x8B,0x87,0x98,0x00,0xE8,0xD0,0x05,0xBE,0x3F,0x54,0xE8,0xD9,0x05,0x8A,0x87, +0xA0,0x00,0xE8,0xA7,0x05,0xBE,0x36,0x54,0xE8,0xCC,0x05,0x8A,0x47,0x28,0xE8,0x9B, +0x05,0xBE,0x48,0x54,0xE8,0xC0,0x05,0x8A,0x87,0xA1,0x00,0xE8,0x8E,0x05,0xE8,0xB3, +0x05,0xBE,0x5A,0x54,0xE8,0xB0,0x05,0x8A,0x87,0xA2,0x00,0xE8,0x7E,0x05,0xBE,0x63, +0x54,0xE8,0xA3,0x05,0x8A,0x87,0xA3,0x00,0xE8,0x71,0x05,0xBE,0x6C,0x54,0xE8,0x96, +0x05,0x8A,0x87,0xA4,0x00,0xE8,0x64,0x05,0xBE,0x75,0x54,0xE8,0x89,0x05,0x8A,0x87, +0xA5,0x00,0xE8,0x57,0x05,0xBE,0x7E,0x54,0xE8,0x7C,0x05,0x8A,0x87,0xA6,0x00,0xE8, +0x4A,0x05,0xBE,0x87,0x54,0xE8,0x6F,0x05,0x8A,0x87,0xA7,0x00,0xE8,0x3D,0x05,0xBE, +0x90,0x54,0xE8,0x62,0x05,0x8A,0x87,0xA8,0x00,0xE8,0x30,0x05,0xE8,0x55,0x05,0xBE, +0x99,0x54,0xE8,0x52,0x05,0x8A,0x87,0xA9,0x00,0xE8,0x20,0x05,0xBE,0xA2,0x54,0xE8, +0x45,0x05,0x8A,0x87,0xAA,0x00,0xE8,0x13,0x05,0xBE,0xAB,0x54,0xE8,0x38,0x05,0x8A, +0x87,0xAB,0x00,0xE8,0x06,0x05,0xBE,0xB4,0x54,0xE8,0x2B,0x05,0x8A,0x87,0xAD,0x00, +0xE8,0xF9,0x04,0xBE,0xBD,0x54,0xE8,0x1E,0x05,0x8A,0x87,0xAE,0x00,0xE8,0xEC,0x04, +0xBE,0xC6,0x54,0xE8,0x11,0x05,0x8A,0x87,0xAF,0x00,0xE8,0xDF,0x04,0xBE,0xCF,0x54, +0xE8,0x04,0x05,0x8A,0x87,0xB0,0x00,0xE8,0xD2,0x04,0xE8,0xF7,0x04,0xBE,0xD8,0x54, +0xE8,0xF4,0x04,0x8A,0x87,0xB1,0x00,0xE8,0xC2,0x04,0xBE,0xE1,0x54,0xE8,0xE7,0x04, +0x8A,0x87,0xB2,0x00,0xE8,0xB5,0x04,0xBE,0xEA,0x54,0xE8,0xDA,0x04,0x8A,0x87,0xB3, +0x00,0xE8,0xA8,0x04,0xBE,0xF3,0x54,0xE8,0xCD,0x04,0x8A,0x87,0xBB,0x00,0xE8,0x9B, +0x04,0xE8,0xC0,0x04,0xBE,0xFC,0x54,0xE8,0xBD,0x04,0x8A,0x87,0xBC,0x00,0xE8,0x8B, +0x04,0xBE,0x05,0x55,0xE8,0xB0,0x04,0x8A,0x87,0xBE,0x00,0xE8,0x7E,0x04,0xBE,0x0E, +0x55,0xE8,0xA3,0x04,0x8A,0x87,0xBF,0x00,0xE8,0x71,0x04,0xE8,0x96,0x04,0x07,0xC3, +0x60,0x06,0x1E,0x16,0x8B,0xEC,0xFF,0x4E,0x16,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01, +0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E,0xC0,0x89,0x2E,0x2D,0x7A,0xE8,0xCB,0x00,0x81, +0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8,0xD8,0x00,0xB8,0xE2,0x5E,0xA3, +0x2B,0x7A,0xE8,0x5D,0x00,0x80,0x3E,0x2A,0x7A,0x00,0x74,0x0A,0x81,0x4E,0x1A,0x00, +0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61,0xCF,0x90,0x60,0x06,0x1E,0x16, +0x8B,0xEC,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01,0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E, +0xC0,0x89,0x2E,0x2D,0x7A,0x81,0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8, +0x92,0x00,0xB8,0xE2,0x5E,0xA3,0x2B,0x7A,0xE8,0x17,0x00,0x80,0x3E,0x2A,0x7A,0x00, +0x74,0x0A,0x81,0x4E,0x1A,0x00,0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61, +0xCF,0x90,0xB8,0xF0,0x00,0xE8,0x8C,0xED,0xFF,0x26,0x2B,0x7A,0xC3,0x90,0x06,0x53, +0x56,0x80,0x3E,0x29,0x7A,0x00,0x74,0x03,0xE8,0x3F,0x00,0xBE,0xD7,0x79,0xE8,0x25, +0x02,0x8B,0xD8,0xA3,0x27,0x7A,0x2E,0x8A,0x07,0xA2,0x29,0x7A,0xB0,0xCC,0x2E,0x88, +0x07,0x5E,0x5B,0x07,0xC3,0xC6,0x06,0x2A,0x7A,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A, +0xC3,0x90,0x8B,0x2E,0x2D,0x7A,0xE8,0x2B,0x00,0xC3,0xC6,0x06,0x2A,0x7A,0x01,0xE8, +0x08,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A,0xC3,0x90,0x57,0x80,0x3E,0x29,0x7A,0x00, +0x74,0x0F,0x8B,0x3E,0x27,0x7A,0xA0,0x29,0x7A,0x2E,0x88,0x05,0xC6,0x06,0x29,0x7A, +0x00,0x5F,0xC3,0x90,0xBE,0x94,0x4D,0xE8,0x06,0x02,0xBE,0xBA,0x51,0xE8,0x00,0x02, +0xFF,0x76,0x14,0x58,0xE8,0x47,0x02,0xBE,0xC0,0x51,0xE8,0xF3,0x01,0xFF,0x76,0x0E, +0x58,0xE8,0x3A,0x02,0xBE,0xC6,0x51,0xE8,0xE6,0x01,0xFF,0x76,0x12,0x58,0xE8,0x2D, +0x02,0xBE,0xCC,0x51,0xE8,0xD9,0x01,0xFF,0x76,0x10,0x58,0xE8,0x20,0x02,0xBE,0xF6, +0x51,0xE8,0xCC,0x01,0xFF,0x76,0x0A,0x58,0xE8,0x13,0x02,0xBE,0xFC,0x51,0xE8,0xBF, +0x01,0xFF,0x76,0x0C,0x58,0xE8,0x06,0x02,0xBE,0xB1,0x51,0xE8,0xB2,0x01,0xFF,0x76, +0x1A,0x58,0xE8,0xF9,0x01,0xBE,0x94,0x4D,0xE8,0xA5,0x01,0xBE,0xD2,0x51,0xE8,0x9F, +0x01,0xFF,0x76,0x18,0x58,0xE8,0xE6,0x01,0xBE,0xD8,0x51,0xE8,0x92,0x01,0xFF,0x76, +0x02,0x58,0xE8,0xD9,0x01,0xBE,0xDE,0x51,0xE8,0x85,0x01,0xFF,0x76,0x04,0x58,0xE8, +0xCC,0x01,0xBE,0xE4,0x51,0xE8,0x78,0x01,0xFF,0x76,0x00,0x58,0xE8,0xBF,0x01,0xBE, +0xEA,0x51,0xE8,0x6B,0x01,0xFF,0x76,0x06,0x58,0xE8,0xB2,0x01,0xBE,0xF0,0x51,0xE8, +0x5E,0x01,0xFF,0x76,0x08,0x58,0xE8,0xA5,0x01,0xBE,0x02,0x52,0xE8,0x51,0x01,0xFF, +0x76,0x16,0x58,0xE8,0x98,0x01,0xBE,0x6B,0x4D,0xE8,0x44,0x01,0xC3,0x90,0xBE,0xAB, +0x4D,0xE8,0x3C,0x01,0xC3,0x3C,0x00,0x74,0x05,0x3C,0x01,0x74,0x59,0xC3,0xC7,0x06, +0x0C,0x7A,0xAF,0x50,0xC7,0x06,0x0E,0x7A,0xD2,0x50,0xC7,0x06,0x10,0x7A,0xCA,0x50, +0xC7,0x06,0x12,0x7A,0xCE,0x50,0xC7,0x06,0x14,0x7A,0xD6,0x50,0xC7,0x06,0x16,0x7A, +0xDD,0x50,0xC7,0x06,0x18,0x7A,0xE5,0x50,0xC7,0x06,0x1A,0x7A,0xED,0x50,0xC7,0x06, +0x1C,0x7A,0xF0,0x50,0xC7,0x06,0x1E,0x7A,0xF2,0x50,0xC7,0x06,0x20,0x7A,0xF4,0x50, +0xC7,0x06,0x22,0x7A,0xF8,0x50,0xC6,0x06,0x24,0x7A,0x01,0xC6,0x06,0x25,0x7A,0x01, +0xC6,0x06,0x26,0x7A,0x03,0xC3,0xC7,0x06,0x0C,0x7A,0xFC,0x50,0xC7,0x06,0x0E,0x7A, +0x2F,0x51,0xC7,0x06,0x10,0x7A,0x29,0x51,0xC7,0x06,0x12,0x7A,0x2C,0x51,0xC7,0x06, +0x14,0x7A,0x31,0x51,0xC7,0x06,0x16,0x7A,0x33,0x51,0xC7,0x06,0x18,0x7A,0x37,0x51, +0xC7,0x06,0x1A,0x7A,0x38,0x51,0xC7,0x06,0x1C,0x7A,0x3B,0x51,0xC7,0x06,0x1E,0x7A, +0x3C,0x51,0xC7,0x06,0x20,0x7A,0x3D,0x51,0xC7,0x06,0x22,0x7A,0x40,0x51,0xC6,0x06, +0x24,0x7A,0x20,0xC6,0x06,0x25,0x7A,0x20,0xC6,0x06,0x26,0x7A,0x02,0xC3,0xA1,0xF8, +0x79,0x48,0x74,0x14,0xBE,0xD7,0x79,0xE8,0x3C,0x00,0x8B,0xF8,0xAC,0x3C,0x3A,0x75, +0x07,0x8E,0xC7,0xE8,0x30,0x00,0x8B,0xF8,0xC3,0x90,0x8B,0xC7,0x2B,0x06,0xFE,0x79, +0x8A,0xF0,0x24,0x0F,0x8A,0xD0,0x02,0xD0,0x02,0xD0,0x80,0xC2,0x0B,0xC0,0xEE,0x04, +0x80,0xC6,0x03,0x04,0x3D,0xC3,0x8C,0xC0,0xE8,0x93,0x00,0xB0,0x3A,0xE8,0xE4,0x00, +0x8B,0xC7,0xE8,0x89,0x00,0xC3,0x51,0x33,0xC9,0x90,0xAC,0x3C,0x20,0x74,0xFB,0x90, +0x0A,0xC0,0x74,0x26,0x2C,0x30,0x72,0x22,0x3C,0x09,0x76,0x14,0x3C,0x11,0x72,0x1A, +0x2C,0x07,0x3C,0x0F,0x76,0x0A,0x3C,0x2A,0x72,0x10,0x2C,0x20,0x3C,0x0F,0x77,0x0A, +0x98,0xC1,0xE1,0x04,0x03,0xC8,0xAC,0xEB,0xD7,0x90,0x4E,0x8B,0xC1,0x59,0xC3,0x90, +0x06,0x8C,0xC8,0x8E,0xC0,0xE8,0x02,0x00,0x07,0xC3,0x26,0x8A,0x04,0x46,0x0A,0xC0, +0x74,0x06,0xE8,0x8F,0x00,0xEB,0xF3,0x90,0xC3,0x90,0x0B,0xC0,0x74,0x7A,0x51,0x33, +0xD2,0xB9,0xE8,0x03,0xF7,0xF1,0x8B,0xCA,0xE8,0x03,0x00,0x8B,0xC1,0x59,0xBA,0x64, +0x00,0xF6,0xF2,0xE8,0x0C,0x00,0x8A,0xC4,0x98,0xB2,0x0A,0xF6,0xF2,0xE8,0x02,0x00, +0x8A,0xC4,0x50,0x0A,0xF0,0x74,0x05,0x04,0x30,0xE8,0x58,0x00,0x58,0xC3,0x86,0xC4, +0xE8,0x07,0x00,0x86,0xC4,0xE8,0x02,0x00,0xC3,0x90,0xC1,0xC8,0x04,0xE8,0x08,0x00, +0xC1,0xC0,0x04,0xE8,0x02,0x00,0xC3,0x90,0x53,0x50,0x24,0x0F,0xBB,0xAC,0x62,0x2E, +0xD7,0xE8,0x30,0x00,0x58,0x5B,0xC3,0x90,0x86,0xC4,0xE8,0x07,0x00,0x86,0xC4,0xE8, +0x02,0x00,0xC3,0x90,0x50,0xB9,0x08,0x00,0x8A,0xE0,0x32,0xC0,0xD1,0xC0,0x04,0x30, +0xE8,0x11,0x00,0xE2,0xF5,0x58,0xC3,0x90,0xB0,0x30,0xE8,0x07,0x00,0xC3,0xB0,0x20, +0xE8,0x01,0x00,0xC3,0x56,0x8B,0x36,0xD0,0x79,0x88,0x84,0xD0,0x77,0x46,0x81,0xE6, +0xFF,0x01,0xFF,0x06,0xD4,0x79,0x89,0x36,0xD0,0x79,0x81,0x3E,0xD4,0x79,0xFE,0x01, +0x75,0x08,0x56,0xE8,0x14,0x00,0x5E,0xEB,0xF1,0x90,0x5E,0xC3,0xBA,0x02,0x02,0xEC, +0x24,0x01,0x74,0x04,0xBA,0x06,0x02,0xEC,0xC3,0x90,0x80,0x3E,0xF6,0x79,0x00,0x74, +0x09,0x60,0xB8,0x01,0x00,0xE8,0x2C,0xEA,0x61,0x90,0xBA,0x02,0x02,0xEC,0xA8,0x04, +0x74,0x28,0x8B,0x36,0xD2,0x79,0x83,0x3E,0xD4,0x79,0x00,0x74,0x1D,0x8A,0x84,0xD0, +0x77,0x46,0x81,0xE6,0xFF,0x01,0x89,0x36,0xD2,0x79,0xFF,0x0E,0xD4,0x79,0xBA,0x06, +0x02,0xEE,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x75,0xDC,0xA1,0xD4,0x79,0xC3,0x52,0xBA, +0x06,0x02,0xEE,0x5A,0xC3,0x90,0x52,0x50,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x74,0x08, +0x58,0x5A,0xE8,0xE9,0xFF,0xF9,0xC3,0x90,0x58,0x5A,0xF8,0xC3,0x52,0x50,0xBA,0x02, +0x02,0xEC,0xA8,0x04,0x74,0xFB,0x58,0x5A,0xE8,0xD3,0xFF,0xC3,0x30,0x31,0x32,0x33, +0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46,0x53,0x50,0x8A,0xE0, +0x80,0xE4,0x0F,0xBB,0xAC,0x62,0xC0,0xE8,0x04,0x2E,0xD7,0xE8,0xCE,0xFF,0x8A,0xC4, +0x2E,0xD7,0xE8,0xC7,0xFF,0x58,0x5B,0xC3,0x86,0xE0,0xE8,0xDF,0xFF,0x86,0xE0,0xE8, +0xDA,0xFF,0xC3,0x90,0xBE,0x94,0x4D,0x50,0x2E,0xAC,0x3C,0x00,0x74,0x05,0xE8,0xAB, +0xFF,0xEB,0xF5,0x58,0xC3,0x90,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0xBF, +0x04,0x00,0xC7,0x46,0xFC,0x00,0x00,0xC7,0x46,0xFA,0x00,0x00,0xC7,0x46,0xF8,0x00, +0x00,0x83,0x7E,0x06,0x00,0x75,0x0E,0x56,0xE8,0xB6,0x0E,0x59,0x0B,0xC0,0x75,0x05, +0x8B,0xC7,0xE9,0x5B,0x01,0x8B,0x46,0xFC,0x89,0x46,0xFE,0x0B,0xFF,0x75,0x05,0xB8, +0x01,0x00,0xEB,0x02,0x33,0xC0,0x50,0x56,0xE8,0xA4,0x0D,0x59,0x59,0xB4,0x00,0x89, +0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xFB,0x08,0x76,0x03,0xE9,0x2B,0x01,0xD1,0xE3,0x2E, +0xFF,0xA7,0x94,0x64,0xB8,0x03,0x00,0xE9,0x26,0x01,0x83,0x7E,0xFA,0x00,0x74,0x14, +0xC7,0x46,0xFA,0x00,0x00,0x8A,0x44,0x58,0x98,0x50,0x8A,0x44,0x59,0x98,0x50,0xE8, +0xC2,0x0F,0x59,0x59,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56, +0xE8,0x9B,0x08,0x59,0x83,0x7E,0x06,0x00,0x75,0x05,0x8B,0xC7,0xE9,0xF1,0x00,0x83, +0xFF,0x04,0x75,0x03,0xE9,0xE6,0x00,0x8B,0xC7,0xE9,0xE4,0x00,0x83,0x7E,0xFE,0x00, +0x75,0x03,0xBF,0x02,0x00,0xE9,0xD5,0x00,0x83,0x7E,0xFE,0x00,0x75,0x03,0xBF,0x01, +0x00,0xE9,0xC9,0x00,0x8B,0x5E,0xFE,0x83,0xFB,0x07,0x76,0x03,0xE9,0x86,0x00,0xD1, +0xE3,0x2E,0xFF,0xA7,0x84,0x64,0x33,0xFF,0xE9,0x7F,0x00,0xBF,0x04,0x00,0x80,0x7C, +0x58,0x0F,0x74,0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x44,0x58,0x6A,0x08,0x56, +0xE8,0x7E,0x0C,0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x72,0x0C,0x59, +0x59,0xC7,0x46,0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00, +0x00,0x56,0xE8,0x19,0x08,0x59,0xEB,0x42,0xBF,0x04,0x00,0x80,0x7C,0x58,0x00,0x74, +0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x4C,0x58,0x6A,0x08,0x56,0xE8,0x41,0x0C, +0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x35,0x0C,0x59,0x59,0xC7,0x46, +0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56,0xE8, +0xDC,0x07,0x59,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xEB,0x31,0xBF,0x04,0x00,0xEB, +0x2C,0xC7,0x46,0xF8,0x01,0x00,0x6A,0x08,0x56,0xE8,0x05,0x0C,0x59,0x59,0x80,0x7C, +0x58,0x09,0x7D,0x04,0xB0,0x0F,0xEB,0x02,0xB0,0x00,0x04,0x80,0x50,0x56,0xE8,0xF0, +0x0B,0x59,0x59,0xBF,0x04,0x00,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xE9,0xA5,0xFE, +0x5F,0x5E,0xC9,0xC3,0xC6,0x63,0x45,0x64,0x45,0x64,0x45,0x64,0x45,0x64,0xCB,0x63, +0x08,0x64,0x33,0x64,0x5A,0x63,0x9C,0x63,0xA8,0x63,0x78,0x64,0xB4,0x63,0x4C,0x64, +0x4C,0x64,0x51,0x64,0x54,0x63,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8B, +0x7E,0x08,0x6A,0x01,0x56,0xE8,0xA9,0x0B,0x59,0x59,0x8A,0x46,0x06,0xC0,0xE0,0x06, +0x04,0x80,0x50,0x56,0xE8,0x9A,0x0B,0x59,0x59,0xC7,0x46,0xFE,0x00,0x00,0x89,0x7E, +0xF8,0xEB,0x03,0xFF,0x46,0xFE,0x8B,0x5E,0xF8,0xFF,0x46,0xF8,0x80,0x3F,0x00,0x75, +0xF2,0x83,0x7E,0xFE,0x10,0x7D,0x25,0xB8,0x10,0x00,0x2B,0x46,0xFE,0xD1,0xF8,0x89, +0x46,0xFC,0xC7,0x46,0xFA,0x00,0x00,0xEB,0x0B,0x6A,0x20,0x56,0xE8,0x62,0x0B,0x59, +0x59,0xFF,0x46,0xFA,0x8B,0x46,0xFA,0x3B,0x46,0xFC,0x7C,0xED,0xEB,0x0C,0x8B,0xDF, +0x47,0x8A,0x07,0x50,0x56,0xE8,0x49,0x0B,0x59,0x59,0x80,0x3D,0x00,0x75,0xEF,0x6A, +0x02,0x56,0xE8,0x3C,0x0B,0x59,0x59,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x04,0x00, +0x00,0x56,0x57,0x8B,0x7E,0x04,0xC7,0x46,0xFE,0x00,0x00,0xBE,0x14,0x00,0xE9,0x09, +0x01,0x8B,0x5E,0xFE,0x83,0xC3,0x04,0x2B,0xDF,0x8A,0x87,0xAC,0x0B,0x88,0x44,0x5A, +0xC6,0x44,0x58,0x08,0x8A,0x46,0xFE,0x88,0x44,0x59,0xC7,0x44,0x06,0x00,0x00,0xC6, +0x44,0x19,0x00,0xC6,0x44,0x1A,0x00,0xC6,0x44,0x1B,0x00,0xC6,0x44,0x1D,0x0D,0xC6, +0x44,0x1E,0x03,0xC6,0x44,0x1F,0x00,0xC6,0x44,0x20,0x00,0xC6,0x44,0x21,0x00,0xC6, +0x44,0x5B,0x00,0xC6,0x44,0x5D,0x00,0xC6,0x44,0x5E,0x00,0xC6,0x44,0x5F,0x00,0xC6, +0x44,0x60,0x00,0xC7,0x46,0xFC,0x00,0x00,0xEB,0x0D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x40,0x30,0x00,0x00,0xFF,0x46,0xFC,0x83,0x7E,0xFC,0x10,0x7C,0xED,0xC7,0x46,0xFC, +0x00,0x00,0xEB,0x0A,0x8B,0x5E,0xFC,0xC6,0x40,0x50,0x00,0xFF,0x46,0xFC,0x83,0x7E, +0xFC,0x04,0x7C,0xF0,0xC7,0x44,0x54,0x00,0x00,0xC7,0x44,0x56,0x00,0x00,0x8A,0x44, +0x5A,0x98,0xBA,0xF8,0x00,0x23,0xD0,0xB8,0x05,0x00,0x0B,0xC2,0x89,0x46,0xFC,0x9C, +0xFA,0x8A,0x46,0xFC,0xBA,0xFE,0x00,0xEE,0xBA,0x00,0x00,0xEC,0x9D,0x24,0x08,0x88, +0x46,0xFC,0x83,0x7E,0xFC,0x00,0x75,0x02,0xEB,0x4A,0xFF,0x76,0xFE,0xE8,0x7A,0x0C, +0x59,0x68,0x35,0x02,0x56,0xE8,0x32,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x34,0x68,0x38, +0x02,0x56,0xE8,0x25,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x27,0x68,0x42,0x02,0x56,0xE8, +0x18,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x1A,0x68,0x4C,0x02,0x56,0xE8,0x0B,0x0A,0x59, +0x59,0x0B,0xC0,0x75,0x0D,0x68,0x56,0x02,0x56,0xE8,0xFE,0x09,0x59,0x59,0x0B,0xC0, +0x74,0x02,0xEB,0x00,0xFF,0x46,0xFE,0x83,0xC6,0x62,0x39,0x7E,0xFE,0x7D,0x03,0xE9, +0xEF,0xFE,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x46, +0x04,0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x8B,0xF0,0x83,0x7E,0x06,0x00,0x74, +0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x08,0x00,0x89,0x44,0x04,0x8A,0x46,0x08,0x88, +0x44,0x5C,0x56,0xE8,0x59,0x04,0x59,0x8B,0xF8,0x8B,0xC7,0x89,0x44,0x56,0x89,0x44, +0x54,0x8A,0x44,0x5D,0x88,0x44,0x2F,0x0B,0xFF,0x75,0x1D,0x68,0xC2,0x0F,0x6A,0x01, +0x56,0xE8,0x02,0xFE,0x83,0xC4,0x06,0xEB,0x00,0x6A,0x01,0x56,0xE8,0x47,0xFC,0x59, +0x59,0x0B,0xC0,0x75,0xF4,0xBF,0x01,0x00,0x89,0x7E,0xFA,0xB9,0x05,0x00,0xBB,0xCB, +0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xFA,0x74,0x07,0x43,0x43,0xE2,0xF4,0xE9,0xA4,0x03, +0x2E,0xFF,0x67,0x0A,0xC7,0x44,0x06,0x02,0x00,0xC7,0x44,0x08,0xF4,0x08,0x8B,0x5E, +0x04,0xD1,0xE3,0x8B,0x87,0xFC,0x08,0x89,0x44,0x0A,0x33,0xC0,0x8B,0xF8,0x89,0x44, +0x54,0xE9,0x80,0x03,0x56,0xE8,0xBB,0x05,0x59,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88, +0x44,0x60,0xE9,0x6F,0x03,0x83,0x7C,0x04,0x08,0x75,0x30,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xE4,0x08,0x56,0xE8, +0xF7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xC4,0x08,0x56,0xE8,0xE2,0x08,0x59,0x59,0xEB,0x2E,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xD4,0x08,0x56,0xE8, +0xC7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xB4,0x08,0x56,0xE8,0xB2,0x08,0x59,0x59,0x6A,0x01,0x56,0xE8,0x87,0xFB,0x59, +0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x2A,0xD1,0xE3,0x2E,0xFF,0xA7,0xC3,0x6A,0xBF, +0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5E,0xEB,0x18,0x8A,0x44,0x5D,0x04,0xFF,0x24, +0x07,0x88,0x44,0x5D,0xEB,0x0C,0x8A,0x44,0x5D,0xFE,0xC0,0x24,0x07,0x88,0x44,0x5D, +0xEB,0x00,0xE9,0xCF,0x02,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7, +0xFD,0x02,0x56,0xE8,0x63,0x08,0x59,0x59,0x68,0x1D,0x03,0x56,0xE8,0x5A,0x08,0x59, +0x59,0x6A,0x01,0x56,0xE8,0x2F,0xFB,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x36, +0xD1,0xE3,0x2E,0xFF,0xA7,0xBB,0x6A,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5F, +0xEB,0x24,0x8A,0x44,0x5D,0x04,0xFF,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22,0xC2,0x88, +0x44,0x5D,0xEB,0x12,0x8A,0x44,0x5D,0xFE,0xC0,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22, +0xC2,0x88,0x44,0x5D,0xEB,0x00,0xE9,0x6B,0x02,0x8B,0x5C,0x06,0x83,0xC3,0xFE,0xD1, +0xE3,0x8B,0x40,0x08,0x89,0x04,0x8B,0x1C,0xFF,0x77,0x06,0x6A,0x00,0x56,0xE8,0x85, +0xFC,0x83,0xC4,0x06,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x40,0x08,0x89,0x44,0x02, +0x8B,0x5C,0x02,0xFF,0x77,0x06,0x6A,0x01,0x56,0xE8,0x6A,0xFC,0x83,0xC4,0x06,0x6A, +0x01,0x56,0xE8,0xB1,0xFA,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x76,0x03,0xE9,0x1F, +0x02,0xD1,0xE3,0x2E,0xFF,0xA7,0xB3,0x6A,0x8B,0x5C,0x02,0x8B,0x47,0x04,0x89,0x44, +0x02,0x8B,0x5C,0x02,0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4, +0x00,0x3B,0x44,0x04,0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B, +0x44,0x02,0x89,0x47,0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40, +0x08,0xE9,0xDE,0x01,0x8B,0x5C,0x02,0x8B,0x47,0x02,0x89,0x44,0x02,0x8B,0x5C,0x02, +0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4,0x00,0x3B,0x44,0x04, +0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B,0x44,0x02,0x89,0x47, +0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40,0x08,0xE9,0xA2,0x01, +0xBF,0x01,0x00,0xE9,0x9C,0x01,0x8B,0x5C,0x02,0x8A,0x07,0xB4,0x00,0x89,0x46,0xF8, +0xB9,0x0C,0x00,0xBB,0x83,0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xF8,0x74,0x07,0x43,0x43, +0xE2,0xF4,0xE9,0x77,0x01,0x2E,0xFF,0x67,0x18,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x5C, +0x02,0x03,0xD8,0x8B,0x47,0x08,0x8B,0x5C,0x06,0xFF,0x44,0x06,0xD1,0xE3,0x89,0x40, +0x08,0x8B,0x1C,0x80,0x7F,0x01,0x00,0x74,0x12,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x40,0x01,0xFF,0x4C, +0x06,0xE9,0x3A,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6, +0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x25,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xFF,0x4C,0x06,0xE9,0x0D, +0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA, +0x30,0x40,0x18,0xE9,0xF8,0x00,0xB8,0xF0,0x10,0x8B,0xF8,0x89,0x44,0x54,0x8A,0x44, +0x5F,0x88,0x44,0x5D,0xE9,0xE7,0x00,0x8A,0x44,0x1C,0x98,0x3D,0x02,0x00,0x74,0x07, +0x3D,0x03,0x00,0x74,0x02,0xEB,0x07,0xC7,0x46,0xFE,0x00,0x00,0xEB,0x2B,0x8A,0x44, +0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x69,0x02,0x56,0xE8,0x6B,0x06,0x59,0x59, +0x6A,0x01,0x56,0xE8,0x40,0xF9,0x59,0x59,0x89,0x46,0xFE,0x83,0x7E,0xFE,0x00,0x74, +0x06,0x83,0x7E,0xFE,0x03,0x75,0xE9,0xEB,0x00,0x83,0x7E,0xFE,0x03,0x74,0x62,0x8A, +0x44,0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x6D,0x02,0x56,0xE8,0x3A,0x06,0x59, +0x59,0x56,0xE8,0x6B,0x97,0x59,0x89,0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xEB,0xFE,0x83, +0xFB,0x03,0x77,0x33,0xD1,0xE3,0x2E,0xFF,0xA7,0x7B,0x6A,0x68,0xAC,0x02,0x56,0xE8, +0x17,0x06,0x59,0x59,0xEB,0x23,0x68,0x8F,0x02,0x56,0xE8,0x0C,0x06,0x59,0x59,0xEB, +0x18,0x68,0x75,0x02,0x56,0xE8,0x01,0x06,0x59,0x59,0xEB,0x0D,0x68,0xC6,0x02,0x56, +0xE8,0xF6,0x05,0x59,0x59,0xEB,0x02,0xEB,0x00,0x6A,0x01,0x56,0xE8,0xC7,0xF8,0x59, +0x59,0xBF,0x01,0x00,0xEB,0x38,0x68,0xDD,0x02,0x56,0xE8,0xDC,0x05,0x59,0x59,0x6A, +0x01,0x56,0xE8,0xB1,0xF8,0x59,0x59,0xBF,0x01,0x00,0xEB,0x22,0xB8,0xD0,0x30,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x60,0x88,0x44,0x5D,0xEB,0x12,0xB8,0xE0,0x20,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x5E,0x88,0x44,0x5D,0xEB,0x02,0xEB,0x00,0xEB,0x02, +0xEB,0x00,0xEB,0x00,0xE9,0x41,0xFC,0x5F,0x5E,0xC9,0xC3,0xFB,0x69,0x06,0x6A,0x11, +0x6A,0x1C,0x6A,0x00,0x00,0x01,0x00,0x02,0x00,0x04,0x00,0x41,0x00,0x42,0x00,0x43, +0x00,0x44,0x00,0x80,0x00,0x81,0x00,0x82,0x00,0xFF,0x00,0xF9,0x68,0x36,0x6A,0x5C, +0x6A,0x87,0x69,0x34,0x69,0x76,0x69,0x4C,0x6A,0x49,0x69,0x34,0x69,0x61,0x69,0x49, +0x69,0x2E,0x69,0xD6,0x68,0x58,0x68,0x94,0x68,0xD0,0x68,0xD7,0x67,0xE2,0x67,0xF4, +0x67,0xD7,0x67,0x7F,0x67,0x8A,0x67,0x96,0x67,0x7F,0x67,0x00,0x00,0x01,0x00,0xF0, +0x10,0xE0,0x20,0xD0,0x30,0x09,0x68,0xD4,0x66,0xA5,0x67,0x05,0x67,0xF4,0x66,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8A,0x44,0x59,0x98,0x89,0x46,0xFC,0x6A, +0x09,0x8B,0x46,0xFC,0x05,0x84,0x01,0x50,0xE8,0x93,0x08,0x59,0x59,0x8B,0xF8,0x8B, +0xC7,0x25,0x00,0xF0,0x3D,0x00,0x10,0x75,0x55,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xF0, +0x00,0x75,0x4B,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44, +0x04,0x3B,0x46,0xFE,0x7D,0x05,0x33,0xC0,0xE9,0xEF,0x00,0x8B,0xC7,0x25,0x0F,0x00, +0xBA,0x0F,0x00,0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xE9,0xDB,0x00,0xC7, +0x44,0x02,0x04,0x09,0x8A,0x46,0xFE,0x88,0x44,0x5F,0x88,0x44,0x5D,0x8B,0x5E,0xFC, +0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x04,0x09,0xB8,0xF0,0x10,0xE9,0xBC,0x00,0x8B,0xC7, +0x25,0x00,0xF0,0x3D,0x00,0x20,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xE0,0x00, +0x75,0x48,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x83,0x7E,0xFE, +0x08,0x7E,0x05,0x33,0xC0,0xE9,0x92,0x00,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00, +0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xEB,0x7F,0x90,0xC7,0x44,0x02,0x0C, +0x09,0x8A,0x46,0xFE,0x88,0x44,0x5E,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x87,0xFC,0x08,0x0C,0x09,0xB8,0xE0,0x20,0xEB,0x60,0x8B,0xC7,0x25,0x00,0xF0,0x3D, +0x00,0x30,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xD0,0x00,0x75,0x48,0x8B,0xC7, +0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44,0x04,0x3B,0x46,0xFE,0x7D, +0x04,0x33,0xC0,0xEB,0x35,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00,0x2B,0xD0,0x3B, +0x56,0xFE,0x74,0x04,0x33,0xC0,0xEB,0x22,0xC7,0x44,0x02,0x14,0x09,0x8A,0x46,0xFE, +0x88,0x44,0x60,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x14, +0x09,0xB8,0xD0,0x30,0xEB,0x04,0x33,0xC0,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x06, +0x00,0x00,0x56,0x8B,0x76,0x04,0x6A,0x08,0x56,0xE8,0x35,0x04,0x59,0x59,0x8A,0x44, +0x58,0x04,0x80,0x50,0x56,0xE8,0x29,0x04,0x59,0x59,0x8B,0x44,0x54,0x3B,0x44,0x56, +0x75,0x0A,0x8A,0x44,0x5D,0x3A,0x44,0x2F,0x75,0x02,0xEB,0x64,0x8B,0x44,0x54,0x89, +0x44,0x56,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x88,0x44,0x2F,0x8A,0x44,0x5D,0xB4,0x00, +0xC1,0xE0,0x08,0x8B,0x54,0x54,0x0B,0xD0,0x8A,0x44,0x5D,0xB4,0x00,0xBB,0x0F,0x00, +0x2B,0xD8,0x0B,0xD3,0x89,0x56,0xFE,0x6A,0x10,0x8A,0x44,0x59,0x98,0x05,0x04,0x00, +0x99,0x05,0x40,0x01,0x83,0xD2,0x00,0x52,0x50,0xE8,0x54,0x08,0x83,0xC4,0x06,0x89, +0x56,0xFC,0x89,0x46,0xFA,0x8B,0x46,0xFE,0x09,0x46,0xFA,0x83,0x4E,0xFC,0x00,0x6A, +0x19,0xFF,0x76,0xFC,0xFF,0x76,0xFA,0xE8,0x73,0x07,0x83,0xC4,0x06,0xE8,0xFE,0x07, +0x5E,0xC9,0xC3,0xC8,0x1C,0x00,0x00,0x56,0x57,0x8B,0x5E,0x04,0x8A,0x47,0x59,0x98, +0x8B,0xF0,0x8B,0x5E,0x04,0x8A,0x47,0x5D,0xB4,0x00,0x89,0x46,0xE6,0x83,0x7E,0xE6, +0x00,0x7D,0x0A,0x8B,0x5E,0x04,0x8B,0x47,0x04,0x48,0x89,0x46,0xE6,0x8B,0x5E,0x04, +0x8B,0x47,0x04,0x3B,0x46,0xE6,0x7F,0x05,0xC7,0x46,0xE6,0x00,0x00,0x8B,0x5E,0x04, +0x8A,0x46,0xE6,0x88,0x47,0x5D,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47, +0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x03,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x61,0x02,0xC6,0x47,0x03,0x30,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00, +0x74,0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA, +0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x59,0x02,0x88,0x57,0x02,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74, +0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA,0x33, +0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA,0xBB, +0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61, +0x02,0x88,0x57,0x02,0x8B,0x5E,0xE6,0xD1,0xE3,0xFF,0xB7,0x12,0x02,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0xD1,0xF6,0x83,0xC4,0x06,0x68,0xD3,0x0F,0x6A,0x01,0xFF,0x76,0x04, +0xE8,0xC3,0xF6,0x83,0xC4,0x06,0xFF,0x76,0xE6,0x56,0xE8,0x1F,0x93,0x59,0x59,0x89, +0x56,0xF2,0x89,0x46,0xF0,0xFF,0x76,0xE6,0x56,0xE8,0x32,0x93,0x59,0x59,0x89,0x56, +0xEE,0x89,0x46,0xEC,0x9C,0xFA,0xC4,0x5E,0xF0,0x26,0x8B,0x07,0x89,0x46,0xEA,0xC4, +0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE,0x9D, +0xC7,0x46,0xE4,0x01,0x00,0xE8,0x08,0xA1,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B, +0x46,0xFC,0x2B,0x46,0xFE,0x3D,0xE8,0x03,0x73,0x03,0xE9,0x80,0x01,0x9C,0xFA,0xBA, +0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B,0x46,0xFC,0x2B,0x46,0xFE,0x89,0x46,0xF8,0xC4, +0x5E,0xF0,0x26,0x8B,0x07,0x2B,0x46,0xEA,0x89,0x46,0xF6,0xC4,0x5E,0xF0,0x26,0x8B, +0x07,0x89,0x46,0xEA,0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x2B,0x46,0xE8,0x89,0x46,0xF4, +0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE, +0x9D,0x81,0x7E,0xF8,0xE8,0x03,0x76,0x1C,0xFF,0x76,0xF8,0xFF,0x76,0xF6,0xE8,0x76, +0x01,0x59,0x59,0x89,0x46,0xF6,0xFF,0x76,0xF8,0xFF,0x76,0xF4,0xE8,0x68,0x01,0x59, +0x59,0x89,0x46,0xF4,0xBF,0x0E,0x00,0xEB,0x17,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59, +0x02,0xC6,0x01,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x01,0x20,0x47, +0x83,0xFF,0x11,0x76,0xE4,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x0D, +0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x0D,0x30,0x83,0x7E,0xF6, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF6,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF6,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF6,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF6,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF6, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF6,0x83,0x7E,0xF6,0x00,0x75,0xD5,0x83,0x7E,0xF4, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF4,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF4,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF4,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF4,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF4, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF4,0x83,0x7E,0xF4,0x00,0x75,0xD5,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x59,0x02,0xFF,0x76,0x04,0xE8,0x6E,0x00,0x59,0x59,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x61,0x02,0xFF,0x76,0x04,0xE8,0x5E,0x00,0x59,0x59,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0x31,0xF3,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x04,0x77,0x1F,0xD1,0xE3, +0x2E,0xFF,0xA7,0xFD,0x6F,0xEB,0x22,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x4E,0xE6,0xEB, +0x0C,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x46,0xE6,0xEB,0x02,0xEB,0x00,0x83,0x7E,0xE4, +0x00,0x74,0x03,0xE9,0x2A,0xFE,0xE9,0xD4,0xFC,0x5F,0x5E,0xC9,0xC3,0xD5,0x6F,0xD7, +0x6F,0xE1,0x6F,0xD5,0x6F,0xEB,0x6F,0x55,0x8B,0xEC,0x8B,0x46,0x04,0xB9,0xE8,0x03, +0xF7,0xE1,0x8B,0x4E,0x06,0xF7,0xF1,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76,0x06, +0xEB,0x0E,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x33,0x00,0x59,0x59, +0x80,0x3C,0x00,0x75,0xED,0xEB,0x00,0x5E,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76, +0x06,0xEB,0x14,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x45,0x00,0x59, +0x59,0x0B,0xC0,0x74,0x02,0xEB,0x07,0x80,0x3C,0x00,0x75,0xE7,0xEB,0x00,0x5E,0x5D, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74, +0x06,0x9D,0xE8,0xAB,0x9E,0xEB,0xE9,0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0xEB, +0x00,0x5E,0xC9,0xC3,0xC8,0x04,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98, +0x89,0x46,0xFE,0xE8,0x00,0xA2,0x89,0x46,0xFC,0xE8,0xFA,0xA1,0x2B,0x46,0xFC,0x3D, +0xB8,0x0B,0x76,0x05,0xB8,0x01,0x00,0xEB,0x23,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74,0x06,0x9D,0xE8,0x62,0x9E,0xEB,0xD9, +0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0x33,0xC0,0xEB,0x00,0x5E,0xC9,0xC3,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x83,0x7E,0x06,0x00,0x74,0x07,0x56,0xE8, +0x03,0x01,0x59,0xEB,0x05,0x56,0xE8,0xA2,0x00,0x59,0x88,0x46,0xFF,0x80,0x7E,0xFF, +0x08,0x77,0x06,0x8A,0x46,0xFF,0xE9,0x84,0x00,0x80,0x7E,0xFF,0x0F,0x76,0x03,0xEB, +0x79,0x90,0x8A,0x46,0xFF,0xB4,0x00,0x2D,0x0A,0x00,0x8B,0xD8,0x83,0xFB,0x04,0x77, +0x67,0xD1,0xE3,0x2E,0xFF,0xA7,0x91,0x71,0xB0,0x00,0xEB,0x61,0x56,0xE8,0x6B,0x00, +0x59,0xB4,0x00,0x25,0x0F,0x00,0x89,0x46,0xFC,0x56,0xE8,0x5E,0x00,0x59,0xB4,0x00, +0x8B,0xF8,0x56,0xE8,0x55,0x00,0x59,0xB4,0x00,0xC1,0xE0,0x08,0x8B,0xD7,0x03,0xD0, +0x8B,0xFA,0x8B,0x5E,0xFC,0xD1,0xE3,0x89,0x78,0x30,0xEB,0x2E,0x56,0xE8,0x3B,0x00, +0x59,0x88,0x44,0x5B,0xEB,0x24,0x56,0xE8,0x31,0x00,0x59,0x88,0x44,0x50,0x56,0xE8, +0x29,0x00,0x59,0x88,0x44,0x51,0x56,0xE8,0x21,0x00,0x59,0x88,0x44,0x52,0x56,0xE8, +0x19,0x00,0x59,0x88,0x44,0x53,0xEB,0x02,0xEB,0x00,0xE9,0x5B,0xFF,0x5F,0x5E,0xC9, +0xC3,0x28,0x71,0x88,0x71,0x2C,0x71,0x5C,0x71,0x66,0x71,0xC8,0x04,0x00,0x00,0x56, +0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE,0x9C,0xFA,0x8A,0x46,0xFE,0xBA, +0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x71,0x9D,0xEB, +0xE9,0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0x32,0xE4,0x24, +0x01,0x9D,0x5E,0xC9,0xC3,0xC8,0x06,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A, +0x98,0x89,0x46,0xFE,0xE8,0x9F,0xA0,0x89,0x46,0xFA,0xE8,0x99,0xA0,0x2B,0x46,0xFA, +0x3D,0xB8,0x0B,0x76,0x04,0xB0,0x08,0xEB,0x24,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x02,0x9D,0xEB,0xDA, +0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9,0xC3, +0x55,0x8B,0xEC,0x56,0x8B,0x56,0x04,0x8A,0x46,0x06,0xEE,0x33,0xF6,0xEB,0x03,0x50, +0x58,0x46,0x83,0xFE,0x14,0x7C,0xF8,0x5E,0x5D,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B, +0x56,0x04,0xEC,0x88,0x46,0xFF,0x33,0xF6,0xEB,0x03,0x50,0x58,0x46,0x83,0xFE,0x14, +0x7C,0xF8,0x8A,0x46,0xFF,0xEB,0x00,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x57, +0x8B,0x76,0x04,0x83,0x3E,0xB0,0x0B,0x00,0x75,0x1F,0xBA,0x88,0x01,0xB0,0x00,0xEE, +0xBA,0x86,0x01,0xB0,0x00,0xEE,0x6A,0x09,0x6A,0x00,0x68,0x30,0x01,0xE8,0x7D,0x01, +0x83,0xC4,0x06,0xC7,0x06,0xB0,0x0B,0x01,0x00,0x6A,0x09,0x8B,0xC6,0x05,0x80,0x01, +0x50,0xE8,0xDA,0x00,0x59,0x59,0x8B,0xF8,0x8B,0xC7,0xC1,0xE8,0x0C,0x25,0x0F,0x00, +0x89,0x46,0xFE,0x8B,0xC7,0xC1,0xE8,0x08,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83,0xF2, +0x0C,0x3B,0xC2,0x75,0x21,0x8B,0xC7,0xC1,0xE8,0x04,0x25,0x0F,0x00,0x8B,0x56,0xFE, +0x83,0xF2,0x06,0x3B,0xC2,0x75,0x0F,0x8B,0xC7,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83, +0xF2,0x09,0x3B,0xC2,0x74,0x0D,0x6A,0x07,0x56,0xE8,0x38,0x00,0x59,0x59,0xC7,0x46, +0xFE,0x07,0x00,0x8A,0x46,0xFE,0x04,0x80,0xA2,0x33,0x02,0x8B,0xC6,0xBA,0x62,0x00, +0xF7,0xEA,0x8A,0x56,0xFE,0x8B,0xD8,0x88,0x97,0x6C,0x00,0x68,0x32,0x02,0x8B,0xC6, +0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x50,0xE8,0x0E,0xFD,0x59,0x59,0xEB,0x00, +0x5F,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x06,0x83,0xE6,0x0F,0x8B, +0xC6,0xC1,0xE0,0x0C,0x8B,0xD6,0x83,0xF2,0x0C,0xC1,0xE2,0x08,0x0B,0xC2,0x8B,0xD6, +0x83,0xF2,0x06,0xC1,0xE2,0x04,0x0B,0xC2,0x8B,0xD6,0x83,0xF2,0x09,0x0B,0xC2,0x89, +0x46,0xFE,0x6A,0x19,0x6A,0x10,0x8B,0x46,0x04,0x99,0x05,0x40,0x01,0x83,0xD2,0x00, +0x52,0x50,0xE8,0x6B,0x01,0x83,0xC4,0x06,0x0B,0x46,0xFE,0x83,0xCA,0x00,0x52,0x50, +0xE8,0x9A,0x00,0x83,0xC4,0x06,0xE8,0x25,0x01,0xEB,0x00,0x5E,0xC9,0xC3,0x55,0x8B, +0xEC,0x56,0x57,0x33,0xFF,0x6A,0x01,0x68,0x86,0x01,0xE8,0xA3,0xFE,0x59,0x59,0xB1, +0x10,0x2A,0x4E,0x06,0xD3,0x66,0x04,0x33,0xF6,0xEB,0x2E,0x81,0x7E,0x04,0x00,0x80, +0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00,0x50,0x68,0x88,0x01,0xE8,0x81,0xFE,0x59, +0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0x77,0xFE,0x59,0x59,0x6A,0x01,0x68,0x86,0x01, +0xE8,0x6D,0xFE,0x59,0x59,0xD1,0x66,0x04,0x46,0x3B,0x76,0x06,0x7C,0xCD,0x33,0xF6, +0xEB,0x24,0xD1,0xE7,0x6A,0x03,0x68,0x86,0x01,0xE8,0x54,0xFE,0x59,0x59,0x6A,0x01, +0x68,0x86,0x01,0xE8,0x4A,0xFE,0x59,0x59,0x68,0x88,0x01,0xE8,0x5C,0xFE,0x59,0x98, +0x25,0x01,0x00,0x0B,0xF8,0x46,0x83,0xFE,0x10,0x7C,0xD7,0x6A,0x00,0x68,0x86,0x01, +0xE8,0x2D,0xFE,0x59,0x59,0x8B,0xC7,0xEB,0x00,0x5F,0x5E,0x5D,0xC3,0x55,0x8B,0xEC, +0x56,0x57,0x8B,0x7E,0x08,0x6A,0x01,0x68,0x86,0x01,0xE8,0x13,0xFE,0x59,0x59,0xB8, +0x20,0x00,0x2B,0xC7,0x50,0xFF,0x76,0x06,0xFF,0x76,0x04,0xE8,0xA2,0x00,0x83,0xC4, +0x06,0x89,0x56,0x06,0x89,0x46,0x04,0x33,0xF6,0xEB,0x47,0x81,0x7E,0x06,0x00,0x80, +0x72,0x0C,0x75,0x06,0x83,0x7E,0x04,0x00,0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00, +0x50,0x68,0x88,0x01,0xE8,0xD9,0xFD,0x59,0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0xCF, +0xFD,0x59,0x59,0x6A,0x01,0x68,0x86,0x01,0xE8,0xC5,0xFD,0x59,0x59,0x6A,0x01,0xFF, +0x76,0x06,0xFF,0x76,0x04,0xE8,0x58,0x00,0x83,0xC4,0x06,0x89,0x56,0x06,0x89,0x46, +0x04,0x46,0x3B,0xF7,0x7C,0xB5,0x6A,0x00,0x68,0x86,0x01,0xE8,0xA2,0xFD,0x59,0x59, +0x6A,0x00,0x68,0x86,0x01,0xE8,0x98,0xFD,0x59,0x59,0x5F,0x5E,0x5D,0xC3,0x55,0x8B, +0xEC,0x56,0x6A,0x01,0x68,0x86,0x01,0xE8,0x86,0xFD,0x59,0x59,0x33,0xF6,0xEB,0x00, +0x68,0x88,0x01,0xE8,0x94,0xFD,0x59,0xA8,0x01,0x75,0x08,0x8B,0xC6,0x46,0x3D,0x64, +0x00,0x7C,0xED,0x6A,0x00,0x68,0x86,0x01,0xE8,0x65,0xFD,0x59,0x59,0x5E,0x5D,0xC3, +0xC8,0x04,0x00,0x00,0x8B,0x46,0x04,0x8B,0x56,0x06,0x8B,0x4E,0x08,0xE3,0x06,0xD1, +0xE0,0xD1,0xD2,0xE2,0xFA,0x89,0x46,0xFC,0x89,0x56,0xFE,0x8B,0x56,0xFE,0x8B,0x46, +0xFC,0xEB,0x00,0xC9,0xC3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x50,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x4D,0x65,0x6E,0x75,0x00,0x42,0x65, +0x67,0x69,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x6F,0x72,0x74, +0x20,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x32, +0x00,0x50,0x6F,0x72,0x74,0x20,0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x34,0x00,0x50, +0x6F,0x72,0x74,0x20,0x35,0x00,0x50,0x6F,0x72,0x74,0x20,0x36,0x00,0x50,0x6F,0x72, +0x74,0x20,0x37,0x00,0x50,0x6F,0x72,0x74,0x20,0x38,0x00,0x50,0x6F,0x72,0x74,0x20, +0x39,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x32,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x34,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x35,0x00,0x9C,0x01,0xA3,0x01,0xAA,0x01,0xB1,0x01,0xB8,0x01,0xBF,0x01,0xC6,0x01, +0xCD,0x01,0xD4,0x01,0xDB,0x01,0xE2,0x01,0xEA,0x01,0xF2,0x01,0xFA,0x01,0x02,0x02, +0x0A,0x02,0x08,0x00,0x00,0x07,0x81,0x00,0x03,0x80,0x80,0x80,0x9F,0x91,0x95,0x91, +0x9F,0x00,0x03,0x81,0x84,0x8E,0x95,0x84,0x84,0x84,0x84,0x00,0x03,0x82,0x84,0x84, +0x84,0x84,0x95,0x8E,0x84,0x00,0x04,0x88,0x00,0xB2,0x0B,0xC6,0x0B,0xDA,0x0B,0xEE, +0x0B,0x02,0x0C,0x16,0x0C,0x2A,0x0C,0x3E,0x0C,0x52,0x0C,0x77,0x0C,0x9C,0x0C,0xBE, +0x0C,0xE0,0x0C,0x02,0x0D,0x01,0x80,0x20,0x54,0x65,0x73,0x74,0x20,0x50,0x61,0x73, +0x73,0x65,0x64,0x20,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01, +0x80,0x20,0x4D,0x69,0x73,0x73,0x69,0x6E,0x67,0x20,0x52,0x78,0x20,0x44,0x61,0x74, +0x61,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x42, +0x61,0x64,0x20,0x52,0x78,0x20,0x44,0x61,0x74,0x61,0x20,0x1F,0x20,0x50,0x72,0x65, +0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x58,0x6D,0x74,0x72,0x20,0x42,0x75, +0x73,0x79,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20, +0x6E,0x6F,0x74,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x6C,0x79,0x1F,0x20,0x20, +0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x65,0x64,0x02,0x00,0x24,0x0D,0x2F, +0x0D,0x3A,0x0D,0x45,0x0D,0x50,0x0D,0x5B,0x0D,0x66,0x0D,0x71,0x0D,0x7C,0x0D,0x87, +0x0D,0x92,0x0D,0x9D,0x0D,0xA8,0x0D,0xB3,0x0D,0xBE,0x0D,0xC9,0x0D,0x53,0x80,0x2C, +0x32,0x54,0x44,0x20,0x53,0x86,0x2C,0x33,0x44,0x54,0x52,0x20,0x53,0x82,0x2C,0x33, +0x52,0x54,0x53,0x20,0x1F,0x53,0x81,0x2C,0x32,0x52,0x44,0x20,0x53,0x85,0x2C,0x32, +0x43,0x44,0x20,0x53,0x83,0x2C,0x33,0x43,0x54,0x53,0x20,0x53,0x84,0x2C,0x33,0x44, +0x53,0x52,0x20,0x53,0x87,0x2C,0x32,0x52,0x49,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x27,0x53,0x85, +0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89, +0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52, +0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x31,0x1F,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x34,0x1F,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x90,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32, +0x32,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x35,0x1F,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D, +0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x32,0x1F,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88, +0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53, +0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x85,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20, +0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x20,0x20,0x20, +0x20,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85, +0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x31,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x34,0x1F,0x20,0x20, +0x20,0x20,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x32,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53, +0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x27,0x02,0x00,0x68,0x04,0x96,0x04,0xB6,0x03,0x3C,0x04,0x0E,0x04,0x89,0x03, +0x5C,0x03,0xE2,0x03,0x60,0x08,0x8A,0x08,0xBE,0x07,0x38,0x08,0x0E,0x08,0x95,0x07, +0x6C,0x07,0xE6,0x07,0x1C,0x05,0x74,0x05,0xFA,0x05,0xC4,0x04,0xF0,0x04,0xCC,0x05, +0xA0,0x05,0x48,0x05,0x78,0x06,0xC8,0x06,0x42,0x07,0x28,0x06,0x50,0x06,0x18,0x07, +0xF0,0x06,0xA0,0x06,0x00,0x00,0xF4,0x08,0xF4,0x08,0xD4,0x0D,0x04,0x09,0x04,0x09, +0x04,0x09,0x04,0x09,0x42,0x00,0x0C,0x09,0x1C,0x09,0xE5,0x0D,0x02,0x00,0x14,0x09, +0x04,0x09,0xF4,0x0D,0x43,0x00,0x1C,0x09,0x0C,0x09,0x05,0x0E,0x00,0x04,0x04,0x09, +0x14,0x09,0x12,0x0E,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x00,0x00,0x3C,0x09, +0x6C,0x09,0x1E,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x01,0x4C,0x09, +0x2C,0x09,0x2D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x02,0x5C,0x09, +0x3C,0x09,0x3D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x03,0x6C,0x09, +0x4C,0x09,0x4D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0xFF,0x00,0x2C,0x09, +0x5C,0x09,0x00,0x00,0x00,0x05,0x84,0x09,0xEC,0x09,0x5E,0x0E,0xF4,0x09,0xF4,0x09, +0xF4,0x09,0xF4,0x09,0x00,0x06,0x94,0x09,0x74,0x09,0x68,0x0E,0xAC,0x0A,0xAC,0x0A, +0xAC,0x0A,0xAC,0x0A,0x00,0x07,0xA4,0x09,0x84,0x09,0x72,0x0E,0xBC,0x0A,0xBC,0x0A, +0xBC,0x0A,0xBC,0x0A,0x00,0x08,0xB4,0x09,0x94,0x09,0x7C,0x0E,0xD4,0x0A,0xD4,0x0A, +0xD4,0x0A,0xD4,0x0A,0x00,0x0B,0xC4,0x09,0xA4,0x09,0x83,0x0E,0xFC,0x0A,0xFC,0x0A, +0xFC,0x0A,0xFC,0x0A,0x00,0x0C,0xD4,0x09,0xB4,0x09,0x90,0x0E,0x14,0x0B,0x14,0x0B, +0x14,0x0B,0x14,0x0B,0x00,0x02,0xE4,0x09,0xC4,0x09,0xA0,0x0E,0x2C,0x0B,0x2C,0x0B, +0x2C,0x0B,0x2C,0x0B,0x04,0x00,0xEC,0x09,0xD4,0x09,0x0E,0x00,0xFF,0x00,0x74,0x09, +0xE4,0x09,0x00,0x00,0x82,0x01,0xFC,0x09,0xA4,0x0A,0xAC,0x0E,0x82,0x02,0x04,0x0A, +0xF4,0x09,0xAF,0x0E,0x82,0x03,0x0C,0x0A,0xFC,0x09,0xB2,0x0E,0x82,0x04,0x14,0x0A, +0x04,0x0A,0xB6,0x0E,0x82,0x05,0x1C,0x0A,0x0C,0x0A,0xBC,0x0E,0x82,0x06,0x24,0x0A, +0x14,0x0A,0xC0,0x0E,0x82,0x07,0x2C,0x0A,0x1C,0x0A,0xC4,0x0E,0x82,0x08,0x34,0x0A, +0x24,0x0A,0xC8,0x0E,0x82,0x09,0x3C,0x0A,0x2C,0x0A,0xCC,0x0E,0x82,0x0A,0x44,0x0A, +0x34,0x0A,0xD1,0x0E,0x82,0x10,0x4C,0x0A,0x3C,0x0A,0xD6,0x0E,0x82,0x0B,0x54,0x0A, +0x44,0x0A,0xDB,0x0E,0x82,0x11,0x5C,0x0A,0x4C,0x0A,0xE0,0x0E,0x82,0x0C,0x64,0x0A, +0x54,0x0A,0xE5,0x0E,0x82,0x12,0x6C,0x0A,0x5C,0x0A,0xEA,0x0E,0x82,0x0D,0x74,0x0A, +0x64,0x0A,0xEF,0x0E,0x82,0x0E,0x7C,0x0A,0x6C,0x0A,0xF4,0x0E,0x82,0x0F,0x84,0x0A, +0x74,0x0A,0xFB,0x0E,0x82,0x13,0x8C,0x0A,0x7C,0x0A,0x02,0x0F,0x82,0x14,0x94,0x0A, +0x84,0x0A,0x09,0x0F,0x82,0x15,0x9C,0x0A,0x8C,0x0A,0x10,0x0F,0x82,0x16,0xA4,0x0A, +0x94,0x0A,0x17,0x0F,0x82,0x17,0xF4,0x09,0x9C,0x0A,0x1E,0x0F,0x82,0x02,0xB4,0x0A, +0xB4,0x0A,0x26,0x0F,0x82,0x03,0xAC,0x0A,0xAC,0x0A,0x2D,0x0F,0x82,0x00,0xC4,0x0A, +0xCC,0x0A,0x34,0x0F,0x82,0x01,0xCC,0x0A,0xBC,0x0A,0x3F,0x0F,0x82,0x02,0xBC,0x0A, +0xC4,0x0A,0x4D,0x0F,0x82,0x00,0xDC,0x0A,0xF4,0x0A,0x59,0x0F,0x82,0x01,0xE4,0x0A, +0xD4,0x0A,0x63,0x0F,0x82,0x02,0xEC,0x0A,0xDC,0x0A,0x6E,0x0F,0x82,0x03,0xF4,0x0A, +0xE4,0x0A,0x7A,0x0F,0x82,0x04,0xD4,0x0A,0xEC,0x0A,0x87,0x0F,0x82,0x00,0x04,0x0B, +0x0C,0x0B,0x93,0x0F,0x82,0x01,0x0C,0x0B,0xFC,0x0A,0x9B,0x0F,0x82,0x02,0xFC,0x0A, +0x04,0x0B,0xA7,0x0F,0x82,0x00,0x1C,0x0B,0x24,0x0B,0xB0,0x0F,0x82,0x01,0x24,0x0B, +0x14,0x0B,0xB5,0x0F,0x82,0x02,0x14,0x0B,0x1C,0x0B,0xBE,0x0F,0x44,0x00,0x34,0x0B, +0xA4,0x0B,0x9C,0x01,0x44,0x01,0x3C,0x0B,0x2C,0x0B,0xA3,0x01,0x44,0x02,0x44,0x0B, +0x34,0x0B,0xAA,0x01,0x44,0x03,0x4C,0x0B,0x3C,0x0B,0xB1,0x01,0x44,0x04,0x54,0x0B, +0x44,0x0B,0xB8,0x01,0x44,0x05,0x5C,0x0B,0x4C,0x0B,0xBF,0x01,0x44,0x06,0x64,0x0B, +0x54,0x0B,0xC6,0x01,0x44,0x07,0x6C,0x0B,0x5C,0x0B,0xCD,0x01,0x44,0x08,0x74,0x0B, +0x64,0x0B,0xD4,0x01,0x44,0x09,0x7C,0x0B,0x6C,0x0B,0xDB,0x01,0x44,0x0A,0x84,0x0B, +0x74,0x0B,0xE2,0x01,0x44,0x0B,0x8C,0x0B,0x7C,0x0B,0xEA,0x01,0x44,0x0C,0x94,0x0B, +0x84,0x0B,0xF2,0x01,0x44,0x0D,0x9C,0x0B,0x8C,0x0B,0xFA,0x01,0x44,0x0E,0xA4,0x0B, +0x94,0x0B,0x02,0x02,0x44,0x0F,0x2C,0x0B,0x9C,0x0B,0x0A,0x02,0x17,0x1F,0x0F,0x2F, +0x00,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20, +0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80, +0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20, +0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0, +0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0x80,0x49,0x6E,0x73,0x74,0x61,0x6C,0x6C,0x20,0x4C,0x6F,0x6F,0x70, +0x62,0x61,0x63,0x6B,0x1F,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x74,0x6F,0x20, +0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x43,0x61,0x62,0x6C,0x65,0x20, +0x74,0x6F,0x20,0x52,0x65,0x6D,0x6F,0x74,0x65,0x1F,0x50,0x72,0x65,0x73,0x73,0x20, +0x80,0x20,0x74,0x6F,0x20,0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x4C, +0x6F,0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20,0x1F,0x20, +0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00,0x01,0x80, +0x52,0x65,0x6D,0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20, +0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00, +0x01,0x80,0x20,0x49,0x6E,0x74,0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61, +0x63,0x6B,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E, +0x02,0x00,0x01,0x80,0x54,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74, +0x74,0x65,0x72,0x6E,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E, +0x2E,0x2E,0x02,0x00,0x01,0x80,0x20,0x20,0x30,0x3A,0x20,0x27,0x43,0x80,0x00,0x01, +0x80,0x20,0x20,0x31,0x3A,0x20,0x27,0x43,0x81,0x00,0x01,0x80,0x20,0x20,0x32,0x3A, +0x20,0x27,0x43,0x82,0x00,0x01,0x80,0x20,0x20,0x33,0x3A,0x20,0x27,0x43,0x83,0x00, +0x01,0x80,0x20,0x20,0x34,0x3A,0x20,0x27,0x43,0x84,0x00,0x01,0x80,0x20,0x20,0x35, +0x3A,0x20,0x27,0x43,0x85,0x00,0x01,0x80,0x20,0x20,0x36,0x3A,0x20,0x27,0x43,0x86, +0x00,0x01,0x80,0x20,0x20,0x37,0x3A,0x20,0x27,0x43,0x87,0x00,0x01,0x80,0x20,0x20, +0x38,0x3A,0x20,0x27,0x43,0x88,0x00,0x01,0x80,0x20,0x20,0x39,0x3A,0x20,0x27,0x43, +0x89,0x00,0x01,0x80,0x20,0x31,0x30,0x3A,0x20,0x27,0x43,0x8A,0x00,0x01,0x80,0x20, +0x31,0x31,0x3A,0x20,0x27,0x43,0x8B,0x00,0x01,0x80,0x20,0x31,0x32,0x3A,0x20,0x27, +0x43,0x8C,0x00,0x01,0x80,0x20,0x31,0x33,0x3A,0x20,0x27,0x43,0x8D,0x00,0x01,0x80, +0x20,0x31,0x34,0x3A,0x20,0x27,0x43,0x8E,0x00,0x01,0x80,0x20,0x31,0x35,0x3A,0x20, +0x27,0x43,0x8F,0x00,0x2A,0x2A,0x20,0x4D,0x61,0x69,0x6E,0x20,0x20,0x4D,0x65,0x6E, +0x75,0x20,0x2A,0x2A,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x50, +0x6F,0x72,0x74,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x53,0x69, +0x67,0x6E,0x61,0x6C,0x00,0x45,0x73,0x74,0x69,0x6D,0x61,0x74,0x65,0x20,0x43,0x50, +0x53,0x00,0x44,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x73,0x00,0x4C,0x6F, +0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x52,0x65,0x6D, +0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x49,0x6E,0x74, +0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x54,0x72,0x61, +0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00,0x42,0x61, +0x75,0x64,0x20,0x52,0x61,0x74,0x65,0x00,0x44,0x61,0x74,0x61,0x20,0x42,0x69,0x74, +0x73,0x00,0x53,0x74,0x6F,0x70,0x20,0x42,0x69,0x74,0x73,0x00,0x50,0x61,0x72,0x69, +0x74,0x79,0x00,0x44,0x61,0x74,0x61,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00, +0x54,0x78,0x20,0x46,0x6C,0x6F,0x77,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x00, +0x50,0x6F,0x72,0x74,0x20,0x4E,0x75,0x6D,0x62,0x65,0x72,0x00,0x35,0x30,0x00,0x37, +0x35,0x00,0x31,0x31,0x30,0x00,0x31,0x33,0x34,0x2E,0x35,0x00,0x31,0x35,0x30,0x00, +0x32,0x30,0x30,0x00,0x33,0x30,0x30,0x00,0x36,0x30,0x30,0x00,0x31,0x32,0x30,0x30, +0x00,0x31,0x38,0x30,0x30,0x00,0x32,0x30,0x30,0x30,0x00,0x32,0x34,0x30,0x30,0x00, +0x33,0x36,0x30,0x30,0x00,0x34,0x38,0x30,0x30,0x00,0x37,0x32,0x30,0x30,0x00,0x39, +0x36,0x30,0x30,0x00,0x31,0x39,0x2C,0x32,0x30,0x30,0x00,0x33,0x38,0x2C,0x34,0x30, +0x30,0x00,0x35,0x36,0x2C,0x30,0x30,0x30,0x00,0x35,0x37,0x2C,0x36,0x30,0x30,0x00, +0x36,0x34,0x2C,0x30,0x30,0x30,0x00,0x37,0x36,0x2C,0x38,0x30,0x30,0x00,0x31,0x31, +0x35,0x2C,0x32,0x30,0x30,0x00,0x37,0x20,0x62,0x69,0x74,0x73,0x00,0x38,0x20,0x62, +0x69,0x74,0x73,0x00,0x31,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x00,0x31, +0x2E,0x35,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x32,0x20,0x73, +0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x6E,0x6F,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x6F,0x64,0x64,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x65,0x76, +0x65,0x6E,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x73,0x70,0x61,0x63,0x65,0x20, +0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x6D,0x61,0x72,0x6B,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x43,0x6F,0x6C,0x75,0x6D,0x6E,0x73,0x00,0x42,0x61,0x72,0x62,0x65, +0x72,0x20,0x50,0x6F,0x6C,0x65,0x00,0x55,0x55,0x55,0x55,0x55,0x2E,0x2E,0x2E,0x00, +0x4E,0x6F,0x6E,0x65,0x00,0x58,0x6F,0x6E,0x2F,0x58,0x6F,0x66,0x66,0x00,0x43,0x54, +0x53,0x00,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x66,0x6F,0x72,0x20,0x6D,0x65, +0x6E,0x75,0x00,0x28,0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x2E,0x2E,0x2E,0x29, +0x00,0x00,0x65,0x4E,0x64,0x20,0x4F,0x66,0x20,0x43,0x6F,0x44,0x65,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/drivers/char/ip2/i2cmd.c b/drivers/char/ip2/i2cmd.c new file mode 100644 index 000000000..ce4216631 --- /dev/null +++ b/drivers/char/ip2/i2cmd.c @@ -0,0 +1,264 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable +* only when the standard loadware is active. (This is included +* source code, not a separate compilation module.) +* +*******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects additional commands. +// 24 February 1992 MAG Additional commands for 1.4.x loadware +// 11 March 1992 MAG Additional commands +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a +// packet: affects implementation. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include "i2cmd.h" /* To get some bit-defines */ + +//------------------------------------------------------------------------------ +// Here is the table of global arrays which represent each type of command +// supported in the IntelliPort standard loadware. See also i2cmd.h +// for a more complete explanation of what is going on. +//------------------------------------------------------------------------------ + +// Here are the various globals: note that the names are not used except through +// the macros defined in i2cmd.h. Also note that although they are character +// arrays here (for extendability) they are cast to structure pointers in the +// i2cmd.h macros. See i2cmd.h for flags definitions. + +// Length Flags Command +static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP +static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN +static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP +static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN +static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL +static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD +static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS +static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP +static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY +static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON +static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF +static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL +static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK +//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET +static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS +static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS +static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB +static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB +static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB +static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB +static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB +static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB +static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB +static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB +static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB +static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB +//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE +//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved +static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB +static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB +static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB +static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB +static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE +static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK +static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE +//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE + +// The following is listed for completeness, but should never be sent directly +// by user-level code. It is sent only by library routines in response to data +// movement. +//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET + +// Back to normal +static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ +//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON +//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF +static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME +//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD +//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD +//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING +//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB +//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB +static UCHAR ct47[] = { 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS +//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB +//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB +//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB +//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB +//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET +static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP +static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1 +static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2 +static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE +static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND +static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND +static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK +static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ +//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP +//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT +static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON +static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF +//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS +//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT +//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL +//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL +//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF +//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB +//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB +//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB +//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB +//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL +//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA +//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON +//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF +//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC +static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW +//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH +//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS +//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT +//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT +//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS +//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT +//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE +static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST +//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD +//static UCHAR ct89[]={ 1, BYP, 0x59 }; // DSS_NOW +//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO +//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break + +// Some composite commands as well +//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP +//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN + +//******** +//* Code * +//******** + +//****************************************************************************** +// Function: i2cmdSetSeq(type, size, string) +// Parameters: type - sequence number +// size - length of sequence +// string - substitution string +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 38 Define Hot Key sequence (alias +// "special receive sequence"). Returns a pointer to the structure. Endeavours +// to be bullet-proof in that the sequence number is forced in range, and any +// out-of-range sizes are forced to zero. +//****************************************************************************** +cmdSyntaxPtr +i2cmdSetSeq(unsigned char type, unsigned char size, unsigned char *string) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct38; + unsigned char *pc; + + pCM->cmd[1] = ((type > 0xf) ? 0xf : type); // Sequence number + size = ((size > 0x8) ? 0 : size); // size + pCM->cmd[2] = size; + pCM->length = 3 + size; // UPDATES THE LENGTH! + + pc = &(pCM->cmd[3]); + + while(size--) { + *pc++ = *string++; + } + return pCM; +} + +//****************************************************************************** +// Function: i2cmdUnixFlags(iflag, cflag, lflag) +// Parameters: Unix tty flags +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 47 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47; + + pCM->cmd[1] = (unsigned char) iflag; + pCM->cmd[2] = (unsigned char) (iflag >> 8); + pCM->cmd[3] = (unsigned char) cflag; + pCM->cmd[4] = (unsigned char) (cflag >> 8); + pCM->cmd[5] = (unsigned char) lflag; + pCM->cmd[6] = (unsigned char) (lflag >> 8); + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudRemap(dest,src) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 53 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudRemap(unsigned char dest, unsigned char src) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct53; + + pCM->cmd[1] = dest; + pCM->cmd[2] = src; + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudDef(which, rate) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of commands 54 or 55 (according to the +// argument which), and returns a pointer to the appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudDef(int which, unsigned short rate) +{ + cmdSyntaxPtr pCM; + + switch(which) + { + case 1: + pCM = (cmdSyntaxPtr) ct54; + break; + default: + case 2: + pCM = (cmdSyntaxPtr) ct55; + break; + } + pCM->cmd[1] = (unsigned char) rate; + pCM->cmd[2] = (unsigned char) (rate >> 8); + return pCM; +} + diff --git a/drivers/char/ip2/i2cmd.h b/drivers/char/ip2/i2cmd.h new file mode 100644 index 000000000..83475167f --- /dev/null +++ b/drivers/char/ip2/i2cmd.h @@ -0,0 +1,660 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions and support for In-line and Bypass commands. +* Applicable only when the standard loadware is active. +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects some new commands +// 20 February 1992 MAG CMD_HOTACK corrected: no argument. +// 24 February 1992 MAG Support added for new commands for 1.4.x loadware. +// 11 March 1992 MAG Additional commands. +// 16 March 1992 MAG Additional commands. +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Changed CMD_OPOST +// +//------------------------------------------------------------------------------ +#ifndef I2CMD_H // To prevent multiple includes +#define I2CMD_H 1 + +#include "ip2types.h" + +// This module is designed to provide a uniform method of sending commands to +// the board through command packets. The difficulty is, some commands take +// parameters, others do not. Furthermore, it is often useful to send several +// commands to the same channel as part of the same packet. (See also i2pack.h.) +// +// This module is designed so that the caller should not be responsible for +// remembering the exact syntax of each command, or at least so that the +// compiler could check things somewhat. I'll explain as we go... +// +// First, a structure which can embody the syntax of each type of command. +// +typedef struct _cmdSyntax +{ + UCHAR length; // Number of bytes in the command + UCHAR flags; // Information about the command (see below) + + // The command and its parameters, which may be of arbitrary length. Don't + // worry yet how the parameters will be initialized; macros later take care + // of it. Also, don't worry about the arbitrary length issue; this structure + // is never used to allocate space (see i2cmd.c). + UCHAR cmd[2]; +} cmdSyntax, *cmdSyntaxPtr; + +// Bit assignments for flags + +#define INL 1 // Set if suitable for inline commands +#define BYP 2 // Set if suitable for bypass commands +#define BTH (INL|BYP) // suitable for either! +#define END 4 // Set if this must be the last command in a block +#define VIP 8 // Set if this command is special in some way and really + // should only be sent from the library-level and not + // directly from user-level +#define VAR 0x10 // This command is of variable length! + +//----------------------------------- +// External declarations for i2cmd.c +//----------------------------------- +// Routine to set up parameters for the "define hot-key sequence" command. Since +// there is more than one parameter to assign, we must use a function rather +// than a macro (used usually). +// +extern cmdSyntaxPtr i2cmdSetSeq(UCHAR seqno, UCHAR size, UCHAR *string); +extern cmdSyntaxPtr i2cmdUnixFlags(USHORT iflag,USHORT cflag,USHORT lflag); +extern cmdSyntaxPtr i2cmdBaudRemap(UCHAR dest, UCHAR src); +extern cmdSyntaxPtr i2cmdBaudDef(int which, USHORT rate); + +// Declarations for the global arrays used to bear the commands and their +// arguments. +// +// Note: Since these are globals and the arguments might change, it is important +// that the library routine COPY these into buffers from whence they would be +// sent, rather than merely storing the pointers. In multi-threaded +// environments, important that the copy should obtain before any context switch +// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND +// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call. +// +static UCHAR ct02[]; +static UCHAR ct03[]; +static UCHAR ct04[]; +static UCHAR ct05[]; +static UCHAR ct06[]; +static UCHAR ct07[]; +static UCHAR ct08[]; +static UCHAR ct09[]; +static UCHAR ct10[]; +static UCHAR ct11[]; +static UCHAR ct12[]; +static UCHAR ct13[]; +static UCHAR ct14[]; +static UCHAR ct15[]; +static UCHAR ct16[]; +static UCHAR ct17[]; +static UCHAR ct18[]; +static UCHAR ct19[]; +static UCHAR ct20[]; +static UCHAR ct21[]; +static UCHAR ct22[]; +static UCHAR ct23[]; +static UCHAR ct24[]; +static UCHAR ct25[]; +static UCHAR ct26[]; +static UCHAR ct27[]; +static UCHAR ct28[]; +static UCHAR ct29[]; +static UCHAR ct30[]; +static UCHAR ct31[]; +static UCHAR ct32[]; +static UCHAR ct33[]; +static UCHAR ct34[]; +static UCHAR ct35[]; +static UCHAR ct36[]; +static UCHAR ct36a[]; +static UCHAR ct41[]; +static UCHAR ct42[]; +static UCHAR ct43[]; +static UCHAR ct44[]; +static UCHAR ct45[]; +static UCHAR ct46[]; +static UCHAR ct48[]; +static UCHAR ct49[]; +static UCHAR ct50[]; +static UCHAR ct51[]; +static UCHAR ct52[]; +static UCHAR ct56[]; +static UCHAR ct57[]; +static UCHAR ct58[]; +static UCHAR ct59[]; +static UCHAR ct60[]; +static UCHAR ct61[]; +static UCHAR ct62[]; +static UCHAR ct63[]; +static UCHAR ct64[]; +static UCHAR ct65[]; +static UCHAR ct66[]; +static UCHAR ct67[]; +static UCHAR ct68[]; +static UCHAR ct69[]; +static UCHAR ct70[]; +static UCHAR ct71[]; +static UCHAR ct72[]; +static UCHAR ct73[]; +static UCHAR ct74[]; +static UCHAR ct75[]; +static UCHAR ct76[]; +static UCHAR ct77[]; +static UCHAR ct78[]; +static UCHAR ct79[]; +static UCHAR ct80[]; +static UCHAR ct81[]; +static UCHAR ct82[]; +static UCHAR ct83[]; +static UCHAR ct84[]; +static UCHAR ct85[]; +static UCHAR ct86[]; +static UCHAR ct87[]; +static UCHAR ct88[]; +static UCHAR ct89[]; +static UCHAR ct90[]; +static UCHAR ct91[]; +static UCHAR cc01[]; +static UCHAR cc02[]; + +// Now, refer to i2cmd.c, and see the character arrays defined there. They are +// cast here to cmdSyntaxPtr. +// +// There are library functions for issuing bypass or inline commands. These +// functions take one or more arguments of the type cmdSyntaxPtr. The routine +// then can figure out how long each command is supposed to be and easily add it +// to the list. +// +// For ease of use, we define manifests which return pointers to appropriate +// cmdSyntaxPtr things. But some commands also take arguments. If a single +// argument is used, we define a macro which performs the single assignment and +// (through the expedient of a comma expression) references the appropriate +// pointer. For commands requiring several arguments, we actually define a +// function to perform the assignments. + +#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR +#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR +#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS +#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS +#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data + +#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS +#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS + +// Set Baud Rate for transmit and receive +#define CMD_SETBAUD(arg) \ + (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07)) + +#define CBR_50 1 +#define CBR_75 2 +#define CBR_110 3 +#define CBR_134 4 +#define CBR_150 5 +#define CBR_200 6 +#define CBR_300 7 +#define CBR_600 8 +#define CBR_1200 9 +#define CBR_1800 10 +#define CBR_2400 11 +#define CBR_4800 12 +#define CBR_9600 13 +#define CBR_19200 14 +#define CBR_38400 15 +#define CBR_2000 16 +#define CBR_3600 17 +#define CBR_7200 18 +#define CBR_56000 19 +#define CBR_57600 20 +#define CBR_64000 21 +#define CBR_76800 22 +#define CBR_115200 23 +#define CBR_C1 24 // Custom baud rate 1 +#define CBR_C2 25 // Custom baud rate 2 +#define CBR_153600 26 +#define CBR_230400 27 +#define CBR_307200 28 +#define CBR_460800 29 +#define CBR_921600 30 + +// Set Character size +// +#define CMD_SETBITS(arg) \ + (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08)) + +#define CSZ_5 0 +#define CSZ_6 1 +#define CSZ_7 2 +#define CSZ_8 3 + +// Set number of stop bits +// +#define CMD_SETSTOP(arg) \ + (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09)) + +#define CST_1 0 +#define CST_15 1 // 1.5 stop bits +#define CST_2 2 + +// Set parity option +// +#define CMD_SETPAR(arg) \ + (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10)) + +#define CSP_NP 0 // no parity +#define CSP_OD 1 // odd parity +#define CSP_EV 2 // Even parity +#define CSP_SP 3 // Space parity +#define CSP_MK 4 // Mark parity + +// Define xon char for transmitter flow control +// +#define CMD_DEF_IXON(arg) \ + (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11)) + +// Define xoff char for transmitter flow control +// +#define CMD_DEF_IXOFF(arg) \ + (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12)) + +#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data + +// Acknowledge receipt of hotkey signal +// +#define CMD_HOTACK (cmdSyntaxPtr)(ct14) + +// Define irq level to use. Should actually be sent by library-level code, not +// directly from user... +// +#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command + // is sent, board processing doesn't really start. +#define CMD_SET_IRQ(arg) \ + (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15)) + +#define CIR_POLL 0 // No IRQ - Poll +#define CIR_3 3 // IRQ 3 +#define CIR_4 4 // IRQ 4 +#define CIR_5 5 // IRQ 5 +#define CIR_7 7 // IRQ 7 +#define CIR_10 10 // IRQ 10 +#define CIR_11 11 // IRQ 11 +#define CIR_12 12 // IRQ 12 +#define CIR_15 15 // IRQ 15 + +// Select transmit flow xon/xoff options +// +#define CMD_IXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16)) + +#define CIX_NONE 0 // Incoming Xon/Xoff characters not special +#define CIX_XON 1 // Xoff disable, Xon enable +#define CIX_XANY 2 // Xoff disable, any key enable + +// Select receive flow xon/xoff options +// +#define CMD_OXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17)) + +#define COX_NONE 0 // Don't send Xon/Xoff +#define COX_XON 1 // Send xon/xoff to start/stop incoming data + + +#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting +#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting + +#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting +#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting + +#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting +#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting + +#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting +#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting + +// Enable break reporting and select style +// +#define CMD_BRK_REP(arg) \ + (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26)) + +#define CBK_STAT 0x00 // Report breaks as a status (exception,irq) +#define CBK_NULL 0x01 // Report breaks as a good null +#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character + // sequence FFh, 01h, 10h +#define CBK_SEQ 0x03 // Report breaks as the in-band + //sequence FFh, 01h, 10h ONLY. +#define CBK_FLSH 0x04 // if this bit set also flush input data +#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence +#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ + //then reports single null instead of triple + +#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting + +// Specify maximum block size for received data +// +#define CMD_MAX_BLOCK(arg) \ + (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28)) + +// -- COMMAND 29 is reserved -- + +#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control +#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control +#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control +#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control + +// Specify istrip option +// +#define CMD_ISTRIP_OPT(arg) \ + (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34)) + +#define CIS_NOSTRIP 0 // Strip characters to character size +#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits + +// Send a break of arg milliseconds +// +#define CMD_SEND_BRK(arg) \ + (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35)) + +// Set error reporting mode +// +#define CMD_SET_ERROR(arg) \ + (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36)) + +#define CSE_ESTAT 0 // Report error in a status packet +#define CSE_NOREP 1 // Treat character as though it were good +#define CSE_DROP 2 // Discard the character +#define CSE_NULL 3 // Replace with a null +#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix) + +#define CMD_SET_REPLACEMENT(arg,ch) \ + (((cmdSyntaxPtr)(ct36a))->cmd[1] = (arg), \ + (((cmdSyntaxPtr)(ct36a))->cmd[2] = (ch), \ + (cmdSyntaxPtr)(ct36a)) + +#define CSE_REPLACE 0x8 // Replace the errored character with the + // replacement character defined here + +#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the + // replacement character defined here AND + // report the error as a status packet (as in + // CSE_ESTAT). + + +// COMMAND 37, to send flow control packets, is handled only by low-level +// library code in response to data movement and shouldn't ever be sent by the +// user code. See i2pack.h and the body of i2lib.c for details. + +// COMMAND 38: Define the hot-key sequence +// seqno: sequence number 0-15 +// size: number of characters in sequence (1-8) +// string: pointer to the characters +// (if size == 0, "undefines" this sequence +// +#define CMD_SET_SEQ(seqno,size,string) i2cmdSetSeq(seqno,size,string) + +// Enable on-board post-processing, using options given in oflag argument. +// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command +// because the loadware does not permit sending back-to-back CMD_OPOST_ON +// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that +// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a +// solo packet). This means the caller must specify separately CMD_OPOST_OFF, +// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure +// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok. +// +#define CMD_OPOST_ON(oflag) \ + (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \ + (cmdSyntaxPtr)(ct39)) + +#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc + +#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON + // were received; + +// Set Transmit baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_TX(arg) \ + (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42)) + +// Set Receive baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_RX(arg) \ + (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43)) + +// Request interrupt from board each arg milliseconds. Interrupt will specify +// "received data", even though there may be no data present. If arg == 0, +// disables any such interrupts. +// +#define CMD_PING_REQ(arg) \ + (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44)) + +#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking +#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking + +// COMMAND 47: Send Protocol info via Unix flags: +// iflag = Unix tty t_iflag +// cflag = Unix tty t_cflag +// lflag = Unix tty t_lflag +// See System V Unix/Xenix documentation for the meanings of the bit fields +// within these flags +// +#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag) + +#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl +#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl +#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control +#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control +#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table + +// COMMAND 53: Remap baud rate table +// dest = index of table entry to be changed +// src = index value to substitute. +// at default mapping table is f(x) = x +// +#define CMD_BAUD_REMAP(dest,src) i2cmdBaudRemap(dest,src) + +// COMMAND 54: Define custom rate #1 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate) + +// COMMAND 55: Define custom rate #2 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate) + +// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.) +// +#define CMD_PAUSE(arg) \ + (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56)) + +#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output +#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output + +// Set parity-checking options +// +#define CMD_PARCHK(arg) \ + (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59)) + +#define CPK_ENAB 0 // Enable parity checking on input +#define CPK_DSAB 1 // Disable parity checking on input + +#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request + + +// Enable/Disable internal loopback mode +// +#define CMD_INLOOP(arg) \ + (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61)) + +#define CIN_DISABLE 0 // Normal operation (default) +#define CIN_ENABLE 1 // Internal (local) loopback +#define CIN_REMOTE 2 // Remote loopback + +// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0 +// --> no timeout: wait forever. +// +#define CMD_HOT_TIME(arg) \ + (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62)) + + +// Define (outgoing) xon for receive flow control +// +#define CMD_DEF_OXON(arg) \ + (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63)) + +// Define (outgoing) xoff for receiver flow control +// +#define CMD_DEF_OXOFF(arg) \ + (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64)) + +// Enable/Disable RTS on transmit (1/2 duplex-style) +// +#define CMD_RTS_XMIT(arg) \ + (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65)) + +#define CHD_DISABLE 0 +#define CHD_ENABLE 1 + +// Set high-water-mark level (debugging use only) +// +#define CMD_SETHIGHWAT(arg) \ + (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66)) + +// Start flushing tagged data (tag = 0-14) +// +#define CMD_START_SELFL(tag) \ + (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67)) + +// End flushing tagged data (tag = 0-14) +// +#define CMD_END_SELFL(tag) \ + (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68)) + +#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control +#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c +#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c +#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c +#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c + +// Set transmit interrupt load level. Count should be an even value 2-12 +// +#define CMD_LOADLEVEL(count) \ + (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74)) + +// If reporting DSS changes, map to character sequence FFh, 2, MSR +// +#define CMD_STATDATA(arg) \ + (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75)) + +#define CSTD_DISABLE// Report DSS changes as status packets only (default) +#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as + // by status packet. + +#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit +#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit +#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet + // from board. + +// Transmit this character immediately +// +#define CMD_XMIT_NOW(ch) \ + (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79)) + +// Set baud rate via "divisor latch" +// +#define CMD_DIVISOR_LATCH(which,value) \ + (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \ + *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \ + (cmdSyntaxPtr)(ct80)) + +#define CDL_RX 1 // Set receiver rate +#define CDL_TX 2 // Set transmit rate + // (CDL_TX | CDL_RX) Set both rates + +// Request for special diagnostic status pkt from the board. +// +#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81) + +// Request time-stamped transmit character count packet. +// +#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82) + +// Request time-stamped receive character count packet. +// +#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83) + +// Request for box/board I.D. packet. +#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84) + +// Enable or disable multiple channels according to bit-mapped ushorts box 1-4 +// +#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \ + (((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \ + (cmdSyntaxPtr)(ct85)) + +#define CEM_DISABLE 0 +#define CEM_ENABLE 1 + +// Enable or disable receiver or receiver interrupts (default both enabled) +// +#define CMD_RCV_ENABLE(ch) \ + (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86)) + +#define CRE_OFF 0 // Disable the receiver +#define CRE_ON 1 // Enable the receiver +#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware) +#define CRE_INTON 3 // Enable receiver interrupts (to loadware) + +// Starts up a hardware test process, which runs transparently, and sends a +// STAT_HWFAIL packet in case a hardware failure is detected. +// +#define CMD_HW_TEST (cmdSyntaxPtr)(ct87) + +// Change receiver threshold and timeout value: +// Defaults: timeout = 20mS +// threshold count = 8 when DTRflow not in use, +// threshold count = 5 when DTRflow in use. +// +#define CMD_RCV_THRESHOLD(count,ms) \ + (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \ + ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \ + (cmdSyntaxPtr)(ct88)) + +// Makes the loadware report DSS signals for this channel immediately. +// +#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89) + +// Set the receive silo parameters +// timeout is ms idle wait until delivery (~VTIME) +// threshold is max characters cause interrupt (~VMIN) +// +#define CMD_SET_SILO(timeout,threshold) \ + (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \ + ((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \ + (cmdSyntaxPtr)(ct90)) + +// Set timed break in decisecond (1/10s) +// +#define CMD_LBREAK(ds) \ + (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66)) + + + +#endif // I2CMD_H diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c new file mode 100644 index 000000000..c45509bcd --- /dev/null +++ b/drivers/char/ip2/i2ellis.c @@ -0,0 +1,1470 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Low-level interface code for the device driver +* (This is included source code, not a separate compilation +* module.) +* +*******************************************************************************/ +//--------------------------------------------- +// Function declarations private to this module +//--------------------------------------------- +// Functions called only indirectly through i2eBordStr entries. + +static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int); + +static unsigned short iiReadWord16(i2eBordStrPtr); +static unsigned short iiReadWord8(i2eBordStrPtr); +static void iiWriteWord16(i2eBordStrPtr, unsigned short); +static void iiWriteWord8(i2eBordStrPtr, unsigned short); + +static int iiWaitForTxEmptyII(i2eBordStrPtr, int); +static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int); +static int iiTxMailEmptyII(i2eBordStrPtr); +static int iiTxMailEmptyIIEX(i2eBordStrPtr); +static int iiTrySendMailII(i2eBordStrPtr, unsigned char); +static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char); + +static unsigned short iiGetMailII(i2eBordStrPtr); +static unsigned short iiGetMailIIEX(i2eBordStrPtr); + +static void iiEnableMailIrqII(i2eBordStrPtr); +static void iiEnableMailIrqIIEX(i2eBordStrPtr); +static void iiWriteMaskII(i2eBordStrPtr, unsigned char); +static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char); + +static void ii2DelayTimer(unsigned int); +static void ii2DelayWakeup(unsigned long id); +static void ii2Nop(void); + +//*************** +//* Static Data * +//*************** + +static int ii2Safe = 0; // Safe I/O address for delay routine + +static int iiDelayed = 0; // Set when the iiResetDelay function is + // called. Cleared when ANY board is reset. +static struct timer_list * pDelayTimer; // Used by iiDelayTimer +static wait_queue_head_t pDelayWait; // Used by iiDelayTimer +static spinlock_t Dl_spinlock; + +//******** +//* Code * +//******** + +//======================================================= +// Initialization Routines +// +// iiSetAddress +// iiReset +// iiResetDelay +// iiInitialize +//======================================================= + +//****************************************************************************** +// Function: iiEllisInit() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required initialization of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisInit(void) +{ + pDelayTimer = kmalloc ( sizeof (struct timer_list), GFP_KERNEL ); + init_waitqueue_head(&pDelayWait); + LOCK_INIT(&Dl_spinlock); +} + +//****************************************************************************** +// Function: iiEllisCleanup() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required cleanup of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisCleanup(void) +{ + if ( pDelayTimer != NULL ) { + kfree ( pDelayTimer ); + } +} + +//****************************************************************************** +// Function: iiSetAddress(pB, address, delay) +// Parameters: pB - pointer to the board structure +// address - the purported I/O address of the board +// delay - pointer to the 1-ms delay function to use +// in this and any future operations to this board +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// This routine (roughly) checks for address validity, sets the i2eValid OK and +// sets the state to II_STATE_COLD which means that we haven't even sent a reset +// yet. +// +//****************************************************************************** +static int +iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) +{ + // Should any failure occur before init is finished... + pB->i2eValid = I2E_INCOMPLETE; + + // Cannot check upper limit except extremely: Might be microchannel + // Address must be on an 8-byte boundary + + if ((unsigned int)address <= 0x100 + || (unsigned int)address >= 0xfff8 + || (address & 0x7) + ) + { + COMPLETE(pB,I2EE_BADADDR); + } + + // Initialize accelerators + pB->i2eBase = address; + pB->i2eData = address + FIFO_DATA; + pB->i2eStatus = address + FIFO_STATUS; + pB->i2ePointer = address + FIFO_PTR; + pB->i2eXMail = address + FIFO_MAIL; + pB->i2eXMask = address + FIFO_MASK; + + // Initialize i/o address for ii2DelayIO + ii2Safe = address + FIFO_NOP; + + // Initialize the delay routine + pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop); + + pB->i2eValid = I2E_MAGIC; + pB->i2eState = II_STATE_COLD; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReset(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to reset the board (see also i2hw.h). Normally, we would use this to +// reset a board immediately after iiSetAddress(), but it is valid to reset a +// board from any state, say, in order to change or re-load loadware. (Under +// such circumstances, no reason to re-run iiSetAddress(), which is why it is a +// separate routine and not included in this routine. +// +//****************************************************************************** +static int +iiReset(i2eBordStrPtr pB) +{ + // Magic number should be set, else even the address is suspect + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + OUTB(pB->i2eBase + FIFO_RESET, 0); // Any data will do + iiDelay(pB, 50); // Pause between resets + OUTB(pB->i2eBase + FIFO_RESET, 0); // Second reset + + // We must wait before even attempting to read anything from the FIFO: the + // board's P.O.S.T may actually attempt to read and write its end of the + // FIFO in order to check flags, loop back (where supported), etc. On + // completion of this testing it would reset the FIFO, and on completion + // of all // P.O.S.T., write the message. We must not mistake data which + // might have been sent for testing as part of the reset message. To + // better utilize time, say, when resetting several boards, we allow the + // delay to be performed externally; in this way the caller can reset + // several boards, delay a single time, then call the initialization + // routine for all. + + pB->i2eState = II_STATE_RESET; + + iiDelayed = 0; // i.e., the delay routine hasn't been called since the most + // recent reset. + + // Ensure anything which would have been of use to standard loadware is + // blanked out, since board has now forgotten everything!. + + pB->i2eUsingIrq = IRQ_UNDEFINED; // Not set up to use an interrupt yet + pB->i2eWaitingForEmptyFifo = 0; + pB->i2eOutMailWaiting = 0; + pB->i2eChannelPtr = NULL; + pB->i2eChannelCnt = 0; + + pB->i2eLeadoffWord[0] = 0; + pB->i2eFifoInInts = 0; + pB->i2eFifoOutInts = 0; + pB->i2eFatalTrap = NULL; + pB->i2eFatal = 0; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiResetDelay(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Using the delay defined in board structure, waits two seconds (for board to +// reset). +// +//****************************************************************************** +static int +iiResetDelay(i2eBordStrPtr pB) +{ + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_RESET) { + COMPLETE(pB, I2EE_BADSTATE); + } + iiDelay(pB,2000); /* Now we wait for two seconds. */ + iiDelayed = 1; /* Delay has been called: ok to initialize */ + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiInitialize(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to read the Power-on reset message. Initializes any remaining fields +// in the pB structure. +// +// This should be called as the third step of a process beginning with +// iiReset(), then iiResetDelay(). This routine checks to see that the structure +// is "valid" and in the reset state, also confirms that the delay routine has +// been called since the latest reset (to any board! overly strong!). +// +//****************************************************************************** +static int +iiInitialize(i2eBordStrPtr pB) +{ + int itemp; + unsigned char c; + unsigned short utemp; + unsigned int ilimit; + + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + if (pB->i2eState != II_STATE_RESET || !iiDelayed) + { + COMPLETE(pB, I2EE_BADSTATE); + } + + // In case there is a failure short of our completely reading the power-up + // message. + pB->i2eValid = I2E_INCOMPLETE; + + + // Now attempt to read the message. + + for (itemp = 0; itemp < sizeof(porStr); itemp++) + { + // We expect the entire message is ready. + if (HAS_NO_INPUT(pB)) + { + pB->i2ePomSize = itemp; + COMPLETE(pB, I2EE_PORM_SHORT); + } + + pB->i2ePom.c[itemp] = c = BYTE_FROM(pB); + + // We check the magic numbers as soon as they are supposed to be read + // (rather than after) to minimize effect of reading something we + // already suspect can't be "us". + if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) || + (itemp == POR_2_INDEX && c != POR_MAGIC_2)) + { + pB->i2ePomSize = itemp+1; + COMPLETE(pB, I2EE_BADMAGIC); + } + } + + pB->i2ePomSize = itemp; + + // Ensure that this was all the data... + if (HAS_INPUT(pB)) + COMPLETE(pB, I2EE_PORM_LONG); + + // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: + // Implying we will not be able to download any code either: That's ok: the + // condition is pretty explicit. + if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) + { + COMPLETE(pB, I2EE_POSTERR); + } + + // Determine anything which must be done differently depending on the family + // of boards! + switch (pB->i2ePom.e.porID & POR_ID_FAMILY) + { + case POR_ID_FII: // IntelliPort-II + + pB->i2eFifoStyle = FIFO_II; + pB->i2eFifoSize = 512; // 512 bytes, always + pB->i2eDataWidth16 = NO; + + pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit + // slot, we do allow it to be done (documentation!) + + pB->i2eGoodMap[1] = + pB->i2eGoodMap[2] = + pB->i2eGoodMap[3] = + pB->i2eChannelMap[1] = + pB->i2eChannelMap[2] = + pB->i2eChannelMap[3] = 0; + + switch (pB->i2ePom.e.porID & POR_ID_SIZE) + { + case POR_ID_II_4: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x0f; // four-port + + // Since porPorts1 is based on the Hardware ID register, the numbers + // should always be consistent for IntelliPort-II. Ditto below... + if (pB->i2ePom.e.porPorts1 != 4) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_8: + case POR_ID_II_8R: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0xff; // Eight port + if (pB->i2ePom.e.porPorts1 != 8) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_6: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x3f; // Six Port + if (pB->i2ePom.e.porPorts1 != 6) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + } + + // Fix up the "good channel list based on any errors reported. + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1) + { + pB->i2eGoodMap[0] &= ~0x0f; + } + + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2) + { + pB->i2eGoodMap[0] &= ~0xf0; + } + + break; // POR_ID_FII case + + case POR_ID_FIIEX: // IntelliPort-IIEX + + pB->i2eFifoStyle = FIFO_IIEX; + + itemp = pB->i2ePom.e.porFifoSize; + + // Implicit assumption that fifo would not grow beyond 32k, + // nor would ever be less than 256. + + if (itemp < 8 || itemp > 15) + { + COMPLETE(pB, I2EE_INCONSIST); + } + pB->i2eFifoSize = (1 << itemp); + + // These are based on what P.O.S.T thinks should be there, based on + // box ID registers + ilimit = pB->i2ePom.e.porNumBoxes; + if (ilimit > ABS_MAX_BOXES) + { + ilimit = ABS_MAX_BOXES; + } + + // For as many boxes as EXIST, gives the type of box. + // Added 8/6/93: check for the ISA-4 (asic) which looks like an + // expandable but for whom "8 or 16?" is not the right question. + + utemp = pB->i2ePom.e.porFlags; + if (utemp & POR_CEX4) + { + pB->i2eChannelMap[0] = 0x000f; + } else { + utemp &= POR_BOXES; + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eChannelMap[itemp] = + ((utemp & POR_BOX_16) ? 0xffff : 0x00ff); + utemp >>= 1; + } + } + + // These are based on what P.O.S.T actually found. + + utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1; + + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eGoodMap[itemp] = 0; + if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f; + if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0; + if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00; + if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000; + utemp >>= 4; + } + + // Now determine whether we should transfer in 8 or 16-bit mode. + switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) + { + case POR_BUS_SLOT16 | POR_BUS_DIP16: + pB->i2eDataWidth16 = YES; + pB->i2eMaxIrq = 15; + break; + + case POR_BUS_SLOT16: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 15; + break; + + case 0: + case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. + default: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 7; + break; + } + break; // POR_ID_FIIEX case + + default: // Unknown type of board + COMPLETE(pB, I2EE_BAD_FAMILY); + break; + } // End the switch based on family + + // Temporarily, claim there is no room in the outbound fifo. + // We will maintain this whenever we check for an empty outbound FIFO. + pB->i2eFifoRemains = 0; + + // Now, based on the bus type, should we expect to be able to re-configure + // interrupts (say, for testing purposes). + switch (pB->i2ePom.e.porBus & POR_BUS_TYPE) + { + case POR_BUS_T_ISA: + case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. + pB->i2eChangeIrq = YES; + break; + case POR_BUS_T_MCA: + case POR_BUS_T_EISA: + pB->i2eChangeIrq = NO; + break; + default: + COMPLETE(pB, I2EE_BADBUS); + } + + if (pB->i2eDataWidth16 == YES) + { + pB->i2eWriteBuf = iiWriteBuf16; + pB->i2eReadBuf = iiReadBuf16; + pB->i2eWriteWord = iiWriteWord16; + pB->i2eReadWord = iiReadWord16; + } else { + pB->i2eWriteBuf = iiWriteBuf8; + pB->i2eReadBuf = iiReadBuf8; + pB->i2eWriteWord = iiWriteWord8; + pB->i2eReadWord = iiReadWord8; + } + + switch(pB->i2eFifoStyle) + { + case FIFO_II: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII; + pB->i2eTxMailEmpty = iiTxMailEmptyII; + pB->i2eTrySendMail = iiTrySendMailII; + pB->i2eGetMail = iiGetMailII; + pB->i2eEnableMailIrq = iiEnableMailIrqII; + pB->i2eWriteMask = iiWriteMaskII; + + break; + + case FIFO_IIEX: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX; + pB->i2eTxMailEmpty = iiTxMailEmptyIIEX; + pB->i2eTrySendMail = iiTrySendMailIIEX; + pB->i2eGetMail = iiGetMailIIEX; + pB->i2eEnableMailIrq = iiEnableMailIrqIIEX; + pB->i2eWriteMask = iiWriteMaskIIEX; + + break; + + default: + COMPLETE(pB, I2EE_INCONSIST); + } + + // Initialize state information. + pB->i2eState = II_STATE_READY; // Ready to load loadware. + + // Some Final cleanup: + // For some boards, the bootstrap firmware may perform some sort of test + // resulting in a stray character pending in the incoming mailbox. If one is + // there, it should be read and discarded, especially since for the standard + // firmware, it's the mailbox that interrupts the host. + + pB->i2eStartMail = iiGetMail(pB); + + // Everything is ok now, return with good status/ + + pB->i2eValid = I2E_MAGIC; + COMPLETE(pB, I2EE_GOOD); +} + +//======================================================= +// Delay Routines +// +// iiDelayIO +// iiNop +//======================================================= + +static void +ii2DelayWakeup(unsigned long id) +{ + wake_up_interruptible ( &pDelayWait ); +} + +//****************************************************************************** +// Function: ii2DelayTimer(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It uses the +// Linux timer_list mechanism. +// +// The Linux timers use a unit called "jiffies" which are 10mS in the Intel +// architecture. This function rounds the delay period up to the next "jiffy". +// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended +// for Alpha platforms at this time. +// +//****************************************************************************** +static void +ii2DelayTimer(unsigned int mseconds) +{ + init_timer ( pDelayTimer ); + + pDelayTimer->expires = jiffies + ( mseconds + 9 ) / 10; + pDelayTimer->function = ii2DelayWakeup; + pDelayTimer->data = 0; + + add_timer ( pDelayTimer ); + interruptible_sleep_on ( &pDelayWait ); + del_timer ( pDelayTimer ); +} + +#if 0 +//static void ii2DelayIO(unsigned int); +//****************************************************************************** +// !!! Not Used, this is DOS crap, some of you young folks may be interested in +// in how things were done in the stone age of caculating machines !!! +// Function: ii2DelayIO(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It is intended +// for use where a clock-based function is impossible: for example, DOS drivers. +// +// This function uses the IN instruction to place bounds on the timing and +// assumes that ii2Safe has been set. This is because I/O instructions are not +// subject to caching and will therefore take a certain minimum time. To ensure +// the delay is at least long enough on fast machines, it is based on some +// fastest-case calculations. On slower machines this may cause VERY long +// delays. (3 x fastest case). In the fastest case, everything is cached except +// the I/O instruction itself. +// +// Timing calculations: +// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O +// operation in question is a byte operation to an odd address. For 8-bit +// operations, the architecture generally enforces two wait states. At 10 MHz, a +// single cycle time is 100nS. A read operation at two wait states takes 6 +// cycles for a total time of 600nS. Therefore approximately 1666 iterations +// would be required to generate a single millisecond delay. The worst +// (reasonable) case would be an 8MHz system with no cacheing. In this case, the +// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code +// fetch of other instructions in the loop would take time (zero wait states, +// however) and would be hard to estimate. This is minimized by using in-line +// assembler for the in inner loop of IN instructions. This consists of just a +// few bytes. So we'll guess about four code fetches per loop. Each code fetch +// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is +// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS. +// +// So much for theoretical timings: results using 1666 value on some actual +// machines: +// IBM 286 6MHz 3.15 mS +// Zenith 386 33MHz 2.45 mS +// (brandX) 386 33MHz 1.90 mS (has cache) +// (brandY) 486 33MHz 2.35 mS +// NCR 486 ?? 1.65 mS (microchannel) +// +// For most machines, it is probably safe to scale this number back (remember, +// for robust operation use an actual timed delay if possible), so we are using +// a value of 1190. This yields 1.17 mS for the fastest machine in our sample, +// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine. +// +// 1/29/93: +// The above timings are too slow. Actual cycle times might be faster. ISA cycle +// times could approach 500 nS, and ... +// The IBM model 77 being microchannel has no wait states for 8-bit reads and +// seems to be accessing the I/O at 440 nS per access (from start of one to +// start of next). This would imply we need 1000/.440 = 2272 iterations to +// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in +// fact enough. For diagnostics, we keep the level at 1190, but developers note +// this needs tuning. +// +// Safe assumption: 2270 i/o reads = 1 millisecond +// +//****************************************************************************** + + +static int ii2DelValue = 1190; // See timing calculations below + // 1666 for fastest theoretical machine + // 1190 safe for most fast 386 machines + // 1000 for fastest machine tested here + // 540 (sic) for AT286/6Mhz +static void +ii2DelayIO(unsigned int mseconds) +{ + if (!ii2Safe) + return; /* Do nothing if this variable uninitialized */ + + while(mseconds--) { + int i = ii2DelValue; + while ( i-- ) { + INB ( ii2Safe ); + } + } +} +#endif + +//****************************************************************************** +// Function: ii2Nop() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This +// saves checking for a NULL pointer at every call. +//****************************************************************************** +static void +ii2Nop(void) +{ + return; // no mystery here +} + +//======================================================= +// Routines which are available in 8/16-bit versions, or +// in different fifo styles. These are ALL called +// indirectly through the board structure. +//======================================================= + +//****************************************************************************** +// Function: iiWriteBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiWriteBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). This is to be consistant with the 16-bit version. +// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + /* Rudimentary sanity checking here */ + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSB ( pB->i2eData, address, count ); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). This to match the 16-bit behaviour. Uses +// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSB ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadWord16(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses a 16-bit operation. Is called indirectly through +// pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord16(i2eBordStrPtr pB) +{ + return (unsigned short)( INW(pB->i2eData) ); +} + +//****************************************************************************** +// Function: iiReadWord8(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is +// called indirectly through pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord8(i2eBordStrPtr pB) +{ + unsigned short urs; + + urs = INB ( pB->i2eData ); + + return ( ( INB ( pB->i2eData ) << 8 ) | urs ); +} + +//****************************************************************************** +// Function: iiWriteWord16(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses 16-bit operation. Is called indirectly through +// pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord16(i2eBordStrPtr pB, unsigned short value) +{ + WORD_TO(pB, (int)value); +} + +//****************************************************************************** +// Function: iiWriteWord8(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations (writes LSB first). Is called +// indirectly through pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord8(i2eBordStrPtr pB, unsigned short value) +{ + BYTE_TO(pB, (char)value); + BYTE_TO(pB, (char)(value >> 8) ); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyII(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-II - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + int itemp; + + for (;;) + { + // This routine hinges on being able to see the "other" status register + // (as seen by the local processor). His incoming fifo is our outgoing + // FIFO. + // + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + // + // Even worse, however, this routine "shifts" the status register to + // point to the local status register which is not the usual situation. + // Therefore for extra safety, we force the critical section to be + // completely atomic, and pick up after ourselves before allowing any + // interrupts of any kind. + + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_SH); + + itemp = INB(pB->i2eStatus); + + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_UNSH); + + if (itemp & ST_IN_EMPTY) + { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); /* 1 mS granularity on checking condition */ + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyIIEX(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + + for (;;) + { + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + + if (INB(pB->i2eStatus) & STE_OUT_MT) { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); // 1 mS granularity on checking condition + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiTxMailEmptyII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyII(i2eBordStrPtr pB) +{ + int port = pB->i2ePointer; + OUTB ( port, SEL_OUTMAIL ); + return ( INB(port) == 0 ); +} + +//****************************************************************************** +// Function: iiTxMailEmptyIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyIIEX(i2eBordStrPtr pB) +{ + return !(INB(pB->i2eStatus) & STE_OUT_MAIL); +} + +//****************************************************************************** +// Function: iiTrySendMailII(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) +{ + int port = pB->i2ePointer; + + OUTB(port, SEL_OUTMAIL); + if (INB(port) == 0) { + OUTB(port, SEL_OUTMAIL); + OUTB(port, mail); + return 1; + } + return 0; +} + +//****************************************************************************** +// Function: iiTrySendMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) +{ + if(INB(pB->i2eStatus) & STE_OUT_MAIL) { + return 0; + } + OUTB(pB->i2eXMail, mail); + return 1; +} + +//****************************************************************************** +// Function: iiGetMailII(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailII(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + OUTB(pB->i2ePointer, SEL_INMAIL); + return INB(pB->i2ePointer); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiGetMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailIIEX(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + return INB(pB->i2eXMail); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiEnableMailIrqII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqII(i2eBordStrPtr pB) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, ST_IN_MAIL); +} + +//****************************************************************************** +// Function: iiEnableMailIrqIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqIIEX(i2eBordStrPtr pB) +{ + OUTB(pB->i2eXMask, MX_IN_MAIL); +} + +//****************************************************************************** +// Function: iiWriteMaskII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, value); +} + +//****************************************************************************** +// Function: iiWriteMaskIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2eXMask, value); +} + +//****************************************************************************** +// Function: iiDownloadBlock(pB, pSource, isStandard) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// +// Returns: Success or Failure +// +// Description: +// +// Downloads a single block (at pSource)to the board referenced by pB. Caller +// sets isStandard to true/false according to whether the "standard" loadware is +// what's being loaded. The normal process, then, is to perform an iiInitialize +// to the board, then perform some number of iiDownloadBlocks using the returned +// state to determine when download is complete. +// +// Possible return values: (see I2ELLIS.H) +// II_DOWN_BADVALID +// II_DOWN_BADFILE +// II_DOWN_CONTINUING +// II_DOWN_GOOD +// II_DOWN_BAD +// II_DOWN_BADSTATE +// II_DOWN_TIMEOUT +// +// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to +// determine whether this is the first block, whether to check for magic +// numbers, how many blocks there are to go... +// +//****************************************************************************** +static int +iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) +{ + int itemp; + int loadedFirst; + + if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID; + + switch(pB->i2eState) + { + case II_STATE_READY: + + // Loading the first block after reset. Must check the magic number of the + // loadfile, store the number of blocks we expect to load. + if (pSource->e.loadMagic != MAGIC_LOADFILE) + { + return II_DOWN_BADFILE; + } + + // Next we store the total number of blocks to load, including this one. + pB->i2eToLoad = 1 + pSource->e.loadBlocksMore; + + // Set the state, store the version numbers. ('Cause this may have come + // from a file - we might want to report these versions and revisions in + // case of an error! + pB->i2eState = II_STATE_LOADING; + pB->i2eLVersion = pSource->e.loadVersion; + pB->i2eLRevision = pSource->e.loadRevision; + pB->i2eLSub = pSource->e.loadSubRevision; + + // The time and date of compilation is also available but don't bother + // storing it for normal purposes. + loadedFirst = 1; + break; + + case II_STATE_LOADING: + loadedFirst = 0; + break; + + default: + return II_DOWN_BADSTATE; + } + + // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad + // must be positive still, because otherwise we would have cleaned up last + // time and set the state to II_STATE_LOADED. + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + + if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) { + return II_DOWN_BADVALID; + } + + // If we just loaded the first block, wait for the fifo to empty an extra + // long time to allow for any special startup code in the firmware, like + // sending status messages to the LCD's. + + if (loadedFirst) { + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) { + return II_DOWN_TIMEOUT; + } + } + + // Determine whether this was our last block! + if (--(pB->i2eToLoad)) { + return II_DOWN_CONTINUING; // more to come... + } + + // It WAS our last block: Clean up operations... + // ...Wait for last buffer to drain from the board... + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + // If there were only a single block written, this would come back + // immediately and be harmless, though not strictly necessary. + itemp = MAX_DLOAD_ACK_TIME/10; + while (--itemp) { + if (HAS_INPUT(pB)) { + switch(BYTE_FROM(pB)) + { + case LOADWARE_OK: + pB->i2eState = + isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; + + // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2) + // will, // if there is a debug port attached, require some + // time to send information to the debug port now. It will do + // this before // executing any of the code we just downloaded. + // It may take up to 700 milliseconds. + if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) { + iiDelay(pB, 700); + } + + return II_DOWN_GOOD; + + case LOADWARE_BAD: + default: + return II_DOWN_BAD; + } + } + + iiDelay(pB, 10); // 10 mS granularity on checking condition + } + + // Drop-through --> timed out waiting for firmware confirmation + + pB->i2eState = II_STATE_BADLOAD; + return II_DOWN_TIMEOUT; +} + +//****************************************************************************** +// Function: iiDownloadAll(pB, pSource, isStandard, size) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// size - size of data to download (in bytes) +// +// Returns: Success or Failure +// +// Description: +// +// Given a pointer to a board structure, a pointer to the beginning of some +// loadware, whether it is considered the "standard loadware", and the size of +// the array in bytes loads the entire array to the board as loadware. +// +// Assumes the board has been freshly reset and the power-up reset message read. +// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be +// too much or too little data to load, or if iiDownloadBlock complains. +//****************************************************************************** +static int +iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size) +{ + int status; + + // We know (from context) board should be ready for the first block of + // download. Complain if not. + if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE; + + while (size > 0) { + size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to + // load after the following operation ? + + // Note we just bump pSource by "one", because its size is actually that + // of an entire block, same as LOADWARE_BLOCK_SIZE. + status = iiDownloadBlock(pB, pSource++, isStandard); + + switch(status) + { + case II_DOWN_GOOD: + return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD); + + case II_DOWN_CONTINUING: + break; + + default: + return status; + } + } + + // We shouldn't drop out: it means "while" caught us with nothing left to + // download, yet the previous DownloadBlock did not return complete. Ergo, + // not enough data to match the size byte in the header. + return II_DOWN_UNDER; +} diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h new file mode 100644 index 000000000..56fd03fd2 --- /dev/null +++ b/drivers/char/ip2/i2ellis.h @@ -0,0 +1,609 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// i2ellis.h +// +// IntelliPort-II and IntelliPort-IIEX +// +// Extremely +// Low +// Level +// Interface +// Services +// +// Structure Definitions and declarations for "ELLIS" service routines found in +// i2ellis.c +// +// These routines are based on properties of the IntelliPort-II and -IIEX +// hardware and bootstrap firmware, and are not sensitive to particular +// conventions of any particular loadware. +// +// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material +// here and in i2ellis.c is intended to provice a useful, but not required, +// layer of insulation from the hardware specifics. +//------------------------------------------------------------------------------ +#ifndef I2ELLIS_H /* To prevent multiple includes */ +#define I2ELLIS_H 1 +//------------------------------------------------ +// Revision History: +// +// 30 September 1991 MAG First Draft Started +// 12 October 1991 ...continued... +// +// 20 December 1996 AKM Linux version +//------------------------------------------------- + +//---------------------- +// Mandatory Includes: +//---------------------- +#include "ip2types.h" +#include "i2hw.h" // The hardware definitions + +//------------------------------------------ +// STAT_BOXIDS packets +//------------------------------------------ +#define MAX_BOX 4 + +typedef struct _bidStat +{ + unsigned char bid_value[MAX_BOX]; +} bidStat, *bidStatPtr; + +// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX +// boards, reports the hardware-specific "asynchronous resource register" on +// each expansion box. Boxes not present report 0xff. For -II boards, the first +// element contains 0x80 for 8-port, 0x40 for 4-port boards. + +// Box IDs aka ARR or Async Resource Register (more than you want to know) +// 7 6 5 4 3 2 1 0 +// F F N N L S S S +// ============================= +// F F - Product Family Designator +// =====+++++++++++++++++++++++++++++++ +// 0 0 - Intelliport II EX / ISA-8 +// 1 0 - IntelliServer +// 0 1 - SAC - Port Device (Intelliport III ??? ) +// =====+++++++++++++++++++++++++++++++++++++++ +// N N - Number of Ports +// 0 0 - 8 (eight) +// 0 1 - 4 (four) +// 1 0 - 12 (twelve) +// 1 1 - 16 (sixteen) +// =++++++++++++++++++++++++++++++++++ +// L - LCD Display Module Present +// 0 - No +// 1 - LCD module present +// =========+++++++++++++++++++++++++++++++++++++ +// S S S - Async Signals Supported Designator +// 0 0 0 - 8dss, Mod DCE DB25 Female +// 0 0 1 - 6dss, RJ-45 +// 0 1 0 - RS-232/422 dss, DB25 Female +// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female +// 1 0 0 - 6dss, 921.6 I/F with ST654's +// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin +// 1 1 0 - 6dss, Mod DCE DB25 Female +// 1 1 1 - NO BOX PRESENT + +#define FF(c) ((c & 0xC0) >> 6) +#define NN(c) ((c & 0x30) >> 4) +#define L(c) ((c & 0x08) >> 3) +#define SSS(c) (c & 0x07) + +#define BID_HAS_654(x) (SSS(x) == 0x04) +#define BID_NO_BOX 0xff /* no box */ +#define BID_8PORT 0x80 /* IP2-8 port */ +#define BID_4PORT 0x81 /* IP2-4 port */ +#define BID_EXP_MASK 0x30 /* IP2-EX */ +#define BID_EXP_8PORT 0x00 /* 8, */ +#define BID_EXP_4PORT 0x10 /* 4, */ +#define BID_EXP_UNDEF 0x20 /* UNDEF, */ +#define BID_EXP_16PORT 0x30 /* 16, */ +#define BID_LCD_CTRL 0x08 /* LCD Controller */ +#define BID_LCD_NONE 0x00 /* - no controller present */ +#define BID_LCD_PRES 0x08 /* - controller present */ +#define BID_CON_MASK 0x07 /* - connector pinouts */ +#define BID_CON_DB25 0x00 /* - DB-25 F */ +#define BID_CON_RJ45 0x01 /* - rj45 */ + +//------------------------------------------------------------------------------ +// i2eBordStr +// +// This structure contains all the information the ELLIS routines require in +// dealing with a particular board. +//------------------------------------------------------------------------------ +// There are some queues here which are guaranteed to never contain the entry +// for a single channel twice. So they must be slightly larger to allow +// unambiguous full/empty management +// +#define CH_QUEUE_SIZE ABS_MOST_PORTS+2 + +typedef struct _i2eBordStr +{ + porStr i2ePom; // Structure containing the power-on message. + + unsigned short i2ePomSize; + // The number of bytes actually read if + // different from sizeof i2ePom, indicates + // there is an error! + + unsigned short i2eStartMail; + // Contains whatever inbound mailbox data + // present at startup. NO_MAIL_HERE indicates + // nothing was present. No special + // significance as of this writing, but may be + // useful for diagnostic reasons. + + unsigned short i2eValid; + // Indicates validity of the structure; if + // i2eValid == I2E_MAGIC, then we can trust + // the other fields. Some (especially + // initialization) functions are good about + // checking for validity. Many functions do + // not, it being assumed that the larger + // context assures we are using a valid + // i2eBordStrPtr. + + unsigned short i2eError; + // Used for returning an error condition from + // several functions which use i2eBordStrPtr + // as an argument. + + // Accelerators to characterize separate features of a board, derived from a + // number of sources. + + unsigned short i2eFifoSize; + // Always, the size of the FIFO. For + // IntelliPort-II, always the same, for -IIEX + // taken from the Power-On reset message. + + volatile + unsigned short i2eFifoRemains; + // Used during normal operation to indicate a + // lower bound on the amount of data which + // might be in the outbound fifo. + + unsigned char i2eFifoStyle; + // Accelerator which tells which style (-II or + // -IIEX) FIFO we are using. + + unsigned char i2eDataWidth16; + // Accelerator which tells whether we should + // do 8 or 16-bit data transfers. + + unsigned char i2eMaxIrq; + // The highest allowable IRQ, based on the + // slot size. + + unsigned char i2eChangeIrq; + // Whether tis valid to change IRQ's + // ISA = ok, EISA, MicroChannel, no + + // Accelerators for various addresses on the board + int i2eBase; // I/O Address of the Board + int i2eData; // From here data transfers happen + int i2eStatus; // From here status reads happen + int i2ePointer; // (IntelliPort-II: pointer/commands) + int i2eXMail; // (IntelliPOrt-IIEX: mailboxes + int i2eXMask; // (IntelliPort-IIEX: mask write + + //------------------------------------------------------- + // Information presented in a common format across boards + // For each box, bit map of the channels present. Box closest to + // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable) + // is taken to be box 0. These are derived from product i.d. registers. + + unsigned short i2eChannelMap[ABS_MAX_BOXES]; + + // Same as above, except each is derived from firmware attempting to detect + // the uart presence (by reading a valid GFRCR register). If bits are set in + // i2eChannelMap and not in i2eGoodMap, there is a potential problem. + + unsigned short i2eGoodMap[ABS_MAX_BOXES]; + + // --------------------------- + // For indirect function calls + + // Routine to cause an N-millisecond delay: Patched by the ii2Initialize + // function. + + void (*i2eDelay)(unsigned int); + + // Routine to write N bytes to the board through the FIFO. Returns true if + // all copacetic, otherwise returns false and error is in i2eError field. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Routine to read N bytes from the board through the FIFO. Returns true if + // copacetic, otherwise returns false and error in i2eError. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Returns a word from FIFO. Will use 2 byte operations if needed. + + unsigned short (*i2eReadWord)(struct _i2eBordStr *); + + // Writes a word to FIFO. Will use 2 byte operations if needed. + + void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short); + + // Waits specified time for the Transmit FIFO to go empty. Returns true if + // ok, otherwise returns false and error in i2eError. + + int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int); + + // Returns true or false according to whether the outgoing mailbox is empty. + + int (*i2eTxMailEmpty)(struct _i2eBordStr *); + + // Checks whether outgoing mailbox is empty. If so, sends mail and returns + // true. Otherwise returns false. + + int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char); + + // If no mail available, returns NO_MAIL_HERE, else returns the value in the + // mailbox (guaranteed can't be NO_MAIL_HERE). + + unsigned short (*i2eGetMail)(struct _i2eBordStr *); + + // Enables the board to interrupt the host when it writes to the mailbox. + // Irqs will not occur, however, until the loadware separately enables + // interrupt generation to the host. The standard loadware does this in + // response to a command packet sent by the host. (Also, disables + // any other potential interrupt sources from the board -- other than the + // inbound mailbox). + + void (*i2eEnableMailIrq)(struct _i2eBordStr *); + + // Writes an arbitrary value to the mask register. + + void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char); + + + // State information + + // During downloading, indicates the number of blocks remaining to download + // to the board. + + short i2eToLoad; + + // State of board (see manifests below) (e.g., whether in reset condition, + // whether standard loadware is installed, etc. + + unsigned char i2eState; + + // These three fields are only valid when there is loadware running on the + // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED ) + + unsigned char i2eLVersion; // Loadware version + unsigned char i2eLRevision; // Loadware revision + unsigned char i2eLSub; // Loadware subrevision + + // Flags which only have meaning in the context of the standard loadware. + // Somewhat violates the layering concept, but there is so little additional + // needed at the board level (while much additional at the channel level), + // that this beats maintaining two different per-board structures. + + // Indicates which IRQ the board has been initialized (from software) to use + // For MicroChannel boards, any value different from IRQ_UNDEFINED means + // that the software command has been sent to enable interrupts (or specify + // they are disabled). Special value: IRQ_UNDEFINED indicates that the + // software command to select the interrupt has not yet been sent, therefore + // (since the standard loadware insists that it be sent before any other + // packets are sent) no other packets should be sent yet. + + unsigned short i2eUsingIrq; + + // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us + // putting more in the mailbox until an appropriate mailbox message is + // received. + + unsigned char i2eWaitingForEmptyFifo; + + // Any mailbox bits waiting to be sent to the board are OR'ed in here. + + unsigned char i2eOutMailWaiting; + + // The head of any incoming packet is read into here, is then examined and + // we dispatch accordingly. + + unsigned short i2eLeadoffWord[1]; + + // Running counter of interrupts where the mailbox indicated incoming data. + + unsigned short i2eFifoInInts; + + // Running counter of interrupts where the mailbox indicated outgoing data + // had been stripped. + + unsigned short i2eFifoOutInts; + + // If not void, gives the address of a routine to call if fatal board error + // is found (only applies to standard l/w). + + void (*i2eFatalTrap)(struct _i2eBordStr *); + + // Will point to an array of some sort of channel structures (whose format + // is unknown at this level, being a function of what loadware is + // installed and the code configuration (max sizes of buffers, etc.)). + + void *i2eChannelPtr; + + // Set indicates that the board has gone fatal. + + unsigned short i2eFatal; + + // The number of elements pointed to by i2eChannelPtr. + + unsigned short i2eChannelCnt; + + // Ring-buffers of channel structures whose channels have particular needs. + + spinlock_t Fbuf_spinlock; + volatile + unsigned short i2Fbuf_strip; // Strip index + volatile + unsigned short i2Fbuf_stuff; // Stuff index + void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // flow control packets. + spinlock_t Dbuf_spinlock; + volatile + unsigned short i2Dbuf_strip; // Strip index + volatile + unsigned short i2Dbuf_stuff; // Stuff index + void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // data or in-line command packets. + spinlock_t Bbuf_spinlock; + volatile + unsigned short i2Bbuf_strip; // Strip index + volatile + unsigned short i2Bbuf_stuff; // Stuff index + void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // bypass command packets. + + /* + * A set of flags to indicate that certain events have occurred on at least + * one of the ports on this board. We use this to decide whether to spin + * through the channels looking for breaks, etc. + */ + int got_input; + int status_change; + bidStat channelBtypes; + + /* + * Debugging counters, etc. + */ + unsigned long debugFlowQueued; + unsigned long debugInlineQueued; + unsigned long debugDataQueued; + unsigned long debugBypassQueued; + unsigned long debugFlowCount; + unsigned long debugInlineCount; + unsigned long debugBypassCount; + + spinlock_t read_fifo_spinlock; + spinlock_t write_fifo_spinlock; + +} i2eBordStr, *i2eBordStrPtr; + +//------------------------------------------------------------------- +// Macro Definitions for the indirect calls defined in the i2eBordStr +//------------------------------------------------------------------- +// +#define iiDelay(a,b) (*(a)->i2eDelay)(b) +#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c) +#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c) + +#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b) +#define iiReadWord(a) (*(a)->i2eReadWord)(a) + +#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b) + +#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a) +#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b) + +#define iiGetMail(a) (*(a)->i2eGetMail)(a) +#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a) +#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0) +#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b) + +//------------------------------------------- +// Manifests for i2eBordStr: +//------------------------------------------- + +#define YES 1 +#define NO 0 + +#define NULLFUNC (void (*)(void))0 +#define NULLPTR (void *)0 + +typedef void (*delayFunc_t)(unsigned int); + +// i2eValid +// +#define I2E_MAGIC 0x4251 // Structure is valid. +#define I2E_INCOMPLETE 0x1122 // Structure failed during init. + + +// i2eError +// +#define I2EE_GOOD 0 // Operation successful +#define I2EE_BADADDR 1 // Address out of range +#define I2EE_BADSTATE 2 // Attempt to perform a function when the board + // structure was in the incorrect state +#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize + // reflects what was read +#define I2EE_PORM_SHORT 4 // Power On message too short +#define I2EE_PORM_LONG 5 // Power On message too long +#define I2EE_BAD_FAMILY 6 // Un-supported board family type +#define I2EE_INCONSIST 7 // Firmware reports something impossible, + // e.g. unexpected number of ports... Almost no + // excuse other than bad FIFO... +#define I2EE_POSTERR 8 // Power-On self test reported a bad error +#define I2EE_BADBUS 9 // Unknown Bus type declared in message +#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty +#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and + // complete board structure (for functions which + // require this be so.) +#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and + // what the product is supposed to have. Check + // i2eGoodMap vs i2eChannelMap for details. +#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ +#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for + // functions requiring this). + +// i2eFifoStyle +// +#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */ +#define FIFO_IIEX 1 /* IntelliPort-IIEX style */ + +// i2eGetMail +// +#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly + // promote to 0x1111. +// i2eState +// +#define II_STATE_COLD 0 // Addresses have been defined, but board not even + // reset yet. +#define II_STATE_RESET 1 // Board,if it exists, has just been reset +#define II_STATE_READY 2 // Board ready for its first block +#define II_STATE_LOADING 3 // Board continuing load +#define II_STATE_LOADED 4 // Board has finished load: status ok +#define II_STATE_BADLOAD 5 // Board has finished load: failed! +#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware + +// i2eUsingIrq +// +#define IRQ_UNDEFINED 0x1352 // No valid irq (or polling = 0) can ever + // promote to this! +//------------------------------------------ +// Handy Macros for i2ellis.c and others +// Note these are common to -II and -IIEX +//------------------------------------------ + +// Given a pointer to the board structure, does the input FIFO have any data or +// not? +// +#define HAS_INPUT(pB) !(INB(pB->i2eStatus) & ST_IN_EMPTY) +#define HAS_NO_INPUT(pB) (INB(pB->i2eStatus) & ST_IN_EMPTY) + +// Given a pointer to board structure, read a byte or word from the fifo +// +#define BYTE_FROM(pB) (unsigned char)INB(pB->i2eData) +#define WORD_FROM(pB) (unsigned short)INW(pB->i2eData) + +// Given a pointer to board structure, is there room for any data to be written +// to the data fifo? +// +#define HAS_OUTROOM(pB) !(INB(pB->i2eStatus) & ST_OUT_FULL) +#define HAS_NO_OUTROOM(pB) (INB(pB->i2eStatus) & ST_OUT_FULL) + +// Given a pointer to board structure, write a single byte to the fifo +// structure. Note that for 16-bit interfaces, the high order byte is undefined +// and unknown. +// +#define BYTE_TO(pB, c) OUTB(pB->i2eData,(c)) + +// Write a word to the fifo structure. For 8-bit interfaces, this may have +// unknown results. +// +#define WORD_TO(pB, c) OUTW(pB->i2eData,(c)) + +// Given a pointer to the board structure, is there anything in the incoming +// mailbox? +// +#define HAS_MAIL(pB) (INB(pB->i2eStatus) & ST_IN_MAIL) + +#define UPDATE_FIFO_ROOM(pB) (pB)->i2eFifoRemains=(pB)->i2eFifoSize + +// Handy macro to round up a number (like the buffer write and read routines do) +// +#define ROUNDUP(number) (((number)+1) & (~1)) + +//------------------------------------------ +// Function Declarations for i2ellis.c +//------------------------------------------ +// +// Functions called directly +// +// Initialization of a board & structure is in four (five!) parts: +// +// 0) iiEllisInit() - Initialize iiEllis subsystem. +// 1) iiSetAddress() - Define the board address & delay function for a board. +// 2) iiReset() - Reset the board (provided it exists) +// -- Note you may do this to several boards -- +// 3) iiResetDelay() - Delay for 2 seconds (once for all boards) +// 4) iiInitialize() - Attempt to read Power-up message; further initialize +// accelerators +// +// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write +// loadware. To change loadware, you must begin again with step 2, resetting +// the board again (step 1 not needed). + +static void iiEllisInit(void); +static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); +static int iiReset(i2eBordStrPtr); +static int iiResetDelay(i2eBordStrPtr); +static int iiInitialize(i2eBordStrPtr); + +// Routine to validate that all channels expected are there. +// +extern int iiValidateChannels(i2eBordStrPtr); + +// Routine used to download a block of loadware. +// +static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); + +// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile: +// +#define II_DOWN_BADVALID 0 // board structure is invalid +#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more +#define II_DOWN_GOOD 2 // Download complete, CRC good +#define II_DOWN_BAD 3 // Download complete, but CRC bad +#define II_DOWN_BADFILE 4 // Bad magic number in loadware file +#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for + // downloading loadware. (see i2eState) +#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware +#define II_DOWN_OVER 7 // Too much data +#define II_DOWN_UNDER 8 // Not enough data +#define II_DOWN_NOFILE 9 // Loadware file not found + +// Routine to download an entire loadware module: Return values are a subset of +// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING +// +static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); + +// Called indirectly always. Needed externally so the routine might be +// SPECIFIED as an argument to iiReset() +// +//static void ii2DelayIO(unsigned int); // N-millisecond delay using + //hardware spin +//static void ii2DelayTimer(unsigned int); // N-millisecond delay using Linux + //timer + +// Many functions defined here return True if good, False otherwise, with an +// error code in i2eError field. Here is a handy macro for setting the error +// code and returning. +// +#define COMPLETE(pB,code) \ + if(1){ \ + pB->i2eError = code; \ + return (code == I2EE_GOOD);\ + } + +#endif // I2ELLIS_H diff --git a/drivers/char/ip2/i2hw.h b/drivers/char/ip2/i2hw.h new file mode 100644 index 000000000..9e5b07ca0 --- /dev/null +++ b/drivers/char/ip2/i2hw.h @@ -0,0 +1,648 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions limited to properties of the hardware or the +* bootstrap firmware. As such, they are applicable regardless of +* operating system or loadware (standard or diagnostic). +* +*******************************************************************************/ +#ifndef I2HW_H +#define I2HW_H 1 +//------------------------------------------------------------------------------ +// Revision History: +// +// 23 September 1991 MAG First Draft Started...through... +// 11 October 1991 ... Continuing development... +// 6 August 1993 Added support for ISA-4 (asic) which is architected +// as an ISA-CEX with a single 4-port box. +// +// 20 December 1996 AKM Version for Linux +// +//------------------------------------------------------------------------------ +/*------------------------------------------------------------------------------ + +HARDWARE DESCRIPTION: + +Introduction: + +The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8) +addresses in the host's I/O space. + +Some addresses are used to transfer data to/from the board, some to transfer +so-called "mailbox" messages, and some to read bit-mapped status information. +While all the products in the line are functionally similar, some use a 16-bit +data path to transfer data while others use an 8-bit path. Also, the use of +command /status/mailbox registers differs slightly between the II and IIEX +branches of the family. + +The host determines what type of board it is dealing with by reading a string of +sixteen characters from the board. These characters are always placed in the +fifo by the board's local processor whenever the board is reset (either from +power-on or under software control) and are known as the "Power-on Reset +Message." In order that this message can be read from either type of board, the +hardware registers used in reading this message are the same. Once this message +has been read by the host, then it has the information required to operate. + +General Differences between boards: + +The greatest structural difference is between the -II and -IIEX families of +product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support +the data path, mailbox registers, and status registers. This chip contains some +features which are not used in the IntelliPort-II products; a description of +these is omitted here. Because of these many features, it contains many +registers, too many to access directly within a small address space. They are +accessed by first writing a value to a "pointer" register. This value selects +the register to be accessed. The next read or write to that address accesses +the selected register rather than the pointer register. + +The -IIEX boards use a proprietary design similar to the Am4701 in function. But +because of a simpler, more streamlined design it doesn't require so many +registers. This means they can be accessed directly in single operations rather +than through a pointer register. + +Besides these differences, there are differences in whether 8-bit or 16-bit +transfers are used to move data to the board. + +The -II boards are capable only of 8-bit data transfers, while the -IIEX boards +may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP +switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit +transfers are supported (and will be expected by the standard loadware). The +on-board firmware can determine the position of the switch, and whether the +board is installed in a 16-bit slot; it supplies this information to the host as +part of the power-up reset message. + +The configuration switch (#8) and slot selection do not directly configure the +hardware. It is up to the on-board loadware and host-based drivers to act +according to the selected options. That is, loadware and drivers could be +written to perform 8-bit transfers regardless of the state of the DIP switch or +slot (and in a diagnostic environment might well do so). Likewise, 16-bit +transfers could be performed as long as the card is in a 16-bit slot. + +Note the slot selection and DIP switch selection are provided separately: a +board running in 8-bit mode in a 16-bit slot has a greater range of possible +interrupts to choose from; information of potential use to the host. + +All 8-bit data transfers are done in the same way, regardless of whether on a +-II board or a -IIEX board. + +The host must consider two things then: 1) whether a -II or -IIEX product is +being used, and 2) whether an 8-bit or 16-bit data path is used. + +A further difference is that -II boards always have a 512-byte fifo operating in +each direction. -IIEX boards may use fifos of varying size; this size is +reported as part of the power-up message. + +I/O Map Of IntelliPort-II and IntelliPort-IIEX boards: +(Relative to the chosen base address) + +Addr R/W IntelliPort-II IntelliPort-IIEX +---- --- -------------- ---------------- +0 R/W Data Port (byte) Data Port (byte or word) +1 R/W (Not used) (MSB of word-wide data written to Data Port) +2 R Status Register Status Register +2 W Pointer Register Interrupt Mask Register +3 R/W (Not used) Mailbox Registers (6 bits: 11111100) +4,5 -- Reserved for future products +6 -- Reserved for future products +7 R Guaranteed to have no effect +7 W Hardware reset of board. + + +Rules: +All data transfers are performed using the even i/o address. If byte-wide data +transfers are being used, do INB/OUTB operations on the data port. If word-wide +transfers are used, do INW/OUTW operations. In some circumstances (such as +reading the power-up message) you will do INB from the data port, but in this +case the MSB of each word read is lost. When accessing all other unreserved +registers, use byte operations only. +------------------------------------------------------------------------------*/ + +//------------------------------------------------ +// Mandatory Includes: +//------------------------------------------------ +// +#include "ip2types.h" +#include "i2os.h" /* For any o.s., compiler, or host-related issues */ + +//------------------------------------------------------------------------- +// Manifests for the I/O map: +//------------------------------------------------------------------------- +// R/W: Data port (byte) for IntelliPort-II, +// R/W: Data port (byte or word) for IntelliPort-IIEX +// Incoming or outgoing data passes through a FIFO, the status of which is +// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is +// the primary means of transferring data, commands, flow-control, and status +// information between the host and board. +// +#define FIFO_DATA 0 + +// Another way of passing information between the the board and the host is +// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of +// data. Writing data to the mailbox causes a status bit to be set, and +// potentially interrupting the intended receiver. The sender has some way to +// determine whether the data has been read yet; as soon as it has, it may send +// more. The mailboxes are handled differently on -II and -IIEX products, as +// suggested below. +//------------------------------------------------------------------------------ +// Read: Status Register for IntelliPort-II or -IIEX +// The presence of any bit set here will cause an interrupt to the host, +// provided the corresponding bit has been unmasked in the interrupt mask +// register. Furthermore, interrupts to the host are disabled globally until the +// loadware selects the irq line to use. With the exception of STN_MR, the bits +// remain set so long as the associated condition is true. +// +#define FIFO_STATUS 2 + +// Bit map of status bits which are identical for -II and -IIEX +// +#define ST_OUT_FULL 0x40 // Outbound FIFO full +#define ST_IN_EMPTY 0x20 // Inbound FIFO empty +#define ST_IN_MAIL 0x04 // Inbound Mailbox full + +// The following exists only on the Intelliport-IIEX, and indicates that the +// board has not read the last outgoing mailbox data yet. In the IntelliPort-II, +// the outgoing mailbox may be read back: a zero indicates the board has read +// the data. +// +#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!) + +// The following bits are defined differently for -II and -IIEX boards. Code +// which relies on these bits will need to be functionally different for the two +// types of boards and should be generally avoided because of the additional +// complexity this creates: + +// Bit map of status bits only on -II + +// Fifo has been RESET (cleared when the status register is read). Note that +// this condition cannot be masked and would always interrupt the host, except +// that the hardware reset also disables interrupts globally from the board +// until re-enabled by loadware. This could also arise from the +// Am4701-supported command to reset the chip, but this command is generally not +// used here. +// +#define STN_MR 0x80 + +// See the AMD Am4701 data sheet for details on the following four bits. They +// are not presently used by Computone drivers. +// +#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable) +#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable) +#define STN_BD 0x02 // Inbound byte detected +#define STN_PE 0x01 // Parity/Framing condition detected + +// Bit-map of status bits only on -IIEX +// +#define STE_OUT_HF 0x10 // Outbound FIFO half full +#define STE_IN_HF 0x08 // Inbound FIFO half full +#define STE_IN_FULL 0x02 // Inbound FIFO full +#define STE_OUT_MT 0x01 // Outbound FIFO empty + +//------------------------------------------------------------------------------ + +// Intelliport-II -- Write Only: the pointer register. +// Values are written to this register to select the Am4701 internal register to +// be accessed on the next operation. +// +#define FIFO_PTR 0x02 + +// Values for the pointer register +// +#define SEL_COMMAND 0x1 // Selects the Am4701 command register + +// Some possible commands: +// +#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip +#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the + // status register. +#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its + // own status register. +#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The + // interrupt mask register is bit-mapped to match + // the status register (FIFO_STATUS) except for + // STN_MR. (See above.) +#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not + // normally used except in diagnostics.) +#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back + // a value of zero indicates that the mailbox has + // been read by the board and is available for more + // data./ Writing to the mailbox optionally + // interrupts the board, depending on the loadware's + // setting of its interrupt mask register. +#define SEL_AEAF 0x5 // Selects AE/AF threshold register. +#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read) + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register: +// Unlike IntelliPort-II, bit assignments do NOT match those of the status +// register. +// +#define FIFO_MASK 0x2 + +// Mailbox readback select: +// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If +// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox. +// This is the normal situation. The clearing of a mailbox is determined on +// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback +// capability is provided for diagnostic purposes only. +// +#define MX_OUTMAIL_RSEL 0x80 + +#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes + // full (ST_IN_MAIL set). +#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full + // (STE_IN_FULL). +#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty + // (ST_IN_MT). +#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full + // (ST_OUT_FULL). +#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty + // (STE_OUT_MT). + +// Any remaining bits are reserved, and should be written to ZERO for +// compatibility with future Computone products. + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two +// bits always read back 0). +// Read: One of the mailboxes, usually Inbound. +// Inbound Mailbox (MX_OUTMAIL_RSEL = 0) +// Outbound Mailbox (MX_OUTMAIL_RSEL = 1) +// Write: Outbound Mailbox +// For the IntelliPort-II boards, the outbound mailbox is read back to determine +// whether the board has read the data (0 --> data has been read). For the +// IntelliPort-IIEX, this is done by reading a status register. To determine +// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit +// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by +// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the +// case with the -II boards. For this reason, FIFO_MAIL is normally used to read +// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for +// MX_OUTMAIL_RSEL description.) +// +#define FIFO_MAIL 0x3 + +//------------------------------------------------------------------------------ +// WRITE ONLY: Resets the board. (Data doesn't matter). +// +#define FIFO_RESET 0x7 + +//------------------------------------------------------------------------------ +// READ ONLY: Will have no effect. (Data is undefined.) +// Actually, there will be an effect, in that the operation is sure to generate +// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short +// delays when no comparable time constant is available. +// +#define FIFO_NOP 0x7 + +//------------------------------------------------------------------------------ +// RESET & POWER-ON RESET MESSAGE +/*------------------------------------------------------------------------------ +RESET: + +The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel +reset, and via a write to the reset register described above. For products using +the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses, +the Power-up and channel reset sources cause additional hardware initialization +which should only occur at system startup time. + +The third type of reset, called a "command reset", is done by writing any data +to the FIFO_RESET address described above. This resets the on-board processor, +FIFO, UARTS, and associated hardware. + +This passes control of the board to the bootstrap firmware, which performs a +Power-On Self Test and which detects its current configuration. For example, +-IIEX products determine the size of FIFO which has been installed, and the +number and type of expansion boxes attached. + +This and other information is then written to the FIFO in a 16-byte data block +to be read by the host. This block is guaranteed to be present within two (2) +seconds of having received the command reset. The firmware is now ready to +receive loadware from the host. + +It is good practice to perform a command reset to the board explicitly as part +of your software initialization. This allows your code to properly restart from +a soft boot. (Many systems do not issue channel reset on soft boot). + +Because of a hardware reset problem on some of the Cirrus Logic 1400's which are +used on the product, it is recommended that you reset the board twice, separated +by an approximately 50 milliseconds delay. (VERY approximately: probably ok to +be off by a factor of five. The important point is that the first command reset +in fact generates a reset pulse on the board. This pulse is guaranteed to last +less than 10 milliseconds. The additional delay ensures the 1400 has had the +chance to respond sufficiently to the first reset. Why not a longer delay? Much +more than 50 milliseconds gets to be noticable, but the board would still work. + +Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap +firmware is ready to receive loadware. + +Note on Power-on Reset Message format: +The various fields have been designed with future expansion in view. +Combinations of bitfields and values have been defined which define products +which may not currently exist. This has been done to allow drivers to anticipate +the possible introduction of products in a systematic fashion. This is not +intended to suggest that each potential product is actually under consideration. +------------------------------------------------------------------------------*/ + +//---------------------------------------- +// Format of Power-on Reset Message +//---------------------------------------- + +typedef union _porStr // "por" stands for Power On Reset +{ + unsigned char c[16]; // array used when considering the message as a + // string of undifferentiated characters + + struct // Elements used when considering values + { + // The first two bytes out of the FIFO are two magic numbers. These are + // intended to establish that there is indeed a member of the + // IntelliPort-II(EX) family present. The remaining bytes may be + // expected // to be valid. When reading the Power-on Reset message, + // if the magic numbers do not match it is probably best to stop + // reading immediately. You are certainly not reading our board (unless + // hardware is faulty), and may in fact be reading some other piece of + // hardware. + + unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1 + unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2 + + // The Version, Revision, and Subrevision are stored as absolute numbers + // and would normally be displayed in the format V.R.S (e.g. 1.0.2) + + unsigned char porVersion; // Bootstrap firmware version number + unsigned char porRevision; // Bootstrap firmware revision number + unsigned char porSubRev; // Bootstrap firmware sub-revision number + + unsigned char porID; // Product ID: Bit-mapped according to + // conventions described below. Among other + // things, this allows us to distinguish + // IntelliPort-II boards from IntelliPort-IIEX + // boards. + + unsigned char porBus; // IntelliPort-II: Unused + // IntelliPort-IIEX: Bus Information: + // Bit-mapped below + + unsigned char porMemory; // On-board DRAM size: in 32k blocks + + // porPorts1 (and porPorts2) are used to determine the ports which are + // available to the board. For non-expandable product, a single number + // is sufficient. For expandable product, the board may be connected + // to as many as four boxes. Each box may be (so far) either a 16-port + // or an 8-port size. Whenever an 8-port box is used, the remaining 8 + // ports leave gaps between existing channels. For that reason, + // expandable products must report a MAP of available channels. Since + // each UART supports four ports, we represent each UART found by a + // single bit. Using two bytes to supply the mapping information we + // report the presense or absense of up to 16 UARTS, or 64 ports in + // steps of 4 ports. For -IIEX products, the ports are numbered + // starting at the box closest to the controller in the "chain". + + // Interpreted Differently for IntelliPort-II and -IIEX. + // -II: Number of ports (Derived actually from product ID). See + // Diag1&2 to indicate if uart was actually detected. + // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This + // bitmap is based on detecting the uarts themselves; + // see porFlags for information from the box i.d's. + unsigned char porPorts1; + + unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte + unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte + unsigned char porSpeed; // Speed of local CPU: given as MHz x10 + // e.g., 16.0 MHz CPU is reported as 160 + unsigned char porFlags; // Misc information (see manifests below) + // Bit-mapped: CPU type, UART's present + + unsigned char porPorts2; // -II: Undefined + // -IIEX: Bit-map of UARTS found, MSB (see + // above for LSB) + + // IntelliPort-II: undefined + // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the + // host interface FIFO, in each direction. When running the -IIEX in + // 8-bit mode, fifo capacity is halved. The bootstrap firmware will + // have already accounted for this fact in generating this number. + unsigned char porFifoSize; + + // IntelliPort-II: undefined + // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4) + unsigned char porNumBoxes; + } e; +} porStr, *porStrPtr; + +//-------------------------- +// Values for porStr fields +//-------------------------- + +//--------------------- +// porMagic1, porMagic2 +//---------------------- +// +#define POR_MAGIC_1 0x96 // The only valid value for porMagic1 +#define POR_MAGIC_2 0x35 // The only valid value for porMagic2 +#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1 +#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2 + +//---------------------- +// porID +//---------------------- +// +#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of + // product. +#define POR_ID_FII 0x00 // Family is "IntelliPort-II" +#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX" + +// These bits are reserved, presently zero. May be used at a later date to +// convey other product information. +// +#define POR_ID_RESERVED 0x3c + +#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports & + // Connector information. +#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using + // standard brick. +#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using + // RJ11's (no CTS) +#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using + // RJ45's +#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using + // 4xRJ45 connectors +#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard + // expandable controller (other values reserved) + +//---------------------- +// porBus +//---------------------- + +// IntelliPort-IIEX only: Board is installed in a 16-bit slot +// +#define POR_BUS_SLOT16 0x20 + +// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface +// operation. +// +#define POR_BUS_DIP16 0x10 + +// Bits 0-2 indicate type of bus: This information is stored in the bootstrap +// loadware, different loadware being used on different products for different +// buses. For most situations, the drivers do not need this information; but it +// is handy in a diagnostic environment. For example, on microchannel boards, +// you would not want to try to test several interrupts, only the one for which +// you were configured. +// +#define POR_BUS_TYPE 0x07 + +// Unknown: this product doesn't know what bus it is running in. (e.g. if same +// bootstrap firmware were wanted for two different buses.) +// +#define POR_BUS_T_UNK 0 + +// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK +// state, since the same bootstrap firmware is used for each. + +#define POR_BUS_T_MCA 1 // MCA BUS */ +#define POR_BUS_T_EISA 2 // EISA BUS */ +#define POR_BUS_T_ISA 3 // ISA BUS */ + +// Values 4-7 Reserved + +// Remaining bits are reserved + +//---------------------- +// porDiag1 +//---------------------- + +#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed + +// These two bits valid only for the IntelliPort-II +// +#define POR_BAD_UART1 0x01 // First 1400 bad +#define POR_BAD_UART2 0x02 // Second 1400 bad + +//---------------------- +// porDiag2 +//---------------------- + +#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T +#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet + // available. + // Other bits undefined. +//---------------------- +// porFlags +//---------------------- + +#define POR_CPU 0x03 // These bits indicate supposed CPU type +#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet) +#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products) +#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic) + // which is architected like an ISA-CEX connected + // to a (hitherto impossible) 4-port box. +#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box + // sizes based on box I.D. +#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port + +//------------------------------------- +// LOADWARE and DOWNLOADING CODE +//------------------------------------- + +/* +Loadware may be sent to the board in two ways: +1) It may be read from a (binary image) data file block by block as each block + is sent to the board. This is only possible when the initialization is + performed by code which can access your file system. This is most suitable + for diagnostics and appications which use the interface library directly. + +2) It may be hard-coded into your source by including a .h file (typically + supplied by Computone), which declares a data array and initializes every + element. This acheives the same result as if an entire loadware file had + been read into the array. + + This requires more data space in your program, but access to the file system + is not required. This method is more suited to driver code, which typically + is running at a level too low to access the file system directly. + +At present, loadware can only be generated at Computone. + +All Loadware begins with a header area which has a particular format. This +includes a magic number which identifies the file as being (purportedly) +loadware, CRC (for the loader), and version information. +*/ + + +//----------------------------------------------------------------------------- +// Format of loadware block +// +// This is defined as a union so we can pass a pointer to one of these items +// and (if it is the first block) pick out the version information, etc. +// +// Otherwise, to deal with this as a simple character array +//------------------------------------------------------------------------------ + +#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware + +typedef union _loadHdrStr +{ + unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block + + struct // These fields are valid for only the first block of loadware. + { + unsigned char loadMagic; // Magic number: see below + unsigned char loadBlocksMore; // How many more blocks? + unsigned char loadCRC[2]; // Two CRC bytes: used by loader + unsigned char loadVersion; // Version number + unsigned char loadRevision; // Revision number + unsigned char loadSubRevision; // Sub-revision number + unsigned char loadSpares[9]; // Presently unused + unsigned char loadDates[32]; // Null-terminated string which can give + // date and time of compilation + } e; +} loadHdrStr, *loadHdrStrPtr; + +//------------------------------------ +// Defines for downloading code: +//------------------------------------ + +// The loadMagic field in the first block of the loadfile must be this, else the +// file is not valid. +// +#define MAGIC_LOADFILE 0x3c + +// How do we know the load was successful? On completion of the load, the +// bootstrap firmware returns a code to indicate whether it thought the download +// was valid and intends to execute it. These are the only possible valid codes: +// +#define LOADWARE_OK 0xc3 // Download was ok +#define LOADWARE_BAD 0x5a // Download was bad (CRC error) + +// Constants applicable to writing blocks of loadware: +// The first block of loadware might take 600 mS to load, in extreme cases. +// (Expandable board: worst case for sending startup messages to the LCD's). +// The 600mS figure is not really a calculation, but a conservative +// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks. +// +#define MAX_DLOAD_START_TIME 1000 // 1000 mS +#define MAX_DLOAD_READ_TIME 100 // 100 mS + +// Firmware should respond with status (see above) within this long of host +// having sent the final block. +// +#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again! + +//------------------------------------------------------ +// MAXIMUM NUMBER OF PORTS PER BOARD: +// This is fixed for now (with the expandable), but may +// be expanding according to even newer products. +//------------------------------------------------------ +// +#define ABS_MAX_BOXES 4 // Absolute most boxes per board +#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box +#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) + +#endif // I2HW_H + diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c new file mode 100644 index 000000000..c24bca783 --- /dev/null +++ b/drivers/char/ip2/i2lib.c @@ -0,0 +1,2253 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: High-level interface code for the device driver. Uses the +* Extremely Low Level Interface Support (i2ellis.c). Provides an +* interface to the standard loadware, to support drivers or +* application code. (This is included source code, not a separate +* compilation module.) +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Note on Strategy: +// Once the board has been initialized, it will interrupt us when: +// 1) It has something in the fifo for us to read (incoming data, flow control +// packets, or whatever). +// 2) It has stripped whatever we have sent last time in the FIFO (and +// consequently is ready for more). +// +// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This +// worsens performance considerably, but is done so that a great many channels +// might use only a little memory. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Revision History: +// +// 0.00 - 4/16/91 --- First Draft +// 0.01 - 4/29/91 --- 1st beta release +// 0.02 - 6/14/91 --- Changes to allow small model compilation +// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with +// in-line asm added for moving data to/from ring buffers, +// replacing a variety of methods used previously. +// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until +// i2_enable_interrupts time. Former versions would enqueue +// them at i2_init_channel time, before we knew how many +// channels were supposed to exist! +// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; +// supports new 16-bit protocol and expandable boards. +// - 10/24/91 MAG Most changes in place and stable. +// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no +// argument. +// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt +// level (mostly responses to specific commands.) +// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet +// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE +// turning on the interrupt. +// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check +// some incoming. +// +// 1.1 - 12/25/96 AKM Linux version. +// - 10/09/98 DMC Revised Linux version. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include <linux/sched.h> +#include "i2lib.h" + + +//*********************** +//* Function Prototypes * +//*********************** +static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); +static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); +static void i2StripFifo(i2eBordStrPtr); +static void i2StuffFifoBypass(i2eBordStrPtr); +static void i2StuffFifoFlow(i2eBordStrPtr); +static void i2StuffFifoInline(i2eBordStrPtr); +static int i2RetryFlushOutput(i2ChanStrPtr); + +// Not a documented part of the library routines (careful...) but the Diagnostic +// i2diag.c finds them useful to help the throughput in certain limited +// single-threaded operations. +static void iiSendPendingMail(i2eBordStrPtr); +static void serviceOutgoingFifo(i2eBordStrPtr); + +// Functions defined in ip2.c as part of interrupt handling +static void do_input(i2ChanStrPtr); +static void do_status(i2ChanStrPtr); + +//*************** +//* Debug Data * +//*************** +#ifdef DEBUG_FIFO + +unsigned char DBGBuf[0x4000]; +unsigned short I = 0; + +static void +WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) +{ + char *p = src; + + // XXX: We need a spin lock here if we ever use this again + + while (*s) { // copy label + DBGBuf[I] = *s++; + I = I++ & 0x3fff; + } + while (n--) { // copy data + DBGBuf[I] = *p++; + I = I++ & 0x3fff; + } +} + +static void +fatality(i2eBordStrPtr pB ) +{ + int i; + + for (i=0;i<sizeof(DBGBuf);i++) { + if ((i%16) == 0) + printk("\n%4x:",i); + printk("%02x ",DBGBuf[i]); + } + printk("\n"); + for (i=0;i<sizeof(DBGBuf);i++) { + if ((i%16) == 0) + printk("\n%4x:",i); + if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') { + printk(" %c ",DBGBuf[i]); + } else { + printk(" . "); + } + } + printk("\n"); + printk("Last index %x\n",I); +} +#endif /* DEBUG_FIFO */ + +//******** +//* Code * +//******** + +inline int +i2Validate ( i2ChanStrPtr pCh ) +{ + //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, + // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); + return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) + == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); +} + +//****************************************************************************** +// Function: iiSendPendingMail(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// If any outgoing mail bits are set and there is outgoing mailbox is empty, +// send the mail and clear the bits. +//****************************************************************************** +static inline void +iiSendPendingMail(i2eBordStrPtr pB) +{ + if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) + { + if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) + { + /* If we were already waiting for fifo to empty, + * or just sent MB_OUT_STUFFED, then we are + * still waiting for it to empty, until we should + * receive an MB_IN_STRIPPED from the board. + */ + pB->i2eWaitingForEmptyFifo |= + (pB->i2eOutMailWaiting & MB_OUT_STUFFED); + pB->i2eOutMailWaiting = 0; + } + } +} + +//****************************************************************************** +// Function: i2InitChannels(pB, nChannels, pCh) +// Parameters: Pointer to Ellis Board structure +// Number of channels to initialize +// Pointer to first element in an array of channel structures +// Returns: Success or failure +// +// Description: +// +// This function patches pointers, back-pointers, and initializes all the +// elements in the channel structure array. +// +// This should be run after the board structure is initialized, through having +// loaded the standard loadware (otherwise it complains). +// +// In any case, it must be done before any serious work begins initializing the +// irq's or sending commands... +// +//****************************************************************************** +static int +i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) +{ + int index, stuffIndex; + i2ChanStrPtr *ppCh; + + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_STDLOADED) { + COMPLETE(pB, I2EE_BADSTATE); + } + + LOCK_INIT(&pB->read_fifo_spinlock); + LOCK_INIT(&pB->write_fifo_spinlock); + LOCK_INIT(&pB->Dbuf_spinlock); + LOCK_INIT(&pB->Bbuf_spinlock); + LOCK_INIT(&pB->Fbuf_spinlock); + + // NO LOCK needed yet - this is init + + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nChannels; + + pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; + pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; + pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; + + memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); + + for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); + nChannels && index < ABS_MOST_PORTS; + index++) + { + if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { + continue; + } + LOCK_INIT(&pCh->Ibuf_spinlock); + LOCK_INIT(&pCh->Obuf_spinlock); + LOCK_INIT(&pCh->Cbuf_spinlock); + LOCK_INIT(&pCh->Pbuf_spinlock); + // NO LOCK needed yet - this is init + // Set up validity flag according to support level + if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { + pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; + } else { + pCh->validity = CHANNEL_MAGIC; + } + pCh->pMyBord = pB; /* Back-pointer */ + + // Prepare an outgoing flow-control packet to send as soon as the chance + // occurs. + if ( pCh->validity & CHANNEL_SUPPORT ) { + pCh->infl.hd.i2sChannel = index; + pCh->infl.hd.i2sCount = 5; + pCh->infl.hd.i2sType = PTYPE_BYPASS; + pCh->infl.fcmd = 37; + pCh->infl.asof = 0; + pCh->infl.room = IBUF_SIZE - 1; + + pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full + + // The following is similar to calling i2QueueNeeds, except that this + // is done in longhand, since we are setting up initial conditions on + // many channels at once. + pCh->channelNeeds = NEED_FLOW; // Since starting from scratch + pCh->sinceLastFlow = 0; // No bytes received since last flow + // control packet was queued + stuffIndex++; + *ppCh++ = pCh; // List this channel as needing + // initial flow control packet sent + } + + // Don't allow anything to be sent until the status packets come in from + // the board. + + pCh->outfl.asof = 0; + pCh->outfl.room = 0; + + // Initialize all the ring buffers + + pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; + pCh->Obuf_stuff = pCh->Obuf_strip = 0; + pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; + + memset( &pCh->icount, 0, sizeof (struct async_icount) ); + pCh->hotKeyIn = HOT_CLEAR; + pCh->channelOptions = 0; + pCh->bookMarks = 0; + init_waitqueue_head(&pCh->pBookmarkWait); + + init_waitqueue_head(&pCh->open_wait); + init_waitqueue_head(&pCh->close_wait); + init_waitqueue_head(&pCh->delta_msr_wait); + + // Set base and divisor so default custom rate is 9600 + pCh->BaudBase = 921600; // MAX for ST654, changed after we get + pCh->BaudDivisor = 96; // the boxids (UART types) later + + pCh->dataSetIn = 0; + pCh->dataSetOut = 0; + + pCh->wopen = 0; + pCh->throttled = 0; + + pCh->speed = CBR_9600; + + pCh->flags = 0; + pCh->session = 0; + pCh->pgrp = 0; + + pCh->ClosingDelay = 5*HZ/10; + pCh->ClosingWaitTime = 30*HZ; + +#ifdef USE_IQ + // Initialize task queue objects + pCh->tqueue_input.routine = (void(*)(void*)) do_input; + pCh->tqueue_input.data = pCh; + pCh->tqueue_status.routine = (void(*)(void*)) do_status; + pCh->tqueue_status.data = pCh; +#endif + + pCh->trace = ip2trace; + + ++pCh; + --nChannels; + } + // No need to check for wrap here; this is initialization. + pB->i2Fbuf_stuff = stuffIndex; + COMPLETE(pB, I2EE_GOOD); + +} + +//****************************************************************************** +// Function: i2DeQueueNeeds(pB, type) +// Parameters: Pointer to a board structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: +// Pointer to a channel structure +// +// Description: Returns pointer struct of next channel that needs service of +// the type specified. Otherwise returns a NULL reference. +// +//****************************************************************************** +static i2ChanStrPtr +i2DeQueueNeeds(i2eBordStrPtr pB, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + i2ChanStrPtr pCh = NULL; + + switch(type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) + { + queueIndex = pB->i2Dbuf_strip; + pCh = pB->i2Dbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Dbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_INLINE; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) + { + queueIndex = pB->i2Bbuf_strip; + pCh = pB->i2Bbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Bbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_BYPASS; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) + { + queueIndex = pB->i2Fbuf_strip; + pCh = pB->i2Fbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Fbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_FLOW; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + default: + printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); + break; + } + return pCh; +} + +//****************************************************************************** +// Function: i2QueueNeeds(pB, pCh, type) +// Parameters: Pointer to a board structure +// Pointer to a channel structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: Nothing +// +// Description: +// For each type of need selected, if the given channel is not already in the +// queue, adds it, and sets the flag indicating it is in the queue. +//****************************************************************************** +static void +i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + // We turn off all the interrupts during this brief process, since the + // interrupt-level code might want to put things on the queue as well. + + switch (type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( !(pCh->channelNeeds & NEED_INLINE) ) + { + pCh->channelNeeds |= NEED_INLINE; + queueIndex = pB->i2Dbuf_stuff; + pB->i2Dbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Dbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) + { + pCh->channelNeeds |= NEED_BYPASS; + queueIndex = pB->i2Bbuf_stuff; + pB->i2Bbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Bbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) + { + pCh->channelNeeds |= NEED_FLOW; + queueIndex = pB->i2Fbuf_stuff; + pB->i2Fbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Fbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + + case NEED_CREDIT: + pCh->channelNeeds |= NEED_CREDIT; + break; + default: + printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type); + break; + } + return; +} + +//****************************************************************************** +// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...) +// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE +// pointer to the channel structure +// maximum period to wait +// number of commands (n) +// n commands +// Returns: Number of commands sent, or -1 for error +// +// get board lock before calling +// +// Description: +// Queues up some commands to be sent to a channel. To send possibly several +// bypass or inline commands to the given channel. The timeout parameter +// indicates how many HUNDREDTHS OF SECONDS to wait until there is room: +// 0 = return immediately if no room, -ive = wait forever, +ive = number of +// 1/100 seconds to wait. Return values: +// -1 Some kind of nasty error: bad channel structure or invalid arguments. +// 0 No room to send all the commands +// (+) Number of commands sent +//****************************************************************************** +static int +i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, + cmdSyntaxPtr pCs0,...) +{ + int totalsize = 0; + int blocksize; + int lastended; + cmdSyntaxPtr *ppCs; + cmdSyntaxPtr pCs; + int count; + int flag; + i2eBordStrPtr pB; + + unsigned short maxBlock; + unsigned short maxBuff; + short bufroom; + unsigned short stuffIndex; + unsigned char *pBuf; + unsigned char *pInsert; + unsigned char *pDest, *pSource; + unsigned short channel; + int cnt; + unsigned long flags = 0; + spinlock_t *lock_var_p = NULL; + + // Make sure the channel exists, otherwise do nothing + if ( !i2Validate ( pCh ) ) { + return -1; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 ); +#endif + pB = pCh->pMyBord; + + // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT + if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == IRQ_UNDEFINED) { + return -2; + } + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if ( pB->i2eFatalTrap ) { + (*(pB)->i2eFatalTrap)(pB); + } + return -3; + } + // Set up some variables, Which buffers are we using? How big are they? + switch(type) + { + case PTYPE_INLINE: + flag = INL; + maxBlock = MAX_OBUF_BLOCK; + maxBuff = OBUF_SIZE; + pBuf = pCh->Obuf; + break; + case PTYPE_BYPASS: + flag = BYP; + maxBlock = MAX_CBUF_BLOCK; + maxBuff = CBUF_SIZE; + pBuf = pCh->Cbuf; + break; + default: + return -4; + } + // Determine the total size required for all the commands + totalsize = blocksize = sizeof(i2CmdHeader); + lastended = 0; + ppCs = &pCs0; + for ( count = nCommands; count; count--, ppCs++) + { + pCs = *ppCs; + cnt = pCs->length; + // Will a new block be needed for this one? + // Two possible reasons: too + // big or previous command has to be at the end of a packet. + if ((blocksize + cnt > maxBlock) || lastended) { + blocksize = sizeof(i2CmdHeader); + totalsize += sizeof(i2CmdHeader); + } + totalsize += cnt; + blocksize += cnt; + + // If this command had to end a block, then we will make sure to + // account for it should there be any more blocks. + lastended = pCs->flags & END; + } + for (;;) { + // Make sure any pending flush commands go out before we add more data. + if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) { + // How much room (this time through) ? + switch(type) { + case PTYPE_INLINE: + lock_var_p = &pCh->Obuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Obuf_stuff; + bufroom = pCh->Obuf_strip - stuffIndex; + break; + case PTYPE_BYPASS: + lock_var_p = &pCh->Cbuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Cbuf_stuff; + bufroom = pCh->Cbuf_strip - stuffIndex; + break; + default: + return -5; + } + if (--bufroom < 0) { + bufroom += maxBuff; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom ); +#endif + // Check for overflow + if (totalsize <= bufroom) { + // Normal Expected path - We still hold LOCK + break; /* from for()- Enough room: goto proceed */ + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 3, 1, totalsize ); +#endif + // Prepare to wait for buffers to empty + WRITE_UNLOCK_IRQRESTORE(lock_var_p,flags); + serviceOutgoingFifo(pB); // Dump what we got + + if (timeout == 0) { + return 0; // Tired of waiting + } + if (timeout > 0) + timeout--; // So negative values == forever + + if (!in_interrupt()) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); // short nap + } else { + // we cannot sched/sleep in interrrupt silly + return 0; + } + if (signal_pending(current)) { + return 0; // Wake up! Time to die!!! + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 4, 0 ); +#endif + } // end of for(;;) + + // At this point we have room and the lock - stick them in. + channel = pCh->infl.hd.i2sChannel; + pInsert = &pBuf[stuffIndex]; // Pointer to start of packet + pDest = CMD_OF(pInsert); // Pointer to start of command + + // When we start counting, the block is the size of the header + for (blocksize = sizeof(i2CmdHeader), count = nCommands, + lastended = 0, ppCs = &pCs0; + count; + count--, ppCs++) + { + pCs = *ppCs; // Points to command protocol structure + + // If this is a bookmark request command, post the fact that a bookmark + // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ + // has no parameters! The more general solution would be to reference + // pCs->cmd[0]. + if (pCs == CMD_BMARK_REQ) { + pCh->bookMarks++; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks ); +#endif + } + cnt = pCs->length; + + // If this command would put us over the maximum block size or + // if the last command had to be at the end of a block, we end + // the existing block here and start a new one. + if ((blocksize + cnt > maxBlock) || lastended) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 5, 0 ); +#endif + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt + pDest = CMD_OF(pInsert); + blocksize = sizeof(i2CmdHeader); + } + // Now we know there is room for this one in the current block + + blocksize += cnt; // Total bytes in this command + pSource = pCs->cmd; // Copy the command into the buffer + while (cnt--) { + *pDest++ = *pSource++; + } + // If this command had to end a block, then we will make sure to account + // for it should there be any more blocks. + lastended = pCs->flags & END; + } // end for + // Clean up the final block by writing header, etc + + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + // Updates the index, and post the need for service. When adding these to + // the queue of channels, we turn off the interrupt while doing so, + // because at interrupt level we might want to push a channel back to the + // end of the queue. + switch(type) + { + case PTYPE_INLINE: + pCh->Obuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + pB->debugInlineQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; + + case PTYPE_BYPASS: + pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + + pB->debugBypassQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_BYPASS); + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands ); +#endif + return nCommands; // Good status: number of commands sent +} + +//****************************************************************************** +// Function: i2GetStatus(pCh,resetBits) +// Parameters: Pointer to a channel structure +// Bit map of status bits to clear +// Returns: Bit map of current status bits +// +// Description: +// Returns the state of data set signals, and whether a break has been received, +// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status +// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared +// AFTER the condition is passed. If pCh does not point to a valid channel, +// returns -1 (which would be impossible otherwise. +//****************************************************************************** +static int +i2GetStatus(i2ChanStrPtr pCh, int resetBits) +{ + unsigned short status; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits ); +#endif + + // Make sure the channel exists, otherwise do nothing */ + if ( !i2Validate ( pCh ) ) + return -1; + + pB = pCh->pMyBord; + + status = pCh->dataSetIn; + + // Clear any specified error bits: but note that only actual error bits can + // be cleared, regardless of the value passed. + if (resetBits) + { + pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR)); + pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn ); +#endif + + return status; +} + +//****************************************************************************** +// Function: i2Input(pChpDest,count) +// Parameters: Pointer to a channel structure +// Pointer to data buffer +// Number of bytes to read +// Returns: Number of bytes read, or -1 for error +// +// Description: +// Strips data from the input buffer and writes it to pDest. If there is a +// collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes read. +//****************************************************************************** +static int +i2Input(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned short stripIndex; + int count; + unsigned long flags = 0; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0); +#endif + + // Ensure channel structure seems real + if ( !i2Validate( pCh ) ) { + count = -1; + goto i2Input_exit; + } + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + + // initialize some accelerators and private copies + stripIndex = pCh->Ibuf_strip; + + count = pCh->Ibuf_stuff - stripIndex; + + // If buffer is empty or requested data count was 0, (trivial case) return + // without any further thought. + if ( count == 0 ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + goto i2Input_exit; + } + // Adjust for buffer wrap + if ( count < 0 ) { + count += IBUF_SIZE; + } + // Don't give more than can be taken by the line discipline + amountToMove = pCh->pTTY->ldisc.receive_room( pCh->pTTY ); + if (count > amountToMove) { + count = amountToMove; + } + // How much could we copy without a wrap? + amountToMove = IBUF_SIZE - stripIndex; + + if (amountToMove > count) { + amountToMove = count; + } + // Move the first block + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); + // If we needed to wrap, do the second data move + if (count > amountToMove) { + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->Ibuf, NULL, count - amountToMove ); + } + // Bump and wrap the stripIndex all at once by the amount of data read. This + // method is good regardless of whether the data was in one or two pieces. + stripIndex += count; + if (stripIndex >= IBUF_SIZE) { + stripIndex -= IBUF_SIZE; + } + pCh->Ibuf_strip = stripIndex; + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + pCh->infl.asof += count; + + if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } + +i2Input_exit: + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputFlush(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes stripped, or -1 for error +// +// Description: +// Strips any data from the input buffer. If there is a collosal blunder, +// (invalid structure pointers or the like), returns -1. Otherwise, returns the +// number of bytes stripped. +//****************************************************************************** +static int +i2InputFlush(i2ChanStrPtr pCh) +{ + int count; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 10, 0); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + + // Adjust for buffer wrap + if (count < 0) { + count += IBUF_SIZE; + } + + // Expedient way to zero out the buffer + pCh->Ibuf_strip = pCh->Ibuf_stuff; + + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + + pCh->infl.asof += count; + + if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) + { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 19, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputAvailable(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes available, or -1 for error +// +// Description: +// If there is a collosal blunder, (invalid structure pointers or the like), +// returns -1. Otherwise, returns the number of bytes stripped. Otherwise, +// returns the number of bytes available in the buffer. +//****************************************************************************** +#if 0 +static int +i2InputAvailable(i2ChanStrPtr pCh) +{ + int count; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) return -1; + + + // initialize some accelerators and private copies + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + + // Adjust for buffer wrap + if (count < 0) + { + count += IBUF_SIZE; + } + + return count; +} +#endif + +//****************************************************************************** +// Function: i2Output(pCh, pSource, count) +// Parameters: Pointer to channel structure +// Pointer to source data +// Number of bytes to send +// Returns: Number of bytes sent, or -1 for error +// +// Description: +// Queues the data at pSource to be sent as data packets to the board. If there +// is a collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes written. What if there is not enough +// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then +// we transfer as many characters as we can now, then return. If this bit is +// clear (default), routine will spin along until all the data is buffered. +// Should this occur, the 1-ms delay routine is called while waiting to avoid +// applications that one cannot break out of. +//****************************************************************************** +static int +i2Output(i2ChanStrPtr pCh, const char *pSource, int count, int user ) +{ + i2eBordStrPtr pB; + unsigned char *pInsert; + int amountToMove; + int countOriginal = count; + unsigned short channel; + unsigned short stuffIndex; + unsigned long flags; + int rc = 0; + + int bailout = 10; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, user ); +#endif + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + + // initialize some accelerators and private copies + pB = pCh->pMyBord; + channel = pCh->infl.hd.i2sChannel; + + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return -1; + } + // Proceed as though we would do everything + while ( count > 0 ) { + + // How much room in output buffer is there? + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // Subtract off the headers size and see how much room there is for real + // data. If this is negative, we will discover later. + amountToMove -= sizeof (i2DataHeader); + + // Don't move more (now) than can go in a single packet + if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) { + amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader); + } + // Don't move more than the count we were given + if (amountToMove > count) { + amountToMove = count; + } + // Now we know how much we must move: NB because the ring buffers have + // an overflow area at the end, we needn't worry about wrapping in the + // middle of a packet. + +// Small WINDOW here with no LOCK but I can't call Flush with LOCK +// We would be flushing (or ending flush) anyway + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove ); +#endif + if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) + && amountToMove > 0 ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stuffIndex = pCh->Obuf_stuff; + + // Had room to move some data: don't know whether the block size, + // buffer space, or what was the limiting factor... + pInsert = &(pCh->Obuf[stuffIndex]); + + // Set up the header + CHANNEL_OF(pInsert) = channel; + PTYPE_OF(pInsert) = PTYPE_DATA; + TAG_OF(pInsert) = 0; + ID_OF(pInsert) = ID_ORDINARY_DATA; + DATA_COUNT_OF(pInsert) = amountToMove; + + // Move the data + if ( user ) { + rc=copy_from_user((char*)(DATA_OF(pInsert)), pSource, + amountToMove ); + } else { + memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); + } + // Adjust pointers and indices + pSource += amountToMove; + pCh->Obuf_char_count += amountToMove; + stuffIndex += amountToMove + sizeof(i2DataHeader); + count -= amountToMove; + + if (stuffIndex >= OBUF_SIZE) { + stuffIndex = 0; + } + pCh->Obuf_stuff = stuffIndex; + + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); +#endif + + } else { + + // Cannot move data + // becuz we need to stuff a flush + // or amount to move is <= 0 + +#ifdef IP2DEBUG_TRACE + ip2trace(CHANN, ITRC_OUTPUT, 14, 3, + amountToMove, pB->i2eFifoRemains, pB->i2eWaitingForEmptyFifo ); +#endif + // Put this channel back on queue + // this ultimatly gets more data or wakes write output + i2QueueNeeds(pB, pCh, NEED_INLINE); + + if ( pB->i2eWaitingForEmptyFifo ) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 16, 0 ); +#endif + // or schedule + if (!in_interrupt()) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 61, 0 ); +#endif + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(2); + if (signal_pending(current)) { + break; + } + continue; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 62, 0 ); +#endif + // let interrupt in = WAS restore_flags() + // We hold no lock nor is irq off anymore??? + + break; + } + break; // from while(count) + } + else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 19, 2, pB->i2eFifoRemains, + pB->i2eTxMailEmpty ); +#endif + break; // from while(count) + } else if ( pCh->channelNeeds & NEED_CREDIT ) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 22, 0 ); +#endif + break; // from while(count) + } else if ( --bailout) { + + // Try to throw more things (maybe not us) in the fifo if we're + // not already waiting for it. + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 20, 0 ); +#endif + serviceOutgoingFifo(pB); + //break; CONTINUE; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 21, 3, pB->i2eFifoRemains, + pB->i2eOutMailWaiting, pB->i2eWaitingForEmptyFifo ); +#endif + break; // from while(count) + } + } + } // End of while(count) + + i2QueueNeeds(pB, pCh, NEED_INLINE); + + // We drop through either when the count expires, or when there is some + // count left, but there was a non-blocking write. + if (countOriginal > count) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count ); +#endif + serviceOutgoingFifo( pB ); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count ); +#endif + + return countOriginal - count; +} + +//****************************************************************************** +// Function: i2FlushOutput(pCh) +// Parameters: Pointer to a channel structure +// Returns: Nothing +// +// Description: +// Sends bypass command to start flushing (waiting possibly forever until there +// is room), then sends inline command to stop flushing output, (again waiting +// possibly forever). +//****************************************************************************** +static inline void +i2FlushOutput(i2ChanStrPtr pCh) +{ + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags ); +#endif + + if (pCh->flush_flags) + return; + + if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 2, 0 ); +#endif + } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) { + pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 3, 0 ); +#endif + } +} + +static int +i2RetryFlushOutput(i2ChanStrPtr pCh) +{ + int old_flags = pCh->flush_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags ); +#endif + + pCh->flush_flags = 0; // Clear flag so we can avoid recursion + // and queue the commands + + if ( old_flags & STARTFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + old_flags = STOPFL_FLAG; //Success - send stop flush + } else { + old_flags = STARTFL_FLAG; //Failure - Flag for retry later + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags ); +#endif + } + if ( old_flags & STOPFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) > 0 ) { + old_flags = 0; // Success - clear flags + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags ); +#endif + } + pCh->flush_flags = old_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags ); +#endif + return old_flags; +} + +//****************************************************************************** +// Function: i2DrainOutput(pCh,timeout) +// Parameters: Pointer to a channel structure +// Maximum period to wait +// Returns: ? +// +// Description: +// Uses the bookmark request command to ask the board to send a bookmark back as +// soon as all the data is completely sent. +//****************************************************************************** +static void +i2DrainWakeup(i2ChanStrPtr pCh) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires ); +#endif + pCh->BookmarkTimer.expires = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +} + +static void +i2DrainOutput(i2ChanStrPtr pCh, int timeout) +{ + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires); +#endif + pB = pCh->pMyBord; + // If the board has gone fatal, return bad, + // and also hit the trap routine if it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return; + } + if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) { + // One per customer (channel) + init_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = jiffies + timeout; + pCh->BookmarkTimer.function = (void*)(unsigned long)i2DrainWakeup; + pCh->BookmarkTimer.data = (unsigned long)pCh; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires ); +#endif + + add_timer( &(pCh->BookmarkTimer) ); + } + + i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ ); + serviceOutgoingFifo( pB ); + + interruptible_sleep_on( &(pCh->pBookmarkWait) ); + + // if expires == 0 then timer poped, then do not need to del_timer + if ((timeout > 0) && pCh->BookmarkTimer.expires && + (pCh->BookmarkTimer.expires > jiffies)) { + del_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = 0; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires ); +#endif + + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires ); +#endif + return; +} + +//****************************************************************************** +// Function: i2OutputFree(pCh) +// Parameters: Pointer to a channel structure +// Returns: Space in output buffer +// +// Description: +// Returns -1 if very gross error. Otherwise returns the amount of bytes still +// free in the output buffer. +//****************************************************************************** +static int +i2OutputFree(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) { + return -1; + } + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // If this is negative, we will discover later + amountToMove -= sizeof(i2DataHeader); + + return (amountToMove < 0) ? 0 : amountToMove; +} +static void + +ip2_owake( PTTY tp) +{ + i2ChanStrPtr pCh; + + if (tp == NULL) return; + + pCh = tp->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags, (1 << TTY_DO_WRITE_WAKEUP) ); +#endif + wake_up_interruptible ( &tp->write_wait ); + if ( ( tp->flags & (1 << TTY_DO_WRITE_WAKEUP) ) + && tp->ldisc.write_wakeup ) + { + (tp->ldisc.write_wakeup) ( tp ); +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 11, 0 ); +#endif + } +} + +static inline void +set_baud_params(i2eBordStrPtr pB) +{ + int i,j; + i2ChanStrPtr *pCh; + + pCh = (i2ChanStrPtr *) pB->i2eChannelPtr; + + for (i = 0; i < ABS_MAX_BOXES; i++) { + if (pB->channelBtypes.bid_value[i]) { + if (BID_HAS_654(pB->channelBtypes.bid_value[i])) { + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 921600; // MAX for ST654 + (pCh[i*16+j])->BaudDivisor = 96; + } + } else { // has cirrus cd1400 + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400 + (pCh[i*16+j])->BaudDivisor = 12; + } + } + } + } +} + +//****************************************************************************** +// Function: i2StripFifo(pB) +// Parameters: Pointer to a board structure +// Returns: ? +// +// Description: +// Strips all the available data from the incoming FIFO, identifies the type of +// packet, and either buffers the data or does what needs to be done. +// +// Note there is no overflow checking here: if the board sends more data than it +// ought to, we will not detect it here, but blindly overflow... +//****************************************************************************** + +// A buffer for reading in blocks for unknown channels +static unsigned char junkBuffer[IBUF_SIZE]; + +// A buffer to read in a status packet. Because of the size of the count field +// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE +static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4]; + +// This table changes the bit order from MSR order given by STAT_MODEM packet to +// status bits used in our library. +static char xlatDss[16] = { +0 | 0 | 0 | 0 , +0 | 0 | 0 | I2_CTS , +0 | 0 | I2_DSR | 0 , +0 | 0 | I2_DSR | I2_CTS , +0 | I2_RI | 0 | 0 , +0 | I2_RI | 0 | I2_CTS , +0 | I2_RI | I2_DSR | 0 , +0 | I2_RI | I2_DSR | I2_CTS , +I2_DCD | 0 | 0 | 0 , +I2_DCD | 0 | 0 | I2_CTS , +I2_DCD | 0 | I2_DSR | 0 , +I2_DCD | 0 | I2_DSR | I2_CTS , +I2_DCD | I2_RI | 0 | 0 , +I2_DCD | I2_RI | 0 | I2_CTS , +I2_DCD | I2_RI | I2_DSR | 0 , +I2_DCD | I2_RI | I2_DSR | I2_CTS }; + +static inline void +i2StripFifo(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + int channel; + int count; + unsigned short stuffIndex; + int amountToRead; + unsigned char *pc, *pcLimit; + unsigned char uc; + unsigned char dss_change; + unsigned long bflags,cflags; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); +#endif + + while (HAS_INPUT(pB)) { +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); +#endif + // Process packet from fifo a one atomic unit + WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock,bflags); + + // The first word (or two bytes) will have channel number and type of + // packet, possibly other information + pB->i2eLeadoffWord[0] = iiReadWord(pB); + + switch(PTYPE_OF(pB->i2eLeadoffWord)) + { + case PTYPE_DATA: + pB->got_input = 1; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 ); +#endif + channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */ + count = iiReadWord(pB); /* Count is in the next word */ + +// NEW: Check the count for sanity! Should the hardware fail, our death +// is more pleasant. While an oversize channel is acceptable (just more +// than the driver supports), an over-length count clearly means we are +// sick! + if ( ((unsigned int)count) > IBUF_SIZE ) { + pB->i2eFatal = 2; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + return; /* Bail out ASAP */ + } + // Channel is illegally big ? + if (channel >= pB->i2eChannelCnt || + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) == NULL) + { + iiReadBuf(pB, junkBuffer, count); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + break; /* From switch: ready for next packet */ + } + + // Channel should be valid, then + + // If this is a hot-key, merely post its receipt for now. These are + // always supposed to be 1-byte packets, so we won't even check the + // count. Also we will post an acknowledgement to the board so that + // more data can be forthcoming. Note that we are not trying to use + // these sequences in this driver, merely to robustly ignore them. + if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) + { + pCh->hotKeyIn = iiReadWord(pB) & 0xff; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); + break; /* From the switch: ready for next packet */ + } + + // Normal data! We crudely assume there is room for the data in our + // buffer because the board wouldn't have exceeded his credit limit. + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,cflags);// We have 2 locks now + stuffIndex = pCh->Ibuf_stuff; + amountToRead = IBUF_SIZE - stuffIndex; + if (amountToRead > count) + amountToRead = count; + + // stuffIndex would have been already adjusted so there would + // always be room for at least one, and count is always at least + // one. + + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + + // Update the stuffIndex by the amount of data moved. Note we could + // never ask for more data than would just fit. However, we might + // have read in one more byte than we wanted because the read + // rounds up to even bytes. If this byte is on the end of the + // packet, and is padding, we ignore it. If the byte is part of + // the actual data, we need to move it. + + stuffIndex += amountToRead; + + if (stuffIndex >= IBUF_SIZE) { + if ((amountToRead & 1) && (count > amountToRead)) { + pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE]; + amountToRead++; + stuffIndex = 1; + } else { + stuffIndex = 0; + } + } + + // If there is anything left over, read it as well + if (count > amountToRead) { + amountToRead = count - amountToRead; + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + stuffIndex += amountToRead; + } + + // Update stuff index + pCh->Ibuf_stuff = stuffIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,cflags); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + +#ifdef USE_IQ + queue_task(&pCh->tqueue_input, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_input(pCh); +#endif + + // Note we do not need to maintain any flow-control credits at this + // time: if we were to increment .asof and decrement .room, there + // would be no net effect. Instead, when we strip data, we will + // increment .asof and leave .room unchanged. + + break; // From switch: ready for next packet + + case PTYPE_STATUS: +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 ); +#endif + + count = CMD_COUNT_OF(pB->i2eLeadoffWord); + + iiReadBuf(pB, cmdBuffer, count); + // We can release early with buffer grab + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + + pc = cmdBuffer; + pcLimit = &(cmdBuffer[count]); + + while (pc < pcLimit) { + channel = *pc++; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc ); +#endif + /* check for valid channel */ + if (channel < pB->i2eChannelCnt + && + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL + ) + { + dss_change = 0; + + switch (uc = *pc++) + { + /* Breaks and modem signals are easy: just update status */ + case STAT_CTS_UP: + if ( !(pCh->dataSetIn & I2_CTS) ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn |= I2_CTS; + break; + + case STAT_CTS_DN: + if ( pCh->dataSetIn & I2_CTS ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_CTS; + break; + + case STAT_DCD_UP: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn ); +#endif + if ( !(pCh->dataSetIn & I2_DCD) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_MODEM, 2, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn |= I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DCD_DN: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn ); +#endif + if ( pCh->dataSetIn & I2_DCD ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 5, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DSR_UP: + if ( !(pCh->dataSetIn & I2_DSR) ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn |= I2_DSR; + break; + + case STAT_DSR_DN: + if ( pCh->dataSetIn & I2_DSR ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DSR; + break; + + case STAT_RI_UP: + if ( !(pCh->dataSetIn & I2_RI) ) + { + pCh->dataSetIn |= I2_DRI; + ++pCh->icount.rng; + dss_change = 1; + } + pCh->dataSetIn |= I2_RI ; + break; + + case STAT_RI_DN: + if ( pCh->dataSetIn & I2_RI ) + { + pCh->dataSetIn |= I2_DRI; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_RI ; + break; + + case STAT_BRK_DET: + pCh->dataSetIn |= I2_BRK; + dss_change = 1; + break; + + // Bookmarks? one less request we're waiting for + case STAT_BMARK: + pCh->bookMarks--; + if (pCh->bookMarks <= 0 ) { + pCh->bookMarks = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires ); +#endif + } + break; + + // Flow control packets? Update the new credits, and if + // someone was waiting for output, queue him up again. + case STAT_FLOW: + pCh->outfl.room = + ((flowStatPtr)pc)->room - + (pCh->outfl.asof - ((flowStatPtr)pc)->asof); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room ); +#endif + if (pCh->channelNeeds & NEED_CREDIT) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds); +#endif + pCh->channelNeeds &= ~NEED_CREDIT; + i2QueueNeeds(pB, pCh, NEED_INLINE); + if ( pCh->pTTY ) + ip2_owake(pCh->pTTY); + } +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds); +#endif + pc += sizeof(flowStat); + break; + + /* Special packets: */ + /* Just copy the information into the channel structure */ + + case STAT_STATUS: + + pCh->channelStatus = *((debugStatPtr)pc); + pc += sizeof(debugStat); + break; + + case STAT_TXCNT: + + pCh->channelTcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_RXCNT: + + pCh->channelRcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_BOXIDS: + pB->channelBtypes = *((bidStatPtr)pc); + pc += sizeof(bidStat); +//printk("boxids: %x %x %x %x\n", +// pB->channelBtypes.bid_value[0],pB->channelBtypes.bid_value[1], +// pB->channelBtypes.bid_value[2],pB->channelBtypes.bid_value[3]); + set_baud_params(pB); + break; + + case STAT_HWFAIL: + i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST); + pCh->channelFail = *((failStatPtr)pc); + pc += sizeof(failStat); + break; + + /* No explicit match? then + * Might be an error packet... + */ + default: + switch (uc & STAT_MOD_ERROR) + { + case STAT_ERROR: + if (uc & STAT_E_PARITY) + pCh->dataSetIn |= I2_PAR; + if (uc & STAT_E_FRAMING) + pCh->dataSetIn |= I2_FRA; + if (uc & STAT_E_OVERRUN) + pCh->dataSetIn |= I2_OVR; + break; + + case STAT_MODEM: + pCh->dataSetIn = (pCh->dataSetIn + & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) ) + | xlatDss[uc & 0xf]; + default: + break; + } + } /* End of switch on status type */ + if (dss_change) { +#ifdef USE_IQ + queue_task(&pCh->tqueue_status, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_status(pCh); +#endif + } + } + else /* Or else, channel is invalid */ + { + // Even though the channel is invalid, we must test the + // status to see how much additional data it has (to be + // skipped) + switch (*pc++) + { + case STAT_FLOW: + pc += 4; /* Skip the data */ + break; + + case STAT_CTS_UP: + case STAT_CTS_DN: + case STAT_DCD_UP: + case STAT_DCD_DN: + case STAT_DSR_UP: + case STAT_DSR_DN: + case STAT_RI_UP: + case STAT_RI_DN: + case STAT_BRK_DET: + case STAT_BMARK: + default: + break; + } + } + } // End of while (there is still some status packet left) + break; + + default: // Neither packet? should be impossible +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, + PTYPE_OF(pB->i2eLeadoffWord) ); +#endif + break; + } // End of switch on type of packets + } //while(board HAS_INPUT) +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); +#endif + // Send acknowledgement to the board even if there was no data! + pB->i2eOutMailWaiting |= MB_IN_STRIPPED; + return; +} + +//****************************************************************************** +// Function: i2Write2Fifo(pB,address,count) +// Parameters: Pointer to a board structure, source address, byte count +// Returns: bytes written +// +// Description: +// Writes count bytes to board io address(implied) from source +// Adjusts count, leaves reserve for next time around bypass cmds +//****************************************************************************** +static int +i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) +{ + int rc = 0; + unsigned long flags; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + if (!pB->i2eWaitingForEmptyFifo) { + if (pB->i2eFifoRemains > (count+reserve)) { + pB->i2eFifoRemains -= count; + iiWriteBuf(pB, source, count); + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + rc = count; + } + } + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + return rc; +} +//****************************************************************************** +// Function: i2StuffFifoBypass(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many bypass commands into the fifo as possible. This is simpler +// than stuffing data or inline commands to fifo, since we do not have +// flow-control to deal with. +//****************************************************************************** +static inline void +i2StuffFifoBypass(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned long flags; + + int bailout = 1000; + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) + { + WRITE_LOCK_IRQSAVE(&pCh->Cbuf_spinlock,flags); + stripIndex = pCh->Cbuf_strip; + + // as long as there are packets for this channel... + + while (stripIndex != pCh->Cbuf_stuff) { + pRemove = &(pCh->Cbuf[stripIndex]); + packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); + paddedSize = ROUNDUP(packetSize); + + if (paddedSize > 0) { + if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { + notClogged = 0; /* fifo full */ + i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue + break; // Break from the channel + } + } +#ifdef DEBUG_FIFO +WriteDBGBuf("BYPS", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugBypassCount++; + + pRemove += packetSize; + stripIndex += packetSize; + if (stripIndex >= CBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Cbuf; + } + } + // Done with this channel. Move to next, removing this one from + // the queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Cbuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 ); + } +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoFlow(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many flow control packets into the fifo as possible. This is easier +// even than doing normal bypass commands, because there is always at most one +// packet, already assembled, for each channel. +//****************************************************************************** +static inline void +i2StuffFifoFlow(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned short paddedSize = ROUNDUP(sizeof(flowIn)); + +#ifdef IP2DEBUG_TRACE +ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, pB->i2eFifoRemains, paddedSize ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) { + pB->debugFlowCount++; + + // NO Chan LOCK needed ??? + if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) { + break; + } +#ifdef DEBUG_FIFO +WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize); +#endif /* DEBUG_FIFO */ + + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 ); +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoInline(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as much data and inline commands into the fifo as possible. This is +// the most complex fifo-stuffing operation, since there if now the channel +// flow-control issue to deal with. +//****************************************************************************** +static inline void +i2StuffFifoInline(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned short flowsize; + unsigned long flags; + + int bailout = 1000; + int bailout2; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, + pB->i2Dbuf_strip, pB->i2Dbuf_stuff ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stripIndex = pCh->Obuf_strip; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); +#endif + // as long as there are packets for this channel... + bailout2 = 1000; + while ( --bailout2 && stripIndex != pCh->Obuf_stuff) { + pRemove = &(pCh->Obuf[stripIndex]); + + // Must determine whether this be a data or command packet to + // calculate correctly the header size and the amount of + // flow-control credit this type of packet will use. + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + flowsize = DATA_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2DataHeader); + } else { + flowsize = CMD_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2CmdHeader); + } + flowsize = CREDIT_USAGE(flowsize); + paddedSize = ROUNDUP(packetSize); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); +#endif + // If we don't have enough credits from the board to send the data, + // flag the channel that we are waiting for flow control credit, and + // break out. This will clean up this channel and remove us from the + // queue of hot things to do. +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize ); +#endif + if (pCh->outfl.room <= flowsize) { + // Do Not have the credits to send this packet. + i2QueueNeeds(pB, pCh, NEED_CREDIT); + notClogged = 0; + break; // So to do next channel + } + if ( (paddedSize > 0) + && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) { + // Do Not have room in fifo to send this packet. + notClogged = 0; + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; // Break from the channel + } +#ifdef DEBUG_FIFO +WriteDBGBuf("DATA", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugInlineCount++; + + // Update current credits + pCh->outfl.room -= flowsize; + pCh->outfl.asof += flowsize; + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove); + } + pRemove += packetSize; + stripIndex += packetSize; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip); +#endif + if (stripIndex >= OBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Obuf; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex ); +#endif + } + } /* while */ + if ( !bailout2 ) { + ip2trace (CHANN, ITRC_ERROR, 3, 0 ); + } + // Done with this channel. Move to next, removing this one from the + // queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Obuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if ( notClogged ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 8, 0 ); +#endif + if ( pCh->pTTY ) { + ip2_owake(pCh->pTTY); + } + } + } // Either clogged or finished all the work +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 ); + } +#endif + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip); +#endif +} + +//****************************************************************************** +// Function: serviceOutgoingFifo(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Helper routine to put data in the outgoing fifo, if we aren't already waiting +// for something to be there. If the fifo has only room for a very little data, +// go head and hit the board with a mailbox hit immediately. Otherwise, it will +// have to happen later in the interrupt processing. Since this routine may be +// called both at interrupt and foreground time, we must turn off interrupts +// during the entire process. +//****************************************************************************** +static void +serviceOutgoingFifo(i2eBordStrPtr pB) +{ + // If we aren't currently waiting for the board to empty our fifo, service + // everything that is pending, in priority order (especially, Bypass before + // Inline). + if ( ! pB->i2eWaitingForEmptyFifo ) + { + i2StuffFifoFlow(pB); + i2StuffFifoBypass(pB); + i2StuffFifoInline(pB); + + iiSendPendingMail(pB); + } +} + +//****************************************************************************** +// Function: i2ServiceBoard(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Normally this is called from interrupt level, but there is deliberately +// nothing in here specific to being called from interrupt level. All the +// hardware-specific, interrupt-specific things happen at the outer levels. +// +// For example, a timer interrupt could drive this routine for some sort of +// polled operation. The only requirement is that the programmer deal with any +// atomiticity/concurrency issues that result. +// +// This routine responds to the board's having sent mailbox information to the +// host (which would normally cause an interrupt). This routine reads the +// incoming mailbox. If there is no data in it, this board did not create the +// interrupt and/or has nothing to be done to it. (Except, if we have been +// waiting to write mailbox data to it, we may do so. +// +// Based on the value in the mailbox, we may take various actions. +// +// No checking here of pB validity: after all, it shouldn't have been called by +// the handler unless pB were on the list. +//****************************************************************************** +static inline int +i2ServiceBoard ( i2eBordStrPtr pB ) +{ + unsigned inmail; + unsigned long flags; + + + inmail = iiGetMail(pB); + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail ); +#endif + + if (inmail != NO_MAIL_HERE) { + // If the board has gone fatal, nothing to do but hit a bit that will + // alert foreground tasks to protest! + if ( inmail & MB_FATAL_ERROR ) { + pB->i2eFatal = 1; + goto exit_i2ServiceBoard; + } + + /* Assuming no fatal condition, we proceed to do work */ + if ( inmail & MB_IN_STUFFED ) { + pB->i2eFifoInInts++; + i2StripFifo(pB); /* There might be incoming packets */ + } + + if (inmail & MB_OUT_STRIPPED) { + pB->i2eFifoOutInts++; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + pB->i2eFifoRemains = pB->i2eFifoSize; + pB->i2eWaitingForEmptyFifo = 0; + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); +#endif + } + serviceOutgoingFifo(pB); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 ); +#endif + +exit_i2ServiceBoard: + + return 0; +} diff --git a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h new file mode 100644 index 000000000..21f0e1af9 --- /dev/null +++ b/drivers/char/ip2/i2lib.h @@ -0,0 +1,350 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Header file for high level library functions +* +*******************************************************************************/ +#ifndef I2LIB_H +#define I2LIB_H 1 +//------------------------------------------------------------------------------ +// I2LIB.H +// +// IntelliPort-II and IntelliPort-IIEX +// +// Defines, structure definitions, and external declarations for i2lib.c +//------------------------------------------------------------------------------ +//-------------------------------------- +// Mandatory Includes: +//-------------------------------------- +#include "ip2types.h" +#include "i2ellis.h" +#include "i2pack.h" +#include "i2cmd.h" + +//------------------------------------------------------------------------------ +// i2ChanStr -- Channel Structure: +// Used to track per-channel information for the library routines using standard +// loadware. Note also, a pointer to an array of these structures is patched +// into the i2eBordStr (see i2ellis.h) +//------------------------------------------------------------------------------ +// +// If we make some limits on the maximum block sizes, we can avoid dealing with +// buffer wrap. The wrapping of the buffer is based on where the start of the +// packet is. Then there is always room for the packet contiguously. +// +// Maximum total length of an outgoing data or in-line command block. The limit +// of 36 on data is quite arbitrary and based more on DOS memory limitations +// than the board interface. However, for commands, the maximum packet length is +// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits +// (see I2PACK.H) in such packets. For data packets, the count field size is not +// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE, +// but be careful if wanting to modify either. +// +#define MAX_OBUF_BLOCK 36 + +// Another note on maximum block sizes: we are buffering packets here. Data is +// put into the buffer (if there is room) regardless of the credits from the +// board. The board sends new credits whenever it has removed from his buffers a +// number of characters equal to 80% of total buffer size. (Of course, the total +// buffer size is what is reported when the very first set of flow control +// status packets are received from the board. Therefore, to be robust, you must +// always fill the board to at least 80% of the current credit limit, else you +// might not give it enough to trigger a new report. These conditions are +// obtained here so long as the maximum output block size is less than 20% the +// size of the board's output buffers. This is true at present by "coincidence" +// or "infernal knowledge": the board's output buffers are at least 700 bytes +// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest +// strategy might be to trap the first flow control report and guarantee that +// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first +// reported buffer credit. +// +#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block + +#define IBUF_SIZE 500 // character capacity of input buffer per channel +#define OBUF_SIZE 2048// character capacity of output buffer per channel +#define CBUF_SIZE 10 // character capacity of output bypass buffer + +typedef struct _i2ChanStr +{ + // First, back-pointers so that given a pointer to this structure, you can + // determine the correct board and channel number to reference, (say, when + // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.) + + int port_index; // Index of port in channel structure array attached + // to board structure. + PTTY pTTY; // Pointer to tty structure for port (OS specific) + USHORT validity; // Indicates whether the given channel has been + // initialized, really exists (or is a missing + // channel, e.g. channel 9 on an 8-port box.) + + i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure + + int wopen; // waiting fer carrier + + int throttled; // Set if upper layer can take no data + + int flags; // Defined in tty.h + int session; // Defined in tty.h + int pgrp; // Defined in tty.h + + PWAITQ open_wait; // Pointer for OS sleep function. + PWAITQ close_wait; // Pointer for OS sleep function. + PWAITQ delta_msr_wait;// Pointer for OS sleep function. + + struct timer_list BookmarkTimer; // Used by i2DrainOutput + wait_queue_head_t pBookmarkWait; // Used by i2DrainOutput + + struct termios NormalTermios; + struct termios CalloutTermios; + + int BaudBase; + int BaudDivisor; + + USHORT ClosingDelay; + USHORT ClosingWaitTime; + + volatile + flowIn infl; // This structure is initialized as a completely + // formed flow-control command packet, and as such + // has the channel number, also the capacity and + // "as-of" data needed continuously. + + USHORT sinceLastFlow; // Counts the number of characters read from input + // buffers, since the last time flow control info + // was sent. + + USHORT whenSendFlow; // Determines when new flow control is to be sent to + // the board. Note unlike earlier manifestations of + // the driver, these packets can be sent from + // in-place. + + USHORT channelNeeds; // Bit map of important things which must be done + // for this channel. (See bits below ) + + volatile + flowStat outfl; // Same type of structure is used to hold current + // flow control information used to control our + // output. "asof" is kept updated as data is sent, + // and "room" never goes to zero. + + // The incoming ring buffer + // Unlike the outgoing buffers, this holds raw data, not packets. The two + // extra bytes are used to hold the byte-padding when there is room for an + // odd number of bytes before we must wrap. + // + UCHAR Ibuf[IBUF_SIZE + 2]; + volatile + USHORT Ibuf_stuff; // Stuffing index + volatile + USHORT Ibuf_strip; // Stripping index + + // The outgoing ring-buffer: Holds Data and command packets. N.B., even + // though these are in the channel structure, the channel is also written + // here, the easier to send it to the fifo when ready. HOWEVER, individual + // packets here are NOT padded to even length: the routines for writing + // blocks to the the fifo will pad to even byte counts. + // + UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4]; + volatile + USHORT Obuf_stuff; // Stuffing index + volatile + USHORT Obuf_strip; // Stripping index + int Obuf_char_count; + + // The outgoing bypass-command buffer. Unlike earlier manifestations, the + // flow control packets are sent directly from the structures. As above, the + // channel number is included in the packet, but they are NOT padded to even + // size. + // + UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2]; + volatile + USHORT Cbuf_stuff; // Stuffing index + volatile + USHORT Cbuf_strip; // Stripping index + + // The temporary buffer for the Linux tty driver PutChar entry. + // + UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)]; + volatile + USHORT Pbuf_stuff; // Stuffing index + + // The state of incoming data-set signals + // + USHORT dataSetIn; // Bit-mapped according to below. Also indicates + // whether a break has been detected since last + // inquiry. + + // The state of outcoming data-set signals (as far as we can tell!) + // + USHORT dataSetOut; // Bit-mapped according to below. + + // Most recent hot-key identifier detected + // + USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates + // no hot key detected since last examined. + + // Counter of outstanding requests for bookmarks + // + short bookMarks; // Number of outstanding bookmark requests, (+ive + // whenever a bookmark request if queued up, -ive + // whenever a bookmark is received). + + // Misc options + // + USHORT channelOptions; // See below + + // To store various incoming special packets + // + debugStat channelStatus; + cntStat channelRcount; + cntStat channelTcount; + failStat channelFail; + + // To store the last values for line characteristics we sent to the board. + // + int speed; + + int flush_flags; + + void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...); + +#ifdef __KERNEL__ + /* + * Kernel counters for the 4 input interrupts + */ + struct async_icount icount; + + /* + * Task queues for processing input packets from the board. + */ + struct tq_struct tqueue_input; + struct tq_struct tqueue_status; + struct tq_struct tqueue_hangup; +#endif + + spinlock_t Ibuf_spinlock; + spinlock_t Obuf_spinlock; + spinlock_t Cbuf_spinlock; + spinlock_t Pbuf_spinlock; + +} i2ChanStr, *i2ChanStrPtr; + +//--------------------------------------------------- +// Manifests and bit-maps for elements in i2ChanStr +//--------------------------------------------------- +// +// flush flags +// +#define STARTFL_FLAG 1 +#define STOPFL_FLAG 2 + +// validity +// +#define CHANNEL_MAGIC_BITS 0xff00 +#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) == + // CHANNEL_MAGIC --> structure good + +#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists, + // and passed P.O.S.T. + +// channelNeeds +// +#define NEED_FLOW 1 // Indicates flow control has been queued +#define NEED_INLINE 2 // Indicates inline commands or data queued +#define NEED_BYPASS 4 // Indicates bypass commands queued +#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient + // credit. The data is still in the channel structure, + // but the channel is not enqueued in the board + // structure again until there is a credit received from + // the board. + +// dataSetIn (Also the bits for i2GetStatus return value) +// +#define I2_DCD 1 +#define I2_CTS 2 +#define I2_DSR 4 +#define I2_RI 8 + +// dataSetOut (Also the bits for i2GetStatus return value) +// +#define I2_DTR 1 +#define I2_RTS 2 + +// i2GetStatus() can optionally clear these bits +// +#define I2_BRK 0x10 // A break was detected +#define I2_PAR 0x20 // A parity error was received +#define I2_FRA 0x40 // A framing error was received +#define I2_OVR 0x80 // An overrun error was received + +// i2GetStatus() automatically clears these bits */ +// +#define I2_DDCD 0x100 // DCD changed from its former value +#define I2_DCTS 0x200 // CTS changed from its former value +#define I2_DDSR 0x400 // DSR changed from its former value +#define I2_DRI 0x800 // RI changed from its former value + +// hotKeyIn +// +#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected + +// channelOptions +// +#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default + // is, they do wait.) + +// fcmodes +// +#define I2_OUTFLOW_CTS 0x0001 +#define I2_INFLOW_RTS 0x0002 +#define I2_INFLOW_DSR 0x0004 +#define I2_INFLOW_DTR 0x0008 +#define I2_OUTFLOW_DSR 0x0010 +#define I2_OUTFLOW_DTR 0x0020 +#define I2_OUTFLOW_XON 0x0040 +#define I2_OUTFLOW_XANY 0x0080 +#define I2_INFLOW_XON 0x0100 + +#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS) +#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY) + +//------------------------------------------- +// Macros used from user level like functions +//------------------------------------------- + +// Macros to set and clear channel options +// +#define i2SetOption(pCh, option) pCh->channelOptions |= option +#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option + +// Macro to set fatal-error trap +// +#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine + +//-------------------------------------------- +// Declarations and prototypes for i2lib.c +//-------------------------------------------- +// +static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr); +static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...); +static int i2GetStatus(i2ChanStrPtr, int); +static int i2Input(i2ChanStrPtr); +static int i2InputFlush(i2ChanStrPtr); +static int i2Output(i2ChanStrPtr, const char *, int, int); +static int i2OutputFree(i2ChanStrPtr); +static int i2ServiceBoard(i2eBordStrPtr); +static void i2DrainOutput(i2ChanStrPtr, int); + +// Argument to i2QueueCommands +// +#define C_IN_LINE 1 +#define C_BYPASS 0 + +#endif // I2LIB_H diff --git a/drivers/char/ip2/i2os.h b/drivers/char/ip2/i2os.h new file mode 100644 index 000000000..f85822c1a --- /dev/null +++ b/drivers/char/ip2/i2os.h @@ -0,0 +1,145 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Defines, definitions and includes which are heavily dependant +* on O/S, host, compiler, etc. This file is tailored for: +* Linux v2.0.0 and later +* Gnu gcc c2.7.2 +* 80x86 architecture +* +*******************************************************************************/ + +#ifndef I2OS_H /* To prevent multiple includes */ +#define I2OS_H 1 + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) + +//------------------------------------------------- +// Required Includes +//------------------------------------------------- + +#include "ip2types.h" +#include <asm/io.h> /* For inb, etc */ + +//------------------------------------ +// Defines for I/O instructions: +//------------------------------------ + +#define INB(port) inb(port) +#define OUTB(port,value) outb((value),(port)) +#define INW(port) inw(port) +#define OUTW(port,value) outw((value),(port)) +#define OUTSW(port,addr,count) outsw((port),(addr),(((count)+1)/2)) +#define OUTSB(port,addr,count) outsb((port),(addr),(((count)+1))&-2) +#define INSW(port,addr,count) insw((port),(addr),(((count)+1)/2)) +#define INSB(port,addr,count) insb((port),(addr),(((count)+1))&-2) + +//-------------------------------------------- +// Interrupt control +//-------------------------------------------- + +#if LINUX_VERSION_CODE < 0x00020100 +typedef int spinlock_t; +#define spin_lock_init() +#define spin_lock(a) +#define spin_unlock(a) +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define write_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define write_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#define read_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define read_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#endif + +//#define SAVE_AND_DISABLE_INTS(a,b) spin_lock_irqsave(a,b) +//#define RESTORE_INTS(a,b) spin_unlock_irqrestore(a,b) + +#define LOCK_INIT(a) spin_lock_init(a) + +#define SAVE_AND_DISABLE_INTS(a,b) { \ + /* printk("get_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_lock_irqsave(a,b); \ +} + +#define RESTORE_INTS(a,b) { \ + /* printk("rel_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_unlock_irqrestore(a,b); \ +} + +#define READ_LOCK_IRQSAVE(a,b) { \ + /* printk("get_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_lock_irqsave(a,b); \ +} + +#define READ_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_unlock_irqrestore(a,b); \ +} + +#define WRITE_LOCK_IRQSAVE(a,b) { \ + /* printk("get_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_lock_irqsave(a,b); \ +} + +#define WRITE_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_unlock_irqrestore(a,b); \ +} + + +//------------------------------------------------------------------------------ +// Hardware-delay loop +// +// Probably used in only one place (see i2ellis.c) but this helps keep things +// together. Note we have unwound the IN instructions. On machines with a +// reasonable cache, the eight instructions (1 byte each) should fit in cache +// nicely, and on un-cached machines, the code-fetch would tend not to dominate. +// Note that cx is shifted so that "count" still reflects the total number of +// iterations assuming no unwinding. +//------------------------------------------------------------------------------ + +//#define DELAY1MS(port,count,label) + +//------------------------------------------------------------------------------ +// Macros to switch to a new stack, saving stack pointers, and to restore the +// old stack (Used, for example, in i2lib.c) "heap" is the address of some +// buffer which will become the new stack (working down from highest address). +// The two words at the two lowest addresses in this stack are for storing the +// SS and SP. +//------------------------------------------------------------------------------ + +//#define TO_NEW_STACK(heap,size) +//#define TO_OLD_STACK(heap) + +//------------------------------------------------------------------------------ +// Macros to save the original IRQ vectors and masks, and to patch in new ones. +//------------------------------------------------------------------------------ + +//#define SAVE_IRQ_MASKS(dest) +//#define WRITE_IRQ_MASKS(src) +//#define SAVE_IRQ_VECTOR(value,dest) +//#define WRITE_IRQ_VECTOR(value,src) + +//------------------------------------------------------------------------------ +// Macro to copy data from one far pointer to another. +//------------------------------------------------------------------------------ + +#define I2_MOVE_DATA(fpSource,fpDest,count) memmove(fpDest,fpSource,count); + +//------------------------------------------------------------------------------ +// Macros to issue eoi's to host interrupt control (IBM AT 8259-style). +//------------------------------------------------------------------------------ + +//#define MASTER_EOI +//#define SLAVE_EOI + +#endif /* I2OS_H */ + + diff --git a/drivers/char/ip2/i2pack.h b/drivers/char/ip2/i2pack.h new file mode 100644 index 000000000..e9b87a786 --- /dev/null +++ b/drivers/char/ip2/i2pack.h @@ -0,0 +1,364 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions of the packets used to transfer data and commands +* Host <--> Board. Information provided here is only applicable +* when the standard loadware is active. +* +*******************************************************************************/ +#ifndef I2PACK_H +#define I2PACK_H 1 + +//----------------------------------------------- +// Revision History: +// +// 10 October 1991 MAG First draft +// 24 February 1992 MAG Additions for 1.4.x loadware +// 11 March 1992 MAG New status packets +// +//----------------------------------------------- + +//------------------------------------------------------------------------------ +// Packet Formats: +// +// Information passes between the host and board through the FIFO in packets. +// These have headers which indicate the type of packet. Because the fifo data +// path may be 16-bits wide, the protocol is constrained such that each packet +// is always padded to an even byte count. (The lower-level interface routines +// -- i2ellis.c -- are designed to do this). +// +// The sender (be it host or board) must place some number of complete packets +// in the fifo, then place a message in the mailbox that packets are available. +// Placing such a message interrupts the "receiver" (be it board or host), who +// reads the mailbox message and determines that there are incoming packets +// ready. Since there are no partial packets, and the length of a packet is +// given in the header, the remainder of the packet can be read without checking +// for FIFO empty condition. The process is repeated, packet by packet, until +// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to +// signal the board that it has read the data. Only then can the sender place +// additional data in the fifo. +//------------------------------------------------------------------------------ +// +//------------------------------------------------ +// Definition of Packet Header Area +//------------------------------------------------ +// +// Caution: these only define header areas. In actual use the data runs off +// beyond the end of these structures. +// +// Since these structures are based on sequences of bytes which go to the board, +// there cannot be ANY padding between the elements. +#pragma pack(1) + +//---------------------------- +// DATA PACKETS +//---------------------------- + +typedef struct _i2DataHeader +{ + unsigned char i2sChannel; /* The channel number: 0-255 */ + + // -- Bitfields are allocated LSB first -- + + // For incoming data, indicates whether this is an ordinary packet or a + // special one (e.g., hot key hit). + unsigned i2sId : 2 __attribute__ ((__packed__)); + + // For tagging data packets. There are flush commands which flush only data + // packets bearing a particular tag. (used in implementing IntelliView and + // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has + // meaning internally to the loadware). + unsigned i2sTag : 4; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + + // The count of data to follow: does not include the possible additional + // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0. + unsigned short i2sCount; + +} i2DataHeader, *i2DataHeaderPtr; + +// Structure is immediately followed by the data, proper. + +//---------------------------- +// NON-DATA PACKETS +//---------------------------- + +typedef struct _i2CmdHeader +{ + unsigned char i2sChannel; // The channel number: 0-255 (Except where noted + // - see below + + // Number of bytes of commands, status or whatever to follow + unsigned i2sCount : 6; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + +} i2CmdHeader, *i2CmdHeaderPtr; + +// Structure is immediately followed by the applicable data. + +//--------------------------------------- +// Flow Control Packets (Outbound) +//--------------------------------------- + +// One type of outbound command packet is so important that the entire structure +// is explicitly defined here. That is the flow-control packet. This is never +// sent by user-level code (as would be the commands to raise/lower DTR, for +// example). These are only sent by the library routines in response to reading +// incoming data into the buffers. +// +// The parameters inside the command block are maintained in place, then the +// block is sent at the appropriate time. + +typedef struct _flowIn +{ + i2CmdHeader hd; // Channel #, count, type (see above) + unsigned char fcmd; // The flow control command (37) + unsigned short asof; // As of byte number "asof" (LSB first!) I have room + // for "room" bytes + unsigned short room; +} flowIn, *flowInPtr; + +//---------------------------------------- +// (Incoming) Status Packets +//---------------------------------------- + +// Incoming packets which are non-data packets are status packets. In this case, +// the channel number in the header is unimportant. What follows are one or more +// sub-packets, the first word of which consists of the channel (first or low +// byte) and the status indicator (second or high byte), followed by possibly +// more data. + +#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */ +#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */ +#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */ +#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */ +#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */ +#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */ +#define STAT_RI_UP 6 /* RI raised (no other bytes) */ +#define STAT_RI_DN 7 /* RI dropped (no other bytes) */ +#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */ +#define STAT_FLOW 9 /* Flow control(-- more: see below */ +#define STAT_BMARK 10 /* Bookmark (no other bytes) + * Bookmark is sent as a response to + * a command 60: request for bookmark + */ +#define STAT_STATUS 11 /* Special packet: see below */ +#define STAT_TXCNT 12 /* Special packet: see below */ +#define STAT_RXCNT 13 /* Special packet: see below */ +#define STAT_BOXIDS 14 /* Special packet: see below */ +#define STAT_HWFAIL 15 /* Special packet: see below */ + +#define STAT_MOD_ERROR 0xc0 +#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR: + * == STAT_MODEM, then this is a modem + * status packet, given in response to a + * CMD_DSS_NOW command. + * The low nibble has each data signal: + */ +#define STAT_MOD_DCD 0x8 +#define STAT_MOD_RI 0x4 +#define STAT_MOD_DSR 0x2 +#define STAT_MOD_CTS 0x1 + +#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR + * == STAT_ERROR, then + * sort of error on the channel. + * The remaining seven bits indicate + * what sort of error it is. + */ +/* The low three bits indicate parity, framing, or overrun errors */ + +#define STAT_E_PARITY 4 /* Parity error */ +#define STAT_E_FRAMING 2 /* Framing error */ +#define STAT_E_OVERRUN 1 /* (uxart) overrun error */ + +//--------------------------------------- +// STAT_FLOW packets +//--------------------------------------- + +typedef struct _flowStat +{ + unsigned short asof; + unsigned short room; +}flowStat, *flowStatPtr; + +// flowStat packets are received from the board to regulate the flow of outgoing +// data. A local copy of this structure is also kept to track the amount of +// credits used and credits remaining. "room" is the amount of space in the +// board's buffers, "as of" having received a certain byte number. When sending +// data to the fifo, you must calculate how much buffer space your packet will +// use. Add this to the current "asof" and subtract it from the current "room". +// +// The calculation for the board's buffer is given by CREDIT_USAGE, where size +// is the un-rounded count of either data characters or command characters. +// (Which is to say, the count rounded up, plus two). + +#define CREDIT_USAGE(size) (((size) + 3) & ~1) + +//--------------------------------------- +// STAT_STATUS packets +//--------------------------------------- + +typedef struct _debugStat +{ + unsigned char d_ccsr; + unsigned char d_txinh; + unsigned char d_stat1; + unsigned char d_stat2; +} debugStat, *debugStatPtr; + +// debugStat packets are sent to the host in response to a CMD_GET_STATUS +// command. Each byte is bit-mapped as described below: + +#define D_CCSR_XON 2 /* Has received XON, ready to transmit */ +#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */ +#define D_CCSR_TXENAB 8 /* Transmitter is enabled */ +#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */ + +#define D_TXINH_BREAK 1 /* We are sending a break */ +#define D_TXINH_EMPTY 2 /* No data to send */ +#define D_TXINH_SUSP 4 /* Output suspended via command 57 */ +#define D_TXINH_CMD 8 /* We are processing an in-line command */ +#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */ +#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */ +#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */ +#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */ + +#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */ +#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */ +#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */ +#define D_STAT1_RLM 8 /* Remote loopback mode selected */ +#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */ +#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */ +#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */ +#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */ + +#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */ +#define D_STAT2_RXMT 2 /* Receive buffers are all empty */ +#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote + * transmission: dropped DTR, sent XOFF, + * whatever... + */ +#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host + * until it receives a flow-control packet + */ +//----------------------------------------- +// STAT_TXCNT and STAT_RXCNT packets +//---------------------------------------- + +typedef struct _cntStat +{ + unsigned short cs_time; // (Assumes host is little-endian!) + unsigned short cs_count; +} cntStat, *cntStatPtr; + +// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT +// bypass command. cs_time is a running 1 Millisecond counter which acts as a +// time stamp. cs_count is a running counter of data sent or received from the +// uxarts. (Not including data added by the chip itself, as with CRLF +// processing). +//------------------------------------------ +// STAT_HWFAIL packets +//------------------------------------------ + +typedef struct _failStat +{ + unsigned char fs_written; + unsigned char fs_read; + unsigned short fs_address; +} failStat, *failStatPtr; + +// This packet is sent whenever the on-board diagnostic process detects an +// error. At startup, this process is dormant. The host can wake it up by +// issuing the bypass command CMD_HW_TEST. The process runs at low priority and +// performs continuous hardware verification; writing data to certain on-board +// registers, reading it back, and comparing. If it detects an error, this +// packet is sent to the host, and the process goes dormant again until the host +// sends another CMD_HW_TEST. It then continues with the next register to be +// tested. + +//------------------------------------------------------------------------------ +// Macros to deal with the headers more easily! Note that these are defined so +// they may be used as "left" as well as "right" expressions. +//------------------------------------------------------------------------------ + +// Given a pointer to the packet, reference the channel number +// +#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel + +// Given a pointer to the packet, reference the Packet type +// +#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType + +// The possible types of packets +// +#define PTYPE_DATA 0 /* Host <--> Board */ +#define PTYPE_BYPASS 1 /* Host ---> Board */ +#define PTYPE_INLINE 2 /* Host ---> Board */ +#define PTYPE_STATUS 2 /* Host <--- Board */ + +// Given a pointer to a Data packet, reference the Tag +// +#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag + +// Given a pointer to a Data packet, reference the data i.d. +// +#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId + +// The possible types of ID's +// +#define ID_ORDINARY_DATA 0 +#define ID_HOT_KEY 1 + +// Given a pointer to a Data packet, reference the count +// +#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount + +// Given a pointer to a Data packet, reference the beginning of data +// +#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header + +// Given a pointer to a Non-Data packet, reference the count +// +#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount + +#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count + +// Given a pointer to a Non-Data packet, reference the beginning of data +// +#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header + +//-------------------------------- +// MailBox Bits: +//-------------------------------- + +//-------------------------- +// Outgoing (host to board) +//-------------------------- +// +#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo +#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo + +//-------------------------- +// Incoming (board to host) +//-------------------------- +// +#define MB_IN_STUFFED 0x80 // Board has placed input in fifo +#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo +#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error + +#pragma pack(4) // Reset padding to command-line default + +#endif // I2PACK_H + diff --git a/drivers/char/ip2/ip2.h b/drivers/char/ip2/ip2.h new file mode 100644 index 000000000..7236744b0 --- /dev/null +++ b/drivers/char/ip2/ip2.h @@ -0,0 +1,109 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2_H +#define IP2_H + +#include "ip2types.h" +#include "i2cmd.h" + +/*************/ +/* Constants */ +/*************/ + +/* Device major numbers + * The first set are the major numbers allocated from the Linux Device Registry. + * This was expanded from 64 to 128 with version 2.0.26. If this code is built + * under earlier versions we use majors from the LOCAL/EXPERIMENTAL range. + */ +#if MAX_CHRDEV > 64 +# define IP2_TTY_MAJOR 71 +# define IP2_CALLOUT_MAJOR 72 +# define IP2_IPL_MAJOR 73 +#else +# define IP2_TTY_MAJOR 60 +# define IP2_CALLOUT_MAJOR 61 +# define IP2_IPL_MAJOR 62 +#endif + + +/* Board configuration array. + * This array defines the hardware irq and address for up to IP2_MAX_BOARDS + * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified, + * PCI and EISA boards are probed for and automagicly configed + * iff the addresses are set to 1 and 2 respectivily. + * 0x0100 - 0x03f0 == ISA + * 1 == PCI + * 2 == EISA + * 0 == (skip this board) + * This array defines the hardware addresses for them. Special + * addresses are EISA and PCI which go sniffing for boards. + + * In a multiboard system the position in the array determines which port + * devices are assigned to each board: + * board 0 is assigned ttyF0.. to ttyF63, + * board 1 is assigned ttyF64 to ttyF127, + * board 2 is assigned ttyF128 to ttyF191, + * board 3 is assigned ttyF192 to ttyF255. + * + * In PCI and EISA bus systems each range is mapped to card in + * monotonically increasing slot number order, ISA position is as specified + * here. + + * If the irqs are ALL set to 0,0,0,0 all boards operate in + * polled mode. For interrupt operation ISA boards require that the IRQ be + * specified, while PCI and EISA boards any nonzero entry + * will enable interrupts using the BIOS configured irq for the board. + * An invalid irq entry will default to polled mode for that card and print + * console warning. + + * When the driver is loaded as a module these setting can be overridden on the + * modprobe command line or on an option line in /etc/conf.modules + * or /etc/modules.conf depending on your distrubution. + * If the driver is built-in the configuration must be + * set here for ISA cards and address set to 1 and 2 for PCI and EISA. + * + * Here is an example that shows most if not all possibe combinations: + + *static ip2config_t ip2config = + *{ + * {11,1,0,0}, // irqs + * { // Addresses + * 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11 + * 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS + * 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped + * 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS + * // but polled not irq driven + * } + *}; + */ + + /* this structure is zeroed out because the suggested method is to configure + * the driver as a module, set up the parameters with an options line in + * /etc/modules.conf or /etc/conf.modules and load with modprobe, kerneld or + * kmod, the kernel module loader + */ +static ip2config_t ip2config = +{ + {0,0,0,0}, // irqs + { // Addresses + 0x0000, // Board 0, ttyF0 - ttyF63 + 0x0000, // Board 1, ttyF64 - ttyF127 + 0x0000, // Board 2, ttyF128 - ttyF191 + 0x0000 // Board 3, ttyF192 - ttyF255 + } +}; + +#endif diff --git a/drivers/char/ip2/ip2ioctl.h b/drivers/char/ip2/ip2ioctl.h new file mode 100644 index 000000000..aa0a9da85 --- /dev/null +++ b/drivers/char/ip2/ip2ioctl.h @@ -0,0 +1,35 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ + +#ifndef IP2IOCTL_H +#define IP2IOCTL_H + +//************* +//* Constants * +//************* + +// High baud rates (if not defined elsewhere. +#ifndef B153600 +# define B153600 0010005 +#endif +#ifndef B307200 +# define B307200 0010006 +#endif +#ifndef B921600 +# define B921600 0010007 +#endif + +#endif diff --git a/drivers/char/ip2/ip2trace.h b/drivers/char/ip2/ip2trace.h new file mode 100644 index 000000000..020aabb73 --- /dev/null +++ b/drivers/char/ip2/ip2trace.h @@ -0,0 +1,43 @@ + +// +union ip2breadcrumb +{ + struct { + unsigned char port, cat, codes, label; + } __attribute__ ((packed)) hdr; + unsigned long value; +}; + +#define ITRC_NO_PORT 0xFF +#define PORTN (port->port_index) +#define CHANN (pCh->port_index) + +#define ITRC_ERROR '!' +#define ITRC_INIT 'A' +#define ITRC_OPEN 'B' +#define ITRC_CLOSE 'C' +#define ITRC_DRAIN 'D' +#define ITRC_IOCTL 'E' +#define ITRC_FLUSH 'F' +#define ITRC_STATUS 'G' +#define ITRC_HANGUP 'H' +#define ITRC_INTR 'I' +#define ITRC_SFLOW 'J' +#define ITRC_SBCMD 'K' +#define ITRC_SICMD 'L' +#define ITRC_MODEM 'M' +#define ITRC_INPUT 'N' +#define ITRC_OUTPUT 'O' +#define ITRC_PUTC 'P' +#define ITRC_QUEUE 'Q' +#define ITRC_STFLW 'R' +#define ITRC_SFIFO 'S' +#define ITRC_VERIFY 'V' +#define ITRC_WRITE 'W' + +#define ITRC_ENTER 0x00 +#define ITRC_RETURN 0xFF + +#define ITRC_QUEUE_ROOM 2 +#define ITRC_QUEUE_CMD 6 + diff --git a/drivers/char/ip2/ip2types.h b/drivers/char/ip2/ip2types.h new file mode 100644 index 000000000..8d2b37999 --- /dev/null +++ b/drivers/char/ip2/ip2types.h @@ -0,0 +1,54 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants and type definitions. +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2TYPES_H +#define IP2TYPES_H + +//************* +//* Constants * +//************* + +// Define some limits for this driver. Ports per board is a hardware limitation +// that will not change. Current hardware limits this to 64 ports per board. +// Boards per driver is a self-imposed limit. +// +#define IP2_MAX_BOARDS 4 +#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS +#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD) + +#define ISA 0 +#define PCI 1 +#define EISA 2 + +//******************** +//* Type Definitions * +//******************** + +typedef struct tty_struct * PTTY; +typedef wait_queue_head_t PWAITQ; + +typedef unsigned char UCHAR; +typedef unsigned int UINT; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + +typedef struct +{ + short irq[IP2_MAX_BOARDS]; + unsigned short addr[IP2_MAX_BOARDS]; + int type[IP2_MAX_BOARDS]; +} ip2config_t; + +#endif diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c new file mode 100644 index 000000000..8f0ef75ae --- /dev/null +++ b/drivers/char/ip2main.c @@ -0,0 +1,3235 @@ +/* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +/************/ +/* Includes */ +/************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/version.h> + +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/errno.h> + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/major.h> +#include <linux/wait.h> + +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/termios.h> +#include <linux/tty_driver.h> +#include <linux/serial.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> + +#include <linux/cdk.h> +#include <linux/comstats.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> + +#include <linux/vmalloc.h> +#include <linux/init.h> +#include <asm/serial.h> + +#include <asm/uaccess.h> +#define pcibios_strerror(status) \ + printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); + +#include "./ip2/ip2types.h" +#include "./ip2/ip2trace.h" +#include "./ip2/ip2ioctl.h" +#include "./ip2/ip2.h" +#include "./ip2/i2ellis.h" +#include "./ip2/i2lib.h" + +/***************** + * /proc/ip2mem * + *****************/ + +#include <linux/proc_fs.h> + +int ip2_read_procmem(char *, char **, off_t, int, int ); +int ip2_read_proc(char *, char **, off_t, int, int *, void * ); + +struct proc_dir_entry ip2_proc_entry = { + 0, + 6,"ip2mem", + S_IFREG | S_IRUGO, + 1, 0, 0, + 0, + NULL, + ip2_read_procmem +}; + +/********************/ +/* Type Definitions */ +/********************/ + +/*************/ +/* Constants */ +/*************/ + +/* String constants to identify ourselves */ +static char *pcName = "Computone IntelliPort Plus multiport driver"; +static char *pcVersion = "1.2.4"; + +/* String constants for port names */ +static char *pcDriver_name = "ip2"; +static char *pcTty = "ttyf"; +static char *pcCallout = "cuf"; +static char *pcIpl = "ip2ipl"; + +/* Serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +// cheezy kludge or genius - you decide? +int ip2_loadmain(int *, int *, unsigned char *, int); +static unsigned char *Fip_firmware; +static int Fip_firmware_size; + +/***********************/ +/* Function Prototypes */ +/***********************/ + +/* Global module entry functions */ +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +int old_ip2_init(void); + +/* Private (static) functions */ +static int ip2_open(PTTY, struct file *); +static void ip2_close(PTTY, struct file *); +static int ip2_write(PTTY, int, const unsigned char *, int); +static void ip2_putchar(PTTY, unsigned char); +static void ip2_flush_chars(PTTY); +static int ip2_write_room(PTTY); +static int ip2_chars_in_buf(PTTY); +static void ip2_flush_buffer(PTTY); +static int ip2_ioctl(PTTY, struct file *, UINT, ULONG); +static void ip2_set_termios(PTTY, struct termios *); +static void ip2_set_line_discipline(PTTY); +static void ip2_throttle(PTTY); +static void ip2_unthrottle(PTTY); +static void ip2_stop(PTTY); +static void ip2_start(PTTY); +static void ip2_hangup(PTTY); + +static void set_irq(int, int); +static void ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ip2_poll(unsigned long arg); +static inline void service_all_boards(void); +static inline void do_input(i2ChanStrPtr pCh); +static inline void do_status(i2ChanStrPtr pCh); + +static void ip2_wait_until_sent(PTTY,int); + +static void set_params (i2ChanStrPtr, struct termios *); +static int get_modem_info(i2ChanStrPtr, unsigned int *); +static int set_modem_info(i2ChanStrPtr, unsigned int, unsigned int *); +static int get_serial_info(i2ChanStrPtr, struct serial_struct *); +static int set_serial_info(i2ChanStrPtr, struct serial_struct *); + +static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *) ; +static ssize_t ip2_ipl_write(struct file *, const char *, size_t, loff_t *); +static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); +static int ip2_ipl_open(struct inode *, struct file *); + +void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...); +static int DumpTraceBuffer(char *, int); +static int DumpFifoBuffer( char *, int); + +static void ip2_init_board(int); +static unsigned short find_eisa_board(int); + +/***************/ +/* Static Data */ +/***************/ + +static struct tty_driver ip2_tty_driver; +static struct tty_driver ip2_callout_driver; + +static int ref_count; + +/* Here, then is a table of board pointers which the interrupt routine should + * scan through to determine who it must service. + */ +static unsigned short i2nBoards = 0; // Number of boards here + +static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; + +static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; +//DevTableMem just used to save addresses for kfree +static void *DevTableMem[IP2_MAX_BOARDS] = {NULL,NULL,NULL,NULL}; + +static struct tty_struct * TtyTable[IP2_MAX_PORTS]; +static struct termios * Termios[IP2_MAX_PORTS]; +static struct termios * TermiosLocked[IP2_MAX_PORTS]; + +/* This is the driver descriptor for the ip2ipl device, which is used to + * download the loadware to the boards. + */ +static struct file_operations +ip2_ipl = { + NULL, + ip2_ipl_read, + ip2_ipl_write, + NULL, + NULL, + ip2_ipl_ioctl, + NULL, + ip2_ipl_open, + NULL, + NULL, + NULL, + NULL, + NULL, + /* NULL, NULL 2.2 */ +}; + +static long irq_counter = 0; +static long bh_counter = 0; + +// Use immediate queue to service interrupts +//#define USE_IQI // PCI&2.2 needs work +//#define USE_IQ // PCI&2.2 needs work + +/* The timer_list entry for our poll routine. If interrupt operation is not + * selected, the board is serviced periodically to see if anything needs doing. + */ +#define POLL_TIMEOUT (jiffies + 1) +static struct timer_list PollTimer = { NULL, NULL, 0, 0, ip2_poll }; +// next, prev, expires,data, func() +static char TimerOn = 0; + +#ifdef IP2DEBUG_TRACE +/* Trace (debug) buffer data */ +#define TRACEMAX 1000 +static unsigned long tracebuf[TRACEMAX]; +static int tracestuff = 0; +static int tracestrip = 0; +static int tracewrap = 0; +#endif + +/**********/ +/* Macros */ +/**********/ + +#if defined(MODULE) && defined(IP2DEBUG_OPEN) +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ + kdevname(tty->device),(pCh->flags),ref_count, \ + tty->count,/*GET_USE_COUNT(module)*/0,s) +#else +#define DBG_CNT(s) +#endif + +#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) ) +#define MAX(a,b) ( ( (a) > (b) ) ? (a) : (b) ) + +/********/ +/* Code */ +/********/ + +#include "./ip2/i2ellis.c" /* Extremely low-level interface services */ +#include "./ip2/i2cmd.c" /* Standard loadware command definitions */ +#include "./ip2/i2lib.c" /* High level interface services */ + +/* Configuration area for modprobe */ +#ifdef MODULE +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +#endif /* MODULE */ + +static int poll_only = 0; + +static int Pci_index = 0; +static int Eisa_irq = 0; +static int Eisa_slot = 0; + +static int iindx = 0; +static char rirqs[IP2_MAX_BOARDS] = {0,}; +static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; + +/******************************************************************************/ +/* Initialisation Section */ +/******************************************************************************/ +int +ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) +{ + int i; + /* process command line arguments to modprobe or insmod i.e. iop & irqp */ + /* otherwise ip2config is initialized by what's in ip2/ip2.h */ + /* command line trumps initialization in ip2.h */ + /* first two args are null if builtin to kernel */ + if ((irqp != NULL) || (iop != NULL)) { + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (irqp && irqp[i]) { + ip2config.irq[i] = irqp[i]; + } + if (iop && iop[i]) { + ip2config.addr[i] = iop[i]; + } + } + } + Fip_firmware = firmware; + Fip_firmware_size = firmsize; + return old_ip2_init(); +} + +// Some functions to keep track of what irq's we have + +static int __init +is_valid_irq(int irq) +{ + int *i = Valid_Irqs; + + while ((*i != 0) && (*i != irq)) { + i++; + } + return (*i); +} + +static void __init +mark_requested_irq( char irq ) +{ + rirqs[iindx++] = irq; +} + +static int __init +clear_requested_irq( char irq ) +{ + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) { + rirqs[i] = 0; + return 1; + } + } + return 0; +} + +static int __init +have_requested_irq( char irq ) +{ + // array init to zeros so 0 irq will not be requested as a side effect + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) + return 1; + } + return 0; +} + +/******************************************************************************/ +/* Function: init_module() */ +/* Parameters: None */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It simply calls */ +/* the driver initialisation function and returns what it returns. */ +/******************************************************************************/ +#ifdef MODULE +int +init_module(void) +{ +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Loading module ...\n" ); +#endif + //was return old_ip2_init(); + return 0; +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: cleanup_module() */ +/* Parameters: None */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It has to return */ +/* the device and the driver to a passive state. It should not be necessary */ +/* to reset the board fully, especially as the loadware is downloaded */ +/* externally rather than in the driver. We just want to disable the board */ +/* and clear the loadware to a reset state. To allow this there has to be a */ +/* way to detect whether the board has the loadware running at init time to */ +/* handle subsequent installations of the driver. All memory allocated by the */ +/* driver should be returned since it may be unloaded from memory. */ +/******************************************************************************/ +#ifdef MODULE +void +cleanup_module(void) +{ + int err; + int i; + +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); +#endif + + + /* Stop poll timer if we had one. */ + if ( TimerOn ) { + del_timer ( &PollTimer ); + TimerOn = 0; + } + + /* Reset the boards we have. */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiReset ( i2BoardPtrTable[i] ); + } + } + + /* The following is done at most once, if any boards were installed. */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiResetDelay( i2BoardPtrTable[i] ); + /* free io addresses and Tibet */ + release_region( ip2config.addr[i], 8 ); + } + /* Disable and remove interrupt handler. */ + if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { + free_irq ( ip2config.irq[i], (void *)&pcName); + clear_requested_irq( ip2config.irq[i]); + } + } + if ( ( err = tty_unregister_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); + } + if ( ( err = tty_unregister_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister callout driver (%d)\n", err); + } + if ( ( err = unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) ) { + printk(KERN_ERR "IP2: failed to unregister IPL driver (%d)\n", err); + } + if ( ( err = proc_unregister( &proc_root, ip2_proc_entry.low_ino ) ) ) { + printk(KERN_ERR "IP2: failed to unregister read_procmem (%d)\n", err); + } + + // free memory + for (i = 0; i < IP2_MAX_BOARDS; i++) { + void *pB; + if ((pB = i2BoardPtrTable[i]) != 0 ) { + kfree ( pB ); + i2BoardPtrTable[i] = NULL; + } + if ((DevTableMem[i]) != NULL ) { + kfree ( DevTableMem[i] ); + DevTableMem[i] = NULL; + } + } + + /* Cleanup the iiEllis subsystem. */ + iiEllisCleanup(); +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "IP2 Unloaded\n" ); +#endif +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: old_ip2_init() */ +/* Parameters: irq, io from command line of insmod et. al. */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This was the required entry point for all drivers (now in ip2.c) */ +/* It performs all */ +/* initialisation of the devices and driver structures, and registers itself */ +/* with the relevant kernel modules. */ +/******************************************************************************/ +/* SA_INTERRUPT- if set blocks all interrupts else only this line */ +/* SA_SHIRQ - for shared irq PCI or maybe EISA only */ +/* SA_RANDOM - can be source for cert. random number generators */ +#define IP2_SA_FLAGS 0 + +int __init +old_ip2_init(void) +{ + int i; + int err; + int status = 0; + i2eBordStrPtr pB = NULL; + int rc = -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); +#endif + + /* Announce our presence */ + printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); + + /* if all irq config is zero we shall poll_only */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + poll_only |= ip2config.irq[i]; + } + poll_only = !poll_only; + + /* Initialise the iiEllis subsystem. */ + iiEllisInit(); + + /* Initialize arrays. */ + memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); + memset( DevTable, 0, sizeof DevTable ); + memset( TtyTable, 0, sizeof TtyTable ); + memset( Termios, 0, sizeof Termios ); + memset( TermiosLocked, 0, sizeof TermiosLocked ); + + /* Initialise all the boards we can find (up to the maximum). */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + switch ( ip2config.addr[i] ) { + case 0: /* skip this slot even if card is present */ + break; + default: /* ISA */ + /* ISA address must be specified */ + if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { + printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", + i, ip2config.addr[i] ); + ip2config.addr[i] = 0; + } else { + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if invalid */ + if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); + ip2config.irq[i] = 0;// 0 is polling and is valid in that sense + } + } + break; + case PCI: +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *pci_dev_i = NULL; + pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); + if (pci_dev_i != NULL) { + unsigned int addr; + unsigned char pci_irq; + + ip2config.type[i] = PCI; + /* + * Update Pci_index, so that the next time we go + * searching for a PCI board we find a different + * one. + */ + ++Pci_index; + status = + pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); + if ( addr & 1 ) { + ip2config.addr[i]=(USHORT)(addr&0xfffe); + } else { + printk( KERN_ERR "IP2: PCI I/O address error\n"); + } + status = + pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq); + + if (!is_valid_irq(pci_irq)) { + printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); + pci_irq = 0; + } + ip2config.irq[i] = pci_irq; + } else { // ann error + ip2config.addr[i] = 0; + if (status == PCIBIOS_DEVICE_NOT_FOUND) { + printk( KERN_ERR "IP2: PCI board %d not found\n", i ); + } else { + pcibios_strerror(status); + } + } + } +#else + printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); + printk( KERN_ERR "IP2: configured in this kernel.\n"); + printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); +#endif /* CONFIG_PCI */ + break; + case EISA: + if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + /* Eisa_irq set as side effect, boo */ + ip2config.type[i] = EISA; + } + ip2config.irq[i] = Eisa_irq; + break; + } /* switch */ + } /* for */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ip2config.addr[i] ) { + pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL); + if ( pB != NULL ) { + i2BoardPtrTable[i] = pB; + memset( pB, 0, sizeof(i2eBordStr) ); + iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); + iiReset( pB ); + } else { + printk(KERN_ERR "IP2: board memory allocation error\n"); + } + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { + iiResetDelay( pB ); + break; + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] != NULL ) { + ip2_init_board( i ); + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); +#endif + + /* Zero out the normal tty device structure. */ + memset ( &ip2_tty_driver, 0, sizeof ip2_tty_driver ); + + /* Initialise the relevant fields. */ + ip2_tty_driver.magic = TTY_DRIVER_MAGIC; + ip2_tty_driver.name = pcTty; + ip2_tty_driver.driver_name = pcDriver_name; + ip2_tty_driver.read_proc = ip2_read_proc; + ip2_tty_driver.major = IP2_TTY_MAJOR; + ip2_tty_driver.minor_start = 0; + ip2_tty_driver.num = IP2_MAX_PORTS; + ip2_tty_driver.type = TTY_DRIVER_TYPE_SERIAL; + ip2_tty_driver.subtype = SERIAL_TYPE_NORMAL; + ip2_tty_driver.init_termios = tty_std_termios; + ip2_tty_driver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + ip2_tty_driver.flags = TTY_DRIVER_REAL_RAW; + ip2_tty_driver.refcount = &ref_count; + ip2_tty_driver.table = TtyTable; + ip2_tty_driver.termios = Termios; + ip2_tty_driver.termios_locked = TermiosLocked; + + /* Setup the pointers to the implemented functions. */ + ip2_tty_driver.open = ip2_open; + ip2_tty_driver.close = ip2_close; + ip2_tty_driver.write = ip2_write; + ip2_tty_driver.put_char = ip2_putchar; + ip2_tty_driver.flush_chars = ip2_flush_chars; + ip2_tty_driver.write_room = ip2_write_room; + ip2_tty_driver.chars_in_buffer = ip2_chars_in_buf; + ip2_tty_driver.flush_buffer = ip2_flush_buffer; + ip2_tty_driver.ioctl = ip2_ioctl; + ip2_tty_driver.throttle = ip2_throttle; + ip2_tty_driver.unthrottle = ip2_unthrottle; + ip2_tty_driver.set_termios = ip2_set_termios; + ip2_tty_driver.set_ldisc = ip2_set_line_discipline; + ip2_tty_driver.stop = ip2_stop; + ip2_tty_driver.start = ip2_start; + ip2_tty_driver.hangup = ip2_hangup; + + /* Initialise the callout driver structure from the tty driver, and + * make the needed adjustments. + */ + ip2_callout_driver = ip2_tty_driver; + ip2_callout_driver.name = pcCallout; + ip2_callout_driver.driver_name = pcDriver_name; + ip2_callout_driver.read_proc = NULL; + ip2_callout_driver.major = IP2_CALLOUT_MAJOR; + ip2_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); +#endif + + /* Register the tty devices. */ + if ( ( err = tty_register_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + } else + if ( ( err = tty_register_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register callout driver (%d)\n", err); + } else + /* Register the IPL driver. */ + if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + } else + /* Register the read_procmem thing */ + if ( ( err = proc_register( &proc_root, &ip2_proc_entry ) ) ) { + printk(KERN_ERR "IP2: failed to register read_procmem (%d)\n", err ); + } else { + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); +#endif + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( 0 == ip2config.addr[i] ) { + continue; + } + if (poll_only) { + ip2config.irq[i] = CIR_POLL; + } + if ( ip2config.irq[i] == CIR_POLL ) { +retry: + if (!TimerOn) { + PollTimer.expires = POLL_TIMEOUT; + add_timer ( &PollTimer ); + TimerOn = 1; + printk( KERN_INFO "IP2: polling\n"); + } + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq( ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | (ip2config.type[i] == PCI ? SA_SHIRQ : 0), + pcName, (void *)&pcName); + if (rc) { + printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); + ip2config.irq[i] = CIR_POLL; + printk( KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; + } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half (aka slih). */ + } + } + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } + } + } +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); +#endif + + return 0; +} + +/******************************************************************************/ +/* Function: ip2_init_board() */ +/* Parameters: Index of board in configuration structure */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This function initializes the specified board. The loadware is copied to */ +/* the board, the channel structures are initialized, and the board details */ +/* are reported on the console. */ +/******************************************************************************/ +static void __init +ip2_init_board( int boardnum ) +{ + int i,rc; + int nports = 0, nboxes = 0; + i2ChanStrPtr pCh; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + + if ( !iiInitialize ( pB ) ) { + printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", + pB->i2eBase, pB->i2eError ); + kfree ( pB ); + i2BoardPtrTable[boardnum] = NULL; + return; + } + printk(KERN_INFO "Board %d: addr=0x%x irq=%d ", boardnum + 1, + ip2config.addr[boardnum], ip2config.irq[boardnum] ); + + if (0 != ( rc = check_region( ip2config.addr[boardnum], 8))) { + i2BoardPtrTable[boardnum] = NULL; + printk(KERN_ERR "bad addr=0x%x rc = %d\n", + ip2config.addr[boardnum], rc ); + return; + } + request_region( ip2config.addr[boardnum], 8, pcName ); + + if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size ) + != II_DOWN_GOOD ) { + printk ( KERN_ERR "IP2:failed to download loadware " ); + } else { + printk ( KERN_INFO "fv=%d.%d.%d lv=%d.%d.%d\n", + pB->i2ePom.e.porVersion, + pB->i2ePom.e.porRevision, + pB->i2ePom.e.porSubRev, pB->i2eLVersion, + pB->i2eLRevision, pB->i2eLSub ); + } + + switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { + + default: + printk( KERN_ERR "IP2: Unknown board type, ID = %x", + pB->i2ePom.e.porID ); + nports = 0; + goto ex_exit; + break; + + case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ + printk ( KERN_INFO "ISA-4" ); + nports = 4; + break; + + case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ + printk ( KERN_INFO "ISA-8 std" ); + nports = 8; + break; + + case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ + printk ( KERN_INFO "ISA-8 RJ11" ); + nports = 8; + break; + + case POR_ID_FIIEX: /* IntelliPort IIEX */ + { + int portnum = IP2_PORTS_PER_BOARD * boardnum; + int box; + + for( box = 0; box < ABS_MAX_BOXES; ++box ) { + if ( pB->i2eChannelMap[box] != 0 ) { + ++nboxes; + } + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & 1<< i ) { + ++nports; + } + } + } + DevTableMem[boardnum] = pCh = + kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "i2InitChannels failed: %d\n",pB->i2eError); + } + pB->i2eChannelPtr = &DevTable[portnum]; + pB->i2eChannelCnt = ABS_MOST_PORTS; + + for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & (1 << i) ) { + DevTable[portnum + i] = pCh; + pCh->port_index = portnum + i; + pCh++; + } + } + } + printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit", + nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); + } + goto ex_exit; + break; + } + DevTableMem[boardnum] = pCh = + kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nports; + i2InitChannels ( pB, pB->i2eChannelCnt, pCh ); + pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; + + for( i = 0; i < pB->i2eChannelCnt; ++i ) { + DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; + pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; + pCh++; + } +ex_exit: + printk ( KERN_INFO "\n" ); +} + +/******************************************************************************/ +/* Function: find_eisa_board ( int start_slot ) */ +/* Parameters: First slot to check */ +/* Returns: Address of EISA IntelliPort II controller */ +/* */ +/* Description: */ +/* This function searches for an EISA IntelliPort controller, starting */ +/* from the specified slot number. If the motherboard is not identified as an */ +/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ +/* it returns the base address of the controller. */ +/******************************************************************************/ +static unsigned short __init +find_eisa_board( int start_slot ) +{ + int i, j; + unsigned int idm = 0; + unsigned int idp = 0; + unsigned int base = 0; + unsigned int value; + int setup_address; + int setup_irq; + int ismine = 0; + + /* + * First a check for an EISA motherboard, which we do by comparing the + * EISA ID registers for the system board and the first couple of slots. + * No slot ID should match the system board ID, but on an ISA or PCI + * machine the odds are that an empty bus will return similar values for + * each slot. + */ + i = 0x0c80; + value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); + for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { + j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); + if ( value == j ) + return 0; + } + + /* + * OK, so we are inclined to believe that this is an EISA machine. Find + * an IntelliPort controller. + */ + for( i = start_slot; i < 16; i++ ) { + base = i << 12; + idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); + idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); + ismine = 0; + if ( idm == 0x0e8e ) { + if ( idp == 0x0281 || idp == 0x0218 ) { + ismine = 1; + } else if ( idp == 0x0282 || idp == 0x0283 ) { + ismine = 3; /* Can do edge-trigger */ + } + if ( ismine ) { + Eisa_slot = i; + break; + } + } + } + if ( !ismine ) + return 0; + + /* It's some sort of EISA card, but at what address is it configured? */ + + setup_address = base + 0xc88; + value = inb(base + 0xc86); + setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; + + if ( (ismine & 2) && !(value & 0x10) ) { + ismine = 1; /* Could be edging, but not */ + } + + if ( Eisa_irq == 0 ) { + Eisa_irq = setup_irq; + } else if ( Eisa_irq != setup_irq ) { + printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); + } + +#ifdef IP2DEBUG_INIT +printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", + base >> 12, idm, idp, setup_address); + if ( Eisa_irq ) { + printk(KERN_DEBUG ", Interrupt %d %s\n", + setup_irq, (ismine & 2) ? "(edge)" : "(level)"); + } else { + printk(KERN_DEBUG ", (polled)\n"); + } +#endif + return setup_address; +} + +/******************************************************************************/ +/* Function: set_irq() */ +/* Parameters: index to board in board table */ +/* IRQ to use */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/******************************************************************************/ +static void +set_irq( int boardnum, int boardIrq ) +{ + i2ChanStrPtr pCh; + unsigned char tempCommand[16]; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + unsigned long flags; + + /* + * Notify the boards they may generate interrupts. This is done by + * sending an in-line command to channel 0 on each board. This is why + * the channels have to be defined already. For each board, if the + * interrupt has never been defined, we must do so NOW, directly, since + * board will not send flow control or even give an interrupt until this + * is done. If polling we must send 0 as the interrupt parameter. + */ + + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + + // We will get an interrupt here at the end of this function + + iiDisableMailIrq(pB); + + /* We build up the entire packet header. */ + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_INLINE; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; + (CMD_OF(tempCommand))[1] = boardIrq; + /* + * Write to FIFO; don't bother to adjust fifo capacity for this, since + * board will respond almost immediately after SendMail hit. + */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + pB->i2eUsingIrq = boardIrq; + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + + /* Need to update number of boards before you enable mailbox int */ + ++i2nBoards; + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 6; + (CMD_OF(tempCommand))[0] = 88; // SILO + (CMD_OF(tempCommand))[1] = 64; // chars + (CMD_OF(tempCommand))[2] = 32; // ms + + (CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK + (CMD_OF(tempCommand))[4] = 64; // chars + + (CMD_OF(tempCommand))[5] = 87; // HW_TEST + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 8); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 1; + (CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */ + iiWriteBuf(pB, tempCommand, 3); + +#ifdef XXX + // enable heartbeat for test porpoises + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = 44; /* get ping */ + (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#endif + + iiEnableMailIrq(pB); + iiSendPendingMail(pB); +} + +/******************************************************************************/ +/* Interrupt Handler Section */ +/******************************************************************************/ + +static inline void +service_all_boards() +{ + int i; + i2eBordStrPtr pB; + + /* Service every board on the list */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + i2ServiceBoard( pB ); + } + } +} + + +#ifdef USE_IQI +static struct tq_struct +senior_service = +{ // it's the death that worse than fate + NULL, + 0, + (void(*)(void*)) service_all_boards, + NULL, //later - board address XXX +}; +#endif + +/******************************************************************************/ +/* Function: ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) */ +/* Parameters: irq - interrupt number */ +/* pointer to optional device ID structure */ +/* pointer to register structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int i; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq ); +#endif + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + /* Service just the boards on the list using this irq */ + for( i = 0; i < i2nBoards; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB && (pB->i2eUsingIrq == irq) ) { + i2ServiceBoard( pB ); + } + } + +#endif /* USE_IQI */ + + ++irq_counter; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_poll(unsigned long arg) */ +/* Parameters: ? */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function calls the library routine i2ServiceBoard for each board in */ +/* the board table. This is used instead of the interrupt routine when polled */ +/* mode is specified. */ +/******************************************************************************/ +static void +ip2_poll(unsigned long arg) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); +#endif + TimerOn = 0; // it's the truth but not checked in service + + bh_counter++; + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + // Just polled boards, service_all might be better + ip2_interrupt(0, NULL, NULL); + +#endif /* USE_IQI */ + + PollTimer.expires = POLL_TIMEOUT; + add_timer( &PollTimer ); + TimerOn = 1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +static inline void +do_input( i2ChanStrPtr pCh ) +{ + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 21, 0 ); +#endif + // Data input + if ( pCh->pTTY != NULL ) { + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + } else { +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 22, 0 ); +#endif + i2InputFlush( pCh ); + } +} + +// code duplicated from n_tty (ldisc) +static inline void +isig(int sig, struct tty_struct *tty, int flush) +{ + if (tty->pgrp > 0) + kill_pg(tty->pgrp, sig, 1); + if (flush || !L_NOFLSH(tty)) { + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + i2InputFlush( tty->driver_data ); + } +} + +static inline void +do_status( i2ChanStrPtr pCh ) +{ + int status; + + status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 21, 1, status ); +#endif + + if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) { + if ( (status & I2_BRK) ) { + // code duplicated from n_tty (ldisc) + if (I_IGNBRK(pCh->pTTY)) + goto skip_this; + if (I_BRKINT(pCh->pTTY)) { + isig(SIGINT, pCh->pTTY, 1); + goto skip_this; + } + wake_up_interruptible(&pCh->pTTY->read_wait); + } +#ifdef NEVER_HAPPENS_AS_SETUP_XXX + // and can't work because we don't know the_char + // as the_char is reported on a seperate path + // The intelligent board does this stuff as setup + { + char brkf = TTY_NORMAL; + unsigned char brkc = '\0'; + unsigned char tmp; + if ( (status & I2_BRK) ) { + brkf = TTY_BREAK; + brkc = '\0'; + } + else if (status & I2_PAR) { + brkf = TTY_PARITY; + brkc = the_char; + } else if (status & I2_FRA) { + brkf = TTY_FRAME; + brkc = the_char; + } else if (status & I2_OVR) { + brkf = TTY_OVERRUN; + brkc = the_char; + } + tmp = pCh->pTTY->real_raw; + pCh->pTTY->real_raw = 0; + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->real_raw = tmp; + } +#endif /* NEVER_HAPPENS_AS_SETUP_XXX */ + } +skip_this: + + if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) { + wake_up_interruptible(&pCh->delta_msr_wait); + + if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) { + if ( status & I2_DCD ) { + if ( pCh->wopen ) { + wake_up_interruptible ( &pCh->open_wait ); + } + } else if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) ) { + if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) { + tty_hangup( pCh->pTTY ); + } + } + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 26, 0 ); +#endif +} + +/******************************************************************************/ +/* Device Open/Close/Ioctl Entry Point Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: open_sanity_check() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* Verifies the structure magic numbers and cross links. */ +/******************************************************************************/ +#ifdef IP2DEBUG_OPEN +static void +open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd ) +{ + if ( pBrd->i2eValid != I2E_MAGIC ) { + printk(KERN_ERR "IP2: invalid board structure\n" ); + } else if ( pBrd != pCh->pMyBord ) { + printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n", + pCh->pMyBord ); + } else if ( pBrd->i2eChannelCnt < pCh->port_index ) { + printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index ); + } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) { + } else { + printk(KERN_INFO "IP2: all pointers check out!\n" ); + } +} +#endif + + +/******************************************************************************/ +/* Function: ip2_open() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: (MANDATORY) */ +/* A successful device open has to run a gauntlet of checks before it */ +/* completes. After some sanity checking and pointer setup, the function */ +/* blocks until all conditions are satisfied. It then initialises the port to */ +/* the default characteristics and returns. */ +/******************************************************************************/ +static int +ip2_open( PTTY tty, struct file *pFile ) +{ + int rc = 0; + int do_clocal = 0; + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + +#ifdef IP2DEBUG_TRACE + ip2trace (MINOR(tty->device), ITRC_OPEN, ITRC_ENTER, 0 ); +#endif + + if ( pCh == NULL ) { + return -ENODEV; + } + /* Setup pointer links in device and tty structures */ + pCh->pTTY = tty; + tty->driver_data = pCh; + MOD_INC_USE_COUNT; + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG \ + "IP2:open(tty=%p,pFile=%p):dev=%x,maj=%d,min=%d,ch=%d,idx=%d\n", + tty, pFile, tty->device, MAJOR(tty->device), MINOR(tty->device), + pCh->infl.hd.i2sChannel, pCh->port_index); + open_sanity_check ( pCh, pCh->pMyBord ); +#endif + + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); + serviceOutgoingFifo( pCh->pMyBord ); + + /* Block here until the port is ready (per serial and istallion) */ + /* + * 1. If the port is in the middle of closing wait for the completion + * and then return the appropriate error. + */ + if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { + if ( pCh->flags & ASYNC_CLOSING ) { + interruptible_sleep_on( &pCh->close_wait); + } + if ( tty_hung_up_p(pFile) ) { + return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS; + } + } + /* + * 2. If this is a callout device, make sure the normal port is not in + * use, and that someone else doesn't have the callout device locked. + * (These are the only tests the standard serial driver makes for + * callout devices.) + */ + if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) { + if ( pCh->flags & ASYNC_NORMAL_ACTIVE ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_SESSION_LOCKOUT ) && + ( pCh->session != current->session ) ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_PGRP_LOCKOUT ) && + ( pCh->pgrp != current->pgrp ) ) { + return -EBUSY; + } + pCh->flags |= ASYNC_CALLOUT_ACTIVE; + goto noblock; + } + /* + * 3. Handle a non-blocking open of a normal port. + */ + if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<<TTY_IO_ERROR) )) { + if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) { + return -EBUSY; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + goto noblock; + } + /* + * 4. Now loop waiting for the port to be free and carrier present + * (if required). + */ + if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) { + if ( pCh->NormalTermios.c_cflag & CLOCAL ) { + do_clocal = 1; + } + } else { + if ( tty->termios->c_cflag & CLOCAL ) { + do_clocal = 1; + } + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal); +#endif + + ++pCh->wopen; + for(;;) { + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE)) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + serviceOutgoingFifo( pCh->pMyBord ); + } + if ( tty_hung_up_p(pFile) ) { + return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS; + } + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) && + !(pCh->flags & ASYNC_CLOSING) && + (do_clocal || (pCh->dataSetIn & I2_DCD) )) { + rc = 0; + break; + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "ASYNC_CALLOUT_ACTIVE = %s\n", + (pCh->flags & ASYNC_CALLOUT_ACTIVE)?"True":"False"); + printk(KERN_DEBUG "ASYNC_CLOSING = %s\n", + (pCh->flags & ASYNC_CLOSING)?"True":"False"); + printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n"); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 3, 2, (pCh->flags & ASYNC_CALLOUT_ACTIVE), + (pCh->flags & ASYNC_CLOSING) ); +#endif + /* check for signal */ + if (signal_pending(current)) { + rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); + break; + } + interruptible_sleep_on(&pCh->open_wait); + } + --pCh->wopen; //why count? +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 4, 0 ); +#endif + if (rc != 0 ) { + return rc; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + +noblock: + + /* first open - Assign termios structure to port */ + if ( tty->count == 1 ) { + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( pCh->flags & ASYNC_SPLIT_TERMIOS ) { + if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) { + *tty->termios = pCh->NormalTermios; + } else { + *tty->termios = pCh->CalloutTermios; + } + } + /* Now we must send the termios settings to the loadware */ + set_params( pCh, NULL ); + } + + /* override previous and never reset ??? */ + pCh->session = current->session; + pCh->pgrp = current->pgrp; + + /* + * Now set any i2lib options. These may go away if the i2lib code ends + * up rolled into the mainline. + */ + pCh->channelOptions |= CO_NBLOCK_WRITE; + +#ifdef IP2DEBUG_OPEN + printk (KERN_DEBUG "IP2: open completed\n" ); +#endif + serviceOutgoingFifo( pCh->pMyBord ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, ITRC_RETURN, 0 ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_close() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_close( PTTY tty, struct file *pFile ) +{ + i2ChanStrPtr pCh = tty->driver_data; + + if ( !pCh ) { + return; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_ENTER, 0 ); +#endif + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "IP2:close ttyF%02X:\n",MINOR(tty->device)); +#endif + + if ( tty_hung_up_p ( pFile ) ) { + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 2 ); +#endif + return; + } + if ( tty->count > 1 ) { /* not the last close */ + MOD_DEC_USE_COUNT; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 3 ); +#endif + return; + } + pCh->flags |= ASYNC_CLOSING; // last close actually + + /* + * Save the termios structure, since this port may have separate termios + * for callout and dialin. + */ + if (pCh->flags & ASYNC_NORMAL_ACTIVE) + pCh->NormalTermios = *tty->termios; + if (pCh->flags & ASYNC_CALLOUT_ACTIVE) + pCh->CalloutTermios = *tty->termios; + + tty->closing = 1; + + if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) { + /* + * Before we drop DTR, make sure the transmitter has completely drained. + * This uses an timeout, after which the close + * completes. + */ + ip2_wait_until_sent(tty, pCh->ClosingWaitTime ); + } + /* + * At this point we stop accepting input. Here we flush the channel + * input buffer which will allow the board to send up more data. Any + * additional input is tossed at interrupt/poll time. + */ + i2InputFlush( pCh ); + + /* disable DSS reporting */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + + serviceOutgoingFifo ( pCh->pMyBord ); + + if ( tty->driver.flush_buffer ) + tty->driver.flush_buffer(tty); + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + + pCh->pTTY = NULL; + + if (pCh->wopen) { + if (pCh->ClosingDelay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(pCh->ClosingDelay); + } + wake_up_interruptible(&pCh->open_wait); + } + + pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&pCh->close_wait); + +#ifdef IP2DEBUG_OPEN + DBG_CNT("ip2_close: after wakeups--"); +#endif + + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_RETURN, 1, 1 ); +#endif + return; +} + +/******************************************************************************/ +/* Function: ip2_hangup() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_hangup ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_ENTER, 0 ); +#endif + + ip2_flush_buffer(tty); + + /* disable DSS reporting */ + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP); + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + serviceOutgoingFifo ( pCh->pMyBord ); + + wake_up_interruptible ( &pCh->delta_msr_wait ); + + pCh->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + pCh->pTTY = NULL; + wake_up_interruptible ( &pCh->open_wait ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Output Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_write() */ +/* Parameters: Pointer to tty structure */ +/* Flag denoting data is in user (1) or kernel (0) space */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Number of bytes actually written */ +/* */ +/* Description: (MANDATORY) */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_write( PTTY tty, int user, const unsigned char *pData, int count) +{ + i2ChanStrPtr pCh = tty->driver_data; + int bytesSent = 0; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 ); +#endif + + /* Flush out any buffered data left over from ip2_putchar() calls. */ + ip2_flush_chars( tty ); + + /* This is the actual move bit. Make sure it does what we need!!!!! */ + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesSent = i2Output( pCh, pData, count, user ); + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); +#endif + return bytesSent > 0 ? bytesSent : 0; +} + +/******************************************************************************/ +/* Function: ip2_putchar() */ +/* Parameters: Pointer to tty structure */ +/* Character to write */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_putchar( PTTY tty, unsigned char ch ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_ENTER, 1, ch ); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf[pCh->Pbuf_stuff++] = ch; + if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + ip2_flush_chars( tty ); + } else + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_RETURN, 1, ch ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_flush_chars() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static void +ip2_flush_chars( PTTY tty ) +{ + int strip; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + if ( pCh->Pbuf_stuff ) { +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, 10, 1, strip ); +#endif + // + // We may need to restart i2Output if it does not fullfill this request + // + strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff, 0 ); + if ( strip != pCh->Pbuf_stuff ) { + memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip ); + } + pCh->Pbuf_stuff -= strip; + } + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); +} + +/******************************************************************************/ +/* Function: ip2_write_room() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes that the driver can accept */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static int +ip2_write_room ( PTTY tty ) +{ + int bytesFree; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 11, 1, bytesFree ); +#endif + + return ((bytesFree > 0) ? bytesFree : 0); +} + +/******************************************************************************/ +/* Function: ip2_chars_in_buf() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes queued for transmission */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_chars_in_buf ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + int rc; + unsigned long flags; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n", + pCh->Obuf_char_count + pCh->Pbuf_stuff, + pCh->Obuf_char_count, pCh->Pbuf_stuff ); +#endif + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + rc = pCh->Obuf_char_count; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + rc += pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + return rc; +} + +/******************************************************************************/ +/* Function: ip2_flush_buffer() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_flush_buffer( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_ENTER, 0 ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: flush buffer\n" ); +#endif + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf_stuff = 0; + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + i2FlushOutput( pCh ); + ip2_owake(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_wait_until_sent() */ +/* Parameters: Pointer to tty structure */ +/* Timeout for wait. */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function is used in place of the normal tty_wait_until_sent, which */ +/* only waits for the driver buffers to be empty (or rather, those buffers */ +/* reported by chars_in_buffer) which doesn't work for IP2 due to the */ +/* indeterminate number of bytes buffered on the board. */ +/******************************************************************************/ +static void +ip2_wait_until_sent ( PTTY tty, int timeout ) +{ + int i = jiffies; + i2ChanStrPtr pCh = tty->driver_data; + + tty_wait_until_sent(tty, timeout ); + if ( (i = timeout - (jiffies -i)) > 0) + i2DrainOutput( pCh, i ); +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Input Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_throttle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_throttle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: throttle\n" ); +#endif + /* + * Signal the poll/interrupt handlers not to forward incoming data to + * the line discipline. This will cause the buffers to fill up in the + * library and thus cause the library routines to send the flow control + * stuff. + */ + pCh->throttled = 1; +} + +/******************************************************************************/ +/* Function: ip2_unthrottle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_unthrottle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: unthrottle\n" ); +#endif + + /* Pass incoming data up to the line discipline again. */ + pCh->throttled = 0; + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + serviceOutgoingFifo( pCh->pMyBord ); + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "i2Input called from unthrottle\n" ); +#endif + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +} + +static void +ip2_start ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: start tx\n" ); +#endif +} + +static void +ip2_stop ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: stop tx\n" ); +#endif +} + +/******************************************************************************/ +/* Device Ioctl Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ioctl() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + int rc = 0; + + if ( pCh == NULL ) { + return -ENODEV; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg ); +#endif + +#ifdef IP2DEBUG_IOCTL + printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg ); +#endif + + switch(cmd) { + case TIOCGSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 2, 1, rc ); +#endif + rc = get_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TIOCSSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 3, 1, rc ); +#endif + rc = set_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TCXONC: + rc = tty_check_change(tty); + if (rc) + return rc; + switch (arg) { + case TCOOFF: + //return -ENOIOCTLCMD; + break; + case TCOON: + //return -ENOIOCTLCMD; + break; + case TCIOFF: + if (STOP_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(STOP_CHAR(tty))); + } + break; + case TCION: + if (START_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(START_CHAR(tty))); + } + break; + default: + return -EINVAL; + } + return 0; + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 4, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + if (!arg) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250)); + serviceOutgoingFifo( pCh->pMyBord ); + } + } + break; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 5, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_SEND_BRK(arg ? arg*100 : 250)); + serviceOutgoingFifo ( pCh->pMyBord ); + } + break; + + case TIOCGSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 6, 1, rc ); +#endif + rc=put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + if (rc) + return rc; + break; + + case TIOCSSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 7, 1, rc ); +#endif + rc=get_user(arg,(unsigned long *) arg); + if (rc) + return rc; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) + | (arg ? CLOCAL : 0)); + + break; + + case TIOCMGET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 8, 1, rc ); +#endif + rc = get_modem_info(pCh, (unsigned int *) arg); + if (rc) + return rc; + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 9, 0 ); +#endif + rc = set_modem_info(pCh, cmd, (unsigned int *) arg); + break; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask + * passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS + * for masking). Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + cprev = pCh->icount; /* note the counters on entry */ + for(;;) { +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 10, 0 ); +#endif + interruptible_sleep_on(&pCh->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + cnow = pCh->icount; /* atomic copy */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; /* no change => rc */ + break; + } + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + rc = 0; + break; + } + cprev = cnow; + } + /* NOTREACHED */ + break; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for RI where + * only 0->1 is counted. The controller is quite capable of counting + * both, but this done to preserve compatibility with the standard + * serial driver. + */ + case TIOCGICOUNT: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 11, 1, rc ); +#endif + cnow = pCh->icount; + p_cuser = (struct serial_icounter_struct *) arg; + put_user(cnow.cts, &p_cuser->cts); + put_user(cnow.dsr, &p_cuser->dsr); + put_user(cnow.rng, &p_cuser->rng); + put_user(cnow.dcd, &p_cuser->dcd); + break; + + /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ + case TIOCSERCONFIG: + case TIOCSERGWILD: + case TIOCSERGETLSR: + case TIOCSERSWILD: + case TIOCSERGSTRUCT: + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + + default: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 12, 0 ); +#endif + rc = -ENOIOCTLCMD; + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_RETURN, 0 ); +#endif + return rc; +} +/******************************************************************************/ +/* Function: get_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to destination for data */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +get_modem_info(i2ChanStrPtr pCh, unsigned int *value) +{ + unsigned short status; + unsigned int result; + int rc; + + status = pCh->dataSetIn; // snapshot settings + result = ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) + | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) + | ((status & I2_DCD) ? TIOCM_CAR : 0) + | ((status & I2_RI) ? TIOCM_RNG : 0) + | ((status & I2_DSR) ? TIOCM_DSR : 0) + | ((status & I2_CTS) ? TIOCM_CTS : 0); + rc=put_user(result,value); + return rc; +} + +/******************************************************************************/ +/* Function: set_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Specific ioctl command */ +/* Pointer to source for new settings */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +set_modem_info(i2ChanStrPtr pCh, unsigned cmd, unsigned int *value) +{ + int rc; + unsigned int arg; + + rc=get_user(arg,value); + if (rc) + return rc; + switch(cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + case TIOCMSET: + if ( (arg & TIOCM_RTS) && !(pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } else if ( !(arg & TIOCM_RTS) && (pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if ( (arg & TIOCM_DTR) && !(pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } else if ( !(arg & TIOCM_DTR) && (pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + default: + return -EINVAL; + } + serviceOutgoingFifo( pCh->pMyBord ); + return 0; +} + +/******************************************************************************/ +/* Function: GetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is to support the setserial command, and requires processing of the */ +/* standard Linux serial structure. */ +/******************************************************************************/ +static int +get_serial_info ( i2ChanStrPtr pCh, struct serial_struct *retinfo ) +{ + struct serial_struct tmp; + int rc=0; + + if ( !retinfo ) { + return -EFAULT; + } + + memset ( &tmp, 0, sizeof(tmp) ); + tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16]; + if (BID_HAS_654(tmp.type)) { + tmp.type = PORT_16650; + } else { + tmp.type = PORT_CIRRUS; + } + tmp.line = pCh->port_index; + tmp.port = pCh->pMyBord->i2eBase; + tmp.irq = ip2config.irq[pCh->port_index/64]; + tmp.flags = pCh->flags; + tmp.baud_base = pCh->BaudBase; + tmp.close_delay = pCh->ClosingDelay; + tmp.closing_wait = pCh->ClosingWaitTime; + tmp.custom_divisor = pCh->BaudDivisor; + if(copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + rc= -EFAULT; + return rc; +} + +/******************************************************************************/ +/* Function: SetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function provides support for setserial, which uses the TIOCSSERIAL */ +/* ioctl. Not all setserial parameters are relevant. If the user attempts to */ +/* change the IRQ, address or type of the port the ioctl fails. */ +/******************************************************************************/ +static int +set_serial_info( i2ChanStrPtr pCh, struct serial_struct *new_info ) +{ + struct serial_struct ns; + int old_flags, old_baud_divisor; + int rc = 0; + + if ( !new_info ) { + return -EFAULT; + } + rc=copy_from_user(&ns, new_info, sizeof (ns) ); + if (rc) { + return rc; + } + /* + * We don't allow setserial to change IRQ, board address, type or baud + * base. Also line nunber as such is meaningless but we use it for our + * array index so it is fixed also. + */ + if ( ns.irq != ip2config.irq + || (int) ns.port != ((int) pCh->pMyBord->i2eBase) + || ns.baud_base != pCh->BaudBase + || ns.line != pCh->port_index ) { + return -EINVAL; + } + + old_flags = pCh->flags; + old_baud_divisor = pCh->BaudDivisor; + + if ( !suser() ) { + if ( ( ns.close_delay != pCh->ClosingDelay ) || + ( (ns.flags & ~ASYNC_USR_MASK) != + (pCh->flags & ~ASYNC_USR_MASK) ) ) { + return -EPERM; + } + + pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) | + (ns.flags & ASYNC_USR_MASK); + pCh->BaudDivisor = ns.custom_divisor; + } else { + pCh->flags = (pCh->flags & ~ASYNC_FLAGS) | + (ns.flags & ASYNC_FLAGS); + pCh->BaudDivisor = ns.custom_divisor; + pCh->ClosingDelay = ns.close_delay * HZ/100; + pCh->ClosingWaitTime = ns.closing_wait * HZ/100; + } + + if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) ) + || (old_baud_divisor != pCh->BaudDivisor) ) { + // Invalidate speed and reset parameters + set_params( pCh, NULL ); + } + + return rc; +} + +/******************************************************************************/ +/* Function: ip2_set_termios() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_termios( PTTY tty, struct termios *old_termios ) +{ + i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data; + +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set termios %p\n", old_termios ); +#endif + + set_params( pCh, old_termios ); +} + +/******************************************************************************/ +/* Function: ip2_set_line_discipline() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: Does nothing */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_line_discipline ( PTTY tty ) +{ +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set line discipline\n" ); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: SetLine Characteristics() */ +/* Parameters: Pointer to channel structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This routine is called to update the channel structure with the new line */ +/* characteristics, and send the appropriate commands to the board when they */ +/* change. */ +/******************************************************************************/ +static void +set_params( i2ChanStrPtr pCh, struct termios *o_tios ) +{ + tcflag_t cflag, iflag, lflag; + char stop_char, start_char; + struct termios dummy; + + lflag = pCh->pTTY->termios->c_lflag; + cflag = pCh->pTTY->termios->c_cflag; + iflag = pCh->pTTY->termios->c_iflag; + + if (o_tios == NULL) { + dummy.c_lflag = ~lflag; + dummy.c_cflag = ~cflag; + dummy.c_iflag = ~iflag; + o_tios = &dummy; + } + + { + switch ( cflag & CBAUD ) { + case B0: + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag); + goto service_it; + break; + case B38400: + /* + * This is the speed that is overloaded with all the other high + * speeds, depending upon the flag settings. + */ + if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) { + pCh->speed = CBR_57600; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) { + pCh->speed = CBR_115200; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) { + pCh->speed = CBR_C1; + } else { + pCh->speed = CBR_38400; + } + break; + case B50: pCh->speed = CBR_50; break; + case B75: pCh->speed = CBR_75; break; + case B110: pCh->speed = CBR_110; break; + case B134: pCh->speed = CBR_134; break; + case B150: pCh->speed = CBR_150; break; + case B200: pCh->speed = CBR_200; break; + case B300: pCh->speed = CBR_300; break; + case B600: pCh->speed = CBR_600; break; + case B1200: pCh->speed = CBR_1200; break; + case B1800: pCh->speed = CBR_1800; break; + case B2400: pCh->speed = CBR_2400; break; + case B4800: pCh->speed = CBR_4800; break; + case B9600: pCh->speed = CBR_9600; break; + case B19200: pCh->speed = CBR_19200; break; + case B57600: pCh->speed = CBR_57600; break; + case B115200: pCh->speed = CBR_115200; break; + case B153600: pCh->speed = CBR_153600; break; + case B230400: pCh->speed = CBR_230400; break; + case B307200: pCh->speed = CBR_307200; break; + case B460800: pCh->speed = CBR_460800; break; + case B921600: pCh->speed = CBR_921600; break; + default: pCh->speed = CBR_9600; break; + } + if ( pCh->speed == CBR_C1 ) { + // Process the custom speed parameters. + int bps = pCh->BaudBase / pCh->BaudDivisor; + if ( bps == 921600 ) { + pCh->speed = CBR_921600; + } else { + bps = bps/10; + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) ); + } + } + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed)); + + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + } + if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1)); + } + if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETPAR( + (cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP) + ) + ); + } + /* byte size and parity */ + if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) + { + int datasize; + switch ( cflag & CSIZE ) { + case CS5: datasize = CSZ_5; break; + case CS6: datasize = CSZ_6; break; + case CS7: datasize = CSZ_7; break; + case CS8: datasize = CSZ_8; break; + default: datasize = CSZ_5; break; /* as per serial.c */ + } + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) ); + } + /* Process CTS flow control flag setting */ + if ( (cflag & CRTSCTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB); + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + } + // + // Process XON/XOFF flow control flags settings + // + stop_char = STOP_CHAR(pCh->pTTY); + start_char = START_CHAR(pCh->pTTY); + + //////////// can't be \000 + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; + } + if (start_char == __DISABLED_CHAR ) + { + start_char = ~__DISABLED_CHAR; + } + ///////////////////////////////// + + if ( o_tios->c_cc[VSTART] != start_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char)); + } + if ( o_tios->c_cc[VSTOP] != stop_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char)); + } + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; //TEST123 + goto no_xoff; + } + if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) + { + if ( iflag & IXOFF ) { // Enable XOFF output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON)); + } else { // Disable XOFF output flow control +no_xoff: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE)); + } + } + if (start_char == __DISABLED_CHAR ) + { + goto no_xon; + } + if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) + { + if ( iflag & IXON ) { + if ( iflag & IXANY ) { // Enable XON/XANY output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY)); + } else { // Enable XON output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON)); + } + } else { // Disable XON output flow control +no_xon: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE)); + } + } + if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0))); + } + if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB)); + } + + if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) + ^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) + { + char brkrpt = 0; + char parrpt = 0; + + if ( iflag & IGNBRK ) { /* Ignore breaks altogether */ + /* Ignore breaks altogether */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP); + } else { + if ( iflag & BRKINT ) { + if ( iflag & PARMRK ) { + brkrpt = 0x0a; // exception an inline triple + } else { + brkrpt = 0x1a; // exception and NULL + } + brkrpt |= 0x04; // flush input + } else { + if ( iflag & PARMRK ) { + brkrpt = 0x0b; //POSIX triple \0377 \0 \0 + } else { + brkrpt = 0x01; // Null only + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt)); + } + + if (iflag & IGNPAR) { + parrpt = 0x20; + /* would be 2 for not cirrus bug */ + /* would be 0x20 cept for cirrus bug */ + } else { + if ( iflag & PARMRK ) { + /* + * Replace error characters with 3-byte sequence (\0377,\0,char) + */ + parrpt = 0x04 ; + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0)); + } else { + parrpt = 0x03; + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt)); + } + if (cflag & CLOCAL) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + pCh->flags &= ~ASYNC_CHECK_CD; + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + pCh->flags |= ASYNC_CHECK_CD; + } + +#ifdef XXX +do_flags_thing: // This is a test, we don't do the flags thing + + if ( (cflag & CRTSCTS) ) { + cflag |= 014000000000; + } + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, + CMD_UNIX_FLAGS(iflag,cflag,lflag)); +#endif + +service_it: + i2DrainOutput( pCh, 100 ); +} + +/******************************************************************************/ +/* IPL Device Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ipl_read() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to read */ +/* Returns: Success or failure */ +/* */ +/* Description: Ugly */ +/* */ +/* */ +/******************************************************************************/ + +static +ssize_t +ip2_ipl_read(struct file *pFile, char *pData, size_t count, loff_t *off ) +{ + unsigned int minor = MINOR( pFile->f_dentry->d_inode->i_rdev ); + int rc = 0; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count ); +#endif + + switch( minor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + rc = -EINVAL; + break; + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + rc = DumpTraceBuffer ( pData, count ); + break; + case 4: // Trace device + rc = DumpFifoBuffer ( pData, count ); + break; + default: + rc = -ENODEV; + break; + } + return rc; +} + +static int +DumpFifoBuffer ( char *pData, int count ) +{ +#ifdef DEBUG_FIFO + int rc=0; + if(copy_to_user(pData, DBGBuf, count)) + rc=-EFAULT; + + printk(KERN_DEBUG "Last index %d\n", I ); + + return count; +#endif /* DEBUG_FIFO */ + return 0; +} + +static int +DumpTraceBuffer ( char *pData, int count ) +{ +#ifdef IP2DEBUG_TRACE + int rc; + int dumpcount; + int chunk; + int *pIndex = (int*)pData; + + if ( count < (sizeof(int) * 6) ) { + return -EIO; + } + put_user(tracewrap, pIndex ); + put_user(TRACEMAX, ++pIndex ); + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + pData += sizeof(int) * 6; + count -= sizeof(int) * 6; + + dumpcount = tracestuff - tracestrip; + if ( dumpcount < 0 ) { + dumpcount += TRACEMAX; + } + if ( dumpcount > count ) { + dumpcount = count; + } + chunk = TRACEMAX - tracestrip; + if ( dumpcount > chunk ) { + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0; + pData += chunk * sizeof(tracebuf[0]); + tracestrip = 0; + chunk = dumpcount - chunk; + } else { + chunk = dumpcount; + } + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0 + tracestrip += chunk; + tracewrap = 0; + + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + + return dumpcount; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* Function: ip2_ipl_write() */ +/* Parameters: */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static ssize_t +ip2_ipl_write(struct file *pFile, const char *pData, size_t count, loff_t *off) +{ +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_ipl_ioctl() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + int rc = 0; + ULONG *pIndex = (ULONG*)arg; + i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4]; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); +#endif + + switch ( iplminor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + case 5: + case 9: + case 13: + switch ( cmd ) { + case 64: /* Driver - ip2stat */ + put_user(ref_count, pIndex++ ); + put_user(irq_counter, pIndex++ ); + put_user(bh_counter, pIndex++ ); + break; + + case 65: /* Board - ip2stat */ + if ( pB ) { + if(copy_to_user((char*)arg, (char*)pB, sizeof(i2eBordStr) )) + rc=-EFAULT; + put_user(INB(pB->i2eStatus), + (ULONG*)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); + } else { + rc = -ENODEV; + } + break; + + default: + pCh = DevTable[cmd]; + if ( pCh ) + { + if(copy_to_user((char*)arg, (char*)pCh, sizeof(i2ChanStr) )) + rc = -EFAULT; + } else { + rc = cmd < 64 ? -ENODEV : -EINVAL; + } + } + break; + + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + if ( cmd == 1 ) { + put_user(iiSendPendingMail, pIndex++ ); + put_user(i2InitChannels, pIndex++ ); + put_user(i2QueueNeeds, pIndex++ ); + put_user(i2QueueCommands, pIndex++ ); + put_user(i2GetStatus, pIndex++ ); + put_user(i2Input, pIndex++ ); + put_user(i2InputFlush, pIndex++ ); + put_user(i2Output, pIndex++ ); + put_user(i2FlushOutput, pIndex++ ); + put_user(i2DrainWakeup, pIndex++ ); + put_user(i2DrainOutput, pIndex++ ); + put_user(i2OutputFree, pIndex++ ); + put_user(i2StripFifo, pIndex++ ); + put_user(i2StuffFifoBypass, pIndex++ ); + put_user(i2StuffFifoFlow, pIndex++ ); + put_user(i2StuffFifoInline, pIndex++ ); + put_user(i2ServiceBoard, pIndex++ ); + put_user(serviceOutgoingFifo, pIndex++ ); + // put_user(ip2_init, pIndex++ ); + put_user(ip2_init_board, pIndex++ ); + put_user(find_eisa_board, pIndex++ ); + put_user(set_irq, pIndex++ ); + put_user(ip2_interrupt, pIndex++ ); + put_user(ip2_poll, pIndex++ ); + put_user(service_all_boards, pIndex++ ); + put_user(do_input, pIndex++ ); + put_user(do_status, pIndex++ ); +#ifndef IP2DEBUG_OPEN + put_user(0, pIndex++ ); +#else + put_user(open_sanity_check, pIndex++ ); +#endif + put_user(ip2_open, pIndex++ ); + put_user(ip2_close, pIndex++ ); + put_user(ip2_hangup, pIndex++ ); + put_user(ip2_write, pIndex++ ); + put_user(ip2_putchar, pIndex++ ); + put_user(ip2_flush_chars, pIndex++ ); + put_user(ip2_write_room, pIndex++ ); + put_user(ip2_chars_in_buf, pIndex++ ); + put_user(ip2_flush_buffer, pIndex++ ); + + //put_user(ip2_wait_until_sent, pIndex++ ); + put_user(0, pIndex++ ); + + put_user(ip2_throttle, pIndex++ ); + put_user(ip2_unthrottle, pIndex++ ); + put_user(ip2_ioctl, pIndex++ ); + put_user(get_modem_info, pIndex++ ); + put_user(set_modem_info, pIndex++ ); + put_user(get_serial_info, pIndex++ ); + put_user(set_serial_info, pIndex++ ); + put_user(ip2_set_termios, pIndex++ ); + put_user(ip2_set_line_discipline, pIndex++ ); + put_user(set_params, pIndex++ ); + } else { + rc = -EINVAL; + } + + break; + + default: + rc = -ENODEV; + break; + } + return rc; +} + +/******************************************************************************/ +/* Function: ip2_ipl_open() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_open( struct inode *pInode, struct file *pFile ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: open\n" ); +#endif + + //MOD_INC_USE_COUNT; // Needs close entry with decrement. + + switch(iplminor) { + // These are the IPL devices + case 0: + case 4: + case 8: + case 12: + break; + + // These are the status devices + case 1: + case 5: + case 9: + case 13: + break; + + // These are the debug devices + case 2: + case 6: + case 10: + case 14: + pB = i2BoardPtrTable[iplminor / 4]; + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + break; + + // This is the trace device + case 3: + break; + } + return 0; +} +/******************************************************************************/ +/* Function: ip2_read_procmem */ +/* Parameters: */ +/* */ +/* Returns: Length of output */ +/* */ +/* Description: */ +/* Supplies some driver operating parameters */ +/* Not real useful unless your debugging the fifo */ +/* */ +/******************************************************************************/ + +#define LIMIT (PAGE_SIZE - 120) + +int +ip2_read_procmem(char *buf, char **start, off_t offset, int len, int unused) +{ + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + PTTY tty; + int i; + + len = 0; + +#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n" +#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n" +#define FMTLIN3 " 0x%04x 0x%04x rc flow\n" + + len += sprintf(buf+len,"\n"); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + len += sprintf(buf+len,"board %d:\n",i); + len += sprintf(buf+len,"\tFifo rem: %d mty: %x outM %x\n", + pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting); + } + } + + len += sprintf(buf+len,"#: tty flags, port flags, cflags, iflags\n"); + for (i=0; i < IP2_MAX_PORTS; i++) { + if (len > LIMIT) + break; + pCh = DevTable[i]; + if (pCh) { + tty = pCh->pTTY; + if (tty && tty->count) { + len += sprintf(buf+len,FMTLINE,i,(int)tty->flags,pCh->flags, + tty->termios->c_cflag,tty->termios->c_iflag); + + len += sprintf(buf+len,FMTLIN2, + pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds); + len += sprintf(buf+len,FMTLIN3,pCh->infl.asof,pCh->infl.room); + } + } + } + return len; +} + +/* + * This is the handler for /proc/tty/driver/ip2 + * + * This stretch of code has been largely plagerized from at least three + * different sources including ip2mkdev.c and a couple of other drivers. + * The bugs are all mine. :-) =mhw= + */ +int ip2_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i, j, box; + int len = 0; + int boxes = 0; + int ports = 0; + int tports = 0; + off_t begin = 0; + i2eBordStrPtr pB; + + len += sprintf(page, "ip2info: 1.0 driver: %s\n", pcVersion ); + len += sprintf(page+len, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", + IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, + IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + /* This need to be reset for a board by board count... */ + boxes = 0; + pB = i2BoardPtrTable[i]; + if( pB ) { + switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) + { + case POR_ID_FIIEX: + len += sprintf( page+len, "Board %d: EX ports=", i ); + for( box = 0; box < ABS_MAX_BOXES; ++box ) + { + ports = 0; + + if( pB->i2eChannelMap[box] != 0 ) ++boxes; + for( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if( pB->i2eChannelMap[box] & 1<< j ) { + ++ports; + } + } + len += sprintf( page+len, "%d,", ports ); + tports += ports; + } + + --len; /* Backup over that last comma */ + + len += sprintf( page+len, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8 ); + break; + + case POR_ID_II_4: + len += sprintf(page+len, "Board %d: ISA-4 ports=4 boxes=1", i ); + tports = ports = 4; + break; + + case POR_ID_II_8: + len += sprintf(page+len, "Board %d: ISA-8-std ports=8 boxes=1", i ); + tports = ports = 8; + break; + + case POR_ID_II_8R: + len += sprintf(page+len, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i ); + tports = ports = 8; + break; + + default: + len += sprintf(page+len, "Board %d: unknown", i ); + /* Don't try and probe for minor numbers */ + tports = ports = 0; + } + + } else { + /* Don't try and probe for minor numbers */ + len += sprintf(page+len, "Board %d: vacant", i ); + tports = ports = 0; + } + + if( tports ) { + len += sprintf(page+len, " minors=" ); + + for ( box = 0; box < ABS_MAX_BOXES; ++box ) + { + for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if ( pB->i2eChannelMap[box] & (1 << j) ) + { + len += sprintf (page+len,"%d,", + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES)); + } + } + } + + page[ len - 1 ] = '\n'; /* Overwrite that last comma */ + } else { + len += sprintf (page+len,"\n" ); + } + + if (len+begin > off+count) + break; + if (len+begin < off) { + begin += len; + len = 0; + } + } + + if (i >= IP2_MAX_BOARDS) + *eof = 1; + if (off >= len+begin) + return 0; + + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); + } + +/******************************************************************************/ +/* Function: ip2trace() */ +/* Parameters: Value to add to trace buffer */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +void +ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...) +{ +#ifdef IP2DEBUG_TRACE + long flags; + unsigned long *pCode = &codes; + union ip2breadcrumb bc; + i2ChanStrPtr pCh; + + + tracebuf[tracestuff++] = jiffies; + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + bc.hdr.port = 0xff & pn; + bc.hdr.cat = cat; + bc.hdr.codes = (unsigned char)( codes & 0xff ); + bc.hdr.label = label; + tracebuf[tracestuff++] = bc.value; + + for (;;) { + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + if ( !codes-- ) + break; + + tracebuf[tracestuff++] = *++pCode; + } +#endif +} + + diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index e99decb62..041858997 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -14,7 +14,8 @@ * Printk clean up * 9/12/98 alan@redhat.com Rough port to 2.1.x * - * + * 10/6/99 sameer Merged the ISA and PCI drivers to + * a new unified driver. * *********************************************************** * * To use this driver you also need the support package. You @@ -51,8 +52,21 @@ #include <asm/io.h> #include <asm/system.h> +#include <linux/pci.h> + #include <linux/isicom.h> +static int device_id[] = { 0x2028, + 0x2051, + 0x2052, + 0x2053, + 0x2054, + 0x2055, + 0x2056, + 0x2057, + 0x2058 + }; + static int isicom_refcount = 0; static int prev_card = 3; /* start servicing isi_card[0] */ static struct isi_board * irq_to_board[16] = { NULL, }; @@ -147,7 +161,7 @@ static int ISILoad_release(struct inode *inode, struct file *filp) static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { - unsigned int card, i, j, signature, status; + unsigned int card, i, j, signature, status, portcount = 0; unsigned short word_count, base; bin_frame frame; /* exec_record exec_rec; */ @@ -180,19 +194,38 @@ static int ISILoad_ioctl(struct inode *inode, struct file *filp, printk("."); } signature=(inw(base+0x4)) & 0xff; - - if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) { + if (isi_card[card].isa) { + + if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) { #ifdef ISICOM_DEBUG - printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); + printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); #endif - printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); - return -EIO; - } - + printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); + return -EIO; + } + } + else { + portcount = inw(base+0x2); + if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) { +#ifdef ISICOM_DEBUG + printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); +#endif + printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); + return -EIO; + } + } switch(signature) { case 0xa5: case 0xbb: - case 0xdd: isi_card[card].port_count = 8; + case 0xdd: + if (isi_card[card].isa) + isi_card[card].port_count = 8; + else { + if (portcount == 4) + isi_card[card].port_count = 4; + else + isi_card[card].port_count = 8; + } isi_card[card].shift_count = 12; break; @@ -310,7 +343,8 @@ static int ISILoad_ioctl(struct inode *inode, struct file *filp, outw(0x0, base); outw(0x0, base); InterruptTheCard(base); - + outw(0x0, base+0x4); /* for ISI4608 cards */ + isi_card[card].status |= FIRMWARE_LOADED; return 0; @@ -455,28 +489,7 @@ static void isicom_tx(unsigned long _data) txcount--; } } -/* - * Replaced the code below with hopefully a faster loop - sameer - */ -/* - while (1) { - wrd = port->xmit_buf[port->xmit_tail++]; - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - if (--txcount > 0) { - wrd |= (port->xmit_buf[port->xmit_tail++] << 8); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); - port->xmit_cnt--; - outw(wrd, base); - if (--txcount <= 0) break; - } - else { - outw(wrd, base); - break; - } - } -*/ InterruptTheCard(base); if (port->xmit_cnt <= 0) port->status &= ~ISI_TXOK; @@ -531,12 +544,34 @@ static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs) unsigned char channel; short byte_count; - card = irq_to_board[irq]; + /* + * find the source of interrupt + */ + + for(count = 0; count < BOARD_COUNT; count++) { + card = &isi_card[count]; + if (card->base != 0) { + if (((card->isa == YES) && (card->irq == irq)) || + ((card->isa == NO) && (card->irq == irq) && (inw(card->base+0x0e) & 0x02))) + break; + } + card = NULL; + } + if (!card || !(card->status & FIRMWARE_LOADED)) { - printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq); +/* printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq);*/ return; } + base = card->base; + if (card->isa == NO) { + /* + * disable any interrupts from the PCI card and lower the + * interrupt line + */ + outw(0x8000, base+0x04); + ClearInterrupt(base); + } inw(base); /* get the dummy word out */ header = inw(base); @@ -548,12 +583,18 @@ static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs) if ((channel+1) > card->port_count) { printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n", base, channel+1); - ClearInterrupt(base); + if (card->isa) + ClearInterrupt(base); + else + outw(0x0000, base+0x04); /* enable interrupts */ return; } port = card->ports + channel; if (!(port->flags & ASYNC_INITIALIZED)) { - ClearInterrupt(base); + if (card->isa) + ClearInterrupt(base); + else + outw(0x0000, base+0x04); /* enable interrupts */ return; } @@ -681,7 +722,10 @@ static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs) } queue_task(&tty->flip.tqueue, &tq_timer); } - ClearInterrupt(base); + if (card->isa == YES) + ClearInterrupt(base); + else + outw(0x0000, base+0x04); /* enable interrupts */ return; } @@ -951,7 +995,7 @@ static int block_til_ready(struct tty_struct * tty, struct file * filp, struct i raise_dtr_rts(port); sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; @@ -1023,12 +1067,11 @@ static int isicom_open(struct tty_struct * tty, struct file * filp) return -ENODEV; } - /* open on higher 8 dev files on a 8 port card !!! */ - if (card->port_count == 8) - if (line > ((board * 16)+7)) { - printk(KERN_ERR "ISICOM: Opened >8 on a 8 port card.\n"); - return -ENODEV; - } + /* open on a port greater than the port count for the card !!! */ + if (line > ((board * 16) + card->port_count - 1)) { + printk(KERN_ERR "ISICOM: Open on a port which exceeds the port_count of the card!\n"); + return -ENODEV; + } port = &isi_ports[line]; if (isicom_paranoia_check(port, tty->device, "isicom_open")) return -ENODEV; @@ -1362,7 +1405,7 @@ static int isicom_get_modem_info(struct isi_port * port, unsigned int * value) ((status & ISI_DSR) ? TIOCM_DSR : 0) | ((status & ISI_CTS) ? TIOCM_CTS : 0) | ((status & ISI_RI ) ? TIOCM_RI : 0); - put_user(info, (unsigned long *) value); + put_user(info, (unsigned int *) value); return 0; } @@ -1764,21 +1807,47 @@ static void unregister_drivers(void) static int register_isr(void) { - int count, done=0; + int count, done=0, card; + int flag; + unsigned char request; for (count=0; count < BOARD_COUNT; count++ ) { if (isi_card[count].base) { - if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, ISICOM_NAME, NULL)) { - printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n", - isi_card[count].irq, count+1); + /* + * verify if the required irq has already been requested for + * another ISI Card, if so we already have it, else request it + */ + request = YES; + for(card = 0; card < count; card++) + if ((isi_card[card].base) && (isi_card[card].irq == isi_card[count].irq)) { + request = NO; + if ((isi_card[count].isa == NO) && (isi_card[card].isa == NO)) + break; + /* + * ISA cards cannot share interrupts with other + * PCI or ISA devices hence disable this card. + */ release_region(isi_card[count].base,16); - isi_card[count].base=0; + isi_card[count].base = 0; + break; } - else { - printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n", - count+1, isi_card[count].base, isi_card[count].irq); + flag=0; + if(isi_card[count].isa == NO) + flag |= SA_SHIRQ; - irq_to_board[isi_card[count].irq]=&isi_card[count]; - done++; + if (request == YES) { + if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT|flag, ISICOM_NAME, NULL)) { + printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n", + isi_card[count].irq, count+1); + release_region(isi_card[count].base,16); + isi_card[count].base=0; + } + else { + printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n", + count+1, isi_card[count].base, isi_card[count].irq); + + irq_to_board[isi_card[count].irq]=&isi_card[count]; + done++; + } } } } @@ -1787,14 +1856,24 @@ static int register_isr(void) static void unregister_isr(void) { - int count; - for (count=0; count < BOARD_COUNT; count++ ) + int count, card; + unsigned char freeirq; + for (count=0; count < BOARD_COUNT; count++ ) { if (isi_card[count].base) { - free_irq(isi_card[count].irq, NULL); + freeirq = YES; + for(card = 0; card < count; card++) + if ((isi_card[card].base) && (isi_card[card].irq == isi_card[count].irq)) { + freeirq = NO; + break; + } + if (freeirq == YES) { + free_irq(isi_card[count].irq, NULL); #ifdef ISICOM_DEBUG - printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1); -#endif + printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1); +#endif + } } + } } static int isicom_init(void) @@ -1883,29 +1962,75 @@ MODULE_PARM_DESC(irq, "Interrupts for the cards"); int init_module(void) { - int retval, card; - - for(card=0; card < BOARD_COUNT; card++) - { - isi_card[card].base=io[card]; - isi_card[card].irq=irq[card]; + struct pci_dev *dev = NULL; + int retval, card, idx, count; + unsigned char pciirq; + unsigned int ioaddr; + + card = 0; + for(idx=0; idx < BOARD_COUNT; idx++) { + if (io[idx]) { + isi_card[idx].base=io[idx]; + isi_card[idx].irq=irq[idx]; + isi_card[idx].isa=YES; + card++; + } + else { + isi_card[idx].base = 0; + isi_card[idx].irq = 0; + } } - for (card=0 ;card < BOARD_COUNT; card++) { - if (!((isi_card[card].irq==2)||(isi_card[card].irq==3)|| - (isi_card[card].irq==4)||(isi_card[card].irq==5)|| - (isi_card[card].irq==7)||(isi_card[card].irq==10)|| - (isi_card[card].irq==11)||(isi_card[card].irq==12)|| - (isi_card[card].irq==15))) { + for (idx=0 ;idx < card; idx++) { + if (!((isi_card[idx].irq==2)||(isi_card[idx].irq==3)|| + (isi_card[idx].irq==4)||(isi_card[idx].irq==5)|| + (isi_card[idx].irq==7)||(isi_card[idx].irq==10)|| + (isi_card[idx].irq==11)||(isi_card[idx].irq==12)|| + (isi_card[idx].irq==15))) { - if (isi_card[card].base) { + if (isi_card[idx].base) { printk(KERN_ERR "ISICOM: Irq %d unsupported. Disabling Card%d...\n", - isi_card[card].irq, card+1); - isi_card[card].base=0; + isi_card[idx].irq, idx+1); + isi_card[idx].base=0; + card--; } } } + if (pci_present() && (card < BOARD_COUNT)) { + for (idx=0; idx < DEVID_COUNT; idx++) { + dev = NULL; + for (;;){ + if (!(dev = pci_find_device(VENDOR_ID, device_id[idx], dev))) + break; + if (card >= BOARD_COUNT) + break; + + /* found a PCI ISI card! */ + ioaddr = dev->resource[3].start; /* i.e at offset 0x1c in the + * PCI configuration register + * space. + */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + pciirq = dev->irq; + printk(KERN_INFO "ISI PCI Card(Device ID 0x%x)\n", device_id[idx]); + /* + * allot the first empty slot in the array + */ + for (count=0; count < BOARD_COUNT; count++) { + if (isi_card[count].base == 0) { + isi_card[count].base = ioaddr; + isi_card[count].irq = pciirq; + isi_card[count].isa = NO; + card++; + break; + } + } + } + if (card >= BOARD_COUNT) break; + } + } + if (!(isi_card[0].base || isi_card[1].base || isi_card[2].base || isi_card[3].base)) { printk(KERN_ERR "ISICOM: No valid card configuration. Driver cannot be initialized...\n"); return -EIO; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index b689bb7a5..5176f28f4 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -4,7 +4,7 @@ * istallion.c -- stallion intelligent multiport serial driver. * * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. @@ -167,7 +167,7 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); */ static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; static char *stli_drvname = "istallion"; -static char *stli_drvversion = "5.5.1"; +static char *stli_drvversion = "5.6.0"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -186,7 +186,7 @@ static int stli_refcount; * is already swapping a shared buffer won't make things any worse. */ static char *stli_tmpwritebuf = (char *) NULL; -static struct semaphore stli_tmpwritesem = MUTEX; +static DECLARE_MUTEX(stli_tmpwritesem); #define STLI_TXBUFSIZE 4096 @@ -3375,6 +3375,9 @@ static inline int stli_initports(stlibrd_t *brdp) portp->closing_wait = 30 * HZ; portp->tqhangup.routine = stli_dohangup; portp->tqhangup.data = portp; + init_waitqueue_head(&portp->open_wait); + init_waitqueue_head(&portp->close_wait); + init_waitqueue_head(&portp->raw_wait); portp->normaltermios = stli_deftermios; portp->callouttermios = stli_deftermios; panelport++; @@ -4387,7 +4390,7 @@ stli_donestartup: * Probe and initialize the specified board. */ -__initfunc(static int stli_brdinit(stlibrd_t *brdp)) +static int __init stli_brdinit(stlibrd_t *brdp) { #if DEBUG printk("stli_brdinit(brdp=%x)\n", (int) brdp); @@ -4671,16 +4674,16 @@ static inline int stli_initpcibrd(int brdtype, struct pci_dev *devp) #if DEBUG printk("%s(%d): BAR[]=%x,%x,%x,%x\n", __FILE__, __LINE__, - devp->base_address[0], devp->base_address[1], - devp->base_address[2], devp->base_address[3]); + devp->resource[0].start, devp->resource[1].start, + devp->resource[2].start, devp->resource[3].start); #endif /* * We have all resources from the board, so lets setup the actual * board structure now. */ - brdp->iobase = (devp->base_address[3] & PCI_BASE_ADDRESS_IO_MASK); - brdp->memaddr = (devp->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK); + brdp->iobase = (devp->resource[3].start & PCI_BASE_ADDRESS_IO_MASK); + brdp->memaddr = (devp->resource[2].start & PCI_BASE_ADDRESS_MEM_MASK); stli_brdinit(brdp); return(0); @@ -5305,7 +5308,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un /*****************************************************************************/ -__initfunc(int stli_init(void)) +int __init stli_init(void) { printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c index 4d9d94aa1..941f1795d 100644 --- a/drivers/char/joystick/joystick.c +++ b/drivers/char/joystick/joystick.c @@ -42,8 +42,9 @@ #include <linux/malloc.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include <asm/spinlock.h> +#include <linux/spinlock.h> #include <linux/poll.h> #endif @@ -525,8 +526,8 @@ static int js_read(struct inode *inode, struct file *file, char *buf, int count) if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&jd->wait, &wait); - current->state = TASK_INTERRUPTIBLE; while (GOF(curl->tail) == jd->bhead) { diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index c25f24bc4..def0b7c19 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -155,6 +155,7 @@ struct pt_regs * kbd_pt_regs; #ifdef CONFIG_MAGIC_SYSRQ static int sysrq_pressed; +int sysrq_enabled = 1; #endif /* @@ -247,7 +248,7 @@ void handle_scancode(unsigned char scancode, int down) sysrq_pressed = !up_flag; return; } else if (sysrq_pressed) { - if (!up_flag) { + if (!up_flag && sysrq_enabled) { handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty); return; } @@ -916,7 +917,7 @@ static void kbd_bh(void) } } -__initfunc(int kbd_init(void)) +int __init kbd_init(void) { int i; struct kbd_struct kbd0; diff --git a/drivers/char/logibusmouse.c b/drivers/char/logibusmouse.c new file mode 100644 index 000000000..d8c7d37dd --- /dev/null +++ b/drivers/char/logibusmouse.c @@ -0,0 +1,157 @@ +/* + * Logitech Bus Mouse Driver for Linux + * by James Banks + * + * Mods by Matthew Dillon + * calls verify_area() + * tracks better when X is busy or paging + * + * Heavily modified by David Giller + * changed from queue- to counter- driven + * hacked out a (probably incorrect) mouse_select + * + * Modified again by Nathan Laredo to interface with + * 0.96c-pl1 IRQ handling changes (13JUL92) + * didn't bother touching select code. + * + * Modified the select() code blindly to conform to the VFS + * requirements. 92.07.14 - Linus. Somebody should test it out. + * + * Modified by Johan Myreen to make room for other mice (9AUG92) + * removed assignment chr_fops[10] = &mouse_fops; see mouse.c + * renamed mouse_fops => bus_mouse_fops, made bus_mouse_fops public. + * renamed this file mouse.c => busmouse.c + * + * Minor addition by Cliff Matthews + * added fasync support + * + * Modularised 6-Sep-95 Philip Blundell <pjb27@cam.ac.uk> + * + * Replaced dumb busy loop with udelay() 16 Nov 95 + * Nathan Laredo <laredo@gnu.ai.mit.edu> + * + * Track I/O ports with request_region(). 12 Dec 95 Philip Blundell + * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/logibusmouse.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/random.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/irq.h> + +#include "busmouse.h" + +static int msedev; +static int mouse_irq = MOUSE_IRQ; + +#ifdef MODULE +MODULE_PARM(mouse_irq, "i"); +#endif + +void __init bmouse_setup(char *str, int *ints) +{ + if (ints[0] > 0) + mouse_irq=ints[1]; +} + +static void mouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + char dx, dy; + unsigned char buttons; + + outb(MSE_READ_X_LOW, MSE_CONTROL_PORT); + dx = (inb(MSE_DATA_PORT) & 0xf); + outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT); + dx |= (inb(MSE_DATA_PORT) & 0xf) << 4; + outb(MSE_READ_Y_LOW, MSE_CONTROL_PORT ); + dy = (inb(MSE_DATA_PORT) & 0xf); + outb(MSE_READ_Y_HIGH, MSE_CONTROL_PORT); + buttons = inb(MSE_DATA_PORT); + dy |= (buttons & 0xf) << 4; + buttons = ((buttons >> 5) & 0x07); + busmouse_add_movementbuttons(msedev, dx, -dy, buttons); + MSE_INT_ON(); +} + +/* + * close access to the mouse + */ +static int close_mouse(struct inode * inode, struct file * file) +{ + MSE_INT_OFF(); + free_irq(mouse_irq, NULL); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * open access to the mouse + */ + +static int open_mouse(struct inode * inode, struct file * file) +{ + if (request_irq(mouse_irq, mouse_interrupt, 0, "busmouse", NULL)) + return -EBUSY; + MOD_INC_USE_COUNT; + MSE_INT_ON(); + return 0; +} + +static struct busmouse busmouse = { + LOGITECH_BUSMOUSE, "busmouse", open_mouse, close_mouse, 7 +}; + +int __init logi_busmouse_init(void) +{ + if (check_region(LOGIBM_BASE, LOGIBM_EXTENT)) + return -EIO; + + outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT); + outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT); + udelay(100L); /* wait for reply from mouse */ + if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) + return -EIO; + + outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT); + MSE_INT_OFF(); + + request_region(LOGIBM_BASE, LOGIBM_EXTENT, "busmouse"); + + msedev = register_busmouse(&busmouse); + if (msedev < 0) + printk(KERN_WARNING "Unable to register busmouse driver.\n"); + else + printk(KERN_INFO "Logitech busmouse installed.\n"); + return msedev < 0 ? msedev : 0; +} + +#ifdef MODULE + +int init_module(void) +{ + return logi_busmouse_init(); +} + +void cleanup_module(void) +{ + unregister_busmouse(msedev); + release_region(LOGIBM_BASE, LOGIBM_EXTENT); +} +#endif diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 7aa14a886..4092d6b55 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -333,6 +333,9 @@ static ssize_t lp_write(struct file * file, const char * buf, if (copy_from_user (kbuf, buf, copy_size)) return -EFAULT; + if (down_interruptible (&lp_table[minor].port_mutex)) + return -EINTR; + /* Claim Parport or sleep until it becomes available */ lp_parport_claim (minor); @@ -341,10 +344,6 @@ static ssize_t lp_write(struct file * file, const char * buf, parport_negotiate (port, IEEE1284_MODE_COMPAT); do { - /* Wait until lp_read has finished. */ - if (down_interruptible (&lp_table[minor].port_mutex)) - break; - /* Write the data. */ written = parport_write (port, kbuf, copy_size); if (written >= 0) { @@ -354,8 +353,6 @@ static ssize_t lp_write(struct file * file, const char * buf, retv += written; } - up (&lp_table[minor].port_mutex); - if (signal_pending (current)) { if (retv == 0) retv = -EINTR; @@ -392,6 +389,8 @@ static ssize_t lp_write(struct file * file, const char * buf, lp_parport_release (minor); + up (&lp_table[minor].port_mutex); + return retv; } @@ -414,29 +413,28 @@ static ssize_t lp_read(struct file * file, char * buf, if (count > LP_BUFFER_SIZE) count = LP_BUFFER_SIZE; + if (down_interruptible (&lp_table[minor].port_mutex)) + return -EINTR; + lp_parport_claim (minor); - if (!down_interruptible (&lp_table[minor].port_mutex)) { - for (;;) { - retval = parport_read (port, kbuf, count); + for (;;) { + retval = parport_read (port, kbuf, count); - if (retval) - break; + if (retval) + break; - if (file->f_flags & O_NONBLOCK) - break; + if (file->f_flags & O_NONBLOCK) + break; - /* Wait for an interrupt. */ - interruptible_sleep_on_timeout (&lp_table[minor].waitq, - LP_TIMEOUT_POLLED); + /* Wait for an interrupt. */ + interruptible_sleep_on_timeout (&lp_table[minor].waitq, + LP_TIMEOUT_POLLED); - if (signal_pending (current)) { - retval = -EINTR; - break; - } + if (signal_pending (current)) { + retval = -EINTR; + break; } - - up (&lp_table[minor].port_mutex); } lp_parport_release (minor); @@ -444,6 +442,8 @@ static ssize_t lp_read(struct file * file, char * buf, if (retval > 0 && copy_to_user (buf, kbuf, retval)) retval = -EFAULT; + up (&lp_table[minor].port_mutex); + return retval; } @@ -711,7 +711,7 @@ static int reset = 0; static int parport_ptr = 0; -__initfunc(void lp_setup(char *str, int *ints)) +void __init lp_setup(char *str, int *ints) { if (!str) { if (ints[0] == 0 || ints[1] == 0) { @@ -755,6 +755,17 @@ static int lp_register(int nr, struct parport *port) printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name, (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven"); +#ifdef CONFIG_LP_CONSOLE + if (!nr) { + if (port->modes & PARPORT_MODE_SAFEININT) { + register_console (&lpcons); + printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP); + } else + printk (KERN_ERR "lp%d: cannot run console on %s\n", + CONSOLE_LP, port->name); + } +#endif + return 0; } @@ -842,12 +853,6 @@ int __init lp_init (void) printk (KERN_INFO "lp: (is IEEE 1284.3 support enabled?)\n"); #endif } -#ifdef CONFIG_LP_CONSOLE - else { - register_console (&lpcons); - printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP); - } -#endif return 0; } diff --git a/drivers/char/lp_intern.c b/drivers/char/lp_intern.c deleted file mode 100644 index e4fc30403..000000000 --- a/drivers/char/lp_intern.c +++ /dev/null @@ -1,364 +0,0 @@ - -/* - * split into mid and low-level for better support of different hardware - * by Joerg Dorchain (dorchain@mpi-sb.mpg.de) - * - * Amiga printer device by Michael Rausch (linux@uni-koblenz.de); - * Atari support added by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de); - * based upon work from - * - * Copyright (C) 1992 by Jim Weigand and Linus Torvalds - * Copyright (C) 1992,1993 by Michael K. Johnson - * - Thanks much to Gunter Windau for pointing out to me where the error - * checking ought to be. - * Copyright (C) 1993 by Nigel Gamble (added interrupt code) - */ - -#include <linux/module.h> -#include <linux/config.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <asm/irq.h> -#include <asm/setup.h> -#ifdef CONFIG_AMIGA -#include <asm/amigahw.h> -#include <asm/amigaints.h> -#endif -#ifdef CONFIG_ATARI -#include <linux/delay.h> -#include <asm/atarihw.h> -#include <asm/atariints.h> -#endif -#ifdef CONFIG_MVME16x -#include <asm/mvme16xhw.h> -#endif -#ifdef CONFIG_BVME6000 -#include<asm/bvme6000hw.h> -#endif -#include <linux/lp_intern.h> - -static int minor = -1; -MODULE_PARM(minor,"i"); - -static void lp_int_out(int, int); -static int lp_int_busy(int); -static int lp_int_pout(int); -static int lp_int_online(int); - - -static void -lp_int_out (int c, int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - ciaa.prb = c; - } - break; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - { - int wait = 0; - sound_ym.rd_data_reg_sel = 15; - sound_ym.wd_data = c; - sound_ym.rd_data_reg_sel = 14; - while (wait != lp_table[dev]->wait) wait++; - sound_ym.wd_data = sound_ym.rd_data_reg_sel & ~(1 << 5); - while (wait) wait--; - sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5); - break; - } -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - mvmelp.data = c; - break; - } -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - bvmepit.padr = c; - bvmepit.pacr |= 0x02; - break; - } -#endif - } -} - -static int -lp_int_busy (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 1; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return mfp.par_dt_reg & 1; -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 1; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 0 /* !(bvmepit.psr & 0x40) */ ; -#endif - default: - return 0; - } -} - -static int -lp_int_pout (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 2; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return 0; -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 2; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 0; -#endif - default: - return 0; - } -} - -static int -lp_int_online (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 4; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return !(mfp.par_dt_reg & 1); -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 4; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 1; -#endif - default: - return 0; - } -} - -static void lp_int_interrupt(int irq, void *data, struct pt_regs *fp) -{ -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - mvmelp.ack_icr |= 0x08; -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - bvmepit.pacr &= ~0x02; -#endif - lp_interrupt(minor); -} - -static int lp_int_open(int dev) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static void lp_int_release(int dev) -{ - MOD_DEC_USE_COUNT; -} - -static struct lp_struct tab = { - "Builtin parallel port", /* name */ - 0, /* irq */ - lp_int_out, - lp_int_busy, - lp_int_pout, - lp_int_online, - 0, - NULL, /* ioctl */ - lp_int_open, - lp_int_release, - LP_EXIST, - LP_INIT_CHAR, - LP_INIT_TIME, - LP_INIT_WAIT, - NULL, - NULL, -}; - -__initfunc(int lp_internal_init(void)) -{ -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) - { - ciaa.ddrb = 0xff; - ciab.ddra &= 0xf8; - if (lp_irq) - tab.irq = request_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *) &ciaa.prb; /* dummy, not used */ - tab.type = LP_AMIGA; - } -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - { - unsigned long flags; - - save_flags(flags); - cli(); - sound_ym.rd_data_reg_sel = 7; - sound_ym.wd_data = (sound_ym.rd_data_reg_sel & 0x3f) | 0xc0; - restore_flags(flags); - if (lp_irq) - tab.irq = request_irq(IRQ_MFP_BUSY, lp_int_interrupt, - IRQ_TYPE_SLOW, "builtin printer port", lp_int_interrupt); - tab.base = (void *) &sound_ym.wd_data; /* dummy, not used */ - tab.type = LP_ATARI; - } -#endif -#ifdef CONFIG_MAC - if (MACH_IS_MAC) - return -ENODEV; -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - { - unsigned long flags; - - if (!(mvme16x_config & MVME16x_CONFIG_GOT_LP)) - return -ENODEV; - - save_flags(flags); - cli(); - mvmelp.ack_icr = 0x08; - mvmelp.flt_icr = 0x08; - mvmelp.sel_icr = 0x08; - mvmelp.pe_icr = 0x08; - mvmelp.bsy_icr = 0x08; - mvmelp.cr = 0x10; - mvmelp.ack_icr = 0xd9; /* Int on trailing edge of ACK */ - restore_flags(flags); - - if (lp_irq) - tab.irq = request_irq(MVME167_IRQ_PRN, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *)&mvmelp; /* dummy, not used */ - tab.type = LP_MVME167; - } -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - { - unsigned long flags; - - save_flags(flags); - cli(); - bvmepit.pgcr = 0x0f; - bvmepit.psrr = 0x18; - bvmepit.paddr = 0xff; - bvmepit.pcdr = (bvmepit.pcdr & 0xfc) | 0x02; - bvmepit.pcddr |= 0x03; - bvmepit.pacr = 0x78; - bvmepit.pbcr = 0x00; - bvmepit.pivr = BVME_IRQ_PRN; - bvmepit.pgcr = 0x1f; - restore_flags(flags); - - if (lp_irq) - tab.irq = request_irq(BVME_IRQ_PRN, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *)&bvmepit; /* dummy, not used */ - tab.type = LP_BVME6000; - } -#endif - - - if ((minor = register_parallel(&tab, minor)) < 0) { - printk("builtin lp init: cant get a minor\n"); - if (lp_irq) { -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA) - free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt); -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - free_irq(IRQ_MFP_BUSY, lp_int_interrupt); -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - free_irq(MVME167_IRQ_PRN, lp_int_interrupt); -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - free_irq(BVME_IRQ_PRN, lp_int_interrupt); -#endif - } - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -int init_module(void) -{ -return lp_internal_init(); -} - -void cleanup_module(void) -{ -if (lp_irq) { -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA) - free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt); -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - free_irq(IRQ_MFP_BUSY, lp_int_interrupt); -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - free_irq(MVME167_IRQ_PRN, lp_int_interrupt); -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - free_irq(BVME_IRQ_PRN, lp_int_interrupt); -#endif -} -unregister_parallel(minor); -} -#endif diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c deleted file mode 100644 index 7eecf2965..000000000 --- a/drivers/char/lp_m68k.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * split in two parts for better support of different hardware - * by Joerg Dorchain (dorchain@mpi-sb.mpg.de) - * - * Amiga printer device by Michael Rausch (linux@uni-koblenz.de); - * Atari support added by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de); - * based upon work from - * - * Copyright (C) 1992 by Jim Weigand and Linus Torvalds - * Copyright (C) 1992,1993 by Michael K. Johnson - * - Thanks much to Gunter Windau for pointing out to me where the error - * checking ought to be. - * Copyright (C) 1993 by Nigel Gamble (added interrupt code) - */ - -/* 01/17/95: Matthias Welwarsky (dg8y@rs11.hrz.th-darmstadt.de) - * lp_write(): rewritten from scratch - * lp_interrupt(): fixed cli()/sti()-bug - * - * 95/05/28: Andreas Schwab (schwab@issan.informatik.uni-dortmund.de) - * lp_write() fixed to make it work again. - * 95/08/18: Andreas Schwab - * lp_write_interrupt: fix race condition - * - * * CAUTION, please do check! * - * - * on 68000-based machines sti() must NEVER appear in interrupt driven - * code. The 68k-CPU has a priority-based interrupt scheme. while an interrupt - * with a certain priority is executed, all requests with lower or same - * priority get locked out. executing the sti()-macro allows ANY interrupt - * to be served. this really causes BIG trouble! - * to protect an interrupt driven routine against being interrupted - * (if absolutely needed!) one should use save_flags();cli()/restore_flags()! - * - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/init.h> -#include <linux/kmod.h> - -#ifdef CONFIG_AMIGA -#ifdef CONFIG_MULTIFACE_III_LP -#include <linux/lp_mfc.h> -#endif -#endif - -#include <linux/lp_m68k.h> -#include <linux/lp_intern.h> -#include <linux/malloc.h> -#include <linux/interrupt.h> - -#include <asm/irq.h> -#include <asm/uaccess.h> -#include <asm/system.h> - - -/* - * why bother around with the pio driver when the interrupt works; - * so, for "security" reasons only, it's configurable here. - * saves some bytes, at least ... - */ -#define FORCE_POLLING 0 -#define FORCE_INTERRUPT 1 -/* - * PREFER_INTERRUPT doesn't make much sense on m68k. - * it is preserved here in case of joining with the i386 driver - * -#define PREFER_INTERRUPT 2 - */ - -#define WHICH_DRIVER FORCE_INTERRUPT - -struct lp_struct *lp_table[MAX_LP] = {NULL,}; - -static int max_lp; /* the real number of devices */ - -/* - * All my debugging code assumes that you debug with only one printer at - * a time. RWWH - */ - -#define LP_DEBUG -#undef LP_DEBUG - - -#if WHICH_DRIVER != FORCE_INTERRUPT -#ifdef LP_DEBUG -static int lp_max_count = 1; -#endif - -static int lp_char_polled(char lpchar, int dev) -{ - unsigned long count = 0; - - do { - count ++; - if (current->need_resched) - schedule(); - } while (lp_table[dev]->lp_is_busy(dev) && count < lp_table[dev]->chars); - - if (count == lp_table[dev]->chars) { - return 0; - /* we timed out, and the character was /not/ printed */ - } -#ifdef LP_DEBUG - if (count > lp_max_count) { - printk("lp success after %d counts.\n",count); - lp_max_count = count; - } -#endif - lp_table[dev]->lp_out(lpchar, dev); - return 1; -} -#endif - - -#ifdef LP_DEBUG -unsigned int lp_total_chars = 0; -unsigned int lp_last_call = 0; -#endif - - -#if WHICH_DRIVER != FORCE_POLLING -static __inline__ int lp_char_interrupt(char lpchar, int dev) -{ - if (!lp_table[dev]->lp_is_busy(dev)) { - lp_table[dev]->lp_out(lpchar,dev); - return 1; - } - return 0; -} - -static int lp_error; - -void lp_interrupt(int dev) -{ - if (dev >= 0 && dev < MAX_LP && lp_table[dev]->do_print) - { - if (lp_table[dev]->copy_size) - { - unsigned long flags; - save_flags(flags); - cli(); - if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) { - --lp_table[dev]->copy_size; - ++lp_table[dev]->bytes_written; - restore_flags(flags); - } - else - { - lp_table[dev]->do_print = 0; - restore_flags(flags); - lp_error = 1; - wake_up_interruptible(&lp_table[dev]->lp_wait_q); - } - } - else - { - lp_table[dev]->do_print = 0; - lp_error = 0; - wake_up_interruptible(&lp_table[dev]->lp_wait_q); - } - - } -} - -#if WHICH_DRIVER == FORCE_INTERRUPT -static ssize_t lp_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#else -static ssize_t lp_write_interrupt(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#endif -{ - struct inode *inode = file->f_dentry->d_inode; - unsigned long total_bytes_written = 0; - unsigned int flags; - long timeout; - int rc; - int dev = MINOR(inode->i_rdev); - - do { - lp_table[dev]->do_print = 0; /* disable lp_interrupt() */ - lp_table[dev]->bytes_written = 0; /* init buffer read-pointer */ - lp_error = 0; - lp_table[dev]->copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); - if (copy_from_user(lp_table[dev]->lp_buffer, buf, - lp_table[dev]->copy_size)) - return -EFAULT; - while (lp_table[dev]->copy_size) { - save_flags(flags); - cli(); /* no interrupts now */ - lp_table[dev]->do_print = 1; /* enable lp_interrupt() */ - if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) { - ++lp_table[dev]->bytes_written; - --lp_table[dev]->copy_size; - lp_error = 0; - } else { /* something went wrong */ - lp_table[dev]->do_print = 0; /* disable lp_interrupt() */ - lp_error = 1; /* printer caused error */ - } - if (lp_error) { - - /* something blocked printing, so we don't want to sleep too long, - in case we have to rekick the interrupt */ - - timeout = LP_TIMEOUT_POLLED; - } else { - timeout = LP_TIMEOUT_INTERRUPT; - } - - interruptible_sleep_on_timeout(&lp_table[dev]->lp_wait_q, timeout); - restore_flags(flags); - - /* we're up again and running. we first disable lp_interrupt(), then - check what happened meanwhile */ - - lp_table[dev]->do_print = 0; - rc = total_bytes_written + lp_table[dev]->bytes_written; - - if (signal_pending(current)) { - if (rc == 0) - rc = -EINTR; - return rc; - } - if (lp_error) { - - /* an error has occurred, maybe in lp_interrupt(). - figure out the type of error, exit on request or if nothing has - been printed at all. */ - - if (lp_table[dev]->lp_has_pout(dev)) { - printk(KERN_NOTICE "lp%d: paper-out\n",dev); - if (!rc) rc = -ENOSPC; - } else if (!lp_table[dev]->lp_is_online(dev)) { - printk(KERN_NOTICE "lp%d: off-line\n",dev); - if (!rc) rc = -EIO; - } else if (lp_table[dev]->lp_is_busy(dev)) { - printk(KERN_NOTICE "lp%d: on fire\n",dev); - if (!rc) rc = -EIO; - } - if (lp_table[dev]->flags & LP_ABORT) - return rc; - } - /* check if our buffer was completely printed, if not, most likely - an unsolved error blocks the printer. As we can`t do anything - against, we start all over again. Else we set the read-pointer - of the buffer and count the printed characters */ - - if (!lp_table[dev]->copy_size) { - total_bytes_written += lp_table[dev]->bytes_written; - buf += lp_table[dev]->bytes_written; - count -= lp_table[dev]->bytes_written; - } - } - } while (count > 0); - return total_bytes_written; -} -#else -void (*lp_interrupt)() = NULL; -#endif - -#if WHICH_DRIVER != FORCE_INTERRUPT -#if WHICH_DRIVER == FORCE_POLLING -static ssize_t lp_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#else -static ssize_t lp_write_polled(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#endif -{ - struct inode *inode = file->f_dentry->d_inode; - char *temp = buf; - int dev = MINOR(inode->i_rdev); - -#ifdef LP_DEBUG - if (time_after(jiffies, lp_last_call + lp_table[dev]->time)) { - lp_total_chars = 0; - lp_max_count = 1; - } - lp_last_call = jiffies; -#endif - - temp = buf; - while (count > 0) { - int c; - if (get_user(c, temp)) - return -EFAULT; - if (lp_char_polled(c, dev)) { - /* only update counting vars if character was printed */ - count--; temp++; -#ifdef LP_DEBUG - lp_total_chars++; -#endif - } else { /* if printer timed out */ - unsigned long timeout = LP_TIMEOUT_POLLED; - int error = 0; - if (lp_table[dev]->lp_has_pout(dev)) { - printk(KERN_NOTICE "lp%d: out of paper\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -ENOSPC; - } else if (!lp_table[dev]->lp_is_online(dev)) { - printk(KERN_NOTICE "lp%d: off-line\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -EIO; - } else - /* not offline or out of paper. on fire? */ - if (lp_table[dev]->lp_is_busy(dev)) { - printk(KERN_NOTICE "lp%d: on fire\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -EIO; - } - else - timeout = lp_table[dev]->time; - - /* check for signals before going to sleep */ - if (error == 0 && signal_pending(current)) - error = -EINTR; - if (error) { - if (temp != buf) - return temp-buf; - else - return error; - } - -#ifdef LP_DEBUG - printk("lp sleeping at %d characters for %d jiffies\n", - lp_total_chars, timeout); - lp_total_chars = 0; -#endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(timeout); - } - } - return temp - buf; -} -#endif - -unsigned int lp_irq = 0; - -#if WHICH_DRIVER == PREFER_INTERRUPT -static ssize_t lp_write(struct file *file, const char *buf, size_t count, - loff_t *ppos) -{ - if (lp_irq) - return lp_write_interrupt(file, buf, count, ppos); - else - return lp_write_polled(file, buf, count, ppos); -} -#endif - -static long long lp_lseek(struct file * file, long long offset, int origin) -{ - return -ESPIPE; -} - -static int lp_open(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev); - int ret; - - MOD_INC_USE_COUNT; - - ret = -ENODEV; - if (dev >= MAX_LP) - goto out_err; - - if (!lp_table[dev]) { - char modname[30]; - - sprintf(modname, "char-major-%d-%d", LP_MAJOR, dev); - request_module(modname); - } - if (!lp_table[dev]) - goto out_err; - if (!(lp_table[dev]->flags & LP_EXIST)) - goto out_err; - ret = -EBUSY; - if (lp_table[dev]->flags & LP_BUSY) - goto out_err; - - lp_table[dev]->flags |= LP_BUSY; - - ret = lp_table[dev]->lp_open(dev); - if (ret != 0) { - lp_table[dev]->flags &= ~LP_BUSY; - goto out_err; - } - return ret; - -out_err: - MOD_DEC_USE_COUNT; - return ret; -} - -static int lp_release(struct inode *inode, struct file *file) -{ - int dev =MINOR(inode->i_rdev); - - lp_table[dev]->flags &= ~LP_BUSY; - lp_table[dev]->lp_release(dev); - MOD_DEC_USE_COUNT; - return 0; -} - - -static int lp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - unsigned int minor = MINOR(inode->i_rdev); - int retval = -ENODEV; - -#ifdef LP_DEBUG - printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg); -#endif - if (minor >= max_lp) - goto out; - if (!(lp_table[minor]->flags & LP_EXIST)) - goto out; - retval = 0; - switch (cmd) { - case LPTIME: - lp_table[minor]->time = arg; - break; - case LPCHAR: - lp_table[minor]->chars = arg; - break; - case LPABORT: - if (arg) - lp_table[minor]->flags |= LP_ABORT; - else - lp_table[minor]->flags &= ~LP_ABORT; - break; - case LPWAIT: - lp_table[minor]->wait = arg; - break; - case LPSETIRQ: - case LPGETIRQ: - retval = lp_irq; - break; - default: - retval = -EINVAL; - if (lp_table[minor]->lp_ioctl) - retval = lp_table[minor]->lp_ioctl(minor, cmd, arg); - } -out: - return retval; -} - - -static struct file_operations lp_fops = { - lp_lseek, - NULL, /* lp_read */ - lp_write, - NULL, /* lp_readdir */ - NULL, /* lp_poll */ - lp_ioctl, - NULL, /* lp_mmap */ - lp_open, - NULL, /* flush */ - lp_release -}; - -EXPORT_SYMBOL(lp_table); -EXPORT_SYMBOL(lp_irq); -EXPORT_SYMBOL(lp_interrupt); -EXPORT_SYMBOL(register_parallel); -EXPORT_SYMBOL(unregister_parallel); - -__initfunc(int lp_m68k_init(void)) -{ - extern char m68k_debug_device[]; - - if (!strcmp( m68k_debug_device, "par" )) - return -EBUSY; - - if (register_chrdev(LP_MAJOR,"lp", &lp_fops)) { - printk(KERN_ERR "unable to get major %d for line printer\n", LP_MAJOR); - return -ENXIO; - } - -#if WHICH_DRIVER == FORCE_POLLING - lp_irq = 0; - printk(KERN_INFO "lp_init: lp using polling driver\n"); -#else - - lp_irq = 1; - printk(KERN_INFO "lp_init: lp using interrupt driver\n"); -#endif - -#ifndef MODULE - lp_internal_init(); -#ifdef CONFIG_MULTIFACE_III_LP - lp_mfc_init(); -#endif -#endif - return 0; -} - -/* - * Currently we do not accept any lp-parameters, but that may change. - */ -__initfunc(void lp_setup(char *str, int *ints)) -{ -} - -#ifdef MODULE -int init_module(void) -{ - return lp_m68k_init(); -} - -void cleanup_module(void) -{ - unregister_chrdev(LP_MAJOR, "lp"); -} -#endif - -/* - * (un-)register for hardware drivers - * tab is an inititalised lp_struct, dev the desired minor - * if dev < 0, let the driver choose the first free minor - * if successful return the minor, else -1 - */ -int register_parallel(struct lp_struct *tab, int dev) -{ -if (dev < 0) { - dev = 0; - while ((dev < MAX_LP) && (lp_table[dev] != NULL)) - dev++; -} -if (dev > MAX_LP) - return -1; -if (lp_table[dev] != NULL) - return -1; -lp_table[dev] = tab; -printk(KERN_INFO "lp%d: %s at 0x%08lx\n", dev, tab->name, (long)tab->base); -return dev; -} - -#ifdef CONFIG_MODULES -void unregister_parallel(int dev) -{ -if ((dev < 0) || (dev > MAX_LP) || (lp_table[dev] == NULL)) - printk(KERN_ERR "WARNING: unregister_parallel for non-existant device ignored!\n"); -else - lp_table[dev] = NULL; -} -#endif diff --git a/drivers/char/mem.c b/drivers/char/mem.c index d83775f7c..ff3158e96 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -16,6 +16,8 @@ #include <linux/init.h> #include <linux/joystick.h> #include <linux/i2c.h> +#include <linux/raw.h> +#include <linux/capability.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -266,6 +268,7 @@ static ssize_t write_kmem(struct file * file, const char * buf, return do_write_mem(file, (void*)p, p, buf, count, ppos); } +#if !defined(CONFIG_PPC) && !defined(__mc68000__) static ssize_t read_port(struct file * file, char * buf, size_t count, loff_t *ppos) { @@ -303,6 +306,7 @@ static ssize_t write_port(struct file * file, const char * buf, *ppos = i; return tmp-buf; } +#endif static ssize_t read_null(struct file * file, char * buf, size_t count, loff_t *ppos) @@ -458,11 +462,18 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig) } } +static int open_port(struct inode * inode, struct file * filp) +{ + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + #define mmap_kmem mmap_mem #define zero_lseek null_lseek #define full_lseek null_lseek #define write_zero write_null #define read_full read_zero +#define open_mem open_port +#define open_kmem open_mem static struct file_operations mem_fops = { memory_lseek, @@ -472,7 +483,7 @@ static struct file_operations mem_fops = { NULL, /* mem_poll */ NULL, /* mem_ioctl */ mmap_mem, - NULL, /* no special open code */ + open_mem, NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ @@ -486,7 +497,7 @@ static struct file_operations kmem_fops = { NULL, /* kmem_poll */ NULL, /* kmem_ioctl */ mmap_kmem, - NULL, /* no special open code */ + open_kmem, NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ @@ -506,6 +517,7 @@ static struct file_operations null_fops = { NULL /* fsync */ }; +#if !defined(CONFIG_PPC) && !defined(__mc68000__) static struct file_operations port_fops = { memory_lseek, read_port, @@ -514,11 +526,12 @@ static struct file_operations port_fops = { NULL, /* port_poll */ NULL, /* port_ioctl */ NULL, /* port_mmap */ - NULL, /* no special open code */ + open_port, NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; +#endif static struct file_operations zero_fops = { zero_lseek, @@ -597,11 +610,12 @@ static struct file_operations memory_fops = { NULL /* fsync */ }; -__initfunc(int chr_dev_init(void)) +int __init chr_dev_init(void) { if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); + raw_init(); #ifdef CONFIG_USB usb_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 024706dc6..e9eec975c 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -28,7 +28,7 @@ * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> * * Changes for kmod (from kerneld): - Cyrus Durgin <cider@speakeasy.org> + * Cyrus Durgin <cider@speakeasy.org> */ #include <linux/module.h> @@ -43,14 +43,13 @@ #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/init.h> -#ifdef CONFIG_APM -#include <linux/apm_bios.h> -#endif #include <linux/tty.h> #include <linux/selection.h> #include <linux/kmod.h> +#include "busmouse.h" + /* * Head entry for the doubly linked miscdevice list */ @@ -62,14 +61,7 @@ static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list } #define DYNAMIC_MINORS 64 /* like dynamic majors */ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; -extern int bus_mouse_init(void); -extern int qpmouse_init(void); -extern int ms_bus_mouse_init(void); -extern int atixl_busmouse_init(void); -extern int amiga_mouse_init(void); -extern int atari_mouse_init(void); -extern int sun_mouse_init(void); -extern int adb_mouse_init(void); +extern int psaux_init(void); #ifdef CONFIG_SGI_NEWPORT_GFX extern void gfx_register(void); #endif @@ -85,9 +77,12 @@ extern int ds1286_init(void); extern int dsp56k_init(void); extern int nvram_init(void); extern int radio_init(void); -extern void hfmodem_init(void); extern int pc110pad_init(void); extern int pmu_device_init(void); +extern int qpmouse_init(void); +extern int ds1620_init(void); +extern int nwbutton_init(void); +extern int nwflash_init(void); static int misc_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *private) @@ -184,7 +179,7 @@ int misc_deregister(struct miscdevice * misc) EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); -static struct proc_dir_entry *proc_misc; +static struct proc_dir_entry *proc_misc; int __init misc_init(void) { @@ -197,24 +192,6 @@ int __init misc_init(void) #if defined CONFIG_82C710_MOUSE qpmouse_init(); #endif -#ifdef CONFIG_MS_BUSMOUSE - ms_bus_mouse_init(); -#endif -#ifdef CONFIG_ATIXL_BUSMOUSE - atixl_busmouse_init(); -#endif -#ifdef CONFIG_AMIGAMOUSE - amiga_mouse_init(); -#endif -#ifdef CONFIG_ATARIMOUSE - atari_mouse_init(); -#endif -#ifdef CONFIG_SUN_MOUSE - sun_mouse_init(); -#endif -#ifdef CONFIG_ADBMOUSE - adb_mouse_init(); -#endif #ifdef CONFIG_PC110_PAD pc110pad_init(); #endif @@ -239,9 +216,6 @@ int __init misc_init(void) #ifdef CONFIG_DTLK dtlk_init(); #endif -#ifdef CONFIG_APM - apm_bios_init(); -#endif #ifdef CONFIG_H8 h8_init(); #endif @@ -263,18 +237,12 @@ int __init misc_init(void) #ifdef CONFIG_ATARI_DSP56K dsp56k_init(); #endif -#ifdef CONFIG_HFMODEM - hfmodem_init(); -#endif #ifdef CONFIG_NVRAM nvram_init(); #endif #ifdef CONFIG_MISC_RADIO radio_init(); #endif -#ifdef CONFIG_HFMODEM - hfmodem_init(); -#endif #ifdef CONFIG_PMAC_PBOOK pmu_device_init(); #endif @@ -284,6 +252,15 @@ int __init misc_init(void) #ifdef CONFIG_SGI_IP22 streamable_init (); #endif +#ifdef CONFIG_DS1620 + ds1620_init(); +#endif +#ifdef CONFIG_NWBUTTON + nwbutton_init(); +#endif +#ifdef CONFIG_NWFLASH + nwflash_init(); +#endif if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c index f580be0bb..753c9d460 100644 --- a/drivers/char/msbusmouse.c +++ b/drivers/char/msbusmouse.c @@ -27,6 +27,9 @@ * * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> + * * version 0.3b */ @@ -35,7 +38,7 @@ #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/sched.h> -#include <linux/busmouse.h> +#include <linux/logibusmouse.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/miscdevice.h> @@ -48,14 +51,16 @@ #include <asm/system.h> #include <asm/irq.h> -static struct mouse_status mouse; +#include "busmouse.h" + +static int msedev; static int mouse_irq = MOUSE_IRQ; #ifdef MODULE MODULE_PARM(mouse_irq, "i"); #endif -__initfunc(void msmouse_setup(char *str, int *ints)) +void __init msmouse_setup(char *str, int *ints) { if (ints[0] > 0) mouse_irq=ints[1]; @@ -81,35 +86,17 @@ static void ms_mouse_interrupt(int irq, void *dev_id, struct pt_regs * regs) outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT); outb((inb(MS_MSE_DATA_PORT) & 0xdf), MS_MSE_DATA_PORT); - if (dx != 0 || dy != 0 || buttons != mouse.buttons || ((~buttons) & 0x07)) { - add_mouse_randomness((buttons << 16) + (dy << 8) + dx); - mouse.buttons = buttons; - mouse.dx += dx; - mouse.dy += dy; - mouse.ready = 1; - wake_up_interruptible(&mouse.wait); - if (mouse.fasyncptr) - kill_fasync(mouse.fasyncptr, SIGIO); - } -} - -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - - retval = fasync_helper(fd, filp, on, &mouse.fasyncptr); - if (retval < 0) - return retval; - return 0; + /* why did the original have: + * if (dx != 0 || dy != 0 || buttons != mouse.buttons || + * ((~buttons) & 0x07)) + * ^^^^^^^^^^^^^^^^^^^ this? + */ + busmouse_add_movementbuttons(msedev, dx, -dy, buttons); } static int release_mouse(struct inode * inode, struct file * file) { - fasync_mouse(-1, file, 0); - if (--mouse.active) - return 0; MS_MSE_INT_OFF(); - mouse.ready = 0; free_irq(mouse_irq, NULL); MOD_DEC_USE_COUNT; return 0; @@ -117,86 +104,24 @@ static int release_mouse(struct inode * inode, struct file * file) static int open_mouse(struct inode * inode, struct file * file) { - if (!mouse.present) - return -EINVAL; - if (mouse.active++) - return 0; - if (request_irq(mouse_irq, ms_mouse_interrupt, 0, "MS Busmouse", NULL)) { - mouse.active--; + if (request_irq(mouse_irq, ms_mouse_interrupt, 0, "MS Busmouse", NULL)) return -EBUSY; - } - mouse.ready = mouse.dx = mouse.dy = 0; - mouse.buttons = 0x80; + outb(MS_MSE_START, MS_MSE_CONTROL_PORT); MOD_INC_USE_COUNT; MS_MSE_INT_ON(); return 0; } -static ssize_t write_mouse(struct file * file, - const char * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static ssize_t read_mouse(struct file * file, - char * buffer, size_t count, loff_t *ppos) -{ - int i, dx, dy; - - if (count < 3) - return -EINVAL; - if (!mouse.ready) - return -EAGAIN; - put_user(mouse.buttons | 0x80, buffer); - dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx; - dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy; - put_user((char)dx, buffer + 1); - put_user((char)dy, buffer + 2); - for (i = 3; i < count; i++) - put_user(0x00, buffer + i); - mouse.dx -= dx; - mouse.dy += dy; - mouse.ready = 0; - return i; -} - -static unsigned int mouse_poll(struct file *file, poll_table * wait) -{ - poll_wait(file, &mouse.wait, wait); - if (mouse.ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations ms_bus_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -static struct miscdevice ms_bus_mouse = { - MICROSOFT_BUSMOUSE, "msbusmouse", &ms_bus_mouse_fops +static struct busmouse msbusmouse = { + MICROSOFT_BUSMOUSE, "msbusmouse", open_mouse, release_mouse, 0 }; -__initfunc(int ms_bus_mouse_init(void)) +int __init ms_bus_mouse_init(void) { + int present = 0; int mse_byte, i; - mouse.present = mouse.active = mouse.ready = 0; - mouse.buttons = 0x80; - mouse.dx = mouse.dy = 0; - init_waitqueue_head(&mouse.wait); - if (check_region(MS_MSE_CONTROL_PORT, 0x04)) return -ENODEV; @@ -207,32 +132,30 @@ __initfunc(int ms_bus_mouse_init(void)) for (i = 0; i < 4; i++) { if (inb_p(MS_MSE_SIGNATURE_PORT) == 0xde) { if (inb_p(MS_MSE_SIGNATURE_PORT) == mse_byte) - mouse.present = 1; + present = 1; else - mouse.present = 0; + present = 0; } else - mouse.present = 0; + present = 0; } } - if (mouse.present == 0) + if (present == 0) return -EIO; MS_MSE_INT_OFF(); request_region(MS_MSE_CONTROL_PORT, 0x04, "MS Busmouse"); - printk(KERN_INFO "Microsoft BusMouse detected and installed.\n"); - misc_register(&ms_bus_mouse); - return 0; + msedev = register_busmouse(&msbusmouse); + if (msedev < 0) + printk(KERN_WARNING "Unable to register msbusmouse driver.\n"); + else + printk(KERN_INFO "Microsoft BusMouse detected and installed.\n"); + return msedev < 0 ? msedev : 0; } -#ifdef MODULE -int init_module(void) +void ms_bus_mouse_exit(void) { - return ms_bus_mouse_init(); -} - -void cleanup_module(void) -{ - misc_deregister(&ms_bus_mouse); + unregister_busmouse(msedev); release_region(MS_MSE_CONTROL_PORT, 0x04); } -#endif +module_init(ms_bus_mouse_init) +module_exit(ms_bus_mouse_exit) diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index 5791e149e..2990efbb6 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -795,9 +795,8 @@ static int msp3410d_thread(void *data) UNLOCK_I2C_BUS(msp->bus); /* wait 1 sec */ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - schedule(); + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); if (signal_pending(current)) goto done; if (msp->restart) { @@ -992,16 +991,16 @@ msp3400c_mixer_llseek(struct file *file, loff_t offset, int origin) } static /*const*/ struct file_operations msp3400c_mixer_fops = { - &msp3400c_mixer_llseek, + msp3400c_mixer_llseek, NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ - &msp3400c_mixer_ioctl, + msp3400c_mixer_ioctl, NULL, /* mmap */ - &msp3400c_mixer_open, + msp3400c_mixer_open, NULL, - &msp3400c_mixer_release, + msp3400c_mixer_release, NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c new file mode 100644 index 000000000..a2097967e --- /dev/null +++ b/drivers/char/n_r3964.c @@ -0,0 +1,1488 @@ +/* r3964 linediscipline for linux + * + * ----------------------------------------------------------- + * Copyright by + * Philips Automation Projects + * Kassel (Germany) + * http://www.pap-philips.de + * ----------------------------------------------------------- + * This software may be used and distributed according to the terms of + * the GNU Public License, incorporated herein by reference. + * + * Author: + * L. Haag + * + * $Log: r3964.c,v $ + * Revision 1.7 1999/28/08 11:41:50 dwmw2 + * Port to 2.3 kernel + * + * Revision 1.6 1998/09/30 00:40:40 dwmw2 + * Fixed compilation on 2.0.x kernels + * Updated to newly registered tty-ldisc number 9 + * + * Revision 1.5 1998/09/04 21:57:36 dwmw2 + * Signal handling bug fixes, port to 2.1.x. + * + * Revision 1.4 1998/04/02 20:26:59 lhaag + * select, blocking, ... + * + * Revision 1.3 1998/02/12 18:58:43 root + * fixed some memory leaks + * calculation of checksum characters + * + * Revision 1.2 1998/02/07 13:03:34 root + * ioctl read_telegram + * + * Revision 1.1 1998/02/06 19:21:03 root + * Initial revision + * + * + */ +#define R3964_VERSION "1.7" + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/string.h> /* used in new tty drivers */ +#include <linux/signal.h> /* used in new tty drivers */ +#include <linux/ioctl.h> +#include <linux/n_r3964.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <asm/uaccess.h> + + +//#define DEBUG_QUEUE + +/* Log successfull handshake and protocol operations */ +//#define DEBUG_PROTO_S + +/* Log handshake and protocol errors: */ +//#define DEBUG_PROTO_E + +/* Log Linediscipline operations (open, close, read, write...): */ +//#define DEBUG_LDISC + +/* Log module and memory operations (init, cleanup; kmalloc, kfree): */ +//#define DEBUG_MODUL + +/* Macro helpers for debug output: */ +#define TRACE(format, args...) printk("r3964: " format "\n" , ## args); + +#ifdef DEBUG_MODUL +#define TRACE_M(format, args...) printk("r3964: " format "\n" , ## args); +#else +#define TRACE_M(fmt, arg...) /**/ +#endif + +#ifdef DEBUG_PROTO_S +#define TRACE_PS(format, args...) printk("r3964: " format "\n" , ## args); +#else +#define TRACE_PS(fmt, arg...) /**/ +#endif + +#ifdef DEBUG_PROTO_E +#define TRACE_PE(format, args...) printk("r3964: " format "\n" , ## args); +#else +#define TRACE_PE(fmt, arg...) /**/ +#endif + +#ifdef DEBUG_LDISC +#define TRACE_L(format, args...) printk("r3964: " format "\n" , ## args); +#else +#define TRACE_L(fmt, arg...) /**/ +#endif + +#ifdef DEBUG_QUEUE +#define TRACE_Q(format, args...) printk("r3964: " format "\n" , ## args); +#else +#define TRACE_Q(fmt, arg...) /**/ +#endif + +void cleanup_module(void); + +static void on_timer_1(void*); +static void on_timer_2(void*); +static void add_tx_queue(struct r3964_info *, struct r3964_block_header *); +static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code); +static void put_char(struct r3964_info *pInfo, unsigned char ch); +static void trigger_transmit(struct r3964_info *pInfo); +static void retry_transmit(struct r3964_info *pInfo); +static void transmit_block(struct r3964_info *pInfo); +static void receive_char(struct r3964_info *pInfo, const unsigned char c); +static void receive_error(struct r3964_info *pInfo, const char flag); +static void on_timeout(struct r3964_info *pInfo); +static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg); +static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char *buf); +static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, + int error_code, struct r3964_block_header *pBlock); +static struct r3964_message* remove_msg(struct r3964_info *pInfo, + struct r3964_client_info *pClient); +static void remove_client_block(struct r3964_info *pInfo, + struct r3964_client_info *pClient); + +static int r3964_open(struct tty_struct *tty); +static void r3964_close(struct tty_struct *tty); +static int r3964_read(struct tty_struct *tty, struct file *file, + unsigned char *buf, unsigned int nr); +static int r3964_write(struct tty_struct * tty, struct file * file, + const unsigned char * buf, unsigned int nr); +static int r3964_ioctl(struct tty_struct * tty, struct file * file, + unsigned int cmd, unsigned long arg); +static void r3964_set_termios(struct tty_struct *tty, struct termios * old); +static unsigned int r3964_poll(struct tty_struct * tty, struct file * file, + struct poll_table_struct *wait); +static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count); +static int r3964_receive_room(struct tty_struct *tty); + +static struct tty_ldisc tty_ldisc_N_R3964 = { + TTY_LDISC_MAGIC, /* magic */ + "R3964", /* name */ + 0, /* num */ + 0, /* flags */ + r3964_open, /* open */ + r3964_close, /* close */ + 0, /* flush_buffer */ + 0, /* chars_in_buffer */ + r3964_read, /* read */ + r3964_write, /* write */ + r3964_ioctl, /* ioctl */ + r3964_set_termios, /* set_termios */ + r3964_poll, /* poll */ + r3964_receive_buf, /* receive_buf */ + r3964_receive_room, /* receive_room */ + 0 /* write_wakeup */ +}; + + + +static void dump_block(const unsigned char *block, unsigned int length) +{ + unsigned int i,j; + char linebuf[16*3+1]; + + for(i=0;i<length;i+=16) + { + for(j=0;(j<16) && (j+i<length);j++) + { + sprintf(linebuf+3*j,"%02x ",block[i+j]); + } + linebuf[3*j]='\0'; + TRACE_PS("%s",linebuf); + } +} + + + + +/************************************************************* + * Driver initialisation + *************************************************************/ + + +/************************************************************* + * Module support routines + *************************************************************/ + +#ifdef MODULE + +#define r3964_init init_module + +void cleanup_module(void) +{ + int status; + + TRACE_M ("cleanup_module()"); + + status=tty_register_ldisc(N_R3964, NULL); + + if(status!=0) + { + printk(KERN_ERR "r3964: error unregistering linediscipline: %d\n", status); + } + else + { + TRACE_L("linediscipline successfully unregistered"); + } + +} + + +#endif /* MODULE */ + +static int __init r3964_init(void) +{ + int status; + + printk ("r3964: Philips r3964 Driver V%s\n", R3964_VERSION); + + /* + * Register the tty line discipline + */ + + status = tty_register_ldisc (N_R3964, &tty_ldisc_N_R3964); + if (status == 0) + { + TRACE_L("line discipline %d registered", N_R3964); + TRACE_L("flags=%x num=%x", tty_ldisc_N_R3964.flags, + tty_ldisc_N_R3964.num); + TRACE_L("open=%x", (int)tty_ldisc_N_R3964.open); + TRACE_L("tty_ldisc_N_R3964 = %x", (int)&tty_ldisc_N_R3964); + } + else + { + printk (KERN_ERR "r3964: error registering line discipline: %d\n", status); + } + return status; +} + +#ifndef MODULE +__initcall (r3964_init); +#endif + +/************************************************************* + * Protocol implementation routines + *************************************************************/ + +static void on_timer_1(void *arg) +{ + struct r3964_info *pInfo = (struct r3964_info *)arg; + + if(pInfo->count_down) + { + if(!--pInfo->count_down) + { + on_timeout(pInfo); + } + } + queue_task(&pInfo->bh_2, &tq_timer); +} + +static void on_timer_2(void *arg) +{ + struct r3964_info *pInfo = (struct r3964_info *)arg; + + if(pInfo->count_down) + { + if(!--pInfo->count_down) + { + on_timeout(pInfo); + } + } + queue_task(&pInfo->bh_1, &tq_timer); +} + +static void add_tx_queue(struct r3964_info *pInfo, struct r3964_block_header *pHeader) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + pHeader->next = NULL; + + if(pInfo->tx_last == NULL) + { + pInfo->tx_first = pInfo->tx_last = pHeader; + } + else + { + pInfo->tx_last->next = pHeader; + pInfo->tx_last = pHeader; + } + + restore_flags(flags); + + TRACE_Q("add_tx_queue %x, length %d, tx_first = %x", + (int)pHeader, pHeader->length, (int)pInfo->tx_first ); +} + +static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code) +{ + struct r3964_block_header *pHeader; + unsigned long flags; +#ifdef DEBUG_QUEUE + struct r3964_block_header *pDump; +#endif + + pHeader = pInfo->tx_first; + + if(pHeader==NULL) + return; + +#ifdef DEBUG_QUEUE + printk("r3964: remove_from_tx_queue: %x, length %d - ", + (int)pHeader, (int)pHeader->length ); + for(pDump=pHeader;pDump;pDump=pDump->next) + printk("%x ", (int)pDump); + printk("\n"); +#endif + + + if(pHeader->owner) + { + if(error_code) + { + add_msg(pHeader->owner, R3964_MSG_ACK, 0, + error_code, NULL); + } + else + { + add_msg(pHeader->owner, R3964_MSG_ACK, pHeader->length, + error_code, NULL); + } + wake_up_interruptible (&pInfo->read_wait); + } + + save_flags(flags); + cli(); + + pInfo->tx_first = pHeader->next; + if(pInfo->tx_first==NULL) + { + pInfo->tx_last = NULL; + } + + restore_flags(flags); + + kfree(pHeader); + TRACE_M("remove_from_tx_queue - kfree %x",(int)pHeader); + + TRACE_Q("remove_from_tx_queue: tx_first = %x, tx_last = %x", + (int)pInfo->tx_first, (int)pInfo->tx_last ); +} + +static void add_rx_queue(struct r3964_info *pInfo, struct r3964_block_header *pHeader) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + pHeader->next = NULL; + + if(pInfo->rx_last == NULL) + { + pInfo->rx_first = pInfo->rx_last = pHeader; + } + else + { + pInfo->rx_last->next = pHeader; + pInfo->rx_last = pHeader; + } + pInfo->blocks_in_rx_queue++; + + restore_flags(flags); + + TRACE_Q("add_rx_queue: %x, length = %d, rx_first = %x, count = %d", + (int)pHeader, pHeader->length, + (int)pInfo->rx_first, pInfo->blocks_in_rx_queue); +} + +static void remove_from_rx_queue(struct r3964_info *pInfo, + struct r3964_block_header *pHeader) +{ + unsigned long flags; + struct r3964_block_header *pFind; + + if(pHeader==NULL) + return; + + TRACE_Q("remove_from_rx_queue: rx_first = %x, rx_last = %x, count = %d", + (int)pInfo->rx_first, (int)pInfo->rx_last, pInfo->blocks_in_rx_queue ); + TRACE_Q("remove_from_rx_queue: %x, length %d", + (int)pHeader, (int)pHeader->length ); + + save_flags(flags); + cli(); + + if(pInfo->rx_first == pHeader) + { + /* Remove the first block in the linked list: */ + pInfo->rx_first = pHeader->next; + + if(pInfo->rx_first==NULL) + { + pInfo->rx_last = NULL; + } + pInfo->blocks_in_rx_queue--; + } + else + { + /* Find block to remove: */ + for(pFind=pInfo->rx_first; pFind; pFind=pFind->next) + { + if(pFind->next == pHeader) + { + /* Got it. */ + pFind->next = pHeader->next; + pInfo->blocks_in_rx_queue--; + if(pFind->next==NULL) + { + /* Oh, removed the last one! */ + pInfo->rx_last = pFind; + } + break; + } + } + } + + restore_flags(flags); + + kfree(pHeader); + TRACE_M("remove_from_rx_queue - kfree %x",(int)pHeader); + + TRACE_Q("remove_from_rx_queue: rx_first = %x, rx_last = %x, count = %d", + (int)pInfo->rx_first, (int)pInfo->rx_last, pInfo->blocks_in_rx_queue ); +} + +static void put_char(struct r3964_info *pInfo, unsigned char ch) +{ + struct tty_struct *tty = pInfo->tty; + + if(tty==NULL) + return; + + if(tty->driver.put_char) + { + tty->driver.put_char(tty, ch); + } + pInfo->bcc ^= ch; +} + +static void flush(struct r3964_info *pInfo) +{ + struct tty_struct *tty = pInfo->tty; + + if(tty==NULL) + return; + + if(tty->driver.flush_chars) + { + tty->driver.flush_chars(tty); + } +} + +static void trigger_transmit(struct r3964_info *pInfo) +{ + unsigned long flags; + + + save_flags(flags); + cli(); + + if((pInfo->state == R3964_IDLE) && (pInfo->tx_first!=NULL)) + { + pInfo->state = R3964_TX_REQUEST; + pInfo->count_down = R3964_TO_QVZ; + pInfo->nRetry=0; + pInfo->flags &= ~R3964_ERROR; + + restore_flags(flags); + + TRACE_PS("trigger_transmit - sent STX"); + + put_char(pInfo, STX); + flush(pInfo); + + pInfo->bcc = 0; + } + else + { + restore_flags(flags); + } +} + +static void retry_transmit(struct r3964_info *pInfo) +{ + if(pInfo->nRetry<R3964_MAX_RETRIES) + { + TRACE_PE("transmission failed. Retry #%d", + pInfo->nRetry); + pInfo->bcc = 0; + put_char(pInfo, STX); + flush(pInfo); + pInfo->state = R3964_TX_REQUEST; + pInfo->count_down = R3964_TO_QVZ; + pInfo->nRetry++; + } + else + { + TRACE_PE("transmission failed after %d retries", + R3964_MAX_RETRIES); + + remove_from_tx_queue(pInfo, R3964_TX_FAIL); + + put_char(pInfo, NAK); + flush(pInfo); + pInfo->state = R3964_IDLE; + + trigger_transmit(pInfo); + } +} + + +static void transmit_block(struct r3964_info *pInfo) +{ + struct tty_struct *tty = pInfo->tty; + struct r3964_block_header *pBlock = pInfo->tx_first; + int room=0; + + if((tty==NULL) || (pBlock==NULL)) + { + return; + } + + if(tty->driver.write_room) + room=tty->driver.write_room(tty); + + TRACE_PS("transmit_block %x, room %d, length %d", + (int)pBlock, room, pBlock->length); + + while(pInfo->tx_position < pBlock->length) + { + if(room<2) + break; + + if(pBlock->data[pInfo->tx_position]==DLE) + { + /* send additional DLE char: */ + put_char(pInfo, DLE); + } + put_char(pInfo, pBlock->data[pInfo->tx_position++]); + + room--; + } + + if((pInfo->tx_position == pBlock->length) && (room>=3)) + { + put_char(pInfo, DLE); + put_char(pInfo, ETX); + if(pInfo->flags & R3964_BCC) + { + put_char(pInfo, pInfo->bcc); + } + pInfo->state = R3964_WAIT_FOR_TX_ACK; + pInfo->count_down = R3964_TO_QVZ; + } + flush(pInfo); +} + +static void on_receive_block(struct r3964_info *pInfo) +{ + unsigned int length; + struct r3964_client_info *pClient; + struct r3964_block_header *pBlock; + + length=pInfo->rx_position; + + /* compare byte checksum characters: */ + if(pInfo->flags & R3964_BCC) + { + if(pInfo->bcc!=pInfo->last_rx) + { + TRACE_PE("checksum error - got %x but expected %x", + pInfo->last_rx, pInfo->bcc); + pInfo->flags |= R3964_CHECKSUM; + } + } + + /* check for errors (parity, overrun,...): */ + if(pInfo->flags & R3964_ERROR) + { + TRACE_PE("on_receive_block - transmission failed error %x", + pInfo->flags & R3964_ERROR); + + put_char(pInfo, NAK); + flush(pInfo); + if(pInfo->nRetry<R3964_MAX_RETRIES) + { + pInfo->state=R3964_WAIT_FOR_RX_REPEAT; + pInfo->count_down = R3964_TO_RX_PANIC; + pInfo->nRetry++; + } + else + { + TRACE_PE("on_receive_block - failed after max retries"); + pInfo->state=R3964_IDLE; + } + return; + } + + + /* received block; submit DLE: */ + put_char(pInfo, DLE); + flush(pInfo); + pInfo->count_down=0; + TRACE_PS(" rx success: got %d chars", length); + + /* prepare struct r3964_block_header: */ + pBlock = kmalloc(length+sizeof(struct r3964_block_header), GFP_KERNEL); + TRACE_M("on_receive_block - kmalloc %x",(int)pBlock); + + if(pBlock==NULL) + return; + + pBlock->length = length; + pBlock->data = ((unsigned char*)pBlock)+sizeof(struct r3964_block_header); + pBlock->locks = 0; + pBlock->next = NULL; + pBlock->owner = NULL; + + memcpy(pBlock->data, pInfo->rx_buf, length); + + /* queue block into rx_queue: */ + add_rx_queue(pInfo, pBlock); + + /* notify attached client processes: */ + for(pClient=pInfo->firstClient; pClient; pClient=pClient->next) + { + if(pClient->sig_flags & R3964_SIG_DATA) + { + add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, pBlock); + } + } + wake_up_interruptible (&pInfo->read_wait); + + pInfo->state = R3964_IDLE; + + trigger_transmit(pInfo); +} + + +static void receive_char(struct r3964_info *pInfo, const unsigned char c) +{ + switch(pInfo->state) + { + case R3964_TX_REQUEST: + if(c==DLE) + { + TRACE_PS("TX_REQUEST - got DLE"); + + pInfo->state = R3964_TRANSMITTING; + pInfo->tx_position = 0; + + transmit_block(pInfo); + } + else if(c==STX) + { + if(pInfo->nRetry==0) + { + TRACE_PE("TX_REQUEST - init conflict"); + if(pInfo->priority == R3964_SLAVE) + { + goto start_receiving; + } + } + else + { + TRACE_PE("TX_REQUEST - secondary init conflict!?" + " Switching to SLAVE mode for next rx."); + goto start_receiving; + } + } + else + { + TRACE_PE("TX_REQUEST - char != DLE: %x", c); + retry_transmit(pInfo); + } + break; + case R3964_TRANSMITTING: + if(c==NAK) + { + TRACE_PE("TRANSMITTING - got NAK"); + retry_transmit(pInfo); + } + else + { + TRACE_PE("TRANSMITTING - got illegal char"); + + pInfo->state = R3964_WAIT_ZVZ_BEFORE_TX_RETRY; + pInfo->count_down = R3964_TO_ZVZ; + } + break; + case R3964_WAIT_FOR_TX_ACK: + if(c==DLE) + { + TRACE_PS("WAIT_FOR_TX_ACK - got DLE"); + remove_from_tx_queue(pInfo, R3964_OK); + + pInfo->state = R3964_IDLE; + trigger_transmit(pInfo); + } + else + { + retry_transmit(pInfo); + } + break; + case R3964_WAIT_FOR_RX_REPEAT: + /* FALLTROUGH */ + case R3964_IDLE: + if(c==STX) + { + /* Prevent rx_queue from overflow: */ + if(pInfo->blocks_in_rx_queue >= R3964_MAX_BLOCKS_IN_RX_QUEUE) + { + TRACE_PE("IDLE - got STX but no space in rx_queue!"); + pInfo->state=R3964_WAIT_FOR_RX_BUF; + pInfo->count_down = R3964_TO_NO_BUF; + break; + } +start_receiving: + /* Ok, start receiving: */ + TRACE_PS("IDLE - got STX"); + pInfo->rx_position = 0; + pInfo->last_rx = 0; + pInfo->flags &= ~R3964_ERROR; + pInfo->state=R3964_RECEIVING; + pInfo->count_down = R3964_TO_ZVZ; + pInfo->nRetry = 0; + put_char(pInfo, DLE); + flush(pInfo); + pInfo->bcc = 0; + } + break; + case R3964_RECEIVING: + if(pInfo->rx_position < RX_BUF_SIZE) + { + pInfo->bcc ^= c; + + if(c==DLE) + { + if(pInfo->last_rx==DLE) + { + pInfo->last_rx = 0; + goto char_to_buf; + } + pInfo->last_rx = DLE; + break; + } + else if((c==ETX) && (pInfo->last_rx==DLE)) + { + if(pInfo->flags & R3964_BCC) + { + pInfo->state = R3964_WAIT_FOR_BCC; + pInfo->count_down = R3964_TO_ZVZ; + } + else + { + on_receive_block(pInfo); + } + } + else + { + pInfo->last_rx = c; +char_to_buf: + pInfo->rx_buf[pInfo->rx_position++] = c; + pInfo->count_down = R3964_TO_ZVZ; + } + } + /* else: overflow-msg? BUF_SIZE>MTU; should not happen? */ + break; + case R3964_WAIT_FOR_BCC: + pInfo->last_rx = c; + on_receive_block(pInfo); + break; + } +} + +static void receive_error(struct r3964_info *pInfo, const char flag) +{ + switch (flag) + { + case TTY_NORMAL: + break; + case TTY_BREAK: + TRACE_PE("received break") + pInfo->flags |= R3964_BREAK; + break; + case TTY_PARITY: + TRACE_PE("parity error") + pInfo->flags |= R3964_PARITY; + break; + case TTY_FRAME: + TRACE_PE("frame error") + pInfo->flags |= R3964_FRAME; + break; + case TTY_OVERRUN: + TRACE_PE("frame overrun") + pInfo->flags |= R3964_OVERRUN; + break; + default: + TRACE_PE("receive_error - unknown flag %d", flag); + pInfo->flags |= R3964_UNKNOWN; + break; + } +} + +static void on_timeout(struct r3964_info *pInfo) +{ + switch(pInfo->state) + { + case R3964_TX_REQUEST: + TRACE_PE("TX_REQUEST - timeout"); + retry_transmit(pInfo); + break; + case R3964_WAIT_ZVZ_BEFORE_TX_RETRY: + put_char(pInfo, NAK); + flush(pInfo); + retry_transmit(pInfo); + break; + case R3964_WAIT_FOR_TX_ACK: + TRACE_PE("WAIT_FOR_TX_ACK - timeout"); + retry_transmit(pInfo); + break; + case R3964_WAIT_FOR_RX_BUF: + TRACE_PE("WAIT_FOR_RX_BUF - timeout"); + put_char(pInfo, NAK); + flush(pInfo); + pInfo->state=R3964_IDLE; + break; + case R3964_RECEIVING: + TRACE_PE("RECEIVING - timeout after %d chars", + pInfo->rx_position); + put_char(pInfo, NAK); + flush(pInfo); + pInfo->state=R3964_IDLE; + break; + case R3964_WAIT_FOR_RX_REPEAT: + TRACE_PE("WAIT_FOR_RX_REPEAT - timeout"); + pInfo->state=R3964_IDLE; + break; + case R3964_WAIT_FOR_BCC: + TRACE_PE("WAIT_FOR_BCC - timeout"); + put_char(pInfo, NAK); + flush(pInfo); + pInfo->state=R3964_IDLE; + break; + } +} + +static struct r3964_client_info *findClient( + struct r3964_info *pInfo, pid_t pid) +{ + struct r3964_client_info *pClient; + + for(pClient=pInfo->firstClient; pClient; pClient=pClient->next) + { + if(pClient->pid == pid) + { + return pClient; + } + } + return NULL; +} + +static int enable_signals(struct r3964_info *pInfo, pid_t pid, int arg) +{ + struct r3964_client_info *pClient; + struct r3964_client_info **ppClient; + struct r3964_message *pMsg; + + if((arg & R3964_SIG_ALL)==0) + { + /* Remove client from client list */ + for(ppClient=&pInfo->firstClient; *ppClient; ppClient=&(*ppClient)->next) + { + pClient = *ppClient; + + if(pClient->pid == pid) + { + TRACE_PS("removing client %d from client list", pid); + *ppClient = pClient->next; + while(pClient->msg_count) + { + pMsg=remove_msg(pInfo, pClient); + if(pMsg) + { + kfree(pMsg); + TRACE_M("enable_signals - msg kfree %x",(int)pMsg); + } + } + kfree(pClient); + TRACE_M("enable_signals - kfree %x",(int)pClient); + return 0; + } + } + return -EINVAL; + } + else + { + pClient=findClient(pInfo, pid); + if(pClient) + { + /* update signal options */ + pClient->sig_flags=arg; + } + else + { + /* add client to client list */ + pClient=kmalloc(sizeof(struct r3964_client_info), GFP_KERNEL); + TRACE_M("enable_signals - kmalloc %x",(int)pClient); + if(pClient==NULL) + return -ENOMEM; + + TRACE_PS("add client %d to client list", pid); + pClient->sig_flags=arg; + pClient->pid = pid; + pClient->next=pInfo->firstClient; + pClient->first_msg = NULL; + pClient->last_msg = NULL; + pClient->next_block_to_read = NULL; + pClient->msg_count = 0; + pInfo->firstClient=pClient; + } + } + + return 0; +} + +static int read_telegram(struct r3964_info *pInfo, pid_t pid, unsigned char *buf) +{ + struct r3964_client_info *pClient; + struct r3964_block_header *block; + + if(!buf) + { + return -EINVAL; + } + + pClient=findClient(pInfo,pid); + if(pClient==NULL) + { + return -EINVAL; + } + + block=pClient->next_block_to_read; + if(!block) + { + return 0; + } + else + { + if (copy_to_user (buf, block->data, block->length)) + return -EFAULT; + + remove_client_block(pInfo, pClient); + return block->length; + } + + return -EINVAL; +} + +static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, + int error_code, struct r3964_block_header *pBlock) +{ + struct r3964_message *pMsg; + unsigned long flags; + + if(pClient->msg_count<R3964_MAX_MSG_COUNT-1) + { +queue_the_message: + + save_flags(flags); + cli(); + + pMsg = kmalloc(sizeof(struct r3964_message), GFP_KERNEL); + TRACE_M("add_msg - kmalloc %x",(int)pMsg); + if(pMsg==NULL) + return; + + pMsg->msg_id = msg_id; + pMsg->arg = arg; + pMsg->error_code = error_code; + pMsg->block = pBlock; + pMsg->next = NULL; + + if(pClient->last_msg==NULL) + { + pClient->first_msg=pClient->last_msg=pMsg; + } + else + { + pClient->last_msg->next = pMsg; + pClient->last_msg=pMsg; + } + + pClient->msg_count++; + + if(pBlock!=NULL) + { + pBlock->locks++; + } + restore_flags(flags); + } + else + { + if((pClient->last_msg->msg_id == R3964_MSG_ACK) + && (pClient->last_msg->error_code==R3964_OVERFLOW)) + { + pClient->last_msg->arg++; + TRACE_PE("add_msg - inc prev OVERFLOW-msg"); + } + else + { + msg_id = R3964_MSG_ACK; + arg = 0; + error_code = R3964_OVERFLOW; + pBlock = NULL; + TRACE_PE("add_msg - queue OVERFLOW-msg"); + goto queue_the_message; + } + } + /* Send SIGIO signal to client process: */ + if(pClient->sig_flags & R3964_USE_SIGIO) + { + kill_proc(pClient->pid, SIGIO, 1); + } +} + +static struct r3964_message *remove_msg(struct r3964_info *pInfo, + struct r3964_client_info *pClient) +{ + struct r3964_message *pMsg=NULL; + unsigned long flags; + + if(pClient->first_msg) + { + save_flags(flags); + cli(); + + pMsg = pClient->first_msg; + pClient->first_msg = pMsg->next; + if(pClient->first_msg==NULL) + { + pClient->last_msg = NULL; + } + + pClient->msg_count--; + if(pMsg->block) + { + remove_client_block(pInfo, pClient); + pClient->next_block_to_read = pMsg->block; + } + restore_flags(flags); + } + return pMsg; +} + +static void remove_client_block(struct r3964_info *pInfo, + struct r3964_client_info *pClient) +{ + struct r3964_block_header *block; + + TRACE_PS("remove_client_block PID %d", pClient->pid); + + block=pClient->next_block_to_read; + if(block) + { + block->locks--; + if(block->locks==0) + { + remove_from_rx_queue(pInfo, block); + } + } + pClient->next_block_to_read = NULL; +} + + +/************************************************************* + * Line discipline routines + *************************************************************/ + +static int r3964_open(struct tty_struct *tty) +{ + struct r3964_info *pInfo; + + MOD_INC_USE_COUNT; + + TRACE_L("open"); + TRACE_L("tty=%x, PID=%d, disc_data=%x", + (int)tty, current->pid, (int)tty->disc_data); + + pInfo=kmalloc(sizeof(struct r3964_info), GFP_KERNEL); + TRACE_M("r3964_open - info kmalloc %x",(int)pInfo); + + if(!pInfo) + { + printk(KERN_ERR "r3964: failed to alloc info structure\n"); + return -ENOMEM; + } + + pInfo->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); + TRACE_M("r3964_open - rx_buf kmalloc %x",(int)pInfo->rx_buf); + + if(!pInfo->rx_buf) + { + printk(KERN_ERR "r3964: failed to alloc receive buffer\n"); + kfree(pInfo); + TRACE_M("r3964_open - info kfree %x",(int)pInfo); + return -ENOMEM; + } + + pInfo->tx_buf = kmalloc(TX_BUF_SIZE, GFP_KERNEL); + TRACE_M("r3964_open - tx_buf kmalloc %x",(int)pInfo->tx_buf); + + if(!pInfo->tx_buf) + { + printk(KERN_ERR "r3964: failed to alloc transmit buffer\n"); + kfree(pInfo->rx_buf); + TRACE_M("r3964_open - rx_buf kfree %x",(int)pInfo->rx_buf); + kfree(pInfo); + TRACE_M("r3964_open - info kfree %x",(int)pInfo); + return -ENOMEM; + } + + pInfo->tty = tty; + init_waitqueue_head (&pInfo->read_wait); + pInfo->priority = R3964_MASTER; + pInfo->rx_first = pInfo->rx_last = NULL; + pInfo->tx_first = pInfo->tx_last = NULL; + pInfo->rx_position = 0; + pInfo->tx_position = 0; + pInfo->last_rx = 0; + pInfo->blocks_in_rx_queue = 0; + pInfo->firstClient=NULL; + pInfo->state=R3964_IDLE; + pInfo->flags = R3964_DEBUG; + pInfo->count_down = 0; + pInfo->nRetry = 0; + + tty->disc_data = pInfo; + + /* + * Add 'on_timer' to timer task queue + * (will be called from timer bh) + */ + pInfo->bh_1.next = NULL; + pInfo->bh_1.sync = 0; + pInfo->bh_1.routine = &on_timer_1; + pInfo->bh_1.data = pInfo; + + pInfo->bh_2.next = NULL; + pInfo->bh_2.sync = 0; + pInfo->bh_2.routine = &on_timer_2; + pInfo->bh_2.data = pInfo; + + queue_task(&pInfo->bh_1, &tq_timer); + + return 0; +} + +static void r3964_close(struct tty_struct *tty) +{ + struct tq_struct *tq, *prev; + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + struct r3964_client_info *pClient, *pNext; + struct r3964_message *pMsg; + struct r3964_block_header *pHeader, *pNextHeader; + unsigned long flags; + + TRACE_L("close"); + + /* + * Make sure that our task queue isn't activated. If it + * is, take it out of the linked list. + */ + save_flags(flags); + cli(); + + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if ((tq == &pInfo->bh_1) || (tq==&pInfo->bh_2)) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } + } + restore_flags(flags); + + /* Remove client-structs and message queues: */ + pClient=pInfo->firstClient; + while(pClient) + { + pNext=pClient->next; + while(pClient->msg_count) + { + pMsg=remove_msg(pInfo, pClient); + if(pMsg) + { + kfree(pMsg); + TRACE_M("r3964_close - msg kfree %x",(int)pMsg); + } + } + kfree(pClient); + TRACE_M("r3964_close - client kfree %x",(int)pClient); + pClient=pNext; + } + /* Remove jobs from tx_queue: */ + save_flags(flags); + cli(); + pHeader=pInfo->tx_first; + pInfo->tx_first=pInfo->tx_last=NULL; + restore_flags(flags); + + while(pHeader) + { + pNextHeader=pHeader->next; + kfree(pHeader); + pHeader=pNextHeader; + } + + /* Free buffers: */ + wake_up_interruptible(&pInfo->read_wait); + kfree(pInfo->rx_buf); + TRACE_M("r3964_close - rx_buf kfree %x",(int)pInfo->rx_buf); + kfree(pInfo->tx_buf); + TRACE_M("r3964_close - tx_buf kfree %x",(int)pInfo->tx_buf); + kfree(pInfo); + TRACE_M("r3964_close - info kfree %x",(int)pInfo); + + MOD_DEC_USE_COUNT; +} + +static int r3964_read(struct tty_struct *tty, struct file *file, + unsigned char *buf, unsigned int nr) +{ + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + struct r3964_client_info *pClient; + struct r3964_message *pMsg; + struct r3964_client_message theMsg; + DECLARE_WAITQUEUE (wait, current); + + int pid = current->pid; + int count; + + TRACE_L("read()"); + + pClient=findClient(pInfo, pid); + if(pClient) + { + pMsg = remove_msg(pInfo, pClient); + if(pMsg==NULL) + { + /* no messages available. */ + if (file->f_flags & O_NONBLOCK) + { + return -EAGAIN; + } + /* block until there is a message: */ + add_wait_queue(&pInfo->read_wait, &wait); +repeat: + pMsg = remove_msg(pInfo, pClient); + current->state = TASK_INTERRUPTIBLE; + if (!pMsg && !signal_pending(current)) + { + schedule(); + goto repeat; + } + current->state = TASK_RUNNING; + remove_wait_queue(&pInfo->read_wait, &wait); + } + + /* If we still haven't got a message, we must have been signalled */ + + if (!pMsg) return -EINTR; + + /* deliver msg to client process: */ + theMsg.msg_id = pMsg->msg_id; + theMsg.arg = pMsg->arg; + theMsg.error_code = pMsg->error_code; + count = sizeof(struct r3964_client_message); + + kfree(pMsg); + TRACE_M("r3964_read - msg kfree %x",(int)pMsg); + + if (copy_to_user(buf,&theMsg, count)) + return -EFAULT; + + TRACE_PS("read - return %d", count); + return count; + } + return -EPERM; +} + +static int r3964_write(struct tty_struct * tty, struct file * file, + const unsigned char *data, unsigned int count) +{ + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + struct r3964_block_header *pHeader; + struct r3964_client_info *pClient; + unsigned char *new_data; + int status; + int pid; + + TRACE_L("write request, %d characters", count); +/* + * Verify the pointers + */ + + if(!pInfo) + return -EIO; + + status = verify_area (VERIFY_READ, data, count); + if (status != 0) + { + return status; + } + +/* + * Ensure that the caller does not wish to send too much. + */ + if (count > R3964_MTU) + { + if (pInfo->flags & R3964_DEBUG) + { + TRACE_L (KERN_WARNING + "r3964_write: truncating user packet " + "from %u to mtu %d", count, R3964_MTU); + } + count = R3964_MTU; + } +/* + * Allocate a buffer for the data and fetch it from the user space. + */ + new_data = kmalloc (count+sizeof(struct r3964_block_header), GFP_KERNEL); + TRACE_M("r3964_write - kmalloc %x",(int)new_data); + if (new_data == NULL) { + if (pInfo->flags & R3964_DEBUG) + { + printk (KERN_ERR + "r3964_write: no memory\n"); + } + return -ENOSPC; + } + + pHeader = (struct r3964_block_header *)new_data; + pHeader->data = new_data + sizeof(struct r3964_block_header); + pHeader->length = count; + pHeader->locks = 0; + pHeader->owner = NULL; + + pid=current->pid; + + pClient=findClient(pInfo, pid); + if(pClient) + { + pHeader->owner = pClient; + } + + copy_from_user (pHeader->data, data, count); /* We already verified this */ + + if(pInfo->flags & R3964_DEBUG) + { + dump_block(pHeader->data, count); + } + +/* + * Add buffer to transmit-queue: + */ + add_tx_queue(pInfo, pHeader); + trigger_transmit(pInfo); + + return 0; +} + +static int r3964_ioctl(struct tty_struct * tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + if(pInfo==NULL) + return -EINVAL; + switch(cmd) + { + case R3964_ENABLE_SIGNALS: + return enable_signals(pInfo, current->pid, arg); + case R3964_SETPRIORITY: + if(arg<R3964_MASTER || arg>R3964_SLAVE) + return -EINVAL; + pInfo->priority = arg & 0xff; + return 0; + case R3964_USE_BCC: + if(arg) + pInfo->flags |= R3964_BCC; + else + pInfo->flags &= ~R3964_BCC; + return 0; + case R3964_READ_TELEGRAM: + return read_telegram(pInfo, current->pid, (unsigned char *)arg); + default: + return -ENOIOCTLCMD; + } +} + +static void r3964_set_termios(struct tty_struct *tty, struct termios * old) +{ + TRACE_L("set_termios"); +} + +static unsigned int r3964_poll(struct tty_struct * tty, struct file * file, + struct poll_table_struct *wait) +{ + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + int pid=current->pid; + struct r3964_client_info *pClient; + struct r3964_message *pMsg=NULL; + unsigned int flags; + int result = POLLOUT; + + TRACE_L("POLL"); + + pClient=findClient(pInfo,pid); + if(pClient) + { + poll_wait(file, &pInfo->read_wait, wait); + save_flags(flags); + cli(); + pMsg=pClient->first_msg; + restore_flags(flags); + if(pMsg) + result |= POLLIN | POLLRDNORM; + } + else + { + result = -EINVAL; + } + return result; +} + +static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data; + const unsigned char *p; + char *f, flags = 0; + int i; + + for (i=count, p = cp, f = fp; i; i--, p++) { + if (f) + flags = *f++; + if(flags==TTY_NORMAL) + { + receive_char(pInfo, *p); + } + else + { + receive_error(pInfo, flags); + } + + } +} + +static int r3964_receive_room(struct tty_struct *tty) +{ + TRACE_L("receive_room"); + return -1; +} + diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index af4daecfb..d95cda30e 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -948,7 +948,7 @@ do_it_again: /* This statement must be first before checking for input so that any interrupt will set the state back to TASK_RUNNING. */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (((minimum - (b - buf)) < tty->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) @@ -1073,7 +1073,7 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, add_wait_queue(&tty->write_wait, &wait); while (1) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { retval = -ERESTARTSYS; break; diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 11e7eadcf..dde61fe77 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -413,7 +413,7 @@ static struct miscdevice nvram_dev = { }; -__initfunc(int nvram_init(void)) +int __init nvram_init(void) { /* First test whether the driver should init at all */ if (!CHECK_DRIVER_INIT()) @@ -479,7 +479,7 @@ static void pc_set_checksum( void ) #ifdef CONFIG_PROC_FS static char *floppy_types[] = { - "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M" + "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", "3.5'' 2.88M" }; static char *gfx_types[] = { @@ -521,14 +521,14 @@ static int pc_proc_infos( unsigned char *nvram, char *buffer, int *len, PRINT_PROC( "HD 0 type : " ); type = nvram[4] >> 4; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[11] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[11] : type ); else PRINT_PROC( "none\n" ); PRINT_PROC( "HD 1 type : " ); type = nvram[4] & 0x0f; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[12] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[12] : type ); else PRINT_PROC( "none\n" ); diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 080cbdef8..2acab7d8f 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -17,7 +17,7 @@ #include <linux/config.h> -#include <asm/spinlock.h> +#include <linux/spinlock.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> @@ -512,11 +512,14 @@ void pckbd_leds(unsigned char leds) #endif /* for "kbd-reset" cmdline param */ -void __init kbd_reset_setup(char *str, int *ints) +static int __init kbd_reset_setup(char *str) { kbd_startup_reset = 1; + return 1; } +__setup("kbd-reset", kbd_reset_setup); + #define KBD_NO_DATA (-1) /* No data */ #define KBD_BAD_DATA (-2) /* Parity or other error */ @@ -694,8 +697,6 @@ static char * __init initialize_kbd(void) void __init pckbd_init_hw(void) { - kbd_request_region(); - /* Flush any pending input. */ kbd_clear_input(); @@ -875,7 +876,7 @@ static ssize_t read_aux(struct file * file, char * buffer, return -EAGAIN; add_wait_queue(&queue->proc_list, &wait); repeat: - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (queue_empty() && !signal_pending(current)) { schedule(); goto repeat; diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c index 7a7d73c06..fa69f138f 100644 --- a/drivers/char/pcwd.c +++ b/drivers/char/pcwd.c @@ -59,6 +59,7 @@ #include <linux/watchdog.h> #include <linux/init.h> #include <linux/proc_fs.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -96,6 +97,7 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; static int current_readport, revision, temp_panic; static int is_open, initial_status, supports_temp, mode_debug; +static spinlock_t io_lock; /* * PCWD_CHECKCARD @@ -246,7 +248,12 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, return i ? -EFAULT : 0; case WDIOC_GETSTATUS: - cdat = inb(current_readport); + spin_lock(&io_lock); + if (revision == PCWD_REVISION_A) + cdat = inb(current_readport); + else + cdat = inb(current_readport + 1 ); + spin_unlock(&io_lock); rv = 0; if (revision == PCWD_REVISION_A) @@ -309,7 +316,9 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, rv = 0; if ((supports_temp) && (mode_debug == 0)) { + spin_lock(&io_lock); rv = inb(current_readport); + spin_unlock(&io_lock); if(put_user(rv, (int*) arg)) return -EFAULT; } else if(put_user(rv, (int*) arg)) @@ -324,9 +333,11 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, if (rv & WDIOS_DISABLECARD) { + spin_lock(&io_lock); outb_p(0xA5, current_readport + 3); outb_p(0xA5, current_readport + 3); cdat = inb_p(current_readport + 2); + spin_unlock(&io_lock); if ((cdat & 0x10) == 0) { printk("pcwd: Could not disable card.\n"); @@ -338,8 +349,10 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, if (rv & WDIOS_ENABLECARD) { + spin_lock(&io_lock); outb_p(0x00, current_readport + 3); cdat = inb_p(current_readport + 2); + spin_unlock(&io_lock); if (cdat & 0x10) { printk("pcwd: Could not enable card.\n"); @@ -388,7 +401,11 @@ static int pcwd_open(struct inode *ino, struct file *filep) MOD_INC_USE_COUNT; /* Enable the port */ if (revision == PCWD_REVISION_C) - outb_p(0x00, current_readport + 3); + { + spin_lock(&io_lock); + outb_p(0x00, current_readport + 3); + spin_unlock(&io_lock); + } is_open = 1; return(0); case TEMP_MINOR: @@ -402,7 +419,7 @@ static int pcwd_open(struct inode *ino, struct file *filep) static ssize_t pcwd_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - unsigned short c = inb(current_readport); + unsigned short c; unsigned char cp; /* Can't seek (pread) on this device */ @@ -415,6 +432,8 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count, * Convert metric to Fahrenheit, since this was * the decided 'standard' for this return value. */ + + c = inb(current_readport); cp = (c * 9 / 5) + 32; if(copy_to_user(buf, &cp, 1)) return -EFAULT; @@ -433,8 +452,10 @@ static int pcwd_close(struct inode *ino, struct file *filep) #ifndef CONFIG_WATCHDOG_NOWAYOUT /* Disable the board */ if (revision == PCWD_REVISION_C) { + spin_lock(&io_lock); outb_p(0xA5, current_readport + 3); outb_p(0xA5, current_readport + 3); + spin_unlock(&io_lock); } #endif } @@ -449,11 +470,15 @@ static inline void get_support(void) static inline int get_revision(void) { + int r = PCWD_REVISION_C; + + spin_lock(&io_lock); if ((inb(current_readport + 2) == 0xFF) || (inb(current_readport + 3) == 0xFF)) - return(PCWD_REVISION_A); + r=PCWD_REVISION_A; + spin_unlock(&io_lock); - return(PCWD_REVISION_C); + return r; } static int __init send_command(int cmd) @@ -581,7 +606,8 @@ int __init pcwatchdog_init(void) #endif { int i, found = 0; - + spin_lock_init(&io_lock); + revision = PCWD_REVISION_A; printk("pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index dda54324e..36db3299d 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -79,6 +79,7 @@ #include <asm/io.h> #include <asm/uaccess.h> #include <asm/bitops.h> +#include <asm/semaphore.h> #define VERSION "1.6.1" @@ -356,7 +357,7 @@ static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct chan memoff(info); } sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if(tty_hung_up_p(filp) || (info->asyncflags & ASYNC_INITIALIZED) == 0) { if(info->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; @@ -894,7 +895,7 @@ static void pcxe_flush_chars(struct tty_struct *tty) * Driver setup function when linked into the kernel to optionally parse multible * "digi="-lines and initialize the driver at boot time. No probing. */ -__initfunc(void pcxx_setup(char *str, int *ints)) +void __init pcxx_setup(char *str, int *ints) { struct board_info board; @@ -1085,7 +1086,7 @@ __initfunc(void pcxx_setup(char *str, int *ints)) * function to initialize the driver with the given parameters, which are either * the default values from this file or the parameters given at boot. */ -__initfunc(int pcxe_init(void)) +int __init pcxe_init(void) { ulong memory_seg=0, memory_size=0; int lowwater, enabled_cards=0, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L; @@ -1588,8 +1589,8 @@ load_fep: ch->blocked_open = 0; ch->callout_termios = pcxe_callout.init_termios; ch->normal_termios = pcxe_driver.init_termios; - ch->open_wait = 0; - ch->close_wait = 0; + init_waitqueue_head(ch->open_wait); + init_waitqueue_head(ch->close_wait); ch->asyncflags = 0; } diff --git a/drivers/char/planb.c b/drivers/char/planb.c index 1c6bf655b..524ab7be5 100644 --- a/drivers/char/planb.c +++ b/drivers/char/planb.c @@ -383,7 +383,7 @@ static void __planb_wait(struct planb *pb) add_wait_queue(&pb->lockq, &wait); repeat: - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); if (pb->lock) { schedule(); goto repeat; @@ -1533,7 +1533,8 @@ static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg) DEBUG("PlanB: IOCTL VIDIOCSFBUF\n"); - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) return -EPERM; if (copy_from_user(&v, arg,sizeof(v))) return -EFAULT; @@ -2359,7 +2360,7 @@ static void release_planb(void) int init_module(void) { #else -__initfunc(int init_planbs(struct video_init *unused)) +int __init init_planbs(struct video_init *unused) { #endif int i; diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 881e521ad..c210b0785 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -11,7 +11,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * A /dev/parportxy device node represents an arbitrary device ('y') + * A /dev/parportx device node represents an arbitrary device * on port 'x'. The following operations are possible: * * open do nothing, set up default IEEE 1284 protocol to be COMPAT @@ -21,6 +21,8 @@ * CLAIM (register device first time) parport_claim_or_block * RELEASE parport_release * SETMODE set the IEEE 1284 protocol to use for read/write + * SETPHASE set the IEEE 1284 phase of a particular mode. Not to be + * confused with ioctl(fd, SETPHASER, &stun). ;-) * DATADIR data_forward / data_reverse * WDATA write_data * RDATA read_data @@ -30,6 +32,8 @@ * RSTATUS read_status * NEGOT parport_negotiate * YIELD parport_yield_blocking + * WCTLONIRQ on interrupt, set control lines + * CLRIRQ clear (and return) interrupt count * read/write read or write in current IEEE 1284 protocol * select wait for interrupt (in readfds) */ @@ -50,16 +54,15 @@ #define min(a,b) ((a) < (b) ? (a) : (b)) #endif -/* The device minor encodes the parport number and (arbitrary) - * pardevice number as (port << 4) | dev. */ -#define PP_PORT(minor) ((minor >> 4) & 0xf) -#define PP_DEV(minor) ((minor) & 0xf) - struct pp_struct { struct pardevice * pdev; wait_queue_head_t irq_wait; - int mode; + atomic_t irqc; unsigned int flags; + int irqresponse; + unsigned char irqctl; + struct ieee1284_info state; + struct ieee1284_info saved_state; }; /* pp_struct.flags bitfields */ @@ -71,111 +74,31 @@ struct pp_struct { #define PP_BUFFER_SIZE 256 #define PARDEVICE_MAX 8 -static struct pp_struct pp_table[PARPORT_MAX][PARDEVICE_MAX]; - -static loff_t pp_lseek (struct file * file, long long offset, int origin) -{ - return -ESPIPE; -} - -/* This looks a bit like parport_read. The difference is that we don't - * determine the mode to use from the port data, but rather from the - * mode the driver told us to use. */ -static ssize_t do_read (struct pp_struct *pp, void *buf, size_t len) +static inline void enable_irq (struct pp_struct *pp) { - size_t (*fn) (struct parport *, void *, size_t, int); struct parport *port = pp->pdev->port; - - switch (pp->mode) { - case IEEE1284_MODE_COMPAT: - /* This is a write-only mode. */ - return -EIO; - - case IEEE1284_MODE_NIBBLE: - fn = port->ops->nibble_read_data; - break; - - case IEEE1284_MODE_BYTE: - fn = port->ops->byte_read_data; - break; - - case IEEE1284_MODE_EPP: - fn = port->ops->epp_read_data; - break; - - case IEEE1284_MODE_ECP: - case IEEE1284_MODE_ECPRLE: - fn = port->ops->ecp_read_data; - break; - - case IEEE1284_MODE_ECPSWE: - fn = parport_ieee1284_ecp_read_data; - break; - - default: - printk (KERN_DEBUG "%s: unknown mode 0x%02x\n", - pp->pdev->name, pp->mode); - return -EINVAL; - } - - return (*fn) (port, buf, len, 0); + port->ops->enable_irq (port); } -/* This looks a bit like parport_write. The difference is that we don't - * determine the mode to use from the port data, but rather from the - * mode the driver told us to use. */ -static ssize_t do_write (struct pp_struct *pp, const void *buf, size_t len) +static loff_t pp_lseek (struct file * file, long long offset, int origin) { - size_t (*fn) (struct parport *, const void *, size_t, int); - struct parport *port = pp->pdev->port; - - switch (pp->mode) { - case IEEE1284_MODE_NIBBLE: - case IEEE1284_MODE_BYTE: - /* Read-only modes. */ - return -EIO; - - case IEEE1284_MODE_COMPAT: - fn = port->ops->compat_write_data; - break; - - case IEEE1284_MODE_EPP: - fn = port->ops->epp_write_data; - break; - - case IEEE1284_MODE_ECP: - case IEEE1284_MODE_ECPRLE: - fn = port->ops->ecp_write_data; - break; - - case IEEE1284_MODE_ECPSWE: - fn = parport_ieee1284_ecp_write_data; - break; - - default: - printk (KERN_DEBUG "%s: unknown mode 0x%02x\n", - pp->pdev->name, pp->mode); - return -EINVAL; - } - - return (*fn) (port, buf, len, 0); + return -ESPIPE; } static ssize_t pp_read (struct file * file, char * buf, size_t count, loff_t * ppos) { unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; char * kbuffer; ssize_t bytes_read = 0; ssize_t got = 0; - if (!(pp_table[portnum][dev].flags & PP_CLAIMED)) { + if (!(pp->flags & PP_CLAIMED)) { /* Don't have the port claimed */ - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL); @@ -185,16 +108,16 @@ static ssize_t pp_read (struct file * file, char * buf, size_t count, while (bytes_read < count) { ssize_t need = min(count - bytes_read, PP_BUFFER_SIZE); - got = do_read (&pp_table[portnum][dev], kbuffer, need); + got = parport_read (pp->pdev->port, kbuffer, need); - if (got < 0) { + if (got <= 0) { if (!bytes_read) bytes_read = got; break; } - if (copy_to_user (kbuffer, buf + bytes_read, got)) { + if (copy_to_user (buf + bytes_read, kbuffer, got)) { bytes_read = -EFAULT; break; } @@ -212,6 +135,7 @@ static ssize_t pp_read (struct file * file, char * buf, size_t count, } kfree (kbuffer); + enable_irq (pp); return bytes_read; } @@ -219,17 +143,16 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, loff_t * ppos) { unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; char * kbuffer; ssize_t bytes_written = 0; ssize_t wrote; - if (!(pp_table[portnum][dev].flags & PP_CLAIMED)) { + if (!(pp->flags & PP_CLAIMED)) { /* Don't have the port claimed */ - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL); @@ -244,7 +167,7 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, break; } - wrote = do_write (&pp_table[portnum][dev], kbuffer, n); + wrote = parport_write (pp->pdev->port, kbuffer, n); if (wrote < 0) { if (!bytes_written) @@ -265,19 +188,25 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, } kfree (kbuffer); + enable_irq (pp); return bytes_written; } static void pp_irq (int irq, void * private, struct pt_regs * unused) { struct pp_struct * pp = (struct pp_struct *) private; + + if (pp->irqresponse) { + parport_write_control (pp->pdev->port, pp->irqctl); + pp->irqresponse = 0; + } + + atomic_inc (&pp->irqc); wake_up_interruptible (&pp->irq_wait); } -static int register_device (int minor) +static int register_device (int minor, struct pp_struct *pp) { - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); struct parport * port; struct pardevice * pdev = NULL; char *name; @@ -287,10 +216,10 @@ static int register_device (int minor) if (name == NULL) return -ENOMEM; - sprintf (name, CHRDEV "%02x", minor); + sprintf (name, CHRDEV "%x", minor); port = parport_enumerate (); /* FIXME: use attach/detach */ - while (port && port->number != portnum) + while (port && port->number != minor) port = port->next; if (!port) { @@ -299,9 +228,9 @@ static int register_device (int minor) return -ENXIO; } - fl = (pp_table[portnum][dev].flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; + fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; pdev = parport_register_device (port, name, NULL, NULL, pp_irq, fl, - &pp_table[portnum][dev]); + pp); if (!pdev) { printk (KERN_WARNING "%s: failed to register device!\n", name); @@ -309,45 +238,68 @@ static int register_device (int minor) return -ENXIO; } - pp_table[portnum][dev].pdev = pdev; + pp->pdev = pdev; printk (KERN_DEBUG "%s: registered pardevice\n", name); return 0; } +static enum ieee1284_phase init_phase (int mode) +{ + switch (mode & ~(IEEE1284_DEVICEID + | IEEE1284_ADDR)) { + case IEEE1284_MODE_NIBBLE: + case IEEE1284_MODE_BYTE: + return IEEE1284_PH_REV_IDLE; + } + return IEEE1284_PH_FWD_IDLE; +} + static int pp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; struct parport * port; /* First handle the cases that don't take arguments. */ if (cmd == PPCLAIM) { - if (pp_table[portnum][dev].flags & PP_CLAIMED) { + struct ieee1284_info *info; + + if (pp->flags & PP_CLAIMED) { printk (KERN_DEBUG CHRDEV - "%02x: you've already got it!\n", minor); + "%x: you've already got it!\n", minor); return -EINVAL; } /* Deferred device registration. */ - if (!pp_table[portnum][dev].pdev) { - int err = register_device (minor); + if (!pp->pdev) { + int err = register_device (minor, pp); if (err) return err; } - parport_claim_or_block (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].flags |= PP_CLAIMED; + parport_claim_or_block (pp->pdev); + pp->flags |= PP_CLAIMED; + + /* For interrupt-reporting to work, we need to be + * informed of each interrupt. */ + enable_irq (pp); + + /* We may need to fix up the state machine. */ + info = &pp->pdev->port->ieee1284; + pp->saved_state.mode = info->mode; + pp->saved_state.phase = info->phase; + info->mode = pp->state.mode; + info->phase = pp->state.phase; + return 0; } - port = pp_table[portnum][dev].pdev->port; if (cmd == PPEXCL) { - if (pp_table[portnum][dev].pdev) { - printk (KERN_DEBUG CHRDEV "%02x: too late for PPEXCL; " + if (pp->pdev) { + printk (KERN_DEBUG CHRDEV "%x: too late for PPEXCL; " "already registered\n", minor); - if (pp_table[portnum][dev].flags & PP_EXCL) + if (pp->flags & PP_EXCL) /* But it's not really an error. */ return 0; /* There's no chance of making the driver happy. */ @@ -356,22 +308,54 @@ static int pp_ioctl(struct inode *inode, struct file *file, /* Just remember to register the device exclusively * when we finally do the registration. */ - pp_table[portnum][dev].flags |= PP_EXCL; + pp->flags |= PP_EXCL; + return 0; + } + + if (cmd == PPSETMODE) { + int mode; + if (copy_from_user (&mode, (int *) arg, sizeof (mode))) + return -EFAULT; + /* FIXME: validate mode */ + pp->state.mode = mode; + pp->state.phase = init_phase (mode); + + if (pp->flags & PP_CLAIMED) { + pp->pdev->port->ieee1284.mode = mode; + pp->pdev->port->ieee1284.phase = pp->state.phase; + } + + return 0; + } + + if (cmd == PPSETPHASE) { + int phase; + if (copy_from_user (&phase, (int *) arg, sizeof (phase))) + return -EFAULT; + /* FIXME: validate phase */ + pp->state.phase = phase; + + if (pp->flags & PP_CLAIMED) + pp->pdev->port->ieee1284.phase = phase; + return 0; } /* Everything else requires the port to be claimed, so check * that now. */ - if ((pp_table[portnum][dev].flags & PP_CLAIMED) == 0) { - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + if ((pp->flags & PP_CLAIMED) == 0) { + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } + port = pp->pdev->port; switch (cmd) { + struct ieee1284_info *info; unsigned char reg; unsigned char mask; int mode; + int ret; case PPRSTATUS: reg = parport_read_status (port); @@ -389,19 +373,18 @@ static int pp_ioctl(struct inode *inode, struct file *file, sizeof (reg)); case PPYIELD: - parport_yield_blocking (pp_table[portnum][dev].pdev); + parport_yield_blocking (pp->pdev); return 0; case PPRELEASE: - parport_release (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].flags &= ~PP_CLAIMED; - return 0; - - case PPSETMODE: - if (copy_from_user (&mode, (int *) arg, sizeof (mode))) - return -EFAULT; - /* FIXME: validate mode */ - pp_table[portnum][dev].mode = mode; + /* Save the state machine's state. */ + info = &pp->pdev->port->ieee1284; + pp->state.mode = info->mode; + pp->state.phase = info->phase; + info->mode = pp->saved_state.mode; + info->phase = pp->saved_state.phase; + parport_release (pp->pdev); + pp->flags &= ~PP_CLAIMED; return 0; case PPWCONTROL: @@ -438,11 +421,38 @@ static int pp_ioctl(struct inode *inode, struct file *file, case PPNEGOT: if (copy_from_user (&mode, (int *) arg, sizeof (mode))) return -EFAULT; - /* FIXME: validate mode */ - return parport_negotiate (port, mode); + switch ((ret = parport_negotiate (port, mode))) { + case 0: break; + case -1: /* handshake failed, peripheral not IEEE 1284 */ + ret = -EIO; + break; + case 1: /* handshake succeeded, peripheral rejected mode */ + ret = -ENXIO; + break; + } + enable_irq (pp); + return ret; + + case PPWCTLONIRQ: + if (copy_from_user (®, (unsigned char *) arg, + sizeof (reg))) + return -EFAULT; + + /* Remember what to set the control lines to, for next + * time we get an interrupt. */ + pp->irqctl = reg; + pp->irqresponse = 1; + return 0; + + case PPCLRIRQ: + ret = atomic_read (&pp->irqc); + if (copy_to_user ((int *) arg, &ret, sizeof (ret))) + return -EFAULT; + atomic_sub (ret, &pp->irqc); + return 0; default: - printk (KERN_DEBUG CHRDEV "%02x: What? (cmd=0x%x\n", minor, + printk (KERN_DEBUG CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd); return -EINVAL; } @@ -454,24 +464,27 @@ static int pp_ioctl(struct inode *inode, struct file *file, static int pp_open (struct inode * inode, struct file * file) { unsigned int minor = MINOR (inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp; - if (portnum >= PARPORT_MAX) + if (minor >= PARPORT_MAX) return -ENXIO; - if (pp_table[portnum][dev].pdev) - return -EBUSY; + pp = kmalloc (GFP_KERNEL, sizeof (struct pp_struct)); + if (!pp) + return -ENOMEM; - pp_table[portnum][dev].mode = IEEE1284_MODE_COMPAT; - pp_table[portnum][dev].flags = 0; - init_waitqueue_head (&pp_table[portnum][dev].irq_wait); + pp->state.mode = IEEE1284_MODE_COMPAT; + pp->state.phase = init_phase (pp->state.mode); + pp->flags = 0; + atomic_set (&pp->irqc, 0); + init_waitqueue_head (&pp->irq_wait); /* Defer the actual device registration until the first claim. * That way, we know whether or not the driver wants to have * exclusive access to the port (PPEXCL). */ - pp_table[portnum][dev].pdev = NULL; + pp->pdev = NULL; + file->private_data = pp; MOD_INC_USE_COUNT; return 0; @@ -480,42 +493,46 @@ static int pp_open (struct inode * inode, struct file * file) static int pp_release (struct inode * inode, struct file * file) { unsigned int minor = MINOR (inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; - if (pp_table[portnum][dev].flags & PP_CLAIMED) { - parport_release (pp_table[portnum][dev].pdev); - printk (KERN_DEBUG CHRDEV "%02x: released pardevice because " + if (pp->flags & PP_CLAIMED) { + parport_release (pp->pdev); + printk (KERN_DEBUG CHRDEV "%x: released pardevice because " "user-space forgot\n", minor); } - if (pp_table[portnum][dev].pdev) { - kfree (pp_table[portnum][dev].pdev->name); - parport_unregister_device (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].pdev = NULL; - printk (KERN_DEBUG CHRDEV "%02x: unregistered pardevice\n", + if (pp->pdev) { + parport_unregister_device (pp->pdev); + kfree (pp->pdev->name); + pp->pdev = NULL; + printk (KERN_DEBUG CHRDEV "%x: unregistered pardevice\n", minor); } + kfree (pp); + MOD_DEC_USE_COUNT; return 0; } -#if 0 static unsigned int pp_poll (struct file * file, poll_table * wait) { - unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - poll_wait (file, &pp_table[minor].irq_wait, wait); - return 0; /* FIXME! Return value is wrong here */ + struct pp_struct *pp = file->private_data; + unsigned int mask = 0; + + if (atomic_read (&pp->irqc)) + mask |= POLLIN | POLLRDNORM; + + poll_wait (file, &pp->irq_wait, wait); + return mask; } -#endif static struct file_operations pp_fops = { pp_lseek, pp_read, pp_write, NULL, /* pp_readdir */ - NULL, /* pp_poll */ + pp_poll, pp_ioctl, NULL, /* pp_mmap */ pp_open, diff --git a/drivers/char/ppdev.h b/drivers/char/ppdev.h index f52d3c79d..976374aed 100644 --- a/drivers/char/ppdev.h +++ b/drivers/char/ppdev.h @@ -63,3 +63,12 @@ struct ppdev_frob_struct { /* Negotiate a particular IEEE 1284 mode. */ #define PPNEGOT _IOW(PP_IOCTL, 0x91, int) + +/* Set control lines when an interrupt occurs. */ +#define PPWCTLONIRQ _IOW(PP_IOCTL, 0x92, unsigned char) + +/* Clear (and return) interrupt count. */ +#define PPCLRIRQ _IOR(PP_IOCTL, 0x93, int) + +/* Set the IEEE 1284 phase that we're in (e.g. IEEE1284_PH_FWD_IDLE) */ +#define PPSETPHASE _IOW(PP_IOCTL, 0x94, int) diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 1e67c604e..5f35b24ad 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -334,7 +334,7 @@ static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios) tty->termios->c_cflag |= (CS8 | CREAD); } -__initfunc(int pty_init(void)) +int __init pty_init(void) { int i; diff --git a/drivers/char/q40_keyb.c b/drivers/char/q40_keyb.c new file mode 100644 index 000000000..7ee9818ef --- /dev/null +++ b/drivers/char/q40_keyb.c @@ -0,0 +1,454 @@ +/* + * linux/drivers/char/q40_keyb.c + * + */ + +#include <linux/config.h> + +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/signal.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/kbd_ll.h> +#include <linux/delay.h> +#include <linux/sysrq.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/malloc.h> + +#include <asm/keyboard.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/q40_master.h> +#include <asm/irq.h> +#include <asm/q40ints.h> + +/* Some configuration switches are present in the include file... */ + +#define KBD_REPORT_ERR + +/* Simple translation table for the SysRq keys */ + +#define SYSRQ_KEY 0x54 + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char q40kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* Q40 uses AT scancodes - no way to change it. so we have to translate ..*/ +/* 0x00 means not a valid entry or no conversion known */ + +unsigned static char q40cl[256] = +{/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, */ + 0x00,0x43,0x00,0x3f,0x3d,0x3b,0x3c,0x58,0x00,0x44,0x42,0x40,0x3e,0x0f,0x29,0x00, /* 0x00 - 0x0f */ + 0x00,0x38,0x2a,0x00,0x1d,0x10,0x02,0x00,0x00,0x00,0x2c,0x1f,0x1e,0x11,0x03,0x00, /* 0x10 - 0x1f */ + 0x00,0x2e,0x2d,0x20,0x12,0x05,0x04,0x00,0x21,0x39,0x2f,0x21,0x14,0x13,0x06,0x00, /* 0x20 - 0x2f 'f' is at 0x2b, what is 0x28 ???*/ + 0x00,0x31,0x30,0x23,0x22,0x15,0x07,0x00,0x24,0x00,0x32,0x24,0x16,0x08,0x09,0x00, /* 0x30 - 0x3f */ + 0x00,0x33,0x25,0x17,0x18,0x0b,0x0a,0x00,0x00,0x34,0x35,0x26,0x27,0x19,0x0c,0x00, /* 0x40 - 0x4f */ + 0x00,0x00,0x28,0x00,0x1a,0x0d,0x00,0x00,0x3a,0x36,0x1c,0x1b,0x00,0x2b,0x00,0x00, /* 0x50 - 0x5f*/ + 0x00,0x56,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00, /* 0x60 - 0x6f */ + 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x00, /* 0x70 - 0x7f */ + 0x00,0x00,0x00,0x41,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x80 - 0x8f 0x84/0x37 is SySrq*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x90 - 0x9f */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xa0 - 0xaf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xb0 - 0xbf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xc0 - 0xcf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xd0 - 0xdf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xe0 - 0xef */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xf0 - 0xff */ +}; + +/* another table, AT 0xe0 codes to PC 0xe0 codes, + 0xff special entry for SysRq - DROPPED right now */ +static unsigned char q40ecl[]= +{/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x00 - 0x0f*/ + 0x00,0x38,0x2a,0x00,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x10 - 0x1f */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x20 - 0x2f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x30 - 0x3f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x00,0x00, /* 0x40 - 0x4f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x1c,0x00,0x00,0x00,0x00,0x00, /* 0x50 - 0x5f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00, /* 0x60 - 0x6f*/ + 0x52,0x53,0x50,0x00,0x4d,0x48,0x00,0x00,0x00,0x00,0x51,0x00,0x00,0x49,0x00,0x00, /* 0x70 - 0x7f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x80 - 0x8f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x90 - 0x9f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xa0 - 0xaf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xb0 - 0xbf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xc0 - 0xcf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xd0 - 0xdf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xe0 - 0xef*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 /* 0xf0 - 0xff*/ +}; + + +spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; + + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +/* this can be changed using setkeys : */ +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned int prev_scancode = 0; /* remember E0, E1 */ + +int q40kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int q40kbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + + +#define disable_keyboard() +#define enable_keyboard() + + + + +int q40kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char q40kbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +static int keyup=0; +static int qprev=0; + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + unsigned char status; + + disable_keyboard(); + spin_lock_irqsave(&kbd_controller_lock, flags); + kbd_pt_regs = regs; + + status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG); + if (status ) + { + unsigned char scancode,qcode; + + qcode = master_inb(KEYCODE_REG); + + if (qcode != 0xf0) + { + if (qcode == 0xe0) + { + qprev=0xe0; + handle_scancode(qprev , 1); + goto exit; + } + + scancode=qprev ? q40ecl[qcode] : q40cl[qcode]; +#if 0 +/* next line is last resort to hanlde some oddities */ + if (qprev && !scancode) scancode=q40cl[qcode]; +#endif + qprev=0; + if (!scancode) + { + printk("unknown scancode %x\n",qcode); + goto exit; + } + if (scancode==0xff) /* SySrq */ + scancode=SYSRQ_KEY; + + handle_scancode(scancode, ! keyup ); + keyup=0; + mark_bh(KEYBOARD_BH); + + } + else + keyup=1; + } +exit: + spin_unlock_irqrestore(&kbd_controller_lock, flags); + master_outb(-1,KEYBOARD_UNLOCK_REG); /* keyb ints reenabled herewith */ + enable_keyboard(); +} + + + + +#ifdef CONFIG_MAGIC_SYSRQ +int kbd_is_sysrq(unsigned char keycode) +{ + return( keycode == SYSRQ_KEY ); +} +#endif /* CONFIG_MAGIC_SYSRQ */ + + + + +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_BAD_DATA (-2) /* Parity or other error */ + +static int __init kbd_read_input(void) +{ + int retval = KBD_NO_DATA; + unsigned char status; + + status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG); + if (status) { + unsigned char data = master_inb(KEYCODE_REG); + + retval = data; + master_outb(-1,KEYBOARD_UNLOCK_REG); + } + return retval; +} + +extern void q40kbd_leds(unsigned char leds) +{ /* nothing can be done */ } + +static void __init kbd_clear_input(void) +{ + int maxread = 100; /* Random number */ + + do { + if (kbd_read_input() == KBD_NO_DATA) + break; + } while (--maxread); +} + + +void __init q40kbd_init_hw(void) +{ +#if 0 + /* Get the keyboard controller registers (incomplete decode) */ + request_region(0x60, 16, "keyboard"); +#endif + /* Flush any pending input. */ + kbd_clear_input(); + + /* Ok, finally allocate the IRQ, and off we go.. */ + request_irq(Q40_IRQ_KEYBOARD, keyboard_interrupt, 0, "keyboard", NULL); + master_outb(-1,KEYBOARD_UNLOCK_REG); + master_outb(1,KEY_IRQ_ENABLE_REG); + +} + diff --git a/drivers/char/qpmouse.c b/drivers/char/qpmouse.c index 2aa736190..8d5f9aa34 100644 --- a/drivers/char/qpmouse.c +++ b/drivers/char/qpmouse.c @@ -267,7 +267,7 @@ static ssize_t read_qp(struct file * file, char * buffer, return -EAGAIN; add_wait_queue(&queue->proc_list, &wait); repeat: - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (queue_empty() && !signal_pending(current)) { schedule(); goto repeat; diff --git a/drivers/char/radio-aimslab.c b/drivers/char/radio-aimslab.c index fc3101f34..ee986b335 100644 --- a/drivers/char/radio-aimslab.c +++ b/drivers/char/radio-aimslab.c @@ -35,6 +35,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */ +#include <asm/semaphore.h> /* Lock for the I/O */ #ifndef CONFIG_RADIO_RTRACK_PORT #define CONFIG_RADIO_RTRACK_PORT -1 @@ -42,6 +43,7 @@ static int io = CONFIG_RADIO_RTRACK_PORT; static int users = 0; +static struct semaphore lock; struct rt_device { @@ -86,19 +88,23 @@ static void rt_incvol(void) static void rt_mute(struct rt_device *dev) { dev->muted = 1; + down(&lock); outb(0xd0, io); /* volume steady, off */ + up(&lock); } static int rt_setvol(struct rt_device *dev, int vol) { int i; + down(&lock); + if(vol == dev->curvol) { /* requested volume = current */ if (dev->muted) { /* user is unmuting the card */ dev->muted = 0; outb (0xd8, io); /* enable card */ } - + up(&lock); return 0; } @@ -107,6 +113,7 @@ static int rt_setvol(struct rt_device *dev, int vol) sleep_delay(2000000); /* make sure it's totally down */ outb(0xd0, io); /* volume steady, off */ dev->curvol = 0; /* track the volume state! */ + up(&lock); return 0; } @@ -119,7 +126,7 @@ static int rt_setvol(struct rt_device *dev, int vol) rt_decvol(); dev->curvol = vol; - + up(&lock); return 0; } @@ -165,6 +172,8 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) freq += 171200; /* Add 10.7 MHz IF */ freq /= 800; /* Convert to 50 kHz units */ + + down(&lock); /* Stop other ops interfering */ send_0_byte (io, dev); /* 0: LSB of frequency */ @@ -191,11 +200,13 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) outb (0xd0, io); /* volume steady + sigstr */ else outb (0xd8, io); /* volume steady + sigstr + on */ + + up(&lock); return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; @@ -324,8 +335,14 @@ static struct video_device rtrack_radio= NULL }; -int __init rtrack_init(struct video_init *v) +static int __init rtrack_init(void) { + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if (check_region(io, 2)) { printk(KERN_ERR "rtrack: port 0x%x already in use\n", io); @@ -340,6 +357,10 @@ int __init rtrack_init(struct video_init *v) request_region(io, 2, "rtrack"); printk(KERN_INFO "AIMSlab Radiotrack/radioreveal card driver.\n"); + /* Set up the I/O locking */ + + init_MUTEX(&lock); + /* mute card - prevents noisy bootups */ /* this ensures that the volume is all the way down */ @@ -351,8 +372,6 @@ int __init rtrack_init(struct video_init *v) return 0; } -#ifdef MODULE - MODULE_AUTHOR("M.Kirkwood"); MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); MODULE_PARM(io, "i"); @@ -360,20 +379,12 @@ MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); EXPORT_NO_SYMBOLS; -int init_module(void) -{ - if(io==-1) - { - printk(KERN_ERR "You must set an I/O address with io=0x???\n"); - return -EINVAL; - } - return rtrack_init(NULL); -} - -void cleanup_module(void) +static void __exit cleanup_rtrack_module(void) { video_unregister_device(&rtrack_radio); release_region(io,2); } -#endif +module_init(rtrack_init); +module_exit(cleanup_rtrack_module); + diff --git a/drivers/char/radio-aztech.c b/drivers/char/radio-aztech.c index 1c26bba93..2fb8714ad 100644 --- a/drivers/char/radio-aztech.c +++ b/drivers/char/radio-aztech.c @@ -42,6 +42,7 @@ static int io = CONFIG_RADIO_AZTECH_PORT; static int radio_wait_time = 1000; static int users = 0; +static struct semaphore lock; struct az_device { @@ -86,7 +87,9 @@ static void send_1_byte (struct az_device *dev) static int az_setvol(struct az_device *dev, int vol) { + down(&lock); outb (volconvert(vol), io); + up(&lock); return 0; } @@ -119,6 +122,8 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) frequency += 171200; /* Add 10.7 MHz IF */ frequency /= 800; /* Convert to 50 kHz units */ + down(&lock); + send_0_byte (dev); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ @@ -146,6 +151,8 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) udelay (radio_wait_time); outb_p(128+64+volconvert(dev->curvol), io); + + up(&lock); return 0; } @@ -279,14 +286,21 @@ static struct video_device aztech_radio= NULL }; -int __init aztech_init(struct video_init *v) +static int __init aztech_init(void) { + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if (check_region(io, 2)) { printk(KERN_ERR "aztech: port 0x%x already in use\n", io); return -EBUSY; } + init_MUTEX(&lock); aztech_radio.priv=&aztech_unit; if(video_register_device(&aztech_radio, VFL_TYPE_RADIO)==-1) @@ -299,8 +313,6 @@ int __init aztech_init(struct video_init *v) return 0; } -#ifdef MODULE - MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); MODULE_DESCRIPTION("A driver for the Aztech radio card."); MODULE_PARM(io, "i"); @@ -308,20 +320,11 @@ MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); EXPORT_NO_SYMBOLS; -int init_module(void) -{ - if(io==-1) - { - printk(KERN_ERR "You must set an I/O address with io=0x???\n"); - return -EINVAL; - } - return aztech_init(NULL); -} - -void cleanup_module(void) +static void __exit aztech_cleanup(void) { video_unregister_device(&aztech_radio); release_region(io,2); } -#endif +module_init(aztech_init); +module_exit(aztech_cleanup); diff --git a/drivers/char/radio-gemtek.c b/drivers/char/radio-gemtek.c index 9cee2342e..523ac0955 100644 --- a/drivers/char/radio-gemtek.c +++ b/drivers/char/radio-gemtek.c @@ -23,6 +23,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */ +#include <linux/spinlock.h> #ifndef CONFIG_RADIO_GEMTEK_PORT #define CONFIG_RADIO_GEMTEK_PORT -1 @@ -30,6 +31,7 @@ static int io = CONFIG_RADIO_GEMTEK_PORT; static int users = 0; +static spinlock_t lock; struct gemtek_device { @@ -48,7 +50,9 @@ static void gemtek_mute(struct gemtek_device *dev) { if(dev->muted) return; + spin_lock(&lock); outb(0x10, io); + spin_unlock(&lock); dev->muted = 1; } @@ -56,7 +60,9 @@ static void gemtek_unmute(struct gemtek_device *dev) { if(dev->muted == 0) return; + spin_lock(&lock); outb(0x20, io); + spin_unlock(&lock); dev->muted = 0; } @@ -87,6 +93,8 @@ static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) freq *= 7825; freq /= 100000; + spin_lock(&lock); + /* 2 start bits */ outb_p(0x03, io); udelay(5); @@ -114,13 +122,17 @@ static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) outb_p(0x07, io); udelay(5); + spin_unlock(&lock); + return 0; } int gemtek_getsigstr(struct gemtek_device *dev) { + spin_lock(&lock); inb(io); udelay(5); + spin_unlock(&lock); if (inb(io) & 8) /* bit set = no signal present */ return 0; return 1; /* signal present */ @@ -250,8 +262,14 @@ static struct video_device gemtek_radio= NULL }; -int __init gemtek_init(struct video_init *v) +static int __init gemtek_init(void) { + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n"); + return -EINVAL; + } + if (check_region(io, 4)) { printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); @@ -266,6 +284,7 @@ int __init gemtek_init(struct video_init *v) request_region(io, 4, "gemtek"); printk(KERN_INFO "GemTek Radio Card driver.\n"); + spin_lock_init(&lock); /* mute card - prevents noisy bootups */ outb(0x10, io); udelay(5); @@ -277,8 +296,6 @@ int __init gemtek_init(struct video_init *v) return 0; } -#ifdef MODULE - MODULE_AUTHOR("Jonas Munsin"); MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); MODULE_PARM(io, "i"); @@ -286,23 +303,14 @@ MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x3 EXPORT_NO_SYMBOLS; -int init_module(void) -{ - if(io==-1) - { - printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n"); - return -EINVAL; - } - return gemtek_init(NULL); -} - -void cleanup_module(void) +static void __exit gemtek_cleanup(void) { video_unregister_device(&gemtek_radio); release_region(io,4); } -#endif +module_init(gemtek_init); +module_exit(gemtek_cleanup); /* Local variables: diff --git a/drivers/char/radio-miropcm20.c b/drivers/char/radio-miropcm20.c index 66314f72f..04beea2d5 100644 --- a/drivers/char/radio-miropcm20.c +++ b/drivers/char/radio-miropcm20.c @@ -205,7 +205,7 @@ static struct video_device pcm20_radio= NULL }; -int __init pcm20_init(struct video_init *v) +static int __init pcm20_init(void) { pcm20_radio.priv=&pcm20_unit; @@ -224,22 +224,16 @@ int __init pcm20_init(struct video_init *v) return 0; } -#ifdef MODULE - MODULE_AUTHOR("Ruurd Reitsma"); MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card."); EXPORT_NO_SYMBOLS; -int init_module(void) -{ - - return pcm20_init(NULL); -} - -void cleanup_module(void) +static void __exit pcm20_cleanup(void) { video_unregister_device(&pcm20_radio); } -#endif +module_init(pcm20_init); +module_exit(pcm20_cleanup); + diff --git a/drivers/char/radio-rtrack2.c b/drivers/char/radio-rtrack2.c index 7370f3a61..8876d789d 100644 --- a/drivers/char/radio-rtrack2.c +++ b/drivers/char/radio-rtrack2.c @@ -16,6 +16,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */ +#include <linux/spinlock.h> #ifndef CONFIG_RADIO_RTRACK2_PORT #define CONFIG_RADIO_RTRACK2_PORT -1 @@ -23,6 +24,7 @@ static int io = CONFIG_RADIO_RTRACK2_PORT; static int users = 0; +static spinlock_t lock; struct rt_device { @@ -38,7 +40,9 @@ static void rt_mute(struct rt_device *dev) { if(dev->muted) return; + spin_lock(&lock); outb(1, io); + spin_unlock(&lock); dev->muted = 1; } @@ -46,7 +50,9 @@ static void rt_unmute(struct rt_device *dev) { if(dev->muted == 0) return; + spin_lock(&lock); outb(0, io); + spin_unlock(&lock); dev->muted = 0; } @@ -69,6 +75,8 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) int i; freq = freq / 200 + 856; + + spin_lock(&lock); outb_p(0xc8, io); outb_p(0xc9, io); @@ -85,11 +93,13 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) outb_p(0xc8, io); if (!dev->muted) - outb_p(0, io); + outb_p(0, io); + + spin_unlock(&lock); return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; @@ -227,7 +237,8 @@ int __init rtrack2_init(struct video_init *v) } rtrack2_radio.priv=&rtrack2_unit; - + + spin_lock_init(&lock); if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO)==-1) return -EINVAL; diff --git a/drivers/char/radio-sf16fmi.c b/drivers/char/radio-sf16fmi.c index c659cc7ba..ef73f2d77 100644 --- a/drivers/char/radio-sf16fmi.c +++ b/drivers/char/radio-sf16fmi.c @@ -22,6 +22,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_SF16MI_PORT */ +#include <asm/semaphore.h> struct fmi_device { @@ -37,6 +38,7 @@ struct fmi_device static int io = CONFIG_RADIO_SF16FMI_PORT; static int users = 0; +static struct semaphore lock; /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ /* It is only usefull to give freq in intervall of 800 (=0.05Mhz), @@ -67,12 +69,16 @@ static void outbits(int bits, unsigned int data, int port) static inline void fmi_mute(int port) { + down(&lock); outb(0x00, port); + up(&lock); } static inline void fmi_unmute(int port) { + down(&lock); outb(0x08, port); + up(&lock); } static inline int fmi_setfreq(struct fmi_device *dev) @@ -81,6 +87,8 @@ static inline int fmi_setfreq(struct fmi_device *dev) unsigned long freq = dev->curfreq; int i; + down(&lock); + outbits(16, RSF16_ENCODE(freq), myport); outbits(8, 0xC0, myport); for(i=0; i< 100; i++) @@ -93,6 +101,8 @@ static inline int fmi_setfreq(struct fmi_device *dev) current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/7); */ + + up(&lock); if (dev->curvol) fmi_unmute(myport); return 0; } @@ -104,6 +114,7 @@ static inline int fmi_getsigstr(struct fmi_device *dev) int myport = dev->port; int i; + down(&lock); val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ outb(val, myport); outb(val | 0x10, myport); @@ -119,6 +130,8 @@ static inline int fmi_getsigstr(struct fmi_device *dev) */ res = (int)inb(myport+1); outb(val, myport); + + up(&lock); return (res & 2) ? 0 : 0xFFFF; } @@ -290,6 +303,8 @@ int __init fmi_init(struct video_init *v) fmi_unit.flags = VIDEO_TUNER_LOW; fmi_radio.priv = &fmi_unit; + init_MUTEX(&lock); + if(video_register_device(&fmi_radio, VFL_TYPE_RADIO)==-1) return -EINVAL; diff --git a/drivers/char/radio-terratec.c b/drivers/char/radio-terratec.c index ae14d11f4..9fb119c30 100644 --- a/drivers/char/radio-terratec.c +++ b/drivers/char/radio-terratec.c @@ -31,6 +31,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev.h> /* kernel radio structs */ #include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT */ +#include <linux/spinlock.h> #ifndef CONFIG_RADIO_TERRATEC_PORT #define CONFIG_RADIO_TERRATEC_PORT 0x590 @@ -50,6 +51,7 @@ static int io = CONFIG_RADIO_TERRATEC_PORT; static int users = 0; +static spinlock_t lock; struct tt_device { @@ -66,12 +68,14 @@ static void cardWriteVol(int volume) { int i; volume = volume+(volume * 32); // change both channels + spin_lock(&lock); for (i=0;i<8;i++) { if (volume & (0x80>>i)) outb(0x80, VOLPORT); else outb(0x00, VOLPORT); } + spin_unlock(&lock); } @@ -148,6 +152,8 @@ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) temp = temp/2; } + spin_lock(&lock); + for (i=24;i>-1;i--) /* bit shift the values to the radiocard */ { if (buffer[i]==1) @@ -163,6 +169,8 @@ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) } } outb(0x00, BASEPORT); + + spin_unlock(&lock); return 0; } @@ -299,7 +307,7 @@ static struct video_device terratec_radio= NULL }; -__initfunc(int terratec_init(struct video_init *v)) +int __init terratec_init(struct video_init *v) { if (check_region(io, 2)) { @@ -309,6 +317,8 @@ __initfunc(int terratec_init(struct video_init *v)) terratec_radio.priv=&terratec_unit; + spin_lock_init(&lock); + if(video_register_device(&terratec_radio, VFL_TYPE_RADIO)==-1) return -EINVAL; diff --git a/drivers/char/radio-trust.c b/drivers/char/radio-trust.c new file mode 100644 index 000000000..15dee607a --- /dev/null +++ b/drivers/char/radio-trust.c @@ -0,0 +1,354 @@ +/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 + * by Eric Lammerts <eric@scintilla.utwente.nl> + * + * Based on radio-aztech.c. Original notes: + * + * Adapted to support the Video for Linux API by + * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: + * + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * + * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ + */ + +#include <stdarg.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/videodev.h> +#include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */ + +/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ + +#ifndef CONFIG_RADIO_TRUST_PORT +#define CONFIG_RADIO_TRUST_PORT -1 +#endif + +static int io = CONFIG_RADIO_TRUST_PORT; +static int ioval = 0xf; +static int users = 0; +static __u16 curvol; +static __u16 curbass; +static __u16 curtreble; +static unsigned long curfreq; +static int curstereo; +static int curmute; + +/* i2c addresses */ +#define TDA7318_ADDR 0x88 +#define TSA6060T_ADDR 0xc4 + +#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0) +#define TR_SET_SCL outb(ioval |= 2, io) +#define TR_CLR_SCL outb(ioval &= 0xfd, io) +#define TR_SET_SDA outb(ioval |= 1, io) +#define TR_CLR_SDA outb(ioval &= 0xfe, io) + +static void write_i2c(int n, ...) +{ + unsigned char val, mask; + va_list args; + + va_start(args, n); + + /* start condition */ + TR_SET_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SDA; + TR_CLR_SCL; + TR_DELAY; + + for(; n; n--) { + val = va_arg(args, unsigned); + for(mask = 0x80; mask; mask >>= 1) { + if(val & mask) + TR_SET_SDA; + else + TR_CLR_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SCL; + TR_DELAY; + } + /* acknowledge bit */ + TR_SET_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SCL; + TR_DELAY; + } + + /* stop condition */ + TR_CLR_SDA; + TR_DELAY; + TR_SET_SCL; + TR_DELAY; + TR_SET_SDA; + TR_DELAY; + + va_end(args); +} + +static void tr_setvol(__u16 vol) +{ + curvol = vol / 2048; + write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f); +} + +static int basstreble2chip[15] = { + 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 +}; + +static void tr_setbass(__u16 bass) +{ + curbass = bass / 4370; + write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]); +} + +static void tr_settreble(__u16 treble) +{ + curtreble = treble / 4370; + write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]); +} + +static void tr_setstereo(int stereo) +{ + curstereo = !!stereo; + ioval = (ioval & 0xfb) | (!curstereo << 2); + outb(ioval, io); +} + +static void tr_setmute(int mute) +{ + curmute = !!mute; + ioval = (ioval & 0xf7) | (curmute << 3); + outb(ioval, io); +} + +static int tr_getsigstr(void) +{ + int i, v; + + for(i = 0, v = 0; i < 100; i++) v |= inb(io); + return (v & 1)? 0 : 0xffff; +} + +static int tr_getstereo(void) +{ + /* don't know how to determine it, just return the setting */ + return curstereo; +} + +static void tr_setfreq(unsigned long f) +{ + f /= 160; /* Convert to 10 kHz units */ + f += 1070; /* Add 10.7 MHz IF */ + + write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); +} + +static int tr_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + + strcpy(v.name, "Trust FM Radio"); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + + v.rangelow = 87500 * 16; + v.rangehigh = 108000 * 16; + v.flags = VIDEO_TUNER_LOW; + v.mode = VIDEO_MODE_AUTO; + + v.signal = tr_getsigstr(); + if(tr_getstereo()) + v.flags |= VIDEO_TUNER_STEREO_ON; + + strcpy(v.name, "FM"); + + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGFREQ: + if(copy_to_user(arg, &curfreq, sizeof(curfreq))) + return -EFAULT; + return 0; + + case VIDIOCSFREQ: + { + unsigned long f; + + if(copy_from_user(&f, arg, sizeof(curfreq))) + return -EFAULT; + tr_setfreq(f); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio v; + + memset(&v,0, sizeof(v)); + v.flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; + v.mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; + v.volume = curvol * 2048; + v.step = 2048; + v.bass = curbass * 4370; + v.treble = curtreble * 4370; + + strcpy(v.name, "Trust FM Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + tr_setvol(v.volume); + tr_setbass(v.bass); + tr_settreble(v.treble); + tr_setstereo(v.mode & VIDEO_SOUND_STEREO); + tr_setmute(v.flags & VIDEO_AUDIO_MUTE); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int tr_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void tr_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct video_device trust_radio= +{ + "Trust FM Radio", + VID_TYPE_TUNER, + VID_HARDWARE_TRUST, + tr_open, + tr_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + tr_ioctl, + NULL, + NULL +}; + +static int __init trust_init(void) +{ + if(io == -1) { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if(check_region(io, 2)) { + printk(KERN_ERR "trust: port 0x%x already in use\n", io); + return -EBUSY; + } + if(video_register_device(&trust_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 2, "Trust FM Radio"); + + printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); + + write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ + + tr_setvol(0x8000); + tr_setbass(0x8000); + tr_settreble(0x8000); + tr_setstereo(1); + + /* mute card - prevents noisy bootups */ + tr_setmute(1); + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); + +EXPORT_NO_SYMBOLS; + +#endif /* MODULE */ + +static void __exit cleanup_trust_module(void) +{ + video_unregister_device(&trust_radio); + release_region(io, 2); +} + +module_init(trust_init); +module_exit(cleanup_trust_module); diff --git a/drivers/char/radio-zoltrix.c b/drivers/char/radio-zoltrix.c index f0d8e37c7..a11de7ba8 100644 --- a/drivers/char/radio-zoltrix.c +++ b/drivers/char/radio-zoltrix.c @@ -47,6 +47,7 @@ struct zol_device { unsigned long curfreq; int muted; unsigned int stereo; + struct semaphore lock; }; @@ -64,26 +65,30 @@ static int zol_setvol(struct zol_device *dev, int vol) if (dev->muted) return 0; + down(&dev->lock); if (vol == 0) { outb(0, io); outb(0, io); inb(io + 3); /* Zoltrix needs to be read to confirm */ + up(&dev->lock); return 0; } outb(dev->curvol-1, io); sleep_delay(); inb(io + 2); - + up(&dev->lock); return 0; } static void zol_mute(struct zol_device *dev) { dev->muted = 1; + down(&dev->lock); outb(0, io); outb(0, io); inb(io + 3); /* Zoltrix needs to be read to confirm */ + up(&dev->lock); } static void zol_unmute(struct zol_device *dev) @@ -107,6 +112,8 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) bitmask = 0xc480402c10080000ull; i = 45; + down(&dev->lock); + outb(0, io); outb(0, io); inb(io + 3); /* Zoltrix needs to be read to confirm */ @@ -141,14 +148,21 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) inb(io+2); udelay(1000); + if (dev->muted) { outb(0, io); outb(0, io); inb(io + 3); udelay(1000); - } else - zol_setvol(dev, dev->curvol); + } + + up(&dev->lock); + + if(!dev->muted) + { + zol_setvol(dev, dev->curvol); + } return 0; } @@ -158,6 +172,7 @@ int zol_getsigstr(struct zol_device *dev) { int a, b; + down(&dev->lock); outb(0x00, io); /* This stuff I found to do nothing */ outb(dev->curvol, io); sleep_delay(); @@ -167,6 +182,8 @@ int zol_getsigstr(struct zol_device *dev) sleep_delay(); b = inb(io); + up(&dev->lock); + if (a != b) return (0); @@ -180,6 +197,8 @@ int zol_is_stereo (struct zol_device *dev) { int x1, x2; + down(&dev->lock); + outb(0x00, io); outb(dev->curvol, io); sleep_delay(); @@ -189,6 +208,8 @@ int zol_is_stereo (struct zol_device *dev) sleep_delay(); x2 = inb(io); + up(&dev->lock); + if ((x1 == x2) && (x1 == 0xcf)) return 1; return 0; @@ -351,6 +372,8 @@ int __init zoltrix_init(struct video_init *v) request_region(io, 2, "zoltrix"); printk(KERN_INFO "Zoltrix Radio Plus card driver.\n"); + init_MUTEX(&zoltrix_unit.lock); + /* mute card - prevents noisy bootups */ /* this ensures that the volume is all the way down */ diff --git a/drivers/char/random.c b/drivers/char/random.c index f1dd26cd2..d97304a13 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,10 +1,10 @@ /* * random.c -- A strong random number generator * - * Version 1.04, last modified 26-Apr-98 + * Version 1.89, last modified 19-Sep-99 * - * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998. All rights - * reserved. + * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All + * rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,15 +27,16 @@ * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ /* @@ -223,8 +224,8 @@ * The code for SHA transform was taken from Peter Gutmann's * implementation, which has been placed in the public domain. * The code for MD5 transform was taken from Colin Plumb's - * implementation, which has been placed in the public domain. The - * MD5 cryptographic checksum was devised by Ronald Rivest, and is + * implementation, which has been placed in the public domain. + * The MD5 cryptographic checksum was devised by Ronald Rivest, and is * documented in RFC 1321, "The MD5 Message Digest Algorithm". * * Further background information on this topic may be obtained from @@ -232,11 +233,6 @@ * Eastlake, Steve Crocker, and Jeff Schiller. */ -/* - * Added a check for signal pending in the extract_entropy() loop to allow - * the read(2) syscall to be interrupted. Copyright (C) 1998 Andrea Arcangeli - */ - #include <linux/utsname.h> #include <linux/config.h> #include <linux/kernel.h> @@ -256,72 +252,77 @@ /* * Configuration information */ -#undef RANDOM_BENCHMARK -#undef BENCHMARK_NOINT -#define ROTATE_PARANOIA +#define DEFAULT_POOL_SIZE 512 +#define SECONDARY_POOL_SIZE 128 +#define BATCH_ENTROPY_SIZE 256 +#define USE_SHA -#define POOLWORDS 128 /* Power of 2 - note that this is 32-bit words */ -#define POOLBITS (POOLWORDS*32) /* - * The pool is stirred with a primitive polynomial of degree POOLWORDS - * over GF(2). The taps for various sizes are defined below. They are - * chosen to be evenly spaced (minimum RMS distance from evenly spaced; - * the numbers in the comments are a scaled squared error sum) except - * for the last tap, which is 1 to get the twisting happening as fast - * as possible. + * The minimum number of bits of entropy before we wake up a read on + * /dev/random. Should always be at least 8, or at least 1 byte. */ -#if POOLWORDS == 2048 /* 115 x^2048+x^1638+x^1231+x^819+x^411+x^1+1 */ -#define TAP1 1638 -#define TAP2 1231 -#define TAP3 819 -#define TAP4 411 -#define TAP5 1 -#elif POOLWORDS == 1024 /* 290 x^1024+x^817+x^615+x^412+x^204+x^1+1 */ -/* Alt: 115 x^1024+x^819+x^616+x^410+x^207+x^2+1 */ -#define TAP1 817 -#define TAP2 615 -#define TAP3 412 -#define TAP4 204 -#define TAP5 1 -#elif POOLWORDS == 512 /* 225 x^512+x^411+x^308+x^208+x^104+x+1 */ -/* Alt: 95 x^512+x^409+x^307+x^206+x^102+x^2+1 - * 95 x^512+x^409+x^309+x^205+x^103+x^2+1 */ -#define TAP1 411 -#define TAP2 308 -#define TAP3 208 -#define TAP4 104 -#define TAP5 1 -#elif POOLWORDS == 256 /* 125 x^256+x^205+x^155+x^101+x^52+x+1 */ -#define TAP1 205 -#define TAP2 155 -#define TAP3 101 -#define TAP4 52 -#define TAP5 1 -#elif POOLWORDS == 128 /* 105 x^128+x^103+x^76+x^51+x^25+x+1 */ -/* Alt: 70 x^128+x^103+x^78+x^51+x^27+x^2+1 */ -#define TAP1 103 -#define TAP2 76 -#define TAP3 51 -#define TAP4 25 -#define TAP5 1 -#elif POOLWORDS == 64 /* 15 x^64+x^52+x^39+x^26+x^14+x+1 */ -#define TAP1 52 -#define TAP2 39 -#define TAP3 26 -#define TAP4 14 -#define TAP5 1 -#elif POOLWORDS == 32 /* 15 x^32+x^26+x^20+x^14+x^7+x^1+1 */ -#define TAP1 26 -#define TAP2 20 -#define TAP3 14 -#define TAP4 7 -#define TAP5 1 -#elif POOLWORDS & (POOLWORDS-1) -#error POOLWORDS must be a power of 2 -#else -#error No primitive polynomial available for chosen POOLWORDS +static int random_read_wakeup_thresh = 8; + +/* + * If the entropy count falls under this number of bits, then we + * should wake up processes which are selecting or polling on write + * access to /dev/random. + */ +static int random_write_wakeup_thresh = 128; + +/* + * A pool of size POOLWORDS is stirred with a primitive polynomial + * of degree POOLWORDS over GF(2). The taps for various sizes are + * defined below. They are chosen to be evenly spaced (minimum RMS + * distance from evenly spaced; the numbers in the comments are a + * scaled squared error sum) except for the last tap, which is 1 to + * get the twisting happening as fast as possible. + */ +static struct poolinfo { + int poolwords; + int tap1, tap2, tap3, tap4, tap5; +} poolinfo_table[] = { + /* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1 -- 115 */ + { 2048, 1638, 1231, 819, 411, 1 }, + + /* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */ + { 1024, 817, 615, 412, 204, 1 }, + +#if 0 /* Alternate polynomial */ + /* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */ + { 1024, 819, 616, 410, 207, 2 }, #endif + + /* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */ + { 512, 411, 308, 208, 104, 1 }, + +#if 0 /* Alternates */ + /* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */ + { 512, 409, 307, 206, 102, 2 }, + /* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */ + { 512, 409, 309, 205, 103, 2 }, +#endif + + /* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */ + { 256, 205, 155, 101, 52, 1 }, + + /* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */ + { 128, 103, 76, 51, 25, 1 }, + +#if 0 /* Alternate polynomial */ + /* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */ + { 128, 103, 78, 51, 27, 2 }, +#endif + + /* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */ + { 64, 52, 39, 26, 14, 1 }, + /* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */ + { 32, 26, 20, 14, 7, 1 }, + + { 0, 0, 0, 0, 0, 0 }, +}; + /* * For the purposes of better mixing, we use the CRC-32 polynomial as * well to make a twisted Generalized Feedback Shift Reigster @@ -332,25 +333,26 @@ * II. ACM Transactions on Mdeling and Computer Simulation 4:254-266) * * Thanks to Colin Plumb for suggesting this. + * * We have not analyzed the resultant polynomial to prove it primitive; * in fact it almost certainly isn't. Nonetheless, the irreducible factors * of a random large-degree polynomial over GF(2) are more than large enough * that periodicity is not a concern. - * - * The input hash is much less sensitive than the output hash. All that - * we want of it is that it be a good non-cryptographic hash; i.e. it - * not produce collisions when fed "random" data of the sort we expect - * to see. As long as the pool state differs for different inputs, we - * have preserved the input entropy and done a good job. The fact that an - * intelligent attacker can construct inputs that will produce controlled - * alterations to the pool's state is not important because we don't - * consider such inputs to contribute any randomness. - * The only property we need with respect to them is - * that the attacker can't increase his/her knowledge of the pool's state. + * + * The input hash is much less sensitive than the output hash. All + * that we want of it is that it be a good non-cryptographic hash; + * i.e. it not produce collisions when fed "random" data of the sort + * we expect to see. As long as the pool state differs for different + * inputs, we have preserved the input entropy and done a good job. + * The fact that an intelligent attacker can construct inputs that + * will produce controlled alterations to the pool's state is not + * important because we don't consider such inputs to contribute any + * randomness. The only property we need with respect to them is that + * the attacker can't increase his/her knowledge of the pool's state. * Since all additions are reversible (knowing the final state and the * input, you can reconstruct the initial state), if an attacker has - * any uncertainty about the initial state, he/she can only shuffle that - * uncertainty about, but never cause any collisions (which would + * any uncertainty about the initial state, he/she can only shuffle + * that uncertainty about, but never cause any collisions (which would * decrease the uncertainty). * * The chosen system lets the state of the pool be (essentially) the input @@ -365,82 +367,36 @@ */ /* - * The minimum number of bits to release a "wait on input". Should - * probably always be 8, since a /dev/random read can return a single - * byte. - */ -#define WAIT_INPUT_BITS 8 -/* - * The limit number of bits under which to release a "wait on - * output". Should probably always be the same as WAIT_INPUT_BITS, so - * that an output wait releases when and only when a wait on input - * would block. + * Linux 2.2 compatibility */ -#define WAIT_OUTPUT_BITS WAIT_INPUT_BITS - -/* There is actually only one of these, globally. */ -struct random_bucket { - unsigned add_ptr; - unsigned entropy_count; -#ifdef ROTATE_PARANOIA - int input_rotate; +#ifndef DECLARE_WAITQUEUE +#define DECLARE_WAITQUEUE(WAIT, PTR) struct wait_queue WAIT = { PTR, NULL } #endif - __u32 pool[POOLWORDS]; -}; - -#ifdef RANDOM_BENCHMARK -/* For benchmarking only */ -struct random_benchmark { - unsigned long long start_time; - int times; /* # of samples */ - unsigned long min; - unsigned long max; - unsigned long accum; /* accumulator for average */ - const char *descr; - int unit; - unsigned long flags; -}; - -#define BENCHMARK_INTERVAL 500 - -static void initialize_benchmark(struct random_benchmark *bench, - const char *descr, int unit); -static void begin_benchmark(struct random_benchmark *bench); -static void end_benchmark(struct random_benchmark *bench); - -struct random_benchmark timer_benchmark; +#ifndef DECLARE_WAIT_QUEUE_HEAD +#define DECLARE_WAIT_QUEUE_HEAD(WAIT) struct wait_queue *WAIT #endif -/* There is one of these per entropy source */ -struct timer_rand_state { - __u32 last_time; - __s32 last_delta,last_delta2; - int dont_count_entropy:1; -}; - -static struct random_bucket random_state; -static struct timer_rand_state keyboard_timer_state; -static struct timer_rand_state mouse_timer_state; -static struct timer_rand_state extract_timer_state; -static struct timer_rand_state *irq_timer_state[NR_IRQS]; -static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV]; +/* + * Static global variables + */ +static struct entropy_store *random_state; /* The default global store */ +static struct entropy_store *sec_random_state; /* secondary store */ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); -static ssize_t random_read(struct file * file, char * buf, - size_t nbytes, loff_t *ppos); -static ssize_t random_read_unlimited(struct file * file, char * buf, - size_t nbytes, loff_t *ppos); -static unsigned int random_poll(struct file *file, poll_table * wait); -static ssize_t random_write(struct file * file, const char * buffer, - size_t count, loff_t *ppos); -static int random_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg); - -static inline void fast_add_entropy_words(struct random_bucket *r, - __u32 x, __u32 y); +/* + * Forward procedure declarations + */ +#ifdef CONFIG_SYSCTL +static void sysctl_init_random(struct entropy_store *random_state); +#endif -static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y); +/***************************************************************** + * + * Utility functions, with some ASM defined functions for speed + * purposes + * + *****************************************************************/ #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -504,185 +460,236 @@ static inline __u32 int_ln_12bits(__u32 word) } #endif - -/* - * Initialize the random pool with standard stuff. +/********************************************************************** * - * NOTE: This is an OS-dependent function. + * OS independent entropy store. Here are the functions which handle + * storing entropy in an entropy pool. + * + **********************************************************************/ + +struct entropy_store { + unsigned add_ptr; + int entropy_count; + int input_rotate; + int extract_count; + struct poolinfo poolinfo; + __u32 *pool; +}; + +/* + * Initialize the entropy store. The input argument is the size of + * the random pool. + * + * Returns an negative error if there is a problem. */ -static void init_std_data(struct random_bucket *r) +static int create_entropy_store(int size, struct entropy_store **ret_bucket) { - __u32 words[2], *p; - int i; - struct timeval tv; + struct entropy_store *r; + struct poolinfo *p; + int poolwords; - do_gettimeofday(&tv); - add_entropy_words(r, tv.tv_sec, tv.tv_usec); + poolwords = (size + 3) / 4; /* Convert bytes->words */ + /* The pool size must be a multiple of 16 32-bit words */ + poolwords = ((poolwords + 15) / 16) * 16; - /* - * This doesnt lock system.utsname. Howeve we are generating - * entropy so a race with a name set here is fine. - */ - p = (__u32 *)&system_utsname; - for (i = sizeof(system_utsname) / sizeof(words); i; i--) { - memcpy(words, p, sizeof(words)); - add_entropy_words(r, words[0], words[1]); - p += sizeof(words)/sizeof(*words); + for (p = poolinfo_table; p->poolwords; p++) { + if (poolwords == p->poolwords) + break; } - + if (p->poolwords == 0) + return -EINVAL; + + r = kmalloc(sizeof(struct entropy_store), GFP_KERNEL); + if (!r) + return -ENOMEM; + + memset (r, 0, sizeof(struct entropy_store)); + r->poolinfo = *p; + + r->pool = kmalloc(poolwords*4, GFP_KERNEL); + if (!r->pool) { + kfree_s(r, sizeof(struct entropy_store)); + return -ENOMEM; + } + memset(r->pool, 0, poolwords*4); + *ret_bucket = r; + return 0; } /* Clear the entropy pool and associated counters. */ -static void rand_clear_pool(void) +static void clear_entropy_store(struct entropy_store *r) { - memset(&random_state, 0, sizeof(random_state)); - init_std_data(&random_state); + r->add_ptr = 0; + r->entropy_count = 0; + r->input_rotate = 0; + r->extract_count = 0; + memset(r->pool, 0, r->poolinfo.poolwords*4); } -__initfunc(void rand_initialize(void)) +static void free_entropy_store(struct entropy_store *r) { - int i; - - rand_clear_pool(); - for (i = 0; i < NR_IRQS; i++) - irq_timer_state[i] = NULL; - for (i = 0; i < MAX_BLKDEV; i++) - blkdev_timer_state[i] = NULL; - memset(&keyboard_timer_state, 0, sizeof(struct timer_rand_state)); - memset(&mouse_timer_state, 0, sizeof(struct timer_rand_state)); - memset(&extract_timer_state, 0, sizeof(struct timer_rand_state)); -#ifdef RANDOM_BENCHMARK - initialize_benchmark(&timer_benchmark, "timer", 0); -#endif - extract_timer_state.dont_count_entropy = 1; + if (r->pool) + kfree(r->pool); + kfree_s(r, sizeof(struct entropy_store)); } -void rand_initialize_irq(int irq) +/* + * This function adds a byte into the entropy "pool". It does not + * update the entropy estimate. The caller should call + * credit_entropy_store if this is appropriate. + * + * The pool is stirred with a primitive polynomial of the appropriate + * degree, and then twisted. We twist by three bits at a time because + * it's cheap to do so and helps slightly in the expected case where + * the entropy is concentrated in the low-order bits. + */ +static void add_entropy_words(struct entropy_store *r, const __u32 *in, + int num) { - struct timer_rand_state *state; - - if (irq >= NR_IRQS || irq_timer_state[irq]) - return; + static __u32 const twist_table[8] = { + 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; + unsigned i; + int new_rotate; + __u32 w; - /* - * If kmalloc returns null, we just won't use that entropy - * source. - */ - state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) { - irq_timer_state[irq] = state; - memset(state, 0, sizeof(struct timer_rand_state)); + while (num--) { + w = rotate_left(r->input_rotate, *in); + i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1); + /* + * Normally, we add 7 bits of rotation to the pool. + * At the beginning of the pool, add an extra 7 bits + * rotation, so that successive passes spread the + * input bits across the pool evenly. + */ + new_rotate = r->input_rotate + 14; + if (i) + new_rotate = r->input_rotate + 7; + r->input_rotate = new_rotate & 31; + + /* XOR in the various taps */ + w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)]; + w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)]; + w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)]; + w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)]; + w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)]; + w ^= r->pool[i]; + r->pool[i] = (w >> 3) ^ twist_table[w & 7]; } } -void rand_initialize_blkdev(int major, int mode) +/* + * Credit (or debit) the entropy store with n bits of entropy + */ +static void credit_entropy_store(struct entropy_store *r, int num) { - struct timer_rand_state *state; - - if (major >= MAX_BLKDEV || blkdev_timer_state[major]) - return; + int max_entropy = r->poolinfo.poolwords*32; - /* - * If kmalloc returns null, we just won't use that entropy - * source. - */ - state = kmalloc(sizeof(struct timer_rand_state), mode); - if (state) { - blkdev_timer_state[major] = state; - memset(state, 0, sizeof(struct timer_rand_state)); - } + if (r->entropy_count + num < 0) + r->entropy_count = 0; + else if (r->entropy_count + num > max_entropy) + r->entropy_count = max_entropy; + else + r->entropy_count = r->entropy_count + num; } -/* - * This function adds a byte into the entropy "pool". It does not - * update the entropy estimate. The caller must do this if appropriate. +/********************************************************************** * - * This function is tuned for speed above most other considerations. + * Entropy batch input management * - * The pool is stirred with a primitive polynomial of the appropriate degree, - * and then twisted. We twist by three bits at a time because it's - * cheap to do so and helps slightly in the expected case where the - * entropy is concentrated in the low-order bits. - */ -#define MASK(x) ((x) & (POOLWORDS-1)) /* Convenient abreviation */ -static inline void fast_add_entropy_words(struct random_bucket *r, - __u32 x, __u32 y) -{ - static __u32 const twist_table[8] = { - 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, - 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; - unsigned i, j; + * We batch entropy to be added to avoid increasing interrupt latency + * + **********************************************************************/ - i = MASK(r->add_ptr - 2); /* i is always even */ - r->add_ptr = i; +static __u32 *batch_entropy_pool; +static int *batch_entropy_credit; +static int batch_max; +static int batch_head, batch_tail; +static struct tq_struct batch_tqueue; +static void batch_entropy_process(void *private_); -#ifdef ROTATE_PARANOIA - j = r->input_rotate + 14; - if (i) - j -= 7; - r->input_rotate = j & 31; +/* note: the size must be a power of 2 */ +static int batch_entropy_init(int size, struct entropy_store *r) +{ + batch_entropy_pool = kmalloc(2*size*sizeof(__u32), GFP_KERNEL); + if (!batch_entropy_pool) + return -1; + batch_entropy_credit =kmalloc(size*sizeof(int), GFP_KERNEL); + if (!batch_entropy_credit) { + kfree(batch_entropy_pool); + return -1; + } + batch_head = batch_tail = 0; + batch_max = size; + batch_tqueue.routine = batch_entropy_process; + batch_tqueue.data = r; + return 0; +} - x = rotate_left(r->input_rotate, x); - y = rotate_left(r->input_rotate, y); -#endif +static void batch_entropy_store(__u32 a, __u32 b, int num) +{ + int new; - /* - * XOR in the various taps. Even though logically, we compute - * x and then compute y, we read in y then x order because most - * caches work slightly better with increasing read addresses. - * If a tap is even then we can use the fact that i is even to - * avoid a masking operation. Every polynomial has at least one - * even tap, so j is always used. - * (Is there a nicer way to arrange this code?) - */ -#if TAP1 & 1 - y ^= r->pool[MASK(i+TAP1)]; x ^= r->pool[MASK(i+TAP1+1)]; -#else - j = MASK(i+TAP1); y ^= r->pool[j]; x ^= r->pool[j+1]; -#endif -#if TAP2 & 1 - y ^= r->pool[MASK(i+TAP2)]; x ^= r->pool[MASK(i+TAP2+1)]; -#else - j = MASK(i+TAP2); y ^= r->pool[j]; x ^= r->pool[j+1]; -#endif -#if TAP3 & 1 - y ^= r->pool[MASK(i+TAP3)]; x ^= r->pool[MASK(i+TAP3+1)]; -#else - j = MASK(i+TAP3); y ^= r->pool[j]; x ^= r->pool[j+1]; -#endif -#if TAP4 & 1 - y ^= r->pool[MASK(i+TAP4)]; x ^= r->pool[MASK(i+TAP4+1)]; -#else - j = MASK(i+TAP4); y ^= r->pool[j]; x ^= r->pool[j+1]; -#endif -#if TAP5 == 1 - /* We need to pretend to write pool[i+1] before computing y */ - y ^= r->pool[i]; - x ^= r->pool[i+1]; - x ^= r->pool[MASK(i+TAP5+1)]; - y ^= r->pool[i+1] = x = (x >> 3) ^ twist_table[x & 7]; - r->pool[i] = (y >> 3) ^ twist_table[y & 7]; -#else -# if TAP5 & 1 - y ^= r->pool[MASK(i+TAP5)]; x ^= r->pool[MASK(i+TAP5+1)]; -# else - j = MASK(i+TAP5); y ^= r->pool[j]; x ^= r->pool[j+1]; -# endif - y ^= r->pool[i]; - x ^= r->pool[i+1]; - r->pool[i] = (y >> 3) ^ twist_table[y & 7]; - r->pool[i+1] = (x >> 3) ^ twist_table[x & 7]; + if (!batch_max) + return; + + batch_entropy_pool[2*batch_head] = a; + batch_entropy_pool[(2*batch_head) + 1] = b; + batch_entropy_credit[batch_head] = num; + + new = (batch_head+1) & (batch_max-1); + if (new != batch_tail) { + queue_task(&batch_tqueue, &tq_timer); + batch_head = new; + } else { +#if 0 + printk(KERN_NOTICE "random: batch entropy buffer full\n"); #endif + } } -/* - * For places where we don't need the inlined version - */ -static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y) +static void batch_entropy_process(void *private_) { - fast_add_entropy_words(r, x, y); + int num = 0; + int max_entropy; + struct entropy_store *r = (struct entropy_store *) private_, *p; + + if (!batch_max) + return; + + max_entropy = r->poolinfo.poolwords*32; + while (batch_head != batch_tail) { + add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2); + p = r; + if (r->entropy_count > max_entropy && (num & 1)) + r = sec_random_state; + credit_entropy_store(r, batch_entropy_credit[batch_tail]); + batch_tail = (batch_tail+1) & (batch_max-1); + num++; + } + if (r->entropy_count >= random_read_wakeup_thresh) + wake_up_interruptible(&random_read_wait); } +/********************************************************************* + * + * Entropy input management + * + *********************************************************************/ + +/* There is one of these per entropy source */ +struct timer_rand_state { + __u32 last_time; + __s32 last_delta,last_delta2; + int dont_count_entropy:1; +}; + +static struct timer_rand_state keyboard_timer_state; +static struct timer_rand_state mouse_timer_state; +static struct timer_rand_state extract_timer_state; +static struct timer_rand_state *irq_timer_state[NR_IRQS]; +static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV]; + /* * This function adds entropy to the entropy "pool" by using timing * delays. It uses the timer_rand_state structure to make an estimate @@ -695,15 +702,12 @@ static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y) * are used for a high-resolution timer. * */ -static void add_timer_randomness(struct random_bucket *r, - struct timer_rand_state *state, unsigned num) +static void add_timer_randomness(struct timer_rand_state *state, unsigned num) { __u32 time; __s32 delta, delta2, delta3; + int entropy = 0; -#ifdef RANDOM_BENCHMARK - begin_benchmark(&timer_benchmark); -#endif #if defined (__i386__) if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { __u32 high; @@ -717,14 +721,12 @@ static void add_timer_randomness(struct random_bucket *r, time = jiffies; #endif - fast_add_entropy_words(r, (__u32)num, time); - /* * Calculate number of bits of randomness we probably added. * We take into account the first, second and third-order deltas * in order to make our estimate. */ - if ((r->entropy_count < POOLBITS) && !state->dont_count_entropy) { + if (!state->dont_count_entropy) { delta = time - state->last_time; state->last_time = time; @@ -753,30 +755,19 @@ static void add_timer_randomness(struct random_bucket *r, delta >>= 1; delta &= (1 << 12) - 1; - r->entropy_count += int_ln_12bits(delta); - - /* Prevent overflow */ - if (r->entropy_count > POOLBITS) - r->entropy_count = POOLBITS; - - /* Wake up waiting processes, if we have enough entropy. */ - if (r->entropy_count >= WAIT_INPUT_BITS) - wake_up_interruptible(&random_read_wait); + entropy = int_ln_12bits(delta); } - -#ifdef RANDOM_BENCHMARK - end_benchmark(&timer_benchmark); -#endif + batch_entropy_store(num, time, entropy); } void add_keyboard_randomness(unsigned char scancode) { - add_timer_randomness(&random_state, &keyboard_timer_state, scancode); + add_timer_randomness(&keyboard_timer_state, scancode); } void add_mouse_randomness(__u32 mouse_data) { - add_timer_randomness(&random_state, &mouse_timer_state, mouse_data); + add_timer_randomness(&mouse_timer_state, mouse_data); } void add_interrupt_randomness(int irq) @@ -784,7 +775,7 @@ void add_interrupt_randomness(int irq) if (irq >= NR_IRQS || irq_timer_state[irq] == 0) return; - add_timer_randomness(&random_state, irq_timer_state[irq], 0x100+irq); + add_timer_randomness(irq_timer_state[irq], 0x100+irq); } void add_blkdev_randomness(int major) @@ -798,10 +789,15 @@ void add_blkdev_randomness(int major) return; } - add_timer_randomness(&random_state, blkdev_timer_state[major], - 0x200+major); + add_timer_randomness(blkdev_timer_state[major], 0x200+major); } +/****************************************************************** + * + * Hash function definition + * + *******************************************************************/ + /* * This chunk of code defines a function * void HASH_TRANSFORM(__u32 digest[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE], @@ -821,12 +817,11 @@ void add_blkdev_randomness(int major) * 3) 0x98badcfe * 4) 0x10325476 * 5) 0xc3d2e1f0 (SHA only) - * + * * For /dev/random purposes, the length of the data being hashed is - * fixed in length (at POOLWORDS words), so appending a bit count in - * the usual way is not cryptographically necessary. + * fixed in length, so appending a bit count in the usual way is not + * cryptographically necessary. */ -#define USE_SHA #ifdef USE_SHA @@ -1074,9 +1069,6 @@ static void SHATransform(__u32 digest[85], __u32 const data[16]) /* * MD5 transform algorithm, taken from code written by Colin Plumb, * and put into the public domain - * - * QUESTION: Replace this with SHA, which as generally received better - * reviews from the cryptographic community? */ /* The four core functions - F1 is optimized somewhat */ @@ -1187,39 +1179,94 @@ static void MD5Transform(__u32 buf[HASH_BUFFER_SIZE], __u32 const in[16]) #endif /* !USE_SHA */ +/********************************************************************* + * + * Entropy extraction routines + * + *********************************************************************/ + +#define EXTRACT_ENTROPY_USER 1 +#define EXTRACT_ENTROPY_SECONDARY 2 +#define TMP_BUF_SIZE (HASH_BUFFER_SIZE + HASH_EXTRA_SIZE) +#define SEC_XFER_SIZE (TMP_BUF_SIZE*4) + +static ssize_t extract_entropy(struct entropy_store *r, void * buf, + size_t nbytes, int flags); + +/* + * This utility inline function is responsible for transfering entropy + * from the primary pool to the secondary extraction pool. We pull + * randomness under two conditions; one is if there isn't enough entropy + * in the secondary pool. The other is after we have extract 1024 bytes, + * at which point we do a "catastrophic reseeding". + */ +static inline void xfer_secondary_pool(struct entropy_store *r, + size_t nbytes) +{ + __u32 tmp[TMP_BUF_SIZE]; + + if (r->entropy_count < nbytes*8) { + extract_entropy(random_state, tmp, sizeof(tmp), 0); + add_entropy_words(r, tmp, TMP_BUF_SIZE); + credit_entropy_store(r, TMP_BUF_SIZE*8); + } + if (r->extract_count > 1024) { + extract_entropy(random_state, tmp, sizeof(tmp), 0); + add_entropy_words(r, tmp, TMP_BUF_SIZE); + r->extract_count = 0; + } +} -#if POOLWORDS % 16 != 0 -#error extract_entropy() assumes that POOLWORDS is a multiple of 16 words. -#endif /* * This function extracts randomness from the "entropy pool", and * returns it in a buffer. This function computes how many remaining * bits of entropy are left in the pool, but it does not restrict the - * number of bytes that are actually obtained. + * number of bytes that are actually obtained. If the EXTRACT_ENTROPY_USER + * flag is given, then the buf pointer is assumed to be in user space. + * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will + * + * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words. */ -static ssize_t extract_entropy(struct random_bucket *r, char * buf, - size_t nbytes, int to_user) +static ssize_t extract_entropy(struct entropy_store *r, void * buf, + size_t nbytes, int flags) { ssize_t ret, i; - __u32 tmp[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE]; + __u32 tmp[TMP_BUF_SIZE]; __u32 x; - add_timer_randomness(r, &extract_timer_state, nbytes); + add_timer_randomness(&extract_timer_state, nbytes); /* Redundant, but just in case... */ - if (r->entropy_count > POOLBITS) - r->entropy_count = POOLBITS; + if (r->entropy_count > r->poolinfo.poolwords) + r->entropy_count = r->poolinfo.poolwords; + + if (flags & EXTRACT_ENTROPY_SECONDARY) + xfer_secondary_pool(r, nbytes); - ret = nbytes; if (r->entropy_count / 8 >= nbytes) r->entropy_count -= nbytes*8; else r->entropy_count = 0; - if (r->entropy_count < WAIT_OUTPUT_BITS) + if (r->entropy_count < random_write_wakeup_thresh) wake_up_interruptible(&random_write_wait); + + r->extract_count += nbytes; + ret = 0; while (nbytes) { + /* + * Check if we need to break out or reschedule.... + */ + if ((flags & EXTRACT_ENTROPY_USER) && current->need_resched) { + if (signal_pending(current)) { + if (ret == 0) + ret = -ERESTARTSYS; + break; + } + schedule(); + } + /* Hash the pool to get the output */ tmp[0] = 0x67452301; tmp[1] = 0xefcdab89; @@ -1228,39 +1275,34 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, #ifdef USE_SHA tmp[4] = 0xc3d2e1f0; #endif - for (i = 0; i < POOLWORDS; i += 16) - HASH_TRANSFORM(tmp, r->pool+i); - /* - * The following code does two separate things that happen - * to both work two words at a time, so are convenient - * to do together. - * - * First, this feeds the output back into the pool so - * that the next call will return different results. - * Any perturbation of the pool's state would do, even - * changing one bit, but this mixes the pool nicely. - * - * Second, this folds the output in half to hide the data - * fed back into the pool from the user and further mask - * any patterns in the hash output. (The exact folding - * pattern is not important; the one used here is quick.) + * As we hash the pool, we mix intermediate values of + * the hash back into the pool. This eliminates + * backtracking attacks (where the attacker knows + * the state of the pool plus the current outputs, and + * attempts to find previous ouputs), unless the hash + * function can be inverted. */ - for (i = 0; i < HASH_BUFFER_SIZE/2; i++) { - x = tmp[i + (HASH_BUFFER_SIZE+1)/2]; - add_entropy_words(r, tmp[i], x); - tmp[i] ^= x; + for (i = 0, x = 0; i < r->poolinfo.poolwords; i += 16, x+=2) { + HASH_TRANSFORM(tmp, r->pool+i); + add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1); } + + /* + * In case the hash function has some recognizable + * output pattern, we fold it in half. + */ + for (i = 0; i < HASH_BUFFER_SIZE/2; i++) + tmp[i] ^= tmp[i + (HASH_BUFFER_SIZE+1)/2]; #if HASH_BUFFER_SIZE & 1 /* There's a middle word to deal with */ x = tmp[HASH_BUFFER_SIZE/2]; - add_entropy_words(r, x, (__u32)((unsigned long)buf)); x ^= (x >> 16); /* Fold it in half */ ((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x; #endif /* Copy data to destination buffer */ i = MIN(nbytes, HASH_BUFFER_SIZE*sizeof(__u32)/2); - if (to_user) { + if (flags & EXTRACT_ENTROPY_USER) { i -= copy_to_user(buf, (__u8 const *)tmp, i); if (!i) { ret = -EFAULT; @@ -1270,16 +1312,8 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, memcpy(buf, (__u8 const *)tmp, i); nbytes -= i; buf += i; - add_timer_randomness(r, &extract_timer_state, nbytes); - if (to_user && current->need_resched) - { - if (signal_pending(current)) - { - ret = -EINTR; - break; - } - schedule(); - } + ret += i; + add_timer_randomness(&extract_timer_state, nbytes); } /* Wipe data just returned from memory */ @@ -1295,9 +1329,114 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, */ void get_random_bytes(void *buf, int nbytes) { - extract_entropy(&random_state, (char *) buf, nbytes, 0); + if (sec_random_state) + extract_entropy(sec_random_state, (char *) buf, nbytes, + EXTRACT_ENTROPY_SECONDARY); + else if (random_state) + extract_entropy(random_state, (char *) buf, nbytes, 0); + else + printk(KERN_NOTICE "get_random_bytes called before " + "random driver initialization\n"); } +/********************************************************************* + * + * Functions to interface with Linux + * + *********************************************************************/ + +/* + * Initialize the random pool with standard stuff. + * + * NOTE: This is an OS-dependent function. + */ +static void init_std_data(struct entropy_store *r) +{ + struct timeval tv; + __u32 words[2]; + char *p; + int i; + + do_gettimeofday(&tv); + words[0] = tv.tv_sec; + words[1] = tv.tv_usec; + add_entropy_words(r, words, 2); + + /* + * This doesn't lock system.utsname. However, we are generating + * entropy so a race with a name set here is fine. + */ + p = (char *) &system_utsname; + for (i = sizeof(system_utsname) / sizeof(words); i; i--) { + memcpy(words, p, sizeof(words)); + add_entropy_words(r, words, sizeof(words)/4); + p += sizeof(words); + } +} + +void __init rand_initialize(void) +{ + int i; + + if (create_entropy_store(DEFAULT_POOL_SIZE, &random_state)) + return; /* Error, return */ + if (batch_entropy_init(BATCH_ENTROPY_SIZE, random_state)) + return; /* Error, return */ + if (create_entropy_store(SECONDARY_POOL_SIZE, &sec_random_state)) + return; /* Error, return */ + clear_entropy_store(random_state); + clear_entropy_store(sec_random_state); + init_std_data(random_state); +#ifdef CONFIG_SYSCTL + sysctl_init_random(random_state); +#endif + for (i = 0; i < NR_IRQS; i++) + irq_timer_state[i] = NULL; + for (i = 0; i < MAX_BLKDEV; i++) + blkdev_timer_state[i] = NULL; + memset(&keyboard_timer_state, 0, sizeof(struct timer_rand_state)); + memset(&mouse_timer_state, 0, sizeof(struct timer_rand_state)); + memset(&extract_timer_state, 0, sizeof(struct timer_rand_state)); + extract_timer_state.dont_count_entropy = 1; +} + +void rand_initialize_irq(int irq) +{ + struct timer_rand_state *state; + + if (irq >= NR_IRQS || irq_timer_state[irq]) + return; + + /* + * If kmalloc returns null, we just won't use that entropy + * source. + */ + state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) { + memset(state, 0, sizeof(struct timer_rand_state)); + irq_timer_state[irq] = state; + } +} + +void rand_initialize_blkdev(int major, int mode) +{ + struct timer_rand_state *state; + + if (major >= MAX_BLKDEV || blkdev_timer_state[major]) + return; + + /* + * If kmalloc returns null, we just won't use that entropy + * source. + */ + state = kmalloc(sizeof(struct timer_rand_state), mode); + if (state) { + memset(state, 0, sizeof(struct timer_rand_state)); + blkdev_timer_state[major] = state; + } +} + + static ssize_t random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) { @@ -1309,11 +1448,13 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) add_wait_queue(&random_read_wait, &wait); while (nbytes > 0) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); n = nbytes; - if (n > random_state.entropy_count / 8) - n = random_state.entropy_count / 8; + if (n > SEC_XFER_SIZE) + n = SEC_XFER_SIZE; + if (n > random_state->entropy_count / 8) + n = random_state->entropy_count / 8; if (n == 0) { if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -1326,7 +1467,9 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) schedule(); continue; } - n = extract_entropy(&random_state, buf, n, 1); + n = extract_entropy(sec_random_state, buf, n, + EXTRACT_ENTROPY_USER | + EXTRACT_ENTROPY_SECONDARY); if (n < 0) { retval = n; break; @@ -1351,10 +1494,12 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) } static ssize_t -random_read_unlimited(struct file * file, char * buf, +urandom_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) { - return extract_entropy(&random_state, buf, nbytes, 1); + return extract_entropy(sec_random_state, buf, nbytes, + EXTRACT_ENTROPY_USER | + EXTRACT_ENTROPY_SECONDARY); } static unsigned int @@ -1365,9 +1510,9 @@ random_poll(struct file *file, poll_table * wait) poll_wait(file, &random_read_wait, wait); poll_wait(file, &random_write_wait, wait); mask = 0; - if (random_state.entropy_count >= WAIT_INPUT_BITS) + if (random_state->entropy_count >= random_read_wakeup_thresh) mask |= POLLIN | POLLRDNORM; - if (random_state.entropy_count < WAIT_OUTPUT_BITS) + if (random_state->entropy_count < random_write_wakeup_thresh) mask |= POLLOUT | POLLWRNORM; return mask; } @@ -1378,7 +1523,6 @@ random_write(struct file * file, const char * buffer, { int ret = 0; size_t bytes; - unsigned i; __u32 buf[16]; const char *p = buffer; size_t c = count; @@ -1393,11 +1537,10 @@ random_write(struct file * file, const char * buffer, } c -= bytes; p += bytes; - - i = (unsigned)((bytes-1) / (2 * sizeof(__u32))); - do { - add_entropy_words(&random_state, buf[2*i], buf[2*i+1]); - } while (i--); + + /* Convert bytes to words */ + bytes = (bytes + 3) / sizeof(__u32); + add_entropy_words(random_state, buf, bytes); } if (p == buffer) { return (ssize_t)ret; @@ -1417,7 +1560,7 @@ random_ioctl(struct inode * inode, struct file * file, switch (cmd) { case RNDGETENTCNT: - ent_count = random_state.entropy_count; + ent_count = random_state->entropy_count; if (put_user(ent_count, (int *) arg)) return -EFAULT; return 0; @@ -1426,45 +1569,31 @@ random_ioctl(struct inode * inode, struct file * file, return -EPERM; if (get_user(ent_count, (int *) arg)) return -EFAULT; - /* - * Add i to entropy_count, limiting the result to be - * between 0 and POOLBITS. - */ - if (ent_count < -random_state.entropy_count) - random_state.entropy_count = 0; - else if (ent_count > POOLBITS) - random_state.entropy_count = POOLBITS; - else { - random_state.entropy_count += ent_count; - if (random_state.entropy_count > POOLBITS) - random_state.entropy_count = POOLBITS; - if (random_state.entropy_count < 0) - random_state.entropy_count = 0; - } + credit_entropy_store(random_state, ent_count); /* * Wake up waiting processes if we have enough * entropy. */ - if (random_state.entropy_count >= WAIT_INPUT_BITS) + if (random_state->entropy_count >= random_read_wakeup_thresh) wake_up_interruptible(&random_read_wait); return 0; case RNDGETPOOL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; p = (int *) arg; - ent_count = random_state.entropy_count; + ent_count = random_state->entropy_count; if (put_user(ent_count, p++)) return -EFAULT; if (get_user(size, p)) return -EFAULT; - if (put_user(POOLWORDS, p++)) + if (put_user(random_state->poolinfo.poolwords, p++)) return -EFAULT; if (size < 0) return -EINVAL; - if (size > POOLWORDS) - size = POOLWORDS; - if (copy_to_user(p, random_state.pool, size*sizeof(__u32))) + if (size > random_state->poolinfo.poolwords) + size = random_state->poolinfo.poolwords; + if (copy_to_user(p, random_state->pool, size*sizeof(__u32))) return -EFAULT; return 0; case RNDADDENTROPY: @@ -1481,36 +1610,25 @@ random_ioctl(struct inode * inode, struct file * file, size, &file->f_pos); if (retval < 0) return retval; - /* - * Add ent_count to entropy_count, limiting the result to be - * between 0 and POOLBITS. - */ - if (ent_count > POOLBITS) - random_state.entropy_count = POOLBITS; - else { - random_state.entropy_count += ent_count; - if (random_state.entropy_count > POOLBITS) - random_state.entropy_count = POOLBITS; - if (random_state.entropy_count < 0) - random_state.entropy_count = 0; - } + credit_entropy_store(random_state, ent_count); /* * Wake up waiting processes if we have enough * entropy. */ - if (random_state.entropy_count >= WAIT_INPUT_BITS) + if (random_state->entropy_count >= random_read_wakeup_thresh) wake_up_interruptible(&random_read_wait); return 0; case RNDZAPENTCNT: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - random_state.entropy_count = 0; + random_state->entropy_count = 0; return 0; case RNDCLEARPOOL: /* Clear the entropy pool and associated counters. */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; - rand_clear_pool(); + clear_entropy_store(random_state); + init_std_data(random_state); return 0; default: return -EINVAL; @@ -1532,7 +1650,7 @@ struct file_operations random_fops = { struct file_operations urandom_fops = { NULL, /* unrandom_lseek */ - random_read_unlimited, + urandom_read, random_write, NULL, /* urandom_readdir */ NULL, /* urandom_poll */ @@ -1543,6 +1661,210 @@ struct file_operations urandom_fops = { NULL /* no special release code */ }; +/*************************************************************** + * Random UUID interface + * + * Used here for a Boot ID, but can be useful for other kernel + * drivers. + ***************************************************************/ + +/* + * Generate random UUID + */ +void generate_random_uuid(unsigned char uuid_out[16]) +{ + get_random_bytes(uuid_out, 16); + /* Set UUID version to 4 --- truely random generation */ + uuid_out[6] = (uuid_out[6] & 0x0F) | 0x40; + /* Set the UUID variant to DCE */ + uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80; +} + +/******************************************************************** + * + * Sysctl interface + * + ********************************************************************/ + +#ifdef CONFIG_SYSCTL + +#include <linux/sysctl.h> + +static int sysctl_poolsize; +static int min_read_thresh, max_read_thresh; +static int min_write_thresh, max_write_thresh; +static char sysctl_bootid[16]; + +/* + * This function handles a request from the user to change the pool size + * of the primary entropy store. + */ +static int change_poolsize(int poolsize) +{ + struct entropy_store *new_store, *old_store; + int ret; + + if ((ret = create_entropy_store(poolsize, &new_store))) + return ret; + + add_entropy_words(new_store, random_state->pool, + random_state->poolinfo.poolwords); + credit_entropy_store(new_store, random_state->entropy_count); + + sysctl_init_random(new_store); + old_store = random_state; + random_state = batch_tqueue.data = new_store; + free_entropy_store(old_store); + return 0; +} + +static int proc_do_poolsize(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int ret; + + sysctl_poolsize = random_state->poolinfo.poolwords * 4; + + ret = proc_dointvec(table, write, filp, buffer, lenp); + if (ret || !write || + (sysctl_poolsize == random_state->poolinfo.poolwords * 4)) + return ret; + + return change_poolsize(sysctl_poolsize); +} + +static int poolsize_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + int len; + + sysctl_poolsize = random_state->poolinfo.poolwords * 4; + + /* + * We only handle the write case, since the read case gets + * handled by the default handler (and we don't care if the + * write case happens twice; it's harmless). + */ + if (newval && newlen) { + len = newlen; + if (len > table->maxlen) + len = table->maxlen; + if (copy_from_user(table->data, newval, len)) + return -EFAULT; + } + + if (sysctl_poolsize != random_state->poolinfo.poolwords * 4) + return change_poolsize(sysctl_poolsize); + + return 0; +} + +/* + * These functions is used to return both the bootid UUID, and random + * UUID. The difference is in whether table->data is NULL; if it is, + * then a new UUID is generated and returned to the user. + * + * If the user accesses this via the proc interface, it will be returned + * as an ASCII string in the standard UUID format. If accesses via the + * sysctl system call, it is returned as 16 bytes of binary data. + */ +static int proc_do_uuid(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + ctl_table fake_table; + unsigned char buf[64], tmp_uuid[16], *uuid; + + uuid = table->data; + if (!uuid) { + uuid = tmp_uuid; + uuid[8] = 0; + } + if (uuid[8] == 0) + generate_random_uuid(uuid); + + sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + fake_table.data = buf; + fake_table.maxlen = sizeof(buf); + + return proc_dostring(&fake_table, write, filp, buffer, lenp); +} + +static int uuid_strategy(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + unsigned char tmp_uuid[16], *uuid; + int len; + + if (!oldval || !oldlenp) + return 1; + + uuid = table->data; + if (!uuid) { + uuid = tmp_uuid; + uuid[8] = 0; + } + if (uuid[8] == 0) + generate_random_uuid(uuid); + + get_user(len, oldlenp); + if (len) { + if (len > 16) + len = 16; + if (copy_to_user(oldval, table->data, len)) + return -EFAULT; + if (put_user(len, oldlenp)) + return -EFAULT; + } + return 1; +} + +ctl_table random_table[] = { + {RANDOM_POOLSIZE, "poolsize", + &sysctl_poolsize, sizeof(int), 0644, NULL, + &proc_do_poolsize, &poolsize_strategy}, + {RANDOM_ENTROPY_COUNT, "entropy_avail", + NULL, sizeof(int), 0444, NULL, + &proc_dointvec}, + {RANDOM_READ_THRESH, "read_wakeup_threshold", + &random_read_wakeup_thresh, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, + &min_read_thresh, &max_read_thresh}, + {RANDOM_WRITE_THRESH, "write_wakeup_threshold", + &random_write_wakeup_thresh, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, + &min_write_thresh, &max_write_thresh}, + {RANDOM_BOOT_ID, "boot_id", + &sysctl_bootid, 16, 0444, NULL, + &proc_do_uuid, &uuid_strategy}, + {RANDOM_UUID, "uuid", + NULL, 16, 0444, NULL, + &proc_do_uuid, &uuid_strategy}, + {0} +}; + +static void sysctl_init_random(struct entropy_store *random_state) +{ + min_read_thresh = 8; + min_write_thresh = 0; + max_read_thresh = max_write_thresh = + random_state->poolinfo.poolwords * 32; + random_table[1].data = &random_state->entropy_count; +} +#endif /* CONFIG_SYSCTL */ + +/******************************************************************** + * + * Random funtions for networking + * + ********************************************************************/ + /* * TCP initial sequence number picking. This uses the random number * generator to pick an initial secret value. This value is hashed @@ -1696,7 +2018,7 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { rekey_time = tv.tv_sec; /* First three words are overwritten below. */ - get_random_bytes(&secret+3, sizeof(secret)-12); + get_random_bytes(&secret[3], sizeof(secret)-12); count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS; } @@ -1831,71 +2153,3 @@ __u32 check_tcp_syn_cookie(__u32 cookie, __u32 saddr, __u32 daddr, __u16 sport, return (cookie - tmp[17]) & COOKIEMASK; /* Leaving the data behind */ } #endif - - -#ifdef RANDOM_BENCHMARK -/* - * This is so we can do some benchmarking of the random driver, to see - * how much overhead add_timer_randomness really takes. This only - * works on a Pentium, since it depends on the timer clock... - * - * Note: the results of this benchmark as of this writing (5/27/96) - * - * On a Pentium, add_timer_randomness() takes between 150 and 1000 - * clock cycles, with an average of around 600 clock cycles. On a 75 - * MHz Pentium, this translates to 2 to 13 microseconds, with an - * average time of 8 microseconds. This should be fast enough so we - * can use add_timer_randomness() even with the fastest of interrupts... - */ -static inline unsigned long long get_clock_cnt(void) -{ - unsigned long low, high; - __asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high)); - return (((unsigned long long) high << 32) | low); -} - -__initfunc(static void -initialize_benchmark(struct random_benchmark *bench, - const char *descr, int unit)) -{ - bench->times = 0; - bench->accum = 0; - bench->max = 0; - bench->min = 1 << 31; - bench->descr = descr; - bench->unit = unit; -} - -static void begin_benchmark(struct random_benchmark *bench) -{ -#ifdef BENCHMARK_NOINT - save_flags(bench->flags); cli(); -#endif - bench->start_time = get_clock_cnt(); -} - -static void end_benchmark(struct random_benchmark *bench) -{ - unsigned long ticks; - - ticks = (unsigned long) (get_clock_cnt() - bench->start_time); -#ifdef BENCHMARK_NOINT - restore_flags(bench->flags); -#endif - if (ticks < bench->min) - bench->min = ticks; - if (ticks > bench->max) - bench->max = ticks; - bench->accum += ticks; - bench->times++; - if (bench->times == BENCHMARK_INTERVAL) { - printk("Random benchmark: %s %d: %lu min, %lu avg, " - "%lu max\n", bench->descr, bench->unit, bench->min, - bench->accum / BENCHMARK_INTERVAL, bench->max); - bench->times = 0; - bench->accum = 0; - bench->max = 0; - bench->min = 1 << 31; - } -} -#endif /* RANDOM_BENCHMARK */ diff --git a/drivers/char/raw.c b/drivers/char/raw.c new file mode 100644 index 000000000..c8936ceb6 --- /dev/null +++ b/drivers/char/raw.c @@ -0,0 +1,394 @@ +/* + * linux/drivers/char/raw.c + * + * Front-end raw character devices. These can be bound to any block + * devices to provide genuine Unix raw character device semantics. + * + * We reserve minor number 0 for a control interface. ioctl()s on this + * device are used to bind the other minor numbers to block devices. + */ + +#include <linux/fs.h> +#include <linux/iobuf.h> +#include <linux/major.h> +#include <linux/blkdev.h> +#include <linux/raw.h> +#include <linux/capability.h> +#include <asm/uaccess.h> + +#define dprintk(x...) + +static kdev_t raw_device_bindings[256] = {}; +static int raw_device_inuse[256] = {}; +static int raw_device_sector_size[256] = {}; +static int raw_device_sector_bits[256] = {}; + +extern struct file_operations * get_blkfops(unsigned int major); + +static ssize_t rw_raw_dev(int rw, struct file *, char *, size_t, loff_t *); + +ssize_t raw_read(struct file *, char *, size_t, loff_t *); +ssize_t raw_write(struct file *, const char *, size_t, loff_t *); +int raw_open(struct inode *, struct file *); +int raw_release(struct inode *, struct file *); +int raw_ctl_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + + +static struct file_operations raw_fops = { + NULL, /* llseek */ + raw_read, /* read */ + raw_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + raw_open, /* open */ + NULL, /* flush */ + raw_release, /* release */ + NULL /* fsync */ +}; + +static struct file_operations raw_ctl_fops = { + NULL, /* llseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + raw_ctl_ioctl, /* ioctl */ + NULL, /* mmap */ + raw_open, /* open */ + NULL, /* flush */ + NULL, /* no special release code */ + NULL /* fsync */ +}; + + + +void __init raw_init(void) +{ + register_chrdev(RAW_MAJOR, "raw", &raw_fops); +} + + +/* + * The raw IO open and release code needs to fake appropriate + * open/release calls to the underlying block devices. + */ + +static int bdev_open(kdev_t dev, int mode) +{ + int err = 0; + struct file dummy_file = {}; + struct dentry dummy_dentry = {}; + struct inode * inode = get_empty_inode(); + + if (!inode) + return -ENOMEM; + + dummy_file.f_op = get_blkfops(MAJOR(dev)); + if (!dummy_file.f_op) { + err = -ENODEV; + goto done; + } + + if (dummy_file.f_op->open) { + inode->i_rdev = dev; + dummy_dentry.d_inode = inode; + dummy_file.f_dentry = &dummy_dentry; + dummy_file.f_mode = mode; + err = dummy_file.f_op->open(inode, &dummy_file); + } + + done: + iput(inode); + return err; +} + +static int bdev_close(kdev_t dev) +{ + int err; + struct inode * inode = get_empty_inode(); + + if (!inode) + return -ENOMEM; + + inode->i_rdev = dev; + err = blkdev_release(inode); + iput(inode); + return err; +} + + + +/* + * Open/close code for raw IO. + */ + +int raw_open(struct inode *inode, struct file *filp) +{ + int minor; + kdev_t bdev; + int err; + int sector_size; + int sector_bits; + + minor = MINOR(inode->i_rdev); + + /* + * Is it the control device? + */ + + if (minor == 0) { + filp->f_op = &raw_ctl_fops; + return 0; + } + + /* + * No, it is a normal raw device. All we need to do on open is + * to check that the device is bound, and force the underlying + * block device to a sector-size blocksize. + */ + + bdev = raw_device_bindings[minor]; + if (bdev == NODEV) + return -ENODEV; + + err = bdev_open(bdev, filp->f_mode); + if (err) + return err; + + /* + * Don't change the blocksize if we already have users using + * this device + */ + + if (raw_device_inuse[minor]++) + return 0; + + /* + * Don't interfere with mounted devices: we cannot safely set + * the blocksize on a device which is already mounted. + */ + + sector_size = 512; + if (lookup_vfsmnt(bdev) != NULL) { + if (blksize_size[MAJOR(bdev)]) + sector_size = blksize_size[MAJOR(bdev)][MINOR(bdev)]; + } else { + if (hardsect_size[MAJOR(bdev)]) + sector_size = hardsect_size[MAJOR(bdev)][MINOR(bdev)]; + } + + set_blocksize(bdev, sector_size); + raw_device_sector_size[minor] = sector_size; + + for (sector_bits = 0; !(sector_size & 1); ) + sector_size>>=1, sector_bits++; + raw_device_sector_bits[minor] = sector_bits; + + return 0; +} + +int raw_release(struct inode *inode, struct file *filp) +{ + int minor; + kdev_t bdev; + + minor = MINOR(inode->i_rdev); + bdev = raw_device_bindings[minor]; + bdev_close(bdev); + raw_device_inuse[minor]--; + return 0; +} + + + +/* + * Deal with ioctls against the raw-device control interface, to bind + * and unbind other raw devices. + */ + +int raw_ctl_ioctl(struct inode *inode, + struct file *flip, + unsigned int command, + unsigned long arg) +{ + struct raw_config_request rq; + int err = 0; + int minor; + + switch (command) { + case RAW_SETBIND: + case RAW_GETBIND: + + /* First, find out which raw minor we want */ + + err = copy_from_user(&rq, (void *) arg, sizeof(rq)); + if (err) + break; + + minor = rq.raw_minor; + if (minor == 0 || minor > MINORMASK) { + err = -EINVAL; + break; + } + + if (command == RAW_SETBIND) { + /* + * This is like making block devices, so demand the + * same capability + */ + if (!capable(CAP_SYS_ADMIN)) { + err = -EPERM; + break; + } + + /* + * For now, we don't need to check that the underlying + * block device is present or not: we can do that when + * the raw device is opened. Just check that the + * major/minor numbers make sense. + */ + + if (rq.block_major == NODEV || + rq.block_major > MAX_BLKDEV || + rq.block_minor > MINORMASK) { + err = -EINVAL; + break; + } + + if (raw_device_inuse[minor]) { + err = -EBUSY; + break; + } + raw_device_bindings[minor] = + MKDEV(rq.block_major, rq.block_minor); + } else { + rq.block_major = MAJOR(raw_device_bindings[minor]); + rq.block_minor = MINOR(raw_device_bindings[minor]); + err = copy_to_user((void *) arg, &rq, sizeof(rq)); + } + break; + + default: + err = -EINVAL; + } + + return err; +} + + + +ssize_t raw_read(struct file *filp, char * buf, + size_t size, loff_t *offp) +{ + return rw_raw_dev(READ, filp, buf, size, offp); +} + +ssize_t raw_write(struct file *filp, const char *buf, + size_t size, loff_t *offp) +{ + return rw_raw_dev(WRITE, filp, (char *) buf, size, offp); +} + +#define SECTOR_BITS 9 +#define SECTOR_SIZE (1U << SECTOR_BITS) +#define SECTOR_MASK (SECTOR_SIZE - 1) + +ssize_t rw_raw_dev(int rw, struct file *filp, char *buf, + size_t size, loff_t *offp) +{ + struct kiobuf * iobuf; + int err; + unsigned long blocknr, blocks; + unsigned long b[KIO_MAX_SECTORS]; + size_t transferred; + int iosize; + int i; + int minor; + kdev_t dev; + unsigned long limit; + + int sector_size, sector_bits, sector_mask; + int max_sectors; + + /* + * First, a few checks on device size limits + */ + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + dev = raw_device_bindings[minor]; + sector_size = raw_device_sector_size[minor]; + sector_bits = raw_device_sector_bits[minor]; + sector_mask = sector_size- 1; + max_sectors = KIO_MAX_SECTORS >> (sector_bits - 9); + + if (blk_size[MAJOR(dev)]) + limit = (((loff_t) blk_size[MAJOR(dev)][MINOR(dev)]) << BLOCK_SIZE_BITS) >> sector_bits; + else + limit = INT_MAX; + dprintk ("rw_raw_dev: dev %d:%d (+%d)\n", + MAJOR(dev), MINOR(dev), limit); + + if ((*offp & sector_mask) || (size & sector_mask)) + return -EINVAL; + if ((*offp >> sector_bits) > limit) + return 0; + + /* + * We'll just use one kiobuf + */ + + err = alloc_kiovec(1, &iobuf); + if (err) + return err; + + /* + * Split the IO into KIO_MAX_SECTORS chunks, mapping and + * unmapping the single kiobuf as we go to perform each chunk of + * IO. + */ + + transferred = 0; + blocknr = *offp >> sector_bits; + while (size > 0) { + blocks = size >> sector_bits; + if (blocks > max_sectors) + blocks = max_sectors; + if (blocks > limit - blocknr) + blocks = limit - blocknr; + if (!blocks) + break; + + iosize = blocks << sector_bits; + + err = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize); + if (err) + break; + + for (i=0; i < blocks; i++) + b[i] = blocknr++; + + err = brw_kiovec(rw, 1, &iobuf, dev, b, sector_size, 0); + + if (err >= 0) { + transferred += err; + size -= err; + buf += err; + } + + unmap_kiobuf(iobuf); + + if (err != iosize) + break; + } + + free_kiovec(1, &iobuf); + + if (transferred) { + *offp += transferred; + return transferred; + } + + return err; +} diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index adfa0a165..d53aa4e52 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -232,7 +232,7 @@ extern inline void rc_long_delay(unsigned long delay) } /* Reset and setup CD180 chip */ -__initfunc(static void rc_init_CD180(struct riscom_board const * bp)) +static void __init rc_init_CD180(struct riscom_board const * bp) { unsigned long flags; @@ -257,7 +257,7 @@ __initfunc(static void rc_init_CD180(struct riscom_board const * bp)) } /* Main probing routine, also sets irq. */ -__initfunc(static int rc_probe(struct riscom_board *bp)) +static int __init rc_probe(struct riscom_board *bp) { unsigned char val1, val2; int irqs = 0; @@ -1027,7 +1027,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, rc_out(bp, RC_DTR, bp->DTR); } sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) @@ -1820,7 +1820,7 @@ static void rc_release_drivers(void) * addresses in this case. * */ -__initfunc(void riscom8_setup(char *str, int * ints)) +void __init riscom8_setup(char *str, int * ints) { int i; @@ -1836,7 +1836,7 @@ __initfunc(void riscom8_setup(char *str, int * ints)) /* * This routine must be called by kernel at boot time */ -__initfunc(int riscom8_init(void)) +int __init riscom8_init(void) { int i; int found = 0; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index cc0027ebe..22f936e19 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -88,8 +88,6 @@ #endif #if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */ #include <linux/init.h> -#else -#define __initfunc(x) x #endif #include "rocket_int.h" @@ -890,7 +888,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, sSetDTR(&info->channel); sSetRTS(&info->channel); } - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ROCKET_INITIALIZED)) { if (info->flags & ROCKET_HUP_NOTIFY) @@ -1958,7 +1956,7 @@ static struct pci_dev *pci_find_slot(unsigned char bus, } #endif -__initfunc(int register_PCI(int i, unsigned int bus, unsigned int device_fn)) +int __init register_PCI(int i, unsigned int bus, unsigned int device_fn) { int num_aiops, aiop, max_num_aiops, num_chan, chan; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; @@ -1973,15 +1971,7 @@ __initfunc(int register_PCI(int i, unsigned int bus, unsigned int device_fn)) if (!dev) return 0; -#if (LINUX_VERSION_CODE >= 0x020163) /* 2.1.99 */ - rcktpt_io_addr[i] = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; -#else - ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, - &port); - if (ret) - return 0; - rcktpt_io_addr[i] = port & PCI_BASE_ADDRESS_IO_MASK; -#endif + rcktpt_io_addr[i] = dev->resource[0].start; switch(dev->device) { case PCI_DEVICE_ID_RP4QUAD: str = "Quadcable"; @@ -2047,7 +2037,7 @@ __initfunc(int register_PCI(int i, unsigned int bus, unsigned int device_fn)) return(1); } -__initfunc(static int init_PCI(int boards_found)) +static int __init init_PCI(int boards_found) { unsigned char bus, device_fn; int i, count = 0; @@ -2102,7 +2092,7 @@ __initfunc(static int init_PCI(int boards_found)) } #endif -__initfunc(static int init_ISA(int i, int *reserved_controller)) +static int __init init_ISA(int i, int *reserved_controller) { int num_aiops, num_chan; int aiop, chan; @@ -2154,7 +2144,7 @@ __initfunc(static int init_ISA(int i, int *reserved_controller)) /* * The module "startup" routine; it's run when the module is loaded. */ -__initfunc(int rp_init(void)) +int __init rp_init(void) { int i, retval, pci_boards_found, isa_boards_found; int reserved_controller = 0; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 4339d5514..cc7155150 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -525,7 +525,7 @@ static struct miscdevice rtc_dev= &rtc_fops }; -__initfunc(int rtc_init(void)) +int __init rtc_init(void) { unsigned long flags; #if defined(__alpha__) || defined(__mips__) @@ -552,7 +552,7 @@ __initfunc(int rtc_init(void)) return -EIO; found: - rtc_port = edev->base_address[0]; + rtc_port = edev->resource[0].start; rtc_irq = edev->irqs[0]; /* * XXX Interrupt pin #7 in Espresso is shared between RTC and diff --git a/drivers/char/saa5249.c b/drivers/char/saa5249.c index 2e93553c5..5ab107a0f 100644 --- a/drivers/char/saa5249.c +++ b/drivers/char/saa5249.c @@ -79,9 +79,9 @@ static const int disp_modes[8][3] = -#define PAGE_WAIT 30 /* Time in jiffies between requesting page and */ +#define PAGE_WAIT (300*HZ/1000) /* Time between requesting page and */ /* checking status bits */ -#define PGBUF_EXPIRE 1500 /* Time in jiffies to wait before retransmitting */ +#define PGBUF_EXPIRE (15*HZ) /* Time to wait before retransmitting */ /* page regardless of infobits */ typedef struct { u8 pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */ @@ -107,9 +107,9 @@ struct saa5249_device #define CCTWR 34 /* I²C write/read-address of vtx-chip */ #define CCTRD 35 #define NOACK_REPEAT 10 /* Retry access this many times on failure */ -#define CLEAR_DELAY 5 /* Time in jiffies required to clear a page */ -#define I2C_TIMEOUT 300 /* open/close/SDA-check timeout in jiffies */ -#define READY_TIMEOUT 3 /* Time in jiffies to wait for ready signal of I²C-bus interface */ +#define CLEAR_DELAY (HZ/20) /* Time required to clear a page */ +#define I2C_TIMEOUT (3*HZ) /* open/close/SDA-check timeout */ +#define READY_TIMEOUT (30*HZ/1000) /* Time to wait for ready signal of I²C-bus interface */ #define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */ #define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */ diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 1529bd7ee..f0f67c0cf 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -301,7 +301,7 @@ int paste_selection(struct tty_struct *tty) poke_blanked_console(); add_wait_queue(&vt->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (test_bit(TTY_THROTTLED, &tty->flags)) { schedule(); continue; diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 6c4b9beac..99513eed6 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -2,6 +2,8 @@ * linux/drivers/char/serial.c * * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o * * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now * much more extensible to support other serial cards based on the @@ -34,6 +36,11 @@ * 4/98: Added changes to support the ARM architecture proposed by * Russell King * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald <stuartm@connecttech.com> + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -64,13 +71,16 @@ * ever possible. */ +#include <linux/config.h> +#include <linux/version.h> + #undef SERIAL_PARANOIA_CHECK #define CONFIG_SERIAL_NOPAUSE_IO #define SERIAL_DO_RESTART +#define CONFIG_SERIAL_PCI_MEMMAPPED #if 0 -/* Normally these defines are controlled by the autoconf.h */ - +/* These defines are normally controlled by the autoconf.h */ #define CONFIG_SERIAL_MANY_PORTS #define CONFIG_SERIAL_SHARE_IRQ #define CONFIG_SERIAL_DETECT_IRQ @@ -78,6 +88,19 @@ #define CONFIG_HUB6 #endif +#if (defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= 131072)) +#define ENABLE_SERIAL_PCI +#define CONFIG_SERIAL_SHARE_IRQ +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI + /* Sanity checks */ #ifdef CONFIG_SERIAL_MULTIPORT @@ -95,18 +118,11 @@ #endif #endif -/* Set of debugging defines */ - -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - #define RS_STROBE_TIME (10*HZ) #define RS_ISR_PASS_LIMIT 256 #define IRQ_T(state) \ - ((state->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + ((state->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) #define SERIAL_INLINE @@ -121,8 +137,33 @@ * End of serial driver configuration section. */ -#include <linux/config.h> +#if (LINUX_VERSION_CODE > 66304) +#define NEW_MODULES +#ifdef LOCAL_HEADERS /* We're building standalone */ +#define MODULE +#endif +#endif + +#ifdef NEW_MODULES +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif +#else /* !NEW_MODULES */ +#ifdef MODVERSIONS +#define MODULE +#endif +#endif /* NEW_MODULES */ #include <linux/module.h> + +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <asm/serial.h> +static char *serial_version = "4.30"; +#endif + #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> @@ -130,8 +171,6 @@ #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/serial_reg.h> #include <linux/major.h> #include <linux/string.h> #include <linux/fcntl.h> @@ -139,31 +178,47 @@ #include <linux/ioport.h> #include <linux/mm.h> #include <linux/malloc.h> +#if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */ #include <linux/init.h> +#else +#define __initfunc(x) x +#endif #include <linux/delay.h> #ifdef CONFIG_SERIAL_CONSOLE #include <linux/console.h> #endif +#ifdef ENABLE_SERIAL_PCI +#include <linux/pci.h> +#endif #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/uaccess.h> #include <asm/bitops.h> -#include <asm/serial.h> + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif #ifdef SERIAL_INLINE #define _INLINE_ inline #endif static char *serial_name = "Serial driver"; -static char *serial_version = "4.27"; static DECLARE_TASK_QUEUE(tq_serial); static struct tty_driver serial_driver, callout_driver; static int serial_refcount; +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 @@ -196,20 +251,33 @@ static struct serial_uart_config uart_config[] = { { "16450", 1, 0 }, { "16550", 1, 0 }, { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "cirrus", 1, 0 }, - { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "cirrus", 1, 0 }, /* usurped by cyclades.c */ + { "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH }, { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, /* usurped by cyclades.c */ + { "16C950", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, { 0, 0} }; -static struct serial_state rs_table[] = { +static struct serial_state rs_table[RS_TABLE_SIZE] = { SERIAL_PORT_DFNS /* Defined in serial.h */ }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) +#ifdef ENABLE_SERIAL_PCI +#define NR_PCI_BOARDS 8 +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; +static int serial_pci_board_idx = 0; +#define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start) +#endif /* ENABLE_SERIAL_PCI */ + static struct tty_struct *serial_table[NR_PORTS]; static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; @@ -228,7 +296,93 @@ static struct termios *serial_termios_locked[NR_PORTS]; * memory if large numbers of serial ports are open. */ static unsigned char *tmp_buf; +#ifdef DECLARE_MUTEX static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +/* + * Provide backwards compatibility for kernels prior to 2.1.XX. + */ +#if (LINUX_VERSION_CODE < 0x20000) +typedef dev_t kdev_t; +#endif + +#if (LINUX_VERSION_CODE < 0x02017E) +static signed long schedule_timeout(signed long timeout) +{ + unsigned long expire; + + expire = timeout + jiffies; + + current->timeout = jiffies + timeout; + schedule(); + + timeout = expire - jiffies; + return timeout < 0 ? 0 : timeout; +} +#endif + +#ifndef time_after +#define time_after(a,b) ((long)(b) - (long)(a) < 0) +#endif + +#if (LINUX_VERSION_CODE < 0x020100) +static inline int irq_cannonicalize(int irq) +{ + return ((irq == 2) ? 9 : irq); +} +#endif + +#if (LINUX_VERSION_CODE < 131336) +static int copy_from_user(void *to, const void *from_user, unsigned long len) +{ + int error; + + error = verify_area(VERIFY_READ, from_user, len); + if (error) + return len; + memcpy_fromfs(to, from_user, len); + return 0; +} + +static int copy_to_user(void *to_user, const void *from, unsigned long len) +{ + int error; + + error = verify_area(VERIFY_WRITE, to_user, len); + if (error) + return len; + memcpy_tofs(to_user, from, len); + return 0; +} + +static inline int signal_pending(struct task_struct *p) +{ + return (p->signal & (~p->blocked != 0)); +} + +#else +#include <asm/uaccess.h> +#endif + +#ifdef CAP_SYS_ADMIN +#define serial_isroot() (capable(CAP_SYS_ADMIN)) +#else +#define serial_isroot() (suser()) +#endif + +#if (LINUX_VERSION_CODE < 131394) /* 2.1.66 */ +#define test_and_clear_bit(x,y) clear_bit(x,y) + +static inline void remove_bh(int nr) +{ + bh_base[nr] = NULL; + bh_mask &= ~(1 << nr); +} +#endif + static inline int serial_paranoia_check(struct async_struct *info, kdev_t device, const char *routine) @@ -325,6 +479,26 @@ static inline void serial_outp(struct async_struct *info, int offset, } /* + * For the 16C950 + */ +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} + +/* * ------------------------------------------------------------ * rs_stop() and rs_start() * @@ -345,6 +519,10 @@ static void rs_stop(struct tty_struct *tty) info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } restore_flags(flags); } @@ -361,6 +539,10 @@ static void rs_start(struct tty_struct *tty) info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } restore_flags(flags); } @@ -475,7 +657,11 @@ static _INLINE_ void receive_chars(struct async_struct *info, ignore_char: *status = serial_inp(info, UART_LSR); } while (*status & UART_LSR_DR); +#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif } static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) @@ -954,7 +1140,7 @@ static int startup(struct async_struct * info) goto errout; } - if (!state->port || !state->type) { + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); @@ -973,8 +1159,28 @@ static int startup(struct async_struct * info) /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); + /* + * Turn off LCR == 0xBF so we actually set the IER + * register on the XR16C850 + */ + serial_outp(info, UART_LCR, 0); serial_outp(info, UART_IER, 0); + /* + * Now reset LCR so we can turn off the ECB bit + */ + serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, 0); + /* + * For a XR16C850, we need to set the trigger levels + */ + if (info->state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } serial_outp(info, UART_LCR, 0); } @@ -983,13 +1189,38 @@ static int startup(struct async_struct * info) serial_outp(info, UART_IER, 0); } + if (info->state->type == PORT_16C950) { + /* Wake up and initialize UART */ + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); /* Reset the UART */ + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } + /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ - if (uart_config[state->type].flags & UART_CLEAR_FIFO) - serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(info, UART_LSR); + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_MSR); /* * At this point there's no way the LSR could still be 0xFF; @@ -997,7 +1228,8 @@ static int startup(struct async_struct * info) * here. */ if (serial_inp(info, UART_LSR) == 0xff) { - if (capable(CAP_SYS_ADMIN)) { + printk("LSR safety check engaged!\n"); + if (serial_isroot()) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); } else @@ -1029,7 +1261,7 @@ static int startup(struct async_struct * info) retval = request_irq(state->irq, handler, IRQ_T(state), "serial", NULL); if (retval) { - if (capable(CAP_SYS_ADMIN)) { + if (serial_isroot()) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); @@ -1050,14 +1282,6 @@ static int startup(struct async_struct * info) figure_IRQ_timeout(state->irq); /* - * Clear the interrupt registers. - */ - /* (void) serial_inp(info, UART_LSR); */ /* (see above) */ - (void) serial_inp(info, UART_RX); - (void) serial_inp(info, UART_IIR); - (void) serial_inp(info, UART_MSR); - - /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ @@ -1075,12 +1299,7 @@ static int startup(struct async_struct * info) if (state->irq != 0) info->MCR |= UART_MCR_OUT2; } -#if defined(__alpha__) && !defined(CONFIG_PCI) - /* - * DEC did something gratutiously wrong.... - */ - info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; -#endif + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); /* @@ -1119,6 +1338,7 @@ static int startup(struct async_struct * info) /* * Set up the tty->alt_speed kludge */ +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; @@ -1129,6 +1349,7 @@ static int startup(struct async_struct * info) if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } +#endif /* * and set the speed of the serial port @@ -1215,12 +1436,7 @@ static void shutdown(struct async_struct * info) } else #endif info->MCR &= ~UART_MCR_OUT2; -#if defined(__alpha__) && !defined(CONFIG_PCI) - /* - * DEC did something gratutiously wrong.... - */ - info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; -#endif + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ /* disable break condition */ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); @@ -1252,6 +1468,37 @@ static void shutdown(struct async_struct * info) restore_flags(flags); } +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; + +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 2; + } + return baud_table[i]; +} +#endif + /* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. @@ -1259,7 +1506,6 @@ static void shutdown(struct async_struct * info) static void change_speed(struct async_struct *info, struct termios *old_termios) { - unsigned short port; int quot = 0, baud_base, baud; unsigned cflag, cval, fcr = 0; int bits; @@ -1268,7 +1514,7 @@ static void change_speed(struct async_struct *info, if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; - if (!(port = info->port)) + if (!CONFIGURED_SERIAL_PORT(info)) return; /* byte size and parity */ @@ -1300,6 +1546,18 @@ static void change_speed(struct async_struct *info, if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ baud_base = info->state->baud_base; + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; @@ -1405,6 +1663,7 @@ static void change_speed(struct async_struct *info, if (info->state->type == PORT_16750) serial_outp(info, UART_FCR, fcr); /* set fcr */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ + info->LCR = cval; /* Save LCR */ if (info->state->type != PORT_16750) serial_outp(info, UART_FCR, fcr); /* set fcr */ restore_flags(flags); @@ -1686,7 +1945,7 @@ static int set_serial_info(struct async_struct * info, change_port = (new_serial.port != state->port) || (new_serial.hub6 != state->hub6); - if (!capable(CAP_SYS_ADMIN)) { + if (!serial_isroot()) { if (change_irq || change_port || (new_serial.baud_base != state->baud_base) || (new_serial.type != state->type) || @@ -1743,11 +2002,14 @@ static int set_serial_info(struct async_struct * info, state->type = new_serial.type; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x200100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; - release_region(state->port,8); + if (state->type != PORT_UNKNOWN && state->port) + release_region(state->port,8); if (change_port || change_irq) { /* * We need to shutdown the serial port at the old @@ -1758,7 +2020,7 @@ static int set_serial_info(struct async_struct * info, info->port = state->port = new_serial.port; info->hub6 = state->hub6 = new_serial.hub6; } - if (state->type != PORT_UNKNOWN) + if ((state->type != PORT_UNKNOWN) && state->port) request_region(state->port,8,"serial(set)"); @@ -1769,6 +2031,7 @@ check_and_exit: if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) @@ -1777,6 +2040,7 @@ check_and_exit: info->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; +#endif change_speed(info, 0); } } else @@ -1805,7 +2069,9 @@ static int get_lsr_info(struct async_struct * info, unsigned int *value) status = serial_in(info, UART_LSR); restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result,value); + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; } @@ -1829,19 +2095,21 @@ static int get_modem_info(struct async_struct * info, unsigned int *value) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); - return put_user(result,value); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; } static int set_modem_info(struct async_struct * info, unsigned int cmd, unsigned int *value) { - int error; unsigned int arg; unsigned long flags; - error = get_user(arg, value); - if (error) - return error; + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) @@ -1885,6 +2153,7 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd, return -EINVAL; } save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_out(info, UART_MCR, info->MCR); restore_flags(flags); return 0; @@ -1894,7 +2163,7 @@ static int do_autoconfig(struct async_struct * info) { int retval; - if (!capable(CAP_SYS_ADMIN)) + if (!serial_isroot()) return -EPERM; if (info->state->count > 1) @@ -1917,6 +2186,22 @@ static int do_autoconfig(struct async_struct * info) /* * rs_break() --- routine which turns the break handling on or off */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +#else static void rs_break(struct tty_struct *tty, int break_state) { struct async_struct * info = (struct async_struct *)tty->driver_data; @@ -1925,17 +2210,17 @@ static void rs_break(struct tty_struct *tty, int break_state) if (serial_paranoia_check(info, tty->device, "rs_break")) return; - if (!info->port) + if (!CONFIGURED_SERIAL_PORT(info)) return; save_flags(flags); cli(); if (break_state == -1) - serial_out(info, UART_LCR, - serial_inp(info, UART_LCR) | UART_LCR_SBC); + info->LCR |= UART_LCR_SBC; else - serial_out(info, UART_LCR, - serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); restore_flags(flags); } +#endif #ifdef CONFIG_SERIAL_MULTIPORT static int get_multiport_struct(struct async_struct * info, @@ -1981,7 +2266,7 @@ static int set_multiport_struct(struct async_struct * info, int retval; void (*handler)(int, void *, struct pt_regs *); - if (!capable(CAP_SYS_ADMIN)) + if (!serial_isroot()) return -EPERM; state = info->state; @@ -2054,11 +2339,13 @@ static int set_multiport_struct(struct async_struct * info, static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - int error; struct async_struct * info = (struct async_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct *p_cuser; /* user space */ + struct serial_icounter_struct icount; unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + int retval, tmp; +#endif if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; @@ -2071,6 +2358,45 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, } switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2146,31 +2472,21 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, save_flags(flags); cli(); cnow = info->state->icount; restore_flags(flags); - p_cuser = (struct serial_icounter_struct *) arg; - error = put_user(cnow.cts, &p_cuser->cts); - if (error) return error; - error = put_user(cnow.dsr, &p_cuser->dsr); - if (error) return error; - error = put_user(cnow.rng, &p_cuser->rng); - if (error) return error; - error = put_user(cnow.dcd, &p_cuser->dcd); - if (error) return error; - error = put_user(cnow.rx, &p_cuser->rx); - if (error) return error; - error = put_user(cnow.tx, &p_cuser->tx); - if (error) return error; - error = put_user(cnow.frame, &p_cuser->frame); - if (error) return error; - error = put_user(cnow.overrun, &p_cuser->overrun); - if (error) return error; - error = put_user(cnow.parity, &p_cuser->parity); - if (error) return error; - error = put_user(cnow.brk, &p_cuser->brk); - if (error) return error; - error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); - if (error) return error; + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; return 0; - case TIOCSERGWILD: case TIOCSERSWILD: /* "setserial -W" is called in Debian boot */ @@ -2444,7 +2760,11 @@ static void rs_hangup(struct tty_struct *tty) static int block_til_ready(struct tty_struct *tty, struct file * filp, struct async_struct *info) { +#ifdef DECLARE_WAITQUEUE DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue wait = { current, NULL }; +#endif struct serial_state *state = info->state; int retval; int do_clocal = 0, extra_count = 0; @@ -2533,7 +2853,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS)); restore_flags(flags); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART @@ -2593,12 +2913,18 @@ static int get_async_struct(int line, struct async_struct **ret_info) return -ENOMEM; } memset(info, 0, sizeof(struct async_struct)); +#ifdef DECLARE_WAITQUEUE init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); +#endif info->magic = SERIAL_MAGIC; info->port = sstate->port; info->flags = sstate->flags; +#ifdef CONFIG_SERIAL_PCI_MEMMAPPED + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; +#endif info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; info->tqueue.routine = do_softint; @@ -2638,21 +2964,20 @@ static int rs_open(struct tty_struct *tty, struct file * filp) } tty->driver_data = info; info->tty = tty; - if (serial_paranoia_check(info, tty->device, "rs_open")) { - /* MOD_DEC_USE_COUNT; "info->tty" will cause this */ + if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; - } #ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count); #endif +#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif if (!tmp_buf) { page = get_free_page(GFP_KERNEL); if (!page) { - /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ return -ENOMEM; } if (tmp_buf) @@ -2668,7 +2993,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); - /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); @@ -2682,13 +3006,11 @@ static int rs_open(struct tty_struct *tty, struct file * filp) */ retval = startup(info); if (retval) { - /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ return retval; } retval = block_til_ready(tty, filp, info); if (retval) { - /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ #ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", retval); @@ -2860,6 +3182,13 @@ static _INLINE_ void show_serial_version(void) printk(" DETECT_IRQ"); #define SERIAL_OPT #endif +#ifdef ENABLE_SERIAL_PCI + printk(" SERIAL_PCI"); +#ifdef CONFIG_SERIAL_PCI_MEMMAPPED + printk(" PCI_IOMEM"); +#endif +#define SERIAL_OPT +#endif #ifdef SERIAL_OPT printk(" enabled\n"); #else @@ -2935,51 +3264,155 @@ static unsigned detect_uart_irq (struct serial_state * state) } /* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar UART's. When + * this function is called we know it is at least a StarTech 16650 V2, + * but it might be one of several StarTech UARTs, or one of its + * clones. (We treat the broken original StarTech 16650 V1 as a + * 16550A, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, status1, status2, old_fctr, old_emsr; + + /* + * Here we check for the XR16C85x family. We do this by + * checking for to see if we can replace the scratch register + * with the receive FIFO count register. + * + * XXX I don't have one of these chips, but it should also be + * possible to check for them by setting DLL and DLM to 0, and + * then reading back DLL and DLM. If the DLM reads back as + * 0x10, then the UART is a XR16C850 and the DLL contains the + * chip revision. + */ + old_fctr = serial_inp(info, UART_FCTR); + serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP); + old_emsr = serial_inp(info, UART_EMSR); + serial_outp(info, UART_EMSR, 0x00); + serial_outp(info, UART_LCR, 0x00); + scratch = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0xa5); + status1 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0x5a); + status2 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, scratch); + if ((status1 != 0xa5) || (status2 != 0x5a)) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP); + serial_outp(info, UART_EMSR, old_emsr); + serial_outp(info, UART_FCTR, old_fctr); + state->type = PORT_16850; + return; + } + serial_outp(info, UART_IER, old_fctr); + + /* + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} + +/* * This routine is called by rs_init() to initialize a specific serial * port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A, since this will determine - * whether or not we can use its FIFO features. + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. */ static void autoconfig(struct serial_state * state) { - unsigned char status1, status2, scratch, scratch2; + unsigned char status1, status2, scratch, scratch2, scratch3; struct async_struct *info, scr_info; unsigned long flags; state->type = PORT_UNKNOWN; - if (!state->port) + if (!CONFIGURED_SERIAL_PORT(state)) return; info = &scr_info; /* This is just for serial_{in,out} */ info->magic = SERIAL_MAGIC; + info->state = state; info->port = state->port; info->flags = state->flags; #ifdef CONFIG_HUB6 info->hub6 = state->hub6; #endif +#ifdef CONFIG_SERIAL_PCI_MEMMAPPED + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; +#endif save_flags(flags); cli(); - /* - * Do a simple existence test first; if we fail this, there's - * no point trying anything else. - * - * 0x80 is used as a nonsense port to prevent against false - * positives due to ISA bus float. The assumption is that - * 0x80 is a non-existent port; which should be safe since - * include/asm/io.h also makes this assumption. - */ - scratch = serial_inp(info, UART_IER); - serial_outp(info, UART_IER, 0); - outb(0xff, 0x080); - scratch2 = serial_inp(info, UART_IER); - serial_outp(info, UART_IER, scratch); - if (scratch2) { - restore_flags(flags); - return; /* We failed; there's nothing here */ + if (!state->iomem_base) { + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against + * false positives due to ISA bus float. The + * assumption is that 0x80 is a non-existent port; + * which should be safe since include/asm/io.h also + * makes this assumption. + */ + scratch = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0); + outb(0xff, 0x080); + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2) { + restore_flags(flags); + return; /* We failed; there's nothing here */ + } } /* @@ -3002,7 +3435,7 @@ static void autoconfig(struct serial_state * state) return; } } - + scratch2 = serial_in(info, UART_LCR); serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ @@ -3024,14 +3457,27 @@ static void autoconfig(struct serial_state * state) break; } if (state->type == PORT_16550A) { + /* Check for Oxford Semiconductor 16C950 */ + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV); + } + } + if (state->type == PORT_16550A) { /* Check for Startech UART's */ - serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); + serial_outp(info, UART_LCR, UART_LCR_DLAB); if (serial_in(info, UART_EFR) == 0) { state->type = PORT_16650; } else { serial_outp(info, UART_LCR, 0xBF); if (serial_in(info, UART_EFR) == 0) - state->type = PORT_16650V2; + autoconfig_startech_uarts(info, state, flags); } } if (state->type == PORT_16550A) { @@ -3041,7 +3487,16 @@ static void autoconfig(struct serial_state * state) UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 7) { + /* + * If this is a 16750, and not a cheap UART + * clone, then it should only go into 64 byte + * mode if the UART_FCR7_64BYTE bit was set + * while UART_LCR_DLAB was latched. + */ + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 6) state->type = PORT_16750; @@ -3067,20 +3522,13 @@ static void autoconfig(struct serial_state * state) return; } - request_region(info->port,8,"serial(auto)"); + if (info->port) + request_region(info->port,8,"serial(auto)"); /* * Reset the UART. */ -#if defined(__alpha__) && !defined(CONFIG_PCI) - /* - * I wonder what DEC did to the OUT1 and OUT2 lines? - * clearing them results in endless interrupts. - */ - serial_outp(info, UART_MCR, 0x0c); -#else - serial_outp(info, UART_MCR, 0x00); -#endif + serial_outp(info, UART_MCR, 0x00 | ALPHA_KLUDGE_MCR); /* Don't ask */ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); @@ -3092,13 +3540,321 @@ static void autoconfig(struct serial_state * state) int register_serial(struct serial_struct *req); void unregister_serial(int line); +#if (LINUX_VERSION_CODE > 0x20100) EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include <linux/symtab_begin.h> + X(register_serial), + X(unregister_serial), +#include <linux/symtab_end.h> +}; +#endif + +#ifdef ENABLE_SERIAL_PCI +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + * + * Note that __init is a no-op if MODULE is defined; we depend on this. + */ +static void __init pci_plx9050_fn(struct pci_dev *dev, + struct pci_board *board, + int enable) +{ + u8 data, *p; + + pci_read_config_byte(dev, PCI_COMMAND, &data); + + if (enable) + pci_write_config_byte(dev, PCI_COMMAND, + data | PCI_COMMAND_MEMORY); + + /* enable/disable interrupts */ + p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80); + writel(enable ? 0x41 : 0x00, p + 0x4c); + iounmap(p); + + if (!enable) + pci_write_config_byte(dev, PCI_COMMAND, + data & ~PCI_COMMAND_MEMORY); +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. + */ +static struct pci_board pci_boards[] = { + /* + * Vendor ID, Device ID, + * Subvendor ID, Subdevice ID, + * Number of Ports, Base (Maximum) Baud Rate, + * Offset of register holding Uart register offset + * Mask to apply to above register's value + */ + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, + SPCI_FL_BASE1, 8, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, + SPCI_FL_BASE1, 4, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, + SPCI_FL_BASE1, 2, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, + SPCI_FL_BASE1, 8, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, + SPCI_FL_BASE1, 4, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, + SPCI_FL_BASE1, 2, 1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, + SPCI_FL_BASE1, 8, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, + SPCI_FL_BASE1, 8, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, + SPCI_FL_BASE1, 4, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, + SPCI_FL_BASE1, 4, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, + SPCI_FL_BASE1, 2, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, + SPCI_FL_BASE1, 8, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, + SPCI_FL_BASE1, 8, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, + SPCI_FL_BASE1, 4, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, + SPCI_FL_BASE1, 4, 921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960V2, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, + SPCI_FL_BASE1, 2, 921600 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 8, 115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, + SPCI_FL_BASE2 | SPCI_FL_IOMEM, 2, 921600, + 0x400, 7, pci_plx9050_fn }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_IOMEM, 4, 921600, + 0x400, 7, pci_plx9050_fn }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_IOMEM, 2, 921600, + 0x400, 7, pci_plx9050_fn }, + { 0, } +}; + +/* + * Query PCI space for known serial boards + * If found, add them to the PCI device space in rs_table[] + * + * Accept a maximum of eight boards + * + */ +static void probe_serial_pci(void) +{ + u16 subvendor, subdevice; + int k, line; + struct pci_dev *dev = NULL; + struct pci_board *board; + struct serial_struct fake_state; + int uart_offset, base_baud, base_idx; + unsigned long port; + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Entered probe_serial_pci()\n"); +#endif + + if (!pcibios_present()) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Leaving probe_serial_pci() (no pcibios)\n"); +#endif + return; + } + + for(dev=pci_devices; dev; dev=dev->next) { + pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, + &subvendor); + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &subdevice); + + for (board = pci_boards; board->vendor; board++) { + if (board->vendor != (unsigned short) PCI_ANY_ID && + dev->vendor != board->vendor) + continue; + if (board->device != (unsigned short) PCI_ANY_ID && + dev->device != board->device) + continue; + if (board->subvendor != (unsigned short) PCI_ANY_ID && + subvendor != board->subvendor) + continue; + if (board->subdevice != (unsigned short) PCI_ANY_ID && + subdevice != board->subdevice) + continue; + break; + } + + if (board->vendor == 0) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG + "Found unknown serial board: %x:%x, %x:%x, %x\n", + dev->vendor, dev->device, subvendor, subdevice, + dev->class); + printk(KERN_DEBUG + " Addresses: %lx, %lx, %lx, %lx\n", + PCI_BASE_ADDRESS(dev, 0), PCI_BASE_ADDRESS(dev, 1), + PCI_BASE_ADDRESS(dev, 2), PCI_BASE_ADDRESS(dev, 3)); +#endif + continue; + } + + /* + * Run the initialization function, if any + */ + if (board->init_fn) + (board->init_fn)(dev, board, 1); + + /* + * Register the serial board in the array so we can + * shutdown the board later, if necessary. + */ + serial_pci_board[serial_pci_board_idx].board = board; + serial_pci_board[serial_pci_board_idx].dev = dev; + serial_pci_board_idx++; + + base_idx = board->flags & SPCI_FL_BASE_MASK; + port = PCI_BASE_ADDRESS(dev, base_idx); + if (board->flags & SPCI_FL_IOMEM) + port &= PCI_BASE_ADDRESS_MEM_MASK; + else + port &= PCI_BASE_ADDRESS_IO_MASK; + + /* + * Set some defaults for the loop below, which + * actually registers each serial port belonging to + * the card. + */ + uart_offset = board->uart_offset; + if (!uart_offset) + uart_offset = 8; + base_baud = board->base_baud; + if (!base_baud) + base_baud = BASE_BAUD; +#ifndef CONFIG_SERIAL_PCI_MEMMAPPED + if (board->flags & SPCI_FL_IOMEM) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG + "Can't support memory mapped PCI serial device\n"); +#endif + continue; + } +#endif + memset(&fake_state, 0, sizeof(fake_state)); + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG + "Found Serial PCI device: %x:%x, %x:%x, %x\n", + dev->vendor, dev->device, subvendor, subdevice, + dev->class); + printk(KERN_DEBUG + " IRQ: %d, base: %lx (%s), num_ports: %d\n", + dev->irq, port, board->flags & SPCI_FL_IOMEM ? + "iomem" : "port", board->num_ports); +#endif + + for (k=0; k < board->num_ports; k++) { + if (board->flags & SPCI_FL_BASE_TABLE) { + port = PCI_BASE_ADDRESS(dev, base_idx++); + if (board->flags & SPCI_FL_IOMEM) + port &= PCI_BASE_ADDRESS_MEM_MASK; + else + port &= PCI_BASE_ADDRESS_IO_MASK; + } + fake_state.irq = dev->irq; + fake_state.port = port; +#ifdef CONFIG_SERIAL_PCI_MEMMAPPED + if (board->flags & SPCI_FL_IOMEM) { + fake_state.iomem_base = + ioremap(port, board->uart_offset); + fake_state.iomem_reg_shift = board->reg_shift; + fake_state.port = 0; + } +#endif + port += uart_offset; + fake_state.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; + line = register_serial(&fake_state); + if (line < 0) + break; + rs_table[line].baud_base = base_baud; + } + } + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); +#endif + return; +} + +#endif /* ENABLE_SERIAL_PCI */ /* * The serial driver boot-time initialization code! */ -__initfunc(int rs_init(void)) +int __init rs_init(void) { int i; struct serial_state * state; @@ -3142,10 +3898,12 @@ __initfunc(int rs_init(void)) memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) serial_driver.driver_name = "serial"; +#endif serial_driver.name = "ttyS"; serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64; + serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; @@ -3169,14 +3927,18 @@ __initfunc(int rs_init(void)) serial_driver.ioctl = rs_ioctl; serial_driver.throttle = rs_throttle; serial_driver.unthrottle = rs_unthrottle; - serial_driver.send_xchar = rs_send_xchar; serial_driver.set_termios = rs_set_termios; serial_driver.stop = rs_stop; serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; serial_driver.wait_until_sent = rs_wait_until_sent; serial_driver.read_proc = rs_read_proc; +#endif /* * The callout device is just like normal device except for @@ -3186,8 +3948,10 @@ __initfunc(int rs_init(void)) callout_driver.name = "cua"; callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) callout_driver.read_proc = 0; callout_driver.proc_entry = 0; +#endif if (tty_register_driver(&serial_driver)) panic("Couldn't register serial driver\n"); @@ -3209,15 +3973,11 @@ __initfunc(int rs_init(void)) state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; state->irq = irq_cannonicalize(state->irq); - if (check_region(state->port,8)) + if (state->port && check_region(state->port,8)) continue; if (state->flags & ASYNC_BOOT_AUTOCONF) autoconfig(state); } - /* - * Detect the IRQ only once every port is initialised, - * because some 16450 do not reset to 0 the MCR register. - */ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { if (state->type == PORT_UNKNOWN) continue; @@ -3226,11 +3986,14 @@ __initfunc(int rs_init(void)) && (state->port != 0)) state->irq = detect_uart_irq(state); printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n", - state->line, + state->line + SERIAL_DEV_OFFSET, (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", state->port, state->irq, uart_config[state->type].name); } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif return 0; } @@ -3247,7 +4010,8 @@ int register_serial(struct serial_struct *req) save_flags(flags); cli(); for (i = 0; i < NR_PORTS; i++) { - if (rs_table[i].port == req->port) + if ((rs_table[i].port == req->port) && + (rs_table[i].iomem_base == req->iomem_base)) break; } if (i == NR_PORTS) { @@ -3270,6 +4034,10 @@ int register_serial(struct serial_struct *req) state->irq = req->irq; state->port = req->port; state->flags = req->flags; +#ifdef CONFIG_SERIAL_PCI_MEMMAPPED + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; +#endif autoconfig(state); if (state->type == PORT_UNKNOWN) { @@ -3279,13 +4047,16 @@ int register_serial(struct serial_struct *req) } restore_flags(flags); - if ((state->flags & ASYNC_AUTO_IRQ) && (state->port != 0)) + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) state->irq = detect_uart_irq(state); - printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n", - state->line, state->port, state->irq, - uart_config[state->type].name); - return state->line; + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + (unsigned long)state->port, + state->irq, uart_config[state->type].name); + return state->line + SERIAL_DEV_OFFSET; } void unregister_serial(int line) @@ -3313,6 +4084,7 @@ void cleanup_module(void) unsigned long flags; int e1, e2; int i; + struct async_struct *info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ save_flags(flags); @@ -3330,9 +4102,26 @@ void cleanup_module(void) restore_flags(flags); for (i = 0; i < NR_PORTS; i++) { - if (rs_table[i].type != PORT_UNKNOWN) + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) release_region(rs_table[i].port, 8); + info = rs_table[i].info; + if (info) { + rs_table[i].info = NULL; + kfree_s(info, sizeof(struct async_struct)); + } +#if defined(ENABLE_SERIAL_PCI) && defined (CONFIG_SERIAL_PCI_MEMMAPPED) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif } +#ifdef ENABLE_SERIAL_PCI + for (i=0; i < serial_pci_board_idx; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (brd->board->init_fn) + (brd->board->init_fn)(brd->dev, brd->board, 0); + } +#endif if (tmp_buf) { free_page((unsigned long) tmp_buf); tmp_buf = NULL; @@ -3466,7 +4255,7 @@ static kdev_t serial_console_device(struct console *c) * - initialize the serial port * Return non-zero if we didn't find a serial port. */ -__initfunc(static int serial_console_setup(struct console *co, char *options)) +static int __init serial_console_setup(struct console *co, char *options) { struct serial_state *ser; unsigned cval; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 3851616ab..5142c8495 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1500,7 +1500,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) | ((status & CyDCD) ? TIOCM_CAR : 0) | ((status & CyDSR) ? TIOCM_DSR : 0) | ((status & CyCTS) ? TIOCM_CTS : 0); - cy_put_user(result,(unsigned long *) value); + cy_put_user(result,(unsigned int *) value); return 0; } /* get_modem_info */ @@ -2094,7 +2094,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif } restore_flags(flags); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED) ){ if (info->flags & ASYNC_HUP_NOTIFY) { diff --git a/drivers/char/softdog.c b/drivers/char/softdog.c index c8f21a184..92cc0ea3e 100644 --- a/drivers/char/softdog.c +++ b/drivers/char/softdog.c @@ -23,6 +23,9 @@ * Added soft_margin; use upon insmod to change the timer delay. * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate * minors. + * + * 19980911 Alan Cox + * Made SMP safe for 2.3.x */ #include <linux/module.h> @@ -106,10 +109,8 @@ static void softdog_ping(void) /* * Refresh the timer. */ - del_timer(&watchdog_ticktock); - watchdog_ticktock.expires=jiffies + (soft_margin * HZ); - add_timer(&watchdog_ticktock); - return; + + mod_timer(&watchdog_ticktock, jiffies + (soft_margin * HZ)); } static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos) @@ -178,7 +179,7 @@ static struct miscdevice softdog_miscdev= &softdog_fops }; -__initfunc(void watchdog_init(void)) +void __init watchdog_init(void) { misc_register(&softdog_miscdev); init_timer(&watchdog_ticktock); diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index c6fecb819..22014dacf 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1398,7 +1398,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } } sti(); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) @@ -1755,7 +1755,7 @@ static int sx_get_modem_info(struct specialix_port * port, unsigned int *value) |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ | ((status & MSVR_CTS) ? TIOCM_CTS : 0); } - put_user(result,(unsigned long *) value); + put_user(result,(unsigned int *) value); return 0; } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 23b8df504..b25be2cd0 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -4,7 +4,7 @@ * stallion.c -- stallion multiport serial driver. * * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). - * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). + * Copyright (C) 1994-1996 Greg Ungerer. * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. @@ -135,7 +135,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); */ static char *stl_drvtitle = "Stallion Multiport Serial Driver"; static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.5.1"; +static char *stl_drvversion = "5.6.0"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -2280,7 +2280,7 @@ static void stl_offintr(void *private) * interrupt across multiple boards. */ -__initfunc(static int stl_mapirq(int irq, char *name)) +static int __init stl_mapirq(int irq, char *name) { int rc, i; @@ -2294,7 +2294,7 @@ __initfunc(static int stl_mapirq(int irq, char *name)) break; } if (i >= stl_numintrs) { - if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) { + if (request_irq(irq, stl_intr, SA_SHIRQ, name, NULL) != 0) { printk("STALLION: failed to register interrupt " "routine for %s irq=%d\n", name, irq); rc = -ENODEV; @@ -2311,7 +2311,7 @@ __initfunc(static int stl_mapirq(int irq, char *name)) * Initialize all the ports on a panel. */ -__initfunc(static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)) +static int __init stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) { stlport_t *portp; int chipmask, i; @@ -2348,6 +2348,8 @@ __initfunc(static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)) portp->callouttermios = stl_deftermios; portp->tqueue.routine = stl_offintr; portp->tqueue.data = portp; + init_waitqueue_head(&portp->open_wait); + init_waitqueue_head(&portp->close_wait); portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; @@ -2700,7 +2702,7 @@ static int inline stl_initech(stlbrd_t *brdp) * since the initial search and setup is very different. */ -__initfunc(static int stl_brdinit(stlbrd_t *brdp)) +static int __init stl_brdinit(stlbrd_t *brdp) { int i; @@ -2780,7 +2782,7 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp) #if DEBUG printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype, - dev->bus->number, dev->devfn); + devp->bus->number, devp->devfn); #endif if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL) @@ -2798,8 +2800,8 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp) */ #if DEBUG printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__, - devp->base_address[0], devp->base_address[1], - devp->base_address[2], devp->base_address[3], devp->irq); + devp->resource[0].start, devp->resource[1].start, + devp->resource[2].start, devp->resource[3].start, devp->irq); #endif /* @@ -2808,21 +2810,21 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp) */ switch (brdtype) { case BRD_ECHPCI: - brdp->ioaddr2 = (devp->base_address[0] & + brdp->ioaddr2 = (devp->resource[0].start & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr1 = (devp->base_address[1] & + brdp->ioaddr1 = (devp->resource[1].start & PCI_BASE_ADDRESS_IO_MASK); break; case BRD_ECH64PCI: - brdp->ioaddr2 = (devp->base_address[2] & + brdp->ioaddr2 = (devp->resource[2].start & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr1 = (devp->base_address[1] & + brdp->ioaddr1 = (devp->resource[1].start & PCI_BASE_ADDRESS_IO_MASK); break; case BRD_EASYIOPCI: - brdp->ioaddr1 = (devp->base_address[2] & + brdp->ioaddr1 = (devp->resource[2].start & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr2 = (devp->base_address[1] & + brdp->ioaddr2 = (devp->resource[1].start & PCI_BASE_ADDRESS_IO_MASK); break; default: @@ -3204,7 +3206,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns /*****************************************************************************/ -__initfunc(int stl_init(void)) +int __init stl_init(void) { printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); @@ -4209,6 +4211,7 @@ static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr) if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + len = MIN(len, sizeof(stl_unwanted)); outb((RDSR + portp->uartaddr), ioaddr); insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; @@ -5175,6 +5178,7 @@ static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack) if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) || ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) { + len = MIN(len, sizeof(stl_unwanted)); outb(GRXFIFO, (ioaddr + XP_ADDR)); insb((ioaddr + XP_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; diff --git a/drivers/char/sx.c b/drivers/char/sx.c new file mode 100644 index 000000000..7e5ff9465 --- /dev/null +++ b/drivers/char/sx.c @@ -0,0 +1,2684 @@ + +/* sx.c -- driver for the Specialix SX series cards. + * + * This driver will also support the older SI, and XIO cards. + * + * + * (C) 1998 R.E.Wolff@BitWizard.nl + * + * Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous + * version of this driver. Some fragments may have been copied. (none + * yet :-) + * + * Specialix pays for the development and support of this driver. + * Please DO contact support@specialix.co.uk if you require + * support. But please read the documentation (sx.txt) first. + * + * + * + * 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. + * + * Revision history: + * $Log: sx.c,v $ + * Revision 1.26 1999/08/05 15:22:14 wolff + * - Port to 2.3.x + * - Reformatted to Linus' liking. + * + * Revision 1.25 1999/07/30 14:24:08 wolff + * Had accidentally left "gs_debug" set to "-1" instead of "off" (=0). + * + * Revision 1.24 1999/07/28 09:41:52 wolff + * - I noticed the remark about use-count straying in sx.txt. I checked + * sx_open, and found a few places where that could happen. I hope it's + * fixed now. + * + * Revision 1.23 1999/07/28 08:56:06 wolff + * - Fixed crash when sx_firmware run twice. + * - Added sx_slowpoll as a module parameter (I guess nobody really wanted + * to change it from the default... ) + * - Fixed a stupid editing problem I introduced in 1.22. + * - Fixed dropping characters on a termios change. + * + * Revision 1.22 1999/07/26 21:01:43 wolff + * Russell Brown noticed that I had overlooked 4 out of six modem control + * signals in sx_getsignals. Ooops. + * + * Revision 1.21 1999/07/23 09:11:33 wolff + * I forgot to free dynamically allocated memory when the driver is unloaded. + * + * Revision 1.20 1999/07/20 06:25:26 wolff + * The "closing wait" wasn't honoured. Thanks to James Griffiths for + * reporting this. + * + * Revision 1.19 1999/07/11 08:59:59 wolff + * Fixed an oops in close, when an open was pending. Changed the memtest + * a bit. Should also test the board in word-mode, however my card fails the + * memtest then. I still have to figure out what is wrong... + * + * Revision 1.18 1999/06/10 09:38:42 wolff + * Changed the format of the firmware revision from %04x to %x.%02x . + * + * Revision 1.17 1999/06/04 09:44:35 wolff + * fixed problem: reference to pci stuff when config_pci was off... + * Thanks to Jorge Novo for noticing this. + * + * Revision 1.16 1999/06/02 08:30:15 wolff + * added/removed the workaround for the DCD bug in the Firmware. + * A bit more debugging code to locate that... + * + * Revision 1.15 1999/06/01 11:35:30 wolff + * when DCD is left low (floating?), on TA's the firmware first tells us + * that DCD is high, but after a short while suddenly comes to the + * conclusion that it is low. All this would be fine, if it weren't that + * Unix requires us to send a "hangup" signal in that case. This usually + * all happens BEFORE the program has had a chance to ioctl the device + * into clocal mode.. + * + * Revision 1.14 1999/05/25 11:18:59 wolff + * Added PCI-fix. + * Added checks for return code of sx_sendcommand. + * Don't issue "reconfig" if port isn't open yet. (bit us on TA modules...) + * + * Revision 1.13 1999/04/29 15:18:01 wolff + * Fixed an "oops" that showed on SuSE 6.0 systems. + * Activate DTR again after stty 0. + * + * Revision 1.12 1999/04/29 07:49:52 wolff + * Improved "stty 0" handling a bit. (used to change baud to 9600 assuming + * the connection would be dropped anyway. That is not always the case, + * and confuses people). + * Told the card to always monitor the modem signals. + * Added support for dynamic gs_debug adjustments. + * Now tells the rest of the system the number of ports. + * + * Revision 1.11 1999/04/24 11:11:30 wolff + * Fixed two stupid typos in the memory test. + * + * Revision 1.10 1999/04/24 10:53:39 wolff + * Added some of Christian's suggestions. + * Fixed an HW_COOK_IN bug (ISIG was not in I_OTHER. We used to trust the + * card to send the signal to the process.....) + * + * Revision 1.9 1999/04/23 07:26:38 wolff + * Included Christian Lademann's 2.0 compile-warning fixes and interrupt + * assignment redesign. + * Cleanup of some other stuff. + * + * Revision 1.8 1999/04/16 13:05:30 wolff + * fixed a DCD change unnoticed bug. + * + * Revision 1.7 1999/04/14 22:19:51 wolff + * Fixed typo that showed up in 2.0.x builds (get_user instead of Get_user!) + * + * Revision 1.6 1999/04/13 18:40:20 wolff + * changed misc-minor to 161, as assigned by HPA. + * + * Revision 1.5 1999/04/13 15:12:25 wolff + * Fixed use-count leak when "hangup" occurred. + * Added workaround for a stupid-PCIBIOS bug. + * + * + * Revision 1.4 1999/04/01 22:47:40 wolff + * Fixed < 1M linux-2.0 problem. + * (vremap isn't compatible with ioremap in that case) + * + * Revision 1.3 1999/03/31 13:45:45 wolff + * Firmware loading is now done through a separate IOCTL. + * + * Revision 1.2 1999/03/28 12:22:29 wolff + * rcs cleanup + * + * Revision 1.1 1999/03/28 12:10:34 wolff + * Readying for release on 2.0.x (sorry David, 1.01 becomes 1.1 for RCS). + * + * Revision 0.12 1999/03/28 09:20:10 wolff + * Fixed problem in 0.11, continueing cleanup. + * + * Revision 0.11 1999/03/28 08:46:44 wolff + * cleanup. Not good. + * + * Revision 0.10 1999/03/28 08:09:43 wolff + * Fixed loosing characters on close. + * + * Revision 0.9 1999/03/21 22:52:01 wolff + * Ported back to 2.2.... (minor things) + * + * Revision 0.8 1999/03/21 22:40:33 wolff + * Port to 2.0 + * + * Revision 0.7 1999/03/21 19:06:34 wolff + * Fixed hangup processing. + * + * Revision 0.6 1999/02/05 08:45:14 wolff + * fixed real_raw problems. Inclusion into kernel imminent. + * + * Revision 0.5 1998/12/21 23:51:06 wolff + * Snatched a nasty bug: sx_transmit_chars was getting re-entered, and it + * shouldn't have. THATs why I want to have transmit interrupts even when + * the buffer is empty. + * + * Revision 0.4 1998/12/17 09:34:46 wolff + * PPP works. ioctl works. Basically works! + * + * Revision 0.3 1998/12/15 13:05:18 wolff + * It works! Wow! Gotta start implementing IOCTL and stuff.... + * + * Revision 0.2 1998/12/01 08:33:53 wolff + * moved over to 2.1.130 + * + * Revision 0.1 1998/11/03 21:23:51 wolff + * Initial revision. Detects SX card. + * + * */ + + +#define RCS_ID "$Id: sx.c,v 1.26 1999/08/05 15:22:14 wolff Exp $" +#define RCS_REV "$Revision: 1.26 $" + + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/kdev_t.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/tqueue.h> +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/malloc.h> +#include <linux/miscdevice.h> + +/* The 3.0.0 version of sxboards/sxwindow.h uses BYTE and WORD.... */ +#define BYTE u8 +#define WORD u16 + +/* .... but the 3.0.4 version uses _u8 and _u16. */ +#define _u8 u8 +#define _u16 u16 + +#include "sxboards.h" +#include "sxwindow.h" + + +/* I don't think that this driver can handle more than 256 ports on + one machine. You'll have to increase the number of boards in sx.h + if you want more than 4 boards. */ + + +/* ************************************************************** */ +/* * This section can be removed when 2.0 becomes outdated.... * */ +/* ************************************************************** */ + + +#if LINUX_VERSION_CODE < 0x020100 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 0x020200 /* less than 2.2.x */ +#warning "Please use a 2.2.x kernel. " +#else +#if LINUX_VERSION_CODE < 0x020300 /* less than 2.3.x */ +#define TWO_TWO +#else +#define TWO_THREE +#endif +#endif +#endif + +#ifdef TWO_ZERO + +/* Here is the section that makes the 2.2 compatible driver source + work for 2.0 too! We mostly try to adopt the "new thingies" from 2.2, + and provide for compatibility stuff here if possible. */ + +#include <linux/bios32.h> + +#define Get_user(a,b) a = get_user(b) +#define Put_user(a,b) 0,put_user(a,b) +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) + +static inline int copy_from_user(void *to,const void *from, int c) +{ + memcpy_fromfs(to, from, c); + return 0; +} + +#define pci_present pcibios_present +#define pci_read_config_word pcibios_read_config_word +#define pci_read_config_dword pcibios_read_config_dword + +static inline unsigned char get_irq (unsigned char bus, unsigned char fn) +{ + unsigned char t; + pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &t); + return t; +} + +static inline void *ioremap(unsigned long base, long length) +{ + if (base < 0x100000) return (void *)base; + return vremap (base, length); +} + +#define my_iounmap(x, b) (((long)x<0x100000)?0:vfree ((void*)x)) + +#define capable(x) suser() + +#define queue_task queue_task_irq_off +#define tty_flip_buffer_push(tty) queue_task(&tty->flip.tqueue, &tq_timer) +#define signal_pending(current) (current->signal & ~current->blocked) +#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +#define time_after(t1,t2) (((long)t1-t2) > 0) + + +#define test_and_set_bit(nr, addr) set_bit(nr, addr) +#define test_and_clear_bit(nr, addr) clear_bit(nr, addr) + +/* Not yet implemented on 2.0 */ +#define ASYNC_SPD_SHI -1 +#define ASYNC_SPD_WARP -1 + + +/* Ugly hack: the driver_name doesn't exist in 2.0.x . So we define it + to the "name" field that does exist. As long as the assignments are + done in the right order, there is nothing to worry about. */ +#define driver_name name + +/* Should be in a header somewhere. They are in tty.h on 2.2 */ +#define TTY_HW_COOK_OUT 14 /* Flag to tell ntty what we can handle */ +#define TTY_HW_COOK_IN 15 /* in hardware - output and input */ + +/* The return type of a "close" routine. */ +#define INT void +#define NO_ERROR /* Nothing */ + +#else + +/* The 2.2.x compatibility section. */ +#include <asm/uaccess.h> + + +#define Get_user(a,b) get_user(a,b) +#define Put_user(a,b) put_user(a,b) +#define get_irq(pdev) pdev->irq + +#define INT int +#define NO_ERROR 0 + +#define my_iounmap(x,b) (iounmap((char *)(b))) + +#endif + +#ifndef TWO_THREE +/* These are new in 2.3. The source now uses 2.3 syntax, and here is + the compatibility define... */ +#define wait_queue_head_t struct wait_queue * +#define DECLARE_MUTEX(name) struct semaphore name = MUTEX +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } + +#endif + + + +#include "generic_serial.h" +#include "sx.h" + + +/* ************************************************************** */ +/* * End of compatibility section.. * */ +/* ************************************************************** */ + + + +/* Why the hell am I defining these here? */ +#define SX_TYPE_NORMAL 1 +#define SX_TYPE_CALLOUT 2 + +#ifndef SX_NORMAL_MAJOR +/* This allows overriding on the compiler commandline, or in a "major.h" + include or something like that */ +#define SX_NORMAL_MAJOR 32 +#define SX_CALLOUT_MAJOR 33 +#endif + +#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 +#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000 +#endif + + + +/* Configurable options: + (Don't be too sure that it'll work if you toggle them) */ + +/* Am I paranoid or not ? ;-) */ +#undef SX_PARANOIA_CHECK + + +/* 20 -> 2000 per second. The card should rate-limit interrupts at 100 + Hz, but it is user configurable. I don't recommend going above 1000 + Hz. The interrupt ratelimit might trigger if the interrupt is + shared with a very active other device. */ +#define IRQ_RATE_LIMIT 20 + +/* Sharing interrupts is possible now. If the other device wants more + than 2000 interrupts per second, we'd gracefully decline further + interrupts. That's not what we want. On the other hand, if the + other device interrupts 2000 times a second, don't use the SX + interrupt. Use polling. */ +#undef IRQ_RATE_LIMIT + + +#if 0 +/* Not implemented */ +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#define SX_REPORT_FIFO +#define SX_REPORT_OVERRUN +#endif + + +/* Function prototypes */ +static void sx_disable_tx_interrupts (void * ptr); +static void sx_enable_tx_interrupts (void * ptr); +static void sx_disable_rx_interrupts (void * ptr); +static void sx_enable_rx_interrupts (void * ptr); +static int sx_get_CD (void * ptr); +static void sx_shutdown_port (void * ptr); +static void sx_set_real_termios (void *ptr); +static void sx_hungup (void *ptr); +static void sx_close (void *ptr); +static int sx_chars_in_buffer (void * ptr); +static int sx_init_board (struct sx_board *board); +static int sx_init_portstructs (int nboards, int nports); +static int sx_fw_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int sx_fw_open(struct inode *inode, struct file *filp); +static INT sx_fw_release(struct inode *inode, struct file *filp); +static int sx_init_drivers(void); +void my_hd (unsigned char *addr, int len); + + + +static struct tty_driver sx_driver, sx_callout_driver; + +static struct tty_struct * sx_table[SX_NPORTS] = { NULL, }; +static struct termios ** sx_termios; +static struct termios ** sx_termios_locked; + +struct sx_board boards[SX_NBOARDS]; +struct sx_port *sx_ports; +int sx_refcount; +int sx_initialized = 0; +int sx_nports = 0; +int sx_debug = 0; + + +/* You can have the driver poll your card. + - Set sx_poll to 1 to poll every timer tick (10ms on Intel). + This is used when the card cannot use an interrupt for some reason. + + - set sx_slowpoll to 100 to do an extra poll once a second (on Intel). If + the driver misses an interrupt (report this if it DOES happen to you!) + everything will continue to work.... + */ +int sx_poll = 1; +int sx_slowpoll = 0; + +/* The card limits the number of interrupts per second. + At 115k2 "100" should be sufficient. + If you're using higher baudrates, you can increase this... + */ + +int sx_maxints = 100; + +/* These are the only open spaces in my computer. Yours may have more + or less.... */ +int sx_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000, + 0xc8000, 0xd8000, 0xe8000}; +int si_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000, + 0xc8000, 0xd8000, 0xe8000}; + +#define NR_SX_ADDRS (sizeof(sx_probe_addrs)/sizeof (int)) +#define NR_SI_ADDRS (sizeof(si_probe_addrs)/sizeof (int)) + + +/* Set the mask to all-ones. This alas, only supports 32 interrupts. + Some architectures may need more. */ +int sx_irqmask = -1; + +#ifndef TWO_ZERO +#ifdef MODULE +MODULE_PARM(sx_poll, "i"); +MODULE_PARM(sx_slowpoll, "i"); +MODULE_PARM(sx_maxints, "i"); +MODULE_PARM(sx_debug, "i"); +MODULE_PARM(sx_irqmask, "i"); +#endif +#endif + +static struct real_driver sx_real_driver = { + sx_disable_tx_interrupts, + sx_enable_tx_interrupts, + sx_disable_rx_interrupts, + sx_enable_rx_interrupts, + sx_get_CD, + sx_shutdown_port, + sx_set_real_termios, + sx_chars_in_buffer, + sx_close, + sx_hungup, + NULL +}; + + +/* + This driver can spew a whole lot of debugging output at you. If you + need maximum performance, you should disable the DEBUG define. To + aid in debugging in the field, I'm leaving the compile-time debug + features enabled, and disable them "runtime". That allows me to + instruct people with problems to enable debugging without requiring + them to recompile... +*/ +#define DEBUG + + +#ifdef DEBUG +#define sx_dprintk(f, str...) if (sx_debug & f) printk (str) +#else +#define sx_dprintk(f, str...) /* nothing */ +#endif + + + +#define func_enter() sx_dprintk (SX_DEBUG_FLOW, "sx: enter " __FUNCTION__ "\n") +#define func_exit() sx_dprintk (SX_DEBUG_FLOW, "sx: exit " __FUNCTION__ "\n") + +#define func_enter2() sx_dprintk (SX_DEBUG_FLOW, "sx: enter " __FUNCTION__ \ + "(port %d)\n", port->line) + + + + +/* + * Firmware loader driver specific routines + * + */ + +static struct file_operations sx_fw_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + sx_fw_ioctl, + NULL, /* mmap */ + sx_fw_open, +#ifndef TWO_ZERO + NULL, /* flush */ +#endif + sx_fw_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ +}; + +struct miscdevice sx_fw_device = { + SXCTL_MISC_MINOR, "sxctl", &sx_fw_fops +}; + + + + + +#ifdef SX_PARANOIA_CHECK + +/* This doesn't work. Who's paranoid around here? Not me! */ + +static inline int sx_paranoia_check(struct sx_port const * port, + kdev_t device, const char *routine) +{ + + static const char *badmagic = + KERN_ERR "sx: Warning: bad sx port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "sx: Warning: null sx port for device %s in %s\n"; + + if (!port) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SX_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } + + return 0; +} +#else +#define sx_paranoia_check(a,b,c) 0 +#endif + +/* The timeouts. First try 30 times as fast as possible. Then give + the card some time to breathe between accesses. (Otherwise the + processor on the card might not be able to access its OWN bus... */ + +#define TIMEOUT_1 30 +#define TIMEOUT_2 1000000 + + +/* This needs redoing for Alpha -- REW -- Done. */ + +inline void write_sx_byte (struct sx_board *board, int offset, u8 byte) +{ + writeb (byte, board->base+offset); +} + +inline u8 read_sx_byte (struct sx_board *board, int offset) +{ + return readb (board->base+offset); +} + + +inline void write_sx_word (struct sx_board *board, int offset, u16 word) +{ + writew (word, board->base+offset); +} + +inline u16 read_sx_word (struct sx_board *board, int offset) +{ + return readw (board->base + offset); +} + + +int sx_busy_wait_eq (struct sx_board *board, + int offset, + int mask, + int correctval) +{ + int i; + + func_enter (); + + for (i=0; i < TIMEOUT_1 > 0;i++) + if ((read_sx_byte (board, offset) & mask) == correctval) { + func_exit (); + return 1; + } + + for (i=0; i < TIMEOUT_2 > 0;i++) { + if ((read_sx_byte (board, offset) & mask) == correctval) { + func_exit (); + return 1; + } + udelay (1); + } + + func_exit (); + return 0; +} + + +int sx_busy_wait_neq (struct sx_board *board, + int offset, + int mask, + int badval) +{ + int i; + + func_enter (); + + for (i=0; i < TIMEOUT_1 > 0;i++) + if ((read_sx_byte (board, offset) & mask) != badval) { + func_exit (); + return 1; + } + + for (i=0; i < TIMEOUT_2 > 0;i++) { + if ((read_sx_byte (board, offset) & mask) != badval) { + func_exit (); + return 1; + } + udelay (1); + } + + func_exit (); + return 0; +} + + + +/* 5.6.4 of 6210028 r2.3 */ +int sx_reset (struct sx_board *board) +{ + func_enter (); + + if (IS_SX_BOARD (board)) { + + write_sx_byte (board, SX_CONFIG, 0); + write_sx_byte (board, SX_RESET, 1); /* Value doesn't matter */ + + if (!sx_busy_wait_eq (board, SX_RESET_STATUS, 1, 0)) { + printk (KERN_INFO "sx: Card doesn't respond to reset....\n"); + return 0; + } + } else { + /* Gory details of the SI/ISA board */ + write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_SET); + write_sx_byte (board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_CLEAR); + write_sx_byte (board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_CLEAR); + write_sx_byte (board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_CLEAR); + write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR); + write_sx_byte (board, SI2_ISA_IRQSET, SI2_ISA_IRQSET_CLEAR); + } + + func_exit (); + return 1; +} + + +/* This doesn't work on machines where "NULL" isn't 0 */ +/* If you have one of those, someone will need to write + the equivalent of this, which will amount to about 3 lines. I don't + want to complicate this right now. -- REW + (See, I do write comments every now and then :-) */ +#define OFFSETOF(strct, elem) ((long)&(((struct strct *)NULL)->elem)) + + +#define CHAN_OFFSET(port,elem) (port->ch_base + OFFSETOF (_SXCHANNEL, elem)) +#define MODU_OFFSET(board,addr,elem) (addr + OFFSETOF (_SXMODULE, elem)) +#define BRD_OFFSET(board,elem) (OFFSETOF (_SXCARD, elem)) + + +#define sx_write_channel_byte(port, elem, val) \ + write_sx_byte (port->board, CHAN_OFFSET (port, elem), val) + +#define sx_read_channel_byte(port, elem) \ + read_sx_byte (port->board, CHAN_OFFSET (port, elem)) + +#define sx_write_channel_word(port, elem, val) \ + write_sx_word (port->board, CHAN_OFFSET (port, elem), val) + +#define sx_read_channel_word(port, elem) \ + read_sx_word (port->board, CHAN_OFFSET (port, elem)) + + +#define sx_write_module_byte(board, addr, elem, val) \ + write_sx_byte (board, MODU_OFFSET (board, addr, elem), val) + +#define sx_read_module_byte(board, addr, elem) \ + read_sx_byte (board, MODU_OFFSET (board, addr, elem)) + +#define sx_write_module_word(board, addr, elem, val) \ + write_sx_word (board, MODU_OFFSET (board, addr, elem), val) + +#define sx_read_module_word(board, addr, elem) \ + read_sx_word (board, MODU_OFFSET (board, addr, elem)) + + +#define sx_write_board_byte(board, elem, val) \ + write_sx_byte (board, BRD_OFFSET (board, elem), val) + +#define sx_read_board_byte(board, elem) \ + read_sx_byte (board, BRD_OFFSET (board, elem)) + +#define sx_write_board_word(board, elem, val) \ + write_sx_word (board, BRD_OFFSET (board, elem), val) + +#define sx_read_board_word(board, elem) \ + read_sx_word (board, BRD_OFFSET (board, elem)) + + +int sx_start_board (struct sx_board *board) +{ + if (IS_SX_BOARD (board)) { + write_sx_byte (board, SX_CONFIG, SX_CONF_BUSEN); + } else { + /* Don't bug me about the clear_set. + I haven't the foggiest idea what it's about -- REW*/ + write_sx_byte (board, SI2_ISA_RESET, SI2_ISA_RESET_CLEAR); + write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET); + } + return 1; +} + +#define SX_IRQ_REG_VAL(board) \ + ((board->flags & SX_ISA_BOARD)?(board->irq << 4):0) + +/* Note. The SX register is write-only. Therefore, we have to enable the + bus too. This is a no-op, if you don't mess with this driver... */ +int sx_start_interrupts (struct sx_board *board) +{ + + /* Don't call this with board->irq == 0 */ + + if (IS_SX_BOARD(board)) { + write_sx_byte (board, SX_CONFIG, SX_IRQ_REG_VAL (board) | + SX_CONF_BUSEN | + SX_CONF_HOSTIRQ); + } else { + switch (board->irq) { + case 11:write_sx_byte (board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET);break; + case 12:write_sx_byte (board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_SET);break; + case 15:write_sx_byte (board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_SET);break; + default:printk (KERN_INFO "sx: SI/XIO card doesn't support interrupt %d.\n", + board->irq); + return 0; + } + write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET); + } + + return 1; +} + + +int sx_send_command (struct sx_port *port, + int command, + int mask, + int newstat) +{ + func_enter2 (); + write_sx_byte (port->board, CHAN_OFFSET (port, hi_hstat), command); + func_exit (); + return sx_busy_wait_eq (port->board, CHAN_OFFSET (port, hi_hstat), mask, newstat); +} + + +char *mod_type_s (int module_type) +{ + switch (module_type) { + case TA4: return "TA4"; + case TA8: return "TA8"; + case TA4_ASIC: return "TA4_ASIC"; + case TA8_ASIC: return "TA8_ASIC"; + case MTA_CD1400:return "MTA_CD1400"; + case SXDC: return "SXDC"; + default:return "Unknown/invalid"; + } +} + + +char *pan_type_s (int pan_type) +{ + switch (pan_type) { + case MOD_RS232DB25: return "MOD_RS232DB25"; + case MOD_RS232RJ45: return "MOD_RS232RJ45"; + case MOD_RS422DB25: return "MOD_RS422DB25"; + case MOD_PARALLEL: return "MOD_PARALLEL"; + case MOD_2_RS232DB25: return "MOD_2_RS232DB25"; + case MOD_2_RS232RJ45: return "MOD_2_RS232RJ45"; + case MOD_2_RS422DB25: return "MOD_2_RS422DB25"; + case MOD_RS232DB25MALE: return "MOD_RS232DB25MALE"; + case MOD_2_PARALLEL: return "MOD_2_PARALLEL"; + case MOD_BLANK: return "empty"; + default:return "invalid"; + } +} + + +int mod_compat_type (int module_type) +{ + return module_type >> 4; +} + + +static void sx_setsignals (struct sx_port *port, int dtr, int rts) +{ + int t; + func_enter2 (); + + t = sx_read_channel_byte (port, hi_op); + if (dtr >= 0) t = dtr? (t | OP_DTR): (t & ~OP_DTR); + if (rts >= 0) t = rts? (t | OP_RTS): (t & ~OP_RTS); + sx_write_channel_byte (port, hi_op, t); + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "setsignals: %d/%d\n", dtr, rts); + func_exit (); +} + + + +static int sx_getsignals (struct sx_port *port) +{ + int i_stat,o_stat; + + o_stat = sx_read_channel_byte (port, hi_op); + i_stat = sx_read_channel_byte (port, hi_ip); + + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "getsignals: %d/%d (%d/%d) %02x/%02x\n", + (o_stat & OP_DTR) != 0, (o_stat & OP_RTS) != 0, + port->c_dcd, sx_get_CD (port), + sx_read_channel_byte (port, hi_ip), + sx_read_channel_byte (port, hi_state)); + + return (((o_stat & OP_DTR)?TIOCM_DTR:0) | + ((o_stat & OP_RTS)?TIOCM_RTS:0) | + ((i_stat & IP_CTS)?TIOCM_CTS:0) | + ((i_stat & IP_DCD)?TIOCM_CAR:0) | + ((i_stat & IP_DSR)?TIOCM_DSR:0) | + ((i_stat & IP_RI)?TIOCM_RNG:0) + ); +} + + +static void sx_set_baud (struct sx_port *port) +{ + int t; + + if (port->board->ta_type == MOD_SXDC) { + switch (port->gs.baud) { + /* Save some typing work... */ +#define e(x) case x:t= BAUD_ ## x ; break + e(50);e(75);e(110);e(150);e(200);e(300);e(600); + e(1200);e(1800);e(2000);e(2400);e(4800);e(7200); + e(9600);e(14400);e(19200);e(28800);e(38400); + e(56000);e(57600);e(64000);e(76800);e(115200); + e(128000);e(150000);e(230400);e(256000);e(460800); + e(921600); + case 134 :t = BAUD_134_5; break; + case 0 :t = -1; + break; + default: + /* Can I return "invalid"? */ + t = BAUD_9600; + printk (KERN_INFO "sx: unsupported baud rate: %d.\n", port->gs.baud); + break; + } +#undef e + if (t > 0) { + /* The baud rate is not set to 0, so we're enabeling DTR... -- REW */ + sx_setsignals (port, 1, -1); + /* XXX This is not TA & MTA compatible */ + sx_write_channel_byte (port, hi_csr, 0xff); + + sx_write_channel_byte (port, hi_txbaud, t); + sx_write_channel_byte (port, hi_rxbaud, t); + } else { + sx_setsignals (port, 0, -1); + } + } else { + switch (port->gs.baud) { +#define e(x) case x:t= CSR_ ## x ; break + e(75);e(150);e(300);e(600);e(1200);e(2400);e(4800); + e(1800);e(9600); + e(19200);e(57600);e(38400); + /* TA supports 110, but not 115200, MTA supports 115200, but not 110 */ + case 110: + if (port->board->ta_type == MOD_TA) { + t = CSR_110; + break; + } else { + t = CSR_9600; + printk (KERN_INFO "sx: Unsupported baud rate: %d.\n", port->gs.baud); + break; + } + case 115200: + if (port->board->ta_type == MOD_TA) { + t = CSR_9600; + printk (KERN_INFO "sx: Unsupported baud rate: %d.\n", port->gs.baud); + break; + } else { + t = CSR_110; + break; + } + case 0 :t = -1; + break; + default: + t = CSR_9600; + printk (KERN_INFO "sx: Unsupported baud rate: %d.\n", port->gs.baud); + break; + } +#undef e + if (t >= 0) { + sx_setsignals (port, 1, -1); + sx_write_channel_byte (port, hi_csr, t * 0x11); + } else { + sx_setsignals (port, 0, -1); + } + } +} + + +/* Simon Allen's version of this routine was 225 lines long. 85 is a lot + better. -- REW */ + +static void sx_set_real_termios (void *ptr) +{ + struct sx_port *port = ptr; + + func_enter2(); + + /* What is this doing here? -- REW + Ha! figured it out. It is to allow you to get DTR active again + if you've dropped it with stty 0. Moved to set_baud, where it + belongs (next to the drop dtr if baud == 0) -- REW */ + /* sx_setsignals (port, 1, -1); */ + + sx_set_baud (port); + +#define CFLAG port->gs.tty->termios->c_cflag + sx_write_channel_byte (port, hi_mr1, + (C_PARENB (port->gs.tty)? MR1_WITH:MR1_NONE) | + (C_PARODD (port->gs.tty)? MR1_ODD:MR1_EVEN) | + (C_CRTSCTS(port->gs.tty)? MR1_RTS_RXFLOW:0) | + (((CFLAG & CSIZE)==CS8) ? MR1_8_BITS:0) | + (((CFLAG & CSIZE)==CS7) ? MR1_7_BITS:0) | + (((CFLAG & CSIZE)==CS6) ? MR1_6_BITS:0) | + (((CFLAG & CSIZE)==CS5) ? MR1_5_BITS:0) ); + + sx_write_channel_byte (port, hi_mr2, + (C_CRTSCTS(port->gs.tty)?MR2_CTS_TXFLOW:0) | + (C_CSTOPB (port->gs.tty)?MR2_2_STOP:MR2_1_STOP)); + + switch (CFLAG & CSIZE) { + case CS8:sx_write_channel_byte (port, hi_mask, 0xff);break; + case CS7:sx_write_channel_byte (port, hi_mask, 0x7f);break; + case CS6:sx_write_channel_byte (port, hi_mask, 0x3f);break; + case CS5:sx_write_channel_byte (port, hi_mask, 0x1f);break; + default: + printk (KERN_INFO "sx: Invalid wordsize: %d\n", CFLAG & CSIZE); + break; + } + + sx_write_channel_byte (port, hi_prtcl, + (I_IXON (port->gs.tty)?SP_TXEN:0) | + (I_IXOFF (port->gs.tty)?SP_RXEN:0) | + (I_IXANY (port->gs.tty)?SP_TANY:0) | + SP_DCEN); + + sx_write_channel_byte (port, hi_break, + I_OTHER(port->gs.tty) ? 0: + (I_IGNBRK(port->gs.tty)?BR_IGN:0 | + I_BRKINT(port->gs.tty)?BR_INT:0)); + + sx_write_channel_byte (port, hi_txon, START_CHAR (port->gs.tty)); + sx_write_channel_byte (port, hi_rxon, START_CHAR (port->gs.tty)); + sx_write_channel_byte (port, hi_txoff, STOP_CHAR (port->gs.tty)); + sx_write_channel_byte (port, hi_rxoff, STOP_CHAR (port->gs.tty)); + + if (sx_read_channel_byte (port, hi_hstat) == HS_IDLE_OPEN) { + if (sx_send_command (port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) { + printk (KERN_WARNING "sx: Sent reconfigure command, but card didn't react.\n"); + } + } else { + sx_dprintk (SX_DEBUG_TERMIOS, + "sx: Not sending reconfigure: port isn't open (%02x).\n", + sx_read_channel_byte (port, hi_hstat)); + } + + + /* Tell line discipline whether we will do input cooking */ + if(I_OTHER(port->gs.tty)) { + clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + } else { + set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + } + sx_dprintk (SX_DEBUG_TERMIOS, "iflags: %x(%d) ", + port->gs.tty->termios->c_iflag, + I_OTHER(port->gs.tty)); + + +/* Tell line discipline whether we will do output cooking. + * If OPOST is set and no other output flags are set then we can do output + * processing. Even if only *one* other flag in the O_OTHER group is set + * we do cooking in software. + */ + if(O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) { + set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + } else { + clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + } + sx_dprintk (SX_DEBUG_TERMIOS, "oflags: %x(%d)\n", + port->gs.tty->termios->c_oflag, + O_OTHER(port->gs.tty)); + /* port->c_dcd = sx_get_CD (port); */ + func_exit (); +} + + + +/* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + +/* Note: + Other drivers use the macro "MIN" to calculate how much to copy. + This has the disadvantage that it will evaluate parts twice. That's + expensive when it's IO (and the compiler cannot optimize those away!). + Moreover, I'm not sure that you're race-free. + + I assign a value, and then only allow the value to decrease. This + is always safe. This makes the code a few lines longer, and you + know I'm dead against that, but I think it is required in this + case. */ + + +void sx_transmit_chars (struct sx_port *port) +{ + int c; + int tx_ip; + int txroom; + + func_enter2 (); + sx_dprintk (SX_DEBUG_TRANSMIT, "Port %p: transmit %d chars\n", + port, port->gs.xmit_cnt); + + if (test_and_set_bit (SX_PORT_TRANSMIT_LOCK, &port->locks)) { + return; + } + + while (1) { + c = port->gs.xmit_cnt; + + sx_dprintk (SX_DEBUG_TRANSMIT, "Copying %d ", c); + tx_ip = sx_read_channel_byte (port, hi_txipos); + + /* Took me 5 minutes to deduce this formula. + Luckily it is literally in the manual in section 6.5.4.3.5 */ + txroom = (sx_read_channel_byte (port, hi_txopos) - tx_ip - 1) & 0xff; + + /* Don't copy more bytes than there is room for in the buffer */ + if (c > txroom) + c = txroom; + sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) ", c, txroom ); + + /* Don't copy past the end of the hardware transmit buffer */ + if (c > 0x100 - tx_ip) + c = 0x100 - tx_ip; + + sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) ", c, 0x100-tx_ip ); + + /* Don't copy pas the end of the source buffer */ + if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail) + c = SERIAL_XMIT_SIZE - port->gs.xmit_tail; + + sx_dprintk (SX_DEBUG_TRANSMIT, " %d(%d) \n", + c, SERIAL_XMIT_SIZE- port->gs.xmit_tail); + + /* If for one reason or another, we can't copy more data, we're done! */ + if (c == 0) break; + + + memcpy_toio (port->board->base + CHAN_OFFSET(port,hi_txbuf) + tx_ip, + port->gs.xmit_buf + port->gs.xmit_tail, c); + + /* Update the pointer in the card */ + sx_write_channel_byte (port, hi_txipos, (tx_ip+c) & 0xff); + + /* Update the kernel buffer end */ + port->gs.xmit_tail = (port->gs.xmit_tail + c) & (SERIAL_XMIT_SIZE-1); + + /* This one last. (this is essential) + It would allow others to start putting more data into the buffer! */ + port->gs.xmit_cnt -= c; + } + + if (port->gs.xmit_cnt == 0) { + sx_disable_tx_interrupts (port); + } + + if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { + if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + port->gs.tty->ldisc.write_wakeup) + (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + sx_dprintk (SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", + port->gs.wakeup_chars); + wake_up_interruptible(&port->gs.tty->write_wait); + } + + clear_bit (SX_PORT_TRANSMIT_LOCK, &port->locks); + func_exit (); +} + + +/* Note the symmetry between receiving chars and transmitting them! + Note: The kernel should have implemented both a receive buffer and + a transmit buffer. */ + +/* Inlined: Called only once. Remove the inline when you add another call */ +inline void sx_receive_chars (struct sx_port *port) +{ + int c; + int rx_op; + struct tty_struct *tty; + int copied=0; + + /* func_enter2 (); */ + tty = port->gs.tty; + while (1) { + rx_op = sx_read_channel_byte (port, hi_rxopos); + c = (sx_read_channel_byte (port, hi_rxipos) - rx_op) & 0xff; + + sx_dprintk (SX_DEBUG_RECEIVE, "rxop=%d, c = %d.\n", rx_op, c); + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + c > TTY_FLIPBUF_SIZE) + c = TTY_FLIPBUF_SIZE - tty->flip.count; + + sx_dprintk (SX_DEBUG_RECEIVE, "c = %d.\n", c); + + /* Don't copy past the end of the hardware receive buffer */ + if (rx_op + c > 0x100) c = 0x100 - rx_op; + + sx_dprintk (SX_DEBUG_RECEIVE, "c = %d.\n", c); + + /* If for one reason or another, we can't copy more data, we're done! */ + if (c == 0) break; + + sx_dprintk (SX_DEBUG_RECEIVE , "Copying over %d chars. First is %d at %lx\n", c, + read_sx_byte (port->board, CHAN_OFFSET(port,hi_rxbuf) + rx_op), + CHAN_OFFSET(port, hi_rxbuf)); + memcpy_fromio (tty->flip.char_buf_ptr, + port->board->base + CHAN_OFFSET(port,hi_rxbuf) + rx_op, c); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, c); + + /* Update the kernel buffer end */ + tty->flip.count += c; + tty->flip.char_buf_ptr += c; + tty->flip.flag_buf_ptr += c; + + /* This one last. ( Not essential.) + It allows the card to start putting more data into the buffer! + Update the pointer in the card */ + sx_write_channel_byte (port, hi_rxopos, (rx_op + c) & 0xff); + + copied += c; + } + if (copied) { + struct timeval tv; + + do_gettimeofday (&tv); + sx_dprintk (SX_DEBUG_RECEIVE, + "pushing flipq port %d (%3d chars): %d.%06d (%d/%d)\n", + port->line, copied, + (int) (tv.tv_sec % 60), (int)tv.tv_usec, tty->raw, tty->real_raw); + + /* Tell the rest of the system the news. Great news. New characters! */ + tty_flip_buffer_push (tty); + /* tty_schedule_flip (tty); */ + } + + /* func_exit (); */ +} + +/* Inlined: it is called only once. Remove the inline if you add another + call */ +inline void sx_check_modem_signals (struct sx_port *port) +{ + int hi_state; + int c_dcd; + + hi_state = sx_read_channel_byte (port, hi_state); + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "Checking modem signals (%d/%d)\n", + port->c_dcd, sx_get_CD (port)); + + if (hi_state & ST_BREAK) { + hi_state &= ~ST_BREAK; + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "got a break.\n"); + + sx_write_channel_byte (port, hi_state, hi_state); + if (port->gs.flags & ASYNC_SAK) { + do_SAK (port->gs.tty); + } + } + if (hi_state & ST_DCD) { + hi_state &= ~ST_DCD; + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "got a DCD change.\n"); + sx_write_channel_byte (port, hi_state, hi_state); + c_dcd = sx_get_CD (port); + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD is now %d\n", c_dcd); + if (c_dcd != port->c_dcd) { + port->c_dcd = c_dcd; + if (sx_get_CD (port)) { + /* DCD went UP */ + if( (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || + ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) && + (sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED)) { + /* Are we blocking in open?*/ + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD active, unblocking open\n"); + wake_up_interruptible(&port->gs.open_wait); + } else { + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD raised. Ignoring.\n"); + } + } else { + /* DCD went down! */ + if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) && + (port->gs.flags & ASYNC_CALLOUT_NOHUP))) { + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD dropped. hanging up....\n"); + tty_hangup (port->gs.tty); + } else { + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD dropped. ignoring.\n"); + } + } + } else { + sx_dprintk (SX_DEBUG_MODEMSIGNALS, "Hmmm. card told us DCD changed, but it didn't.\n"); + } + } +} + + +/* This is what an interrupt routine should look like. + * Small, elegant, clear. + */ + +static void sx_interrupt (int irq, void *ptr, struct pt_regs *regs) +{ + struct sx_board *board = ptr; + struct sx_port *port; + int i; + + /* func_enter (); */ + sx_dprintk (SX_DEBUG_FLOW, "sx: enter sx_interrupt (%d/%d)\n", irq, board->irq); + + /* AAargh! The order in which to do these things is essential and + not trivial. + + - Rate limit goes before "recursive". Otherwise a series of + recursive calls will hang the machine in the interrupt routine. + + - hardware twiddling goes before "recursive". Otherwise when we + poll the card, and a recursive interrupt happens, we wont + ack the card, so it might keep on interrupting us. (especially + level sensitive interrupt systems like PCI). + + - Rate limit goes before hardware twiddling. Otherwise we won't + catch a card that has gone bonkers. + + - The "initialized" test goes after the hardware twiddling. Otherwise + the card will stick us in the interrupt routine again. + + - The initialized test goes before recursive. + */ + + + +#ifdef IRQ_RATE_LIMIT + /* Aaargh! I'm ashamed. This costs more lines-of-code than the + actual interrupt routine!. (Well, used to when I wrote that comment) */ + { + static int lastjif; + static int nintr=0; + + if (lastjif == jiffies) { + if (++nintr > IRQ_RATE_LIMIT) { + free_irq (board->irq, board); + printk (KERN_ERR "sx: Too many interrupts. Turning off interrupt %d.\n", + board->irq); + } + } else { + lastjif = jiffies; + nintr = 0; + } + } +#endif + + + if (board->irq == irq) { + /* Tell the card we've noticed the interrupt. */ + + sx_write_board_word (board, cc_int_pending, 0); + if (IS_SX_BOARD (board)) { + write_sx_byte (board, SX_RESET_IRQ, 1); + } else { + write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR); + write_sx_byte (board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET); + } + } + + if (!sx_initialized) return; + if (!(board->flags & SX_BOARD_INITIALIZED)) return; + + if (test_and_set_bit (SX_BOARD_INTR_LOCK, &board->locks)) { + printk (KERN_ERR "Recursive interrupt! (%d)\n", board->irq); + return; + } + + for (i=0;i<board->nports;i++) { + port = &board->ports[i]; + if (port->gs.flags & GS_ACTIVE) { + if (sx_read_channel_byte (port, hi_state)) { + sx_dprintk (SX_DEBUG_INTERRUPTS, + "Port %d: modem signal change?... \n", i); + sx_check_modem_signals (port); + } + if (port->gs.xmit_cnt) { + sx_transmit_chars (port); + } + if (!(port->gs.flags & SX_RX_THROTTLE)) { + sx_receive_chars (port); + } + } + } + + clear_bit (SX_BOARD_INTR_LOCK, &board->locks); + + sx_dprintk (SX_DEBUG_FLOW, "sx: exit sx_interrupt (%d/%d)\n", irq, board->irq); + /* func_exit (); */ +} + + +static void sx_pollfunc (unsigned long data) +{ + struct sx_board *board = (struct sx_board *) data; + + func_enter (); + + sx_interrupt (0, board, NULL); + + board->timer.expires = jiffies + sx_poll; + add_timer (&board->timer); + func_exit (); +} + + + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the generic_serial driver * + * ********************************************************************** */ + +/* Ehhm. I don't know how to fiddle with interrupts on the SX card. --REW */ +/* Hmm. Ok I figured it out. You don't. */ + +static void sx_disable_tx_interrupts (void * ptr) +{ + struct sx_port *port = ptr; + func_enter2(); + + port->gs.flags &= ~GS_TX_INTEN; + + func_exit(); +} + + +static void sx_enable_tx_interrupts (void * ptr) +{ + struct sx_port *port = ptr; + int data_in_buffer; + func_enter2(); + + /* First transmit the characters that we're supposed to */ + sx_transmit_chars (port); + + /* The sx card will never interrupt us if we don't fill the buffer + past 25%. So we keep considering interrupts off if that's the case. */ + data_in_buffer = (sx_read_channel_byte (port, hi_txipos) - + sx_read_channel_byte (port, hi_txopos)) & 0xff; + + /* XXX Must be "HIGH_WATER" for SI card according to doc. */ + if (data_in_buffer < LOW_WATER) + port->gs.flags &= ~GS_TX_INTEN; + + func_exit(); +} + + +static void sx_disable_rx_interrupts (void * ptr) +{ + /* struct sx_port *port = ptr; */ + func_enter(); + + func_exit(); +} + +static void sx_enable_rx_interrupts (void * ptr) +{ + /* struct sx_port *port = ptr; */ + func_enter(); + + func_exit(); +} + + +/* Jeez. Isn't this simple? */ +static int sx_get_CD (void * ptr) +{ + struct sx_port *port = ptr; + func_enter2(); + + func_exit(); + return ((sx_read_channel_byte (port, hi_ip) & IP_DCD) != 0); +} + + +/* Jeez. Isn't this simple? */ +static int sx_chars_in_buffer (void * ptr) +{ + struct sx_port *port = ptr; + func_enter2(); + + func_exit(); + return ((sx_read_channel_byte (port, hi_txipos) - + sx_read_channel_byte (port, hi_txopos)) & 0xff); +} + + +static void sx_shutdown_port (void * ptr) +{ + struct sx_port *port = ptr; + + func_enter(); + + port->gs.flags &= ~ GS_ACTIVE; + if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + sx_setsignals (port, 0, 0); + } + + func_exit(); +} + + + + + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the rest of the system * + * ********************************************************************** */ + + +static int sx_fw_open(struct inode *inode, struct file *filp) +{ + func_enter (); + MOD_INC_USE_COUNT; + func_exit (); + return 0; +} + + +static INT sx_fw_release(struct inode *inode, struct file *filp) +{ + func_enter (); + MOD_DEC_USE_COUNT; + func_exit (); + return NO_ERROR; +} + + +static int sx_open (struct tty_struct * tty, struct file * filp) +{ + struct sx_port *port; + int retval, line; + + func_enter(); + + if (!sx_initialized) { + return -EIO; + } + + line = MINOR(tty->device); + sx_dprintk (SX_DEBUG_OPEN, "%d: opening line %d. tty=%p ctty=%p, np=%d)\n", + current->pid, line, tty, current->tty, sx_nports); + + if ((line < 0) || (line >= SX_NPORTS) || (line >= sx_nports)) + return -ENODEV; + + port = & sx_ports[line]; + port->c_dcd = 0; /* Make sure that the first interrupt doesn't detect a + 1 -> 0 transition. */ + + + sx_dprintk (SX_DEBUG_OPEN, "port = %p c_dcd = %d\n", port, port->c_dcd); + + tty->driver_data = port; + port->gs.tty = tty; + port->gs.count++; + + sx_dprintk (SX_DEBUG_OPEN, "starting port\n"); + + /* + * Start up serial port + */ + retval = gs_init_port(&port->gs); + sx_dprintk (SX_DEBUG_OPEN, "done gs_init\n"); + if (retval) { + port->gs.count--; + return retval; + } + + port->gs.flags |= GS_ACTIVE; + sx_setsignals (port, 1,1); + + sx_dprintk (SX_DEBUG_OPEN, "before inc_use_count (count=%d.\n", + port->gs.count); + if (port->gs.count == 1) { + MOD_INC_USE_COUNT; + } + sx_dprintk (SX_DEBUG_OPEN, "after inc_use_count\n"); + +#if 0 + if (sx_debug & SX_DEBUG_OPEN) + my_hd ((unsigned char *)port, sizeof (*port)); +#else + if (sx_debug & SX_DEBUG_OPEN) + my_hd ((unsigned char *)port->board->base + port->ch_base, + sizeof (*port)); +#endif + + if (sx_send_command (port, HS_LOPEN, -1, HS_IDLE_OPEN) != 1) { + printk (KERN_ERR "sx: Card didn't respond to LOPEN command.\n"); + MOD_DEC_USE_COUNT; + port->gs.count--; + return -EIO; + } + + retval = block_til_ready(port, filp); + sx_dprintk (SX_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n", + retval, port->gs.count); + + if (retval) { + MOD_DEC_USE_COUNT; + port->gs.count--; + return retval; + } + /* tty->low_latency = 1; */ + + if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = port->gs.normal_termios; + else + *tty->termios = port->gs.callout_termios; + sx_set_real_termios (port); + } + + port->gs.session = current->session; + port->gs.pgrp = current->pgrp; + port->c_dcd = sx_get_CD (port); + sx_dprintk (SX_DEBUG_OPEN, "at open: cd=%d\n", port->c_dcd); + func_exit(); + return 0; + +} + + +/* I haven't the foggiest why the decrement use count has to happen + here. The whole linux serial drivers stuff needs to be redesigned. + My guess is that this is a hack to minimize the impact of a bug + elsewhere. Thinking about it some more. (try it sometime) Try + running minicom on a serial port that is driven by a modularized + driver. Have the modem hangup. Then remove the driver module. Then + exit minicom. I expect an "oops". -- REW */ +static void sx_hungup (void *ptr) +{ + func_enter (); + MOD_DEC_USE_COUNT; + func_exit (); +} + + +static void sx_close (void *ptr) +{ + struct sx_port *port = ptr; + /* Give the port 5 seconds to close down. */ + int to = 5 * HZ; + + func_enter (); + sx_send_command (port, HS_CLOSE, 0, 0); + + while (to-- && (sx_read_channel_byte (port, hi_hstat) != HS_IDLE_CLOSED)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout (1); + if (signal_pending (current)) + break; + } + current->state = TASK_RUNNING; + if (sx_read_channel_byte (port, hi_hstat) != HS_IDLE_CLOSED) { + if (sx_send_command (port, HS_FORCE_CLOSED, -1, HS_IDLE_CLOSED) != 1) { + printk (KERN_ERR + "sx: sent the force_close command, but card didn't react\n"); + } else + sx_dprintk (SX_DEBUG_CLOSE, "sent the force_close command.\n"); + } + + sx_dprintk (SX_DEBUG_CLOSE, "waited %d jiffies for close. count=%d\n", + 5 * HZ - to - 1, port->gs.count); + + MOD_DEC_USE_COUNT; + func_exit (); +} + + + +/* This is relatively thorough. But then again it is only 20 lines. */ +#define MARCHUP for (i=min;i<max;i++) +#define MARCHDOWN for (i=max-1;i>=min;i--) +#define W0 write_sx_byte (board, i, 0x55) +#define W1 write_sx_byte (board, i, 0xaa) +#define R0 if (read_sx_byte (board, i) != 0x55) return 1 +#define R1 if (read_sx_byte (board, i) != 0xaa) return 1 + +/* This memtest takes a human-noticable time. You normally only do it + once a boot, so I guess that it is worth it. */ +int do_memtest (struct sx_board *board, int min, int max) +{ + int i; + + /* This is a marchb. Theoretically, marchb catches much more than + simpler tests. In practise, the longer test just catches more + intermittent errors. -- REW + (For the theory behind memory testing see: + Testing Semiconductor Memories by A.J. van de Goor.) */ + MARCHUP {W0;} + MARCHUP {R0;W1;R1;W0;R0;W1;} + MARCHUP {R1;W0;W1;} + MARCHDOWN {R1;W0;W1;W0;} + MARCHDOWN {R0;W1;W0;} + + return 0; +} + + +#undef MARCHUP +#undef MARCHDOWN +#undef W0 +#undef W1 +#undef R0 +#undef R1 + +#define MARCHUP for (i=min;i<max;i+=2) +#define MARCHDOWN for (i=max-1;i>=min;i-=2) +#define W0 write_sx_word (board, i, 0x55aa) +#define W1 write_sx_word (board, i, 0xaa55) +#define R0 if (read_sx_word (board, i) != 0x55aa) return 1 +#define R1 if (read_sx_word (board, i) != 0xaa55) return 1 + +/* This memtest takes a human-noticable time. You normally only do it + once a boot, so I guess that it is worth it. */ +int do_memtest_w (struct sx_board *board, int min, int max) +{ + int i; + + MARCHUP {W0;} + MARCHUP {R0;W1;R1;W0;R0;W1;} + MARCHUP {R1;W0;W1;} + MARCHDOWN {R1;W0;W1;W0;} + MARCHDOWN {R0;W1;W0;} + + return 0; +} + + +static int sx_fw_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int rc = 0; + int *descr = (int *)arg, i; + static struct sx_board *board = NULL; + int nbytes, offset, data; + char *tmp; + + func_enter(); + +#if 0 + /* Removed superuser check: Sysops can use the permissions on the device + file to restrict access. Recommendation: Root only. (root.root 600) */ + if (!suser ()) { + return -EPERM; + } +#endif + + sx_dprintk (SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg); + + if (!board) board = &boards[0]; + if (board->flags & SX_BOARD_PRESENT) { + sx_dprintk (SX_DEBUG_FIRMWARE, "Board present! (%x)\n", + board->flags); + } else { + sx_dprintk (SX_DEBUG_FIRMWARE, "Board not present! (%x) all:", + board->flags); + for (i=0;i< SX_NBOARDS;i++) + sx_dprintk (SX_DEBUG_FIRMWARE, "<%x> ", boards[i].flags); + sx_dprintk (SX_DEBUG_FIRMWARE, "\n"); + return -EIO; + } + + switch (cmd) { + case SXIO_SET_BOARD: + sx_dprintk (SX_DEBUG_FIRMWARE, "set board to %ld\n", arg); + if (arg > SX_NBOARDS) return -EIO; + sx_dprintk (SX_DEBUG_FIRMWARE, "not out of range\n"); + if (!(boards[arg].flags & SX_BOARD_PRESENT)) return -EIO; + sx_dprintk (SX_DEBUG_FIRMWARE, ".. and present!\n"); + board = &boards[arg]; + break; + case SXIO_GET_TYPE: + rc = IS_SX_BOARD (board)? SX_TYPE_SX:SX_TYPE_SI; + sx_dprintk (SX_DEBUG_FIRMWARE, "returning type= %d\n", rc); + break; + case SXIO_DO_RAMTEST: + if (sx_initialized) /* Already initialized: better not ramtest the board. */ + return -EPERM; + if (IS_SX_BOARD (board)) { + rc = do_memtest (board, 0, 0x7000); + if (!rc) rc = do_memtest (board, 0, 0x7000); + /*if (!rc) rc = do_memtest_w (board, 0, 0x7000);*/ + } else { + rc = do_memtest (board, 0, 0x7ff8); + /* if (!rc) rc = do_memtest_w (board, 0, 0x7ff8); */ + } + sx_dprintk (SX_DEBUG_FIRMWARE, "returning memtest result= %d\n", rc); + break; + case SXIO_DOWNLOAD: + if (sx_initialized) /* Already initialized */ + return -EEXIST; + if (!sx_reset (board)) + return -EIO; + sx_dprintk (SX_DEBUG_INIT, "reset the board...\n"); + + tmp = kmalloc (SX_CHUNK_SIZE, GFP_USER); + if (!tmp) return -ENOMEM; + Get_user (nbytes, descr++); + Get_user (offset, descr++); + Get_user (data, descr++); + while (nbytes && data) { + for (i=0;i<nbytes;i += SX_CHUNK_SIZE) { + copy_from_user (tmp, (char *)data+i, + (i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE); + memcpy_toio ((char *) (board->base + offset + i), tmp, + (i+SX_CHUNK_SIZE>nbytes)?nbytes-i:SX_CHUNK_SIZE); + } + + Get_user (nbytes, descr++); + Get_user (offset, descr++); + Get_user (data, descr++); + } + kfree (tmp); + sx_nports += sx_init_board (board); + rc = sx_nports; + break; + case SXIO_INIT: + if (sx_initialized) /* Already initialized */ + return -EEXIST; + /* This is not allowed until all boards are initialized... */ + for (i=0;i<SX_NBOARDS;i++) { + if ( (boards[i].flags & SX_BOARD_PRESENT) && + !(boards[i].flags & SX_BOARD_INITIALIZED)) + return -EIO; + } + for (i=0;i<SX_NBOARDS;i++) + if (!(boards[i].flags & SX_BOARD_PRESENT)) break; + + sx_dprintk (SX_DEBUG_FIRMWARE, "initing portstructs, %d boards, " + "%d channels, first board: %d ports\n", + i, sx_nports, boards[0].nports); + rc = sx_init_portstructs (i, sx_nports); + sx_init_drivers (); + if (rc >= 0) + sx_initialized++; + break; + case SXIO_SETDEBUG: + sx_debug = arg; + break; + case SXIO_GETDEBUG: + rc = sx_debug; + break; + case SXIO_SETGSDEBUG: + gs_debug = arg; + break; + case SXIO_GETGSDEBUG: + rc = gs_debug; + break; + default: + printk (KERN_WARNING "Unknown ioctl on firmware device (%x).\n", cmd); + break; + } + func_exit (); + return rc; +} + + +static int sx_ioctl (struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + int rc; + struct sx_port *port = tty->driver_data; + int ival; + + /* func_enter2(); */ + + rc = 0; + switch (cmd) { + case TIOCGSOFTCAR: + rc = Put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned int *) arg); + break; + case TIOCSSOFTCAR: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(int))) == 0) { + Get_user(ival, (unsigned int *) arg); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? CLOCAL : 0); + } + break; + case TIOCGSERIAL: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) + gs_getserial(&port->gs, (struct serial_struct *) arg); + break; + case TIOCSSERIAL: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) + rc = gs_setserial(&port->gs, (struct serial_struct *) arg); + break; + case TIOCMGET: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { + ival = sx_getsignals(port); + put_user(ival, (unsigned int *) arg); + } + break; + case TIOCMBIS: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + Get_user(ival, (unsigned int *) arg); + sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & TIOCM_RTS) ? 1 : -1)); + } + break; + case TIOCMBIC: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + Get_user(ival, (unsigned int *) arg); + sx_setsignals(port, ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & TIOCM_RTS) ? 0 : -1)); + } + break; + case TIOCMSET: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + Get_user(ival, (unsigned int *) arg); + sx_setsignals(port, ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & TIOCM_RTS) ? 1 : 0)); + } + break; + + default: + rc = -ENOIOCTLCMD; + break; + } + + /* func_exit(); */ + return rc; +} + + +/* The throttle/unthrottle scheme for the Specialix card is different + * from other drivers and deserves some explanation. + * The Specialix hardware takes care of XON/XOFF + * and CTS/RTS flow control itself. This means that all we have to + * do when signalled by the upper tty layer to throttle/unthrottle is + * to make a note of it here. When we come to read characters from the + * rx buffers on the card (sx_receive_chars()) we look to see if the + * upper layer can accept more (as noted here in sx_rx_throt[]). + * If it can't we simply don't remove chars from the cards buffer. + * When the tty layer can accept chars, we again note that here and when + * sx_receive_chars() is called it will remove them from the cards buffer. + * The card will notice that a ports buffer has drained below some low + * water mark and will unflow control the line itself, using whatever + * flow control scheme is in use for that port. -- Simon Allen + */ + +static void sx_throttle (struct tty_struct * tty) +{ + struct sx_port *port = (struct sx_port *)tty->driver_data; + + func_enter2(); + /* If the port is using any type of input flow + * control then throttle the port. + */ + if((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty)) ) { + port->gs.flags |= SX_RX_THROTTLE; + } + func_exit(); +} + + +static void sx_unthrottle (struct tty_struct * tty) +{ + struct sx_port *port = (struct sx_port *)tty->driver_data; + + func_enter2(); + /* Always unthrottle even if flow control is not enabled on + * this port in case we disabled flow control while the port + * was throttled + */ + port->gs.flags &= ~SX_RX_THROTTLE; + func_exit(); + return; +} + + +/* ********************************************************************** * + * Here are the initialization routines. * + * ********************************************************************** */ + + + + +static int sx_init_board (struct sx_board *board) +{ + int addr; + int chans; + int type; + + func_enter(); + + /* This is preceded by downloading the download code. */ + + board->flags |= SX_BOARD_INITIALIZED; + + /* This resets the processor again, to make sure it didn't do any + foolish things while we were downloading the image */ + if (!sx_reset (board)) + return 0; + + sx_start_board (board); + + if (!sx_busy_wait_neq (board, 0, 0xff, 0)) { + printk (KERN_ERR "sx: Ooops. Board won't initialize.\n"); + return 0; + } + + /* Ok. So now the processor on the card is running. It gathered + some info for us... */ + sx_dprintk (SX_DEBUG_INIT, "The sxcard structure:\n"); + if (sx_debug & SX_DEBUG_INIT) my_hd ((char *)(board->base), 0x10); + sx_dprintk (SX_DEBUG_INIT, "the first sx_module structure:\n"); + if (sx_debug & SX_DEBUG_INIT) my_hd ((char *)(board->base + 0x80), 0x30); + + sx_dprintk (SX_DEBUG_INIT, + "init_status: %x, %dk memory, firmware V%x.%02x,\n", + read_sx_byte (board, 0), read_sx_byte(board, 1), + read_sx_byte (board, 5), read_sx_byte(board, 4)); + + if (read_sx_byte (board, 0) == 0xff) { + printk (KERN_INFO "sx: No modules found. Sorry.\n"); + board->nports = 0; + return 0; + } + + chans = 0; + + if (IS_SX_BOARD(board)) { + sx_write_board_word (board, cc_int_count, sx_maxints); + } else { + if (sx_maxints) + sx_write_board_word (board, cc_int_count, SI_PROCESSOR_CLOCK/8/sx_maxints); + } + + /* grab the first module type... */ + /* board->ta_type = mod_compat_type (read_sx_byte (board, 0x80 + 0x08)); */ + board->ta_type = mod_compat_type (sx_read_module_byte (board, 0x80, mc_chip)); + + /* XXX byteorder */ + for (addr = 0x80;addr != 0;addr = read_sx_word (board, addr) & 0x7fff) { + type = sx_read_module_byte (board, addr, mc_chip); + sx_dprintk (SX_DEBUG_INIT, "Module at %x: %d channels\n", + addr, read_sx_byte (board, addr + 2)); + + chans += sx_read_module_byte (board, addr, mc_type); + + sx_dprintk (SX_DEBUG_INIT, "module is an %s, which has %s/%s panels\n", + mod_type_s (type), + pan_type_s (sx_read_module_byte (board, addr, mc_mods) & 0xf), + pan_type_s (sx_read_module_byte (board, addr, mc_mods) >> 4)); + + sx_dprintk (SX_DEBUG_INIT, "CD1400 versions: %x/%x, ASIC version: %x\n", + sx_read_module_byte (board, addr, mc_rev1), + sx_read_module_byte (board, addr, mc_rev2), + sx_read_module_byte (board, addr, mc_mtaasic_rev)); + + /* The following combinations are illegal: It should theoretically + work, but timing problems make the bus HANG. */ + + if (mod_compat_type (type) != board->ta_type) { + printk (KERN_ERR "sx: This is an invalid configuration.\n" + "Don't mix TA/MTA/SXDC on the same hostadapter.\n"); + chans=0; + break; + } + if (IS_SI_BOARD(board) && (mod_compat_type(type) == 4)) { + printk (KERN_ERR "sx: This is an invalid configuration.\n" + "Don't use SXDCs on an SI/XIO adapter.\n"); + chans=0; + break; + } +#if 0 /* Problem fixed: firmware 3.05 */ + if (IS_SX_BOARD(board) && (type == TA8)) { + /* There are some issues with the firmware and the DCD/RTS + lines. It might work if you tie them together or something. + It might also work if you get a newer sx_firmware. Therefore + this is just a warning. */ + printk (KERN_WARNING "sx: The SX host doesn't work too well " + "with the TA8 adapters.\nSpecialix is working on it.\n"); + } +#endif + } + + if (chans) { + /* board->flags |= SX_BOARD_PRESENT; */ + if(board->irq > 0) { + /* fixed irq, probably PCI */ + if(sx_irqmask & (1 << board->irq)) { /* may we use this irq? */ + if(request_irq(board->irq, sx_interrupt, SA_SHIRQ | SA_INTERRUPT, "sx", board)) { + printk(KERN_ERR "sx: Cannot allocate irq %d.\n", board->irq); + board->irq = 0; + } + } else + board->irq = 0; + } else if(board->irq < 0 && sx_irqmask) { + /* auto-allocate irq */ + int irqnr; + int irqmask = sx_irqmask & (IS_SX_BOARD(board) ? SX_ISA_IRQ_MASK : SI2_ISA_IRQ_MASK); + for(irqnr = 15; irqnr > 0; irqnr--) + if(irqmask & (1 << irqnr)) + if(! request_irq(irqnr, sx_interrupt, SA_SHIRQ | SA_INTERRUPT, "sx", board)) + break; + if(! irqnr) + printk(KERN_ERR "sx: Cannot allocate IRQ.\n"); + board->irq = irqnr; + } else + board->irq = 0; + + if (board->irq) { + /* Found a valid interrupt, start up interrupts! */ + sx_dprintk (SX_DEBUG_INIT, "Using irq %d.\n", board->irq); + sx_start_interrupts (board); + board->poll = sx_slowpoll; + board->flags |= SX_IRQ_ALLOCATED; + } else { + /* no irq: setup board for polled operation */ + board->poll = sx_poll; + sx_dprintk (SX_DEBUG_INIT, "Using poll-interval %d.\n", board->poll); + } + + /* The timer should be initialized anyway: That way we can safely + del_timer it when the module is unloaded. */ + init_timer (&board->timer); + + if (board->poll) { + board->timer.data = (unsigned long) board; + board->timer.function = sx_pollfunc; + board->timer.expires = jiffies + board->poll; + add_timer (&board->timer); + } + } else { + board->irq = 0; + } + + board->nports = chans; + sx_dprintk (SX_DEBUG_INIT, "returning %d ports.", board->nports); + + func_exit(); + return chans; +} + + +void printheader(void) +{ + static int header_printed = 0; + + if (!header_printed) { + printk (KERN_INFO "Specialix SX driver " + "(C) 1998/1999 R.E.Wolff@BitWizard.nl \n"); + printk (KERN_INFO "sx: version %s\n", RCS_ID); + header_printed = 1; + } +} + + +int probe_sx (struct sx_board *board) +{ + struct vpd_prom vpdp; + char *p; + int i; + + func_enter(); + sx_dprintk (SX_DEBUG_PROBE, "Going to verify vpd prom at %x.\n", + board->base + SX_VPD_ROM); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd ((char *)(board->base + SX_VPD_ROM), 0x40); + + p = (char *) &vpdp; + for (i=0;i< sizeof (struct vpd_prom);i++) + *p++ = read_sx_byte (board, SX_VPD_ROM + i*2); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd ((char *)&vpdp, 0x20); + + sx_dprintk (SX_DEBUG_PROBE, "checking identifier...\n"); + + if (strncmp (vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) { + sx_dprintk (SX_DEBUG_PROBE, "Got non-SX identifier: '%s'\n", + vpdp.identifier); + return 0; + } + + printheader (); + + printk (KERN_DEBUG "sx: Found an SX board at %x\n", board->hw_base); + printk (KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, uniq ID:%08x, ", + vpdp.hwrev, vpdp.hwass, vpdp.uniqid); + printk ( "Manufactured: %d/%d\n", + 1970 + vpdp.myear, vpdp.mweek); + + + if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_PCI_UNIQUEID1) && + (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) { + /* This might be a bit harsh. This was the primary reason the + SX/ISA card didn't work at first... */ + printk (KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA card. Sorry: giving up.\n"); + return (0); + } + + if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) == SX_ISA_UNIQUEID1) { + if (board->base & 0x8000) { + printk (KERN_WARNING "sx: Warning: There may be hardware problems with the card at %x.\n", board->base); + printk (KERN_WARNING "sx: Read sx.txt for more info.\n"); + } + } + + + board->nports = -1; + + /* This resets the processor, and keeps it off the bus. */ + if (!sx_reset (board)) + return 0; + sx_dprintk (SX_DEBUG_INIT, "reset the board...\n"); + + board->flags |= SX_BOARD_PRESENT; + + func_exit(); + return 1; +} + + + +/* Specialix probes for this card at 32k increments from 640k to 16M. + I consider machines with less than 16M unlikely nowadays, so I'm + not probing above 1Mb. Also, 0xa0000, 0xb0000, are taken by the VGA + card. 0xe0000 and 0xf0000 are taken by the BIOS. That only leaves + 0xc0000, 0xc8000, 0xd0000 and 0xd8000 . */ + +int probe_si (struct sx_board *board) +{ + int i; + + func_enter(); + sx_dprintk (SX_DEBUG_PROBE, "Going to verify SI signature %x.\n", + board->base + SI2_ISA_ID_BASE); + + if (sx_debug & SX_DEBUG_PROBE) + my_hd ((char *)(board->base + SI2_ISA_ID_BASE), 0x8); + + for (i=0;i<8;i++) { + if ((read_sx_byte (board, SI2_ISA_ID_BASE+7-i) & 7) != i) { + return 0; + } + } + + printheader (); + + printk (KERN_DEBUG "sx: Found an SI board at %x\n", board->hw_base); + /* Compared to the SX boards, it is a complete guess as to what + this card is up to... */ + + board->nports = -1; + + /* This resets the processor, and keeps it off the bus. */ + if (!sx_reset (board)) + return 0; + sx_dprintk (SX_DEBUG_INIT, "reset the board...\n"); + + board->flags |= SX_BOARD_PRESENT; + + func_exit(); + return 1; +} + + +static int sx_init_drivers(void) +{ + int error; + + func_enter(); + + memset(&sx_driver, 0, sizeof(sx_driver)); + sx_driver.magic = TTY_DRIVER_MAGIC; + sx_driver.driver_name = "specialix_sx"; + sx_driver.name = "ttyX"; + sx_driver.major = SX_NORMAL_MAJOR; + sx_driver.num = sx_nports; + sx_driver.type = TTY_DRIVER_TYPE_SERIAL; + sx_driver.subtype = SX_TYPE_NORMAL; + sx_driver.init_termios = tty_std_termios; + sx_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + sx_driver.flags = TTY_DRIVER_REAL_RAW; + sx_driver.refcount = &sx_refcount; + sx_driver.table = sx_table; + sx_driver.termios = sx_termios; + sx_driver.termios_locked = sx_termios_locked; + + sx_driver.open = sx_open; + sx_driver.close = gs_close; + sx_driver.write = gs_write; + sx_driver.put_char = gs_put_char; + sx_driver.flush_chars = gs_flush_chars; + sx_driver.write_room = gs_write_room; + sx_driver.chars_in_buffer = gs_chars_in_buffer; + sx_driver.flush_buffer = gs_flush_buffer; + sx_driver.ioctl = sx_ioctl; + sx_driver.throttle = sx_throttle; + sx_driver.unthrottle = sx_unthrottle; + sx_driver.set_termios = gs_set_termios; + sx_driver.stop = gs_stop; + sx_driver.start = gs_start; + sx_driver.hangup = gs_hangup; + + sx_callout_driver = sx_driver; + sx_callout_driver.name = "cux"; + sx_callout_driver.major = SX_CALLOUT_MAJOR; + sx_callout_driver.subtype = SX_TYPE_CALLOUT; + + if ((error = tty_register_driver(&sx_driver))) { + printk(KERN_ERR "sx: Couldn't register sx driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&sx_callout_driver))) { + tty_unregister_driver(&sx_driver); + printk(KERN_ERR "sx: Couldn't register sx callout driver, error = %d\n", + error); + return 1; + } + + func_exit(); + return 0; +} + + +void * ckmalloc (int size) +{ + void *p; + + p = kmalloc(size, GFP_KERNEL); + if (p) + memset(p, 0, size); + return p; +} + + +static int sx_init_portstructs (int nboards, int nports) +{ + struct sx_board *board; + struct sx_port *port; + int i, j; + int addr, chans; + int portno; + + func_enter(); + + /* Many drivers statically allocate the maximum number of ports + There is no reason not to allocate them dynamically. Is there? -- REW */ + sx_ports = ckmalloc(nports * sizeof (struct sx_port)); + if (!sx_ports) + return -ENOMEM; + + sx_termios = ckmalloc(nports * sizeof (struct termios *)); + if (!sx_termios) { + kfree (sx_ports); + return -ENOMEM; + } + + sx_termios_locked = ckmalloc(nports * sizeof (struct termios *)); + if (!sx_termios_locked) { + kfree (sx_ports); + kfree (sx_termios); + return -ENOMEM; + } + + /* Adjust the values in the "driver" */ + sx_driver.termios = sx_termios; + sx_driver.termios_locked = sx_termios_locked; + + port = sx_ports; + for (i = 0; i < nboards; i++) { + board = &boards[i]; + board->ports = port; + for (j=0; j < boards[i].nports;j++) { + sx_dprintk (SX_DEBUG_INIT, "initing port %d\n", j); + port->gs.callout_termios = tty_std_termios; + port->gs.normal_termios = tty_std_termios; + port->gs.magic = SX_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->board = board; + port->gs.rd = &sx_real_driver; +#ifdef NEW_WRITE_LOCKING + port->gs.port_write_sem = MUTEX; +#endif + port++; + } + } + + port = sx_ports; + portno = 0; + for (i = 0; i < nboards; i++) { + board = &boards[i]; + board->port_base = portno; + /* Possibly the configuration was rejected. */ + sx_dprintk (SX_DEBUG_PROBE, "Board has %d channels\n", board->nports); + if (board->nports <= 0) continue; + /* XXX byteorder ?? */ + for (addr = 0x80;addr != 0;addr = read_sx_word (board, addr) & 0x7fff) { + chans = sx_read_module_byte (board, addr, mc_type); + sx_dprintk (SX_DEBUG_PROBE, "Module at %x: %d channels\n", addr, chans); + sx_dprintk (SX_DEBUG_PROBE, "Port at"); + for (j=0;j<chans;j++) { + /* The "sx-way" is the way it SHOULD be done. That way in the + future, the firmware may for example pack the structures a bit + more efficient. Neil tells me it isn't going to happen anytime + soon though. */ + if (IS_SX_BOARD(board)) + port->ch_base = sx_read_module_word (board, addr+j*2, mc_chan_pointer); + else + port->ch_base = addr + 0x100 + 0x300*j; + + sx_dprintk (SX_DEBUG_PROBE, " %x", port->ch_base); + port->line = portno++; + port++; + } + sx_dprintk (SX_DEBUG_PROBE, "\n"); + } + /* This has to be done earlier. */ + /* board->flags |= SX_BOARD_INITIALIZED; */ + } + + func_exit(); + return 0; +} + + +static void sx_release_drivers(void) +{ + func_enter(); + tty_unregister_driver(&sx_driver); + tty_unregister_driver(&sx_callout_driver); + func_exit(); +} + +#ifdef TWO_ZERO +#define PDEV unsigned char pci_bus, unsigned pci_fun +#define pdev pci_bus, pci_fun +#else +#define PDEV struct pci_dev *pdev +#endif + + +#ifdef CONFIG_PCI + /******************************************************** + * Setting bit 17 in the CNTRL register of the PLX 9050 * + * chip forces a retry on writes while a read is pending.* + * This is to prevent the card locking up on Intel Xeon * + * multiprocessor systems with the NX chipset. -- NV * + ********************************************************/ + +/* Newer cards are produced with this bit set from the configuration + EEprom. As the bit is read/write for the CPU, we can fix it here, + if we detect that it isn't set correctly. -- REW */ + +void fix_sx_pci (PDEV, struct sx_board *board) +{ + unsigned int hwbase; + unsigned long rebase; + int t; + +#define CNTRL_REG_OFFSET 0x14 + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase); + hwbase &= PCI_BASE_ADDRESS_MEM_MASK; + rebase = (ulong) ioremap(hwbase, 0x80); + t = readb (rebase + CNTRL_REG_OFFSET*4 + 2); + if (t != 0x06) { + printk (KERN_DEBUG "sx: performing cntrl reg fix: %02x -> 06\n", t); + writeb (0x06, rebase + CNTRL_REG_OFFSET*4+2); + } + my_iounmap (hwbase, rebase); + +} +#endif + + +#ifdef MODULE +#define sx_init init_module +#endif + +int sx_init(void) +{ + int i; + int found = 0; + struct sx_board *board; + +#ifdef CONFIG_PCI +#ifndef TWO_ZERO + struct pci_dev *pdev = NULL; +#else + unsigned char pci_bus, pci_fun; + /* in 2.2.x pdev is a pointer defining a PCI device. In 2.0 its the bus/fn */ +#endif + unsigned int tint; + unsigned short tshort; +#endif + + func_enter(); + sx_dprintk (SX_DEBUG_INIT, "Initing sx module... (sx_debug=%d)\n", sx_debug); + if (abs ((long) (&sx_debug) - sx_debug) < 0x10000) { + printk (KERN_WARNING "sx: sx_debug is an address, instead of a value. " + "Assuming -1.\n"); + printk ("(%p)\n", &sx_debug); + sx_debug=-1; + } + +#ifdef CONFIG_PCI + if (pci_present ()) { +#ifndef TWO_ZERO + while ((pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX, + PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, + pdev))) { +#else + for (i=0;i< SX_NBOARDS;i++) { + if (pcibios_find_device (PCI_VENDOR_ID_SPECIALIX, + PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, i, + &pci_bus, &pci_fun)) break; +#endif + /* Specialix has a whole bunch of cards with + 0x2000 as the device ID. They say its because + the standard requires it. Stupid standard. */ + /* It seems that reading a word doesn't work reliably on 2.0. + Also, reading a non-aligned dword doesn't work. So we read the + whole dword at 0x2c and extract the word at 0x2e (SUBSYSTEM_ID) + ourselves */ + /* I don't know why the define doesn't work, constant 0x2c does --REW */ + pci_read_config_dword (pdev, 0x2c, &tint); + tshort = (tint >> 16) & 0xffff; + sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x.\n", tint); + /* sx_dprintk (SX_DEBUG_PROBE, "pdev = %d/%d (%x)\n", pdev, tint); */ + if (tshort != 0x0200) { + sx_dprintk (SX_DEBUG_PROBE, "But it's not an SX card (%d)...\n", + tshort); + continue; + } + board = &boards[found]; + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint); + board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK; + board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN); + board->irq = get_irq (pdev); + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SX_PCI_BOARD; + + sx_dprintk (SX_DEBUG_PROBE, "Got a specialix card: %x/%x(%d).\n", + tint, boards[found].base, board->irq); + + if (probe_sx (board)) { + found++; + fix_sx_pci (pdev, board); + } else + my_iounmap (board->hw_base, board->base); + } + } +#endif + + for (i=0;i<NR_SX_ADDRS;i++) { + board = &boards[found]; + board->hw_base = sx_probe_addrs[i]; + board->base = (ulong) ioremap(board->hw_base, SX_WINDOW_LEN); + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SX_ISA_BOARD; + board->irq = sx_irqmask?-1:0; + + if (probe_sx (board)) { + found++; + } else { + my_iounmap (board->hw_base, board->base); + } + } + + for (i=0;i<NR_SI_ADDRS;i++) { + board = &boards[found]; + board->hw_base = si_probe_addrs[i]; + board->base = (ulong) ioremap(board->hw_base, SI2_ISA_WINDOW_LEN); + board->flags &= ~SX_BOARD_TYPE; + board->flags |= SI_ISA_BOARD; + board->irq = sx_irqmask ?-1:0; + + if (probe_si (board)) { + found++; + } else { + my_iounmap (board->hw_base, board->base); + } + } + + if (found) { + printk (KERN_INFO "sx: total of %d boards detected.\n", found); + + if (misc_register(&sx_fw_device) < 0) { + printk(KERN_ERR "SX: Unable to register firmware loader driver.\n"); + return -EIO; + } + } + + func_exit(); + return found?0:-EIO; +} + + + +void cleanup_module(void) +{ + int i; + struct sx_board *board; + + func_enter(); + for (i = 0; i < SX_NBOARDS; i++) { + board = &boards[i]; + if (board->flags & SX_BOARD_INITIALIZED) { + sx_dprintk (SX_DEBUG_CLEANUP, "Cleaning up board at %x\n", board->base); + /* The board should stop messing with us. + (actually I mean the interrupt) */ + sx_reset (board); + if ((board->irq) && (board->flags & SX_IRQ_ALLOCATED)) + free_irq (board->irq, board); + + /* It is safe/allowed to del_timer a non-active timer */ + del_timer (& board->timer); + my_iounmap (board->hw_base, board->base); + } + } + if (misc_deregister(&sx_fw_device) < 0) { + printk (KERN_INFO "sx: couldn't deregister firmware loader device\n"); + } + sx_dprintk (SX_DEBUG_CLEANUP, "Cleaning up drivers (%d)\n", sx_initialized); + if (sx_initialized) + sx_release_drivers (); + + kfree (sx_ports); + kfree (sx_termios); + kfree (sx_termios_locked); + func_exit(); +} + + +#ifdef DEBUG +void my_hd (unsigned char *addr, int len) +{ + int i, j, ch; + + for (i=0;i<len;i+=16) { + printk ("%08x ", (int) addr+i); + for (j=0;j<16;j++) { + printk ("%02x %s", addr[j+i], (j==7)?" ":""); + } + for (j=0;j<16;j++) { + ch = addr[j+i]; + printk ("%c", (ch < 0x20)?'.':((ch > 0x7f)?'.':ch)); + } + printk ("\n"); + } +} +#endif + +#ifdef MODULE +#undef func_enter +#undef func_exit + +#include "generic_serial.c" +#endif + + +/* + * Anybody who knows why this doesn't work for me, please tell me -- REW. + * Snatched from scsi.c (fixed one spelling error): + * Overrides for Emacs so that we follow Linus' 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: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/drivers/char/sx.h b/drivers/char/sx.h new file mode 100644 index 000000000..e046482e4 --- /dev/null +++ b/drivers/char/sx.h @@ -0,0 +1,180 @@ + +/* + * sx.h + * + * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl + * + * SX serial driver. + * -- Supports SI, XIO and SX host cards. + * -- Supports TAs, MTAs and SXDCs. + * + * Version 1.3 -- March, 1999. + * + */ + +#define SX_NBOARDS 4 +#define SX_PORTSPERBOARD 32 +#define SX_NPORTS (SX_NBOARDS * SX_PORTSPERBOARD) + +#ifdef __KERNEL__ + +#define SX_MAGIC 0x12345678 + +struct sx_port { + struct gs_port gs; + /* + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + */ + struct wait_queue *shutdown_wait; + int ch_base; + int c_dcd; + struct sx_board *board; + int line; + int locks; +}; + +struct sx_board { + int magic; + unsigned int base; + unsigned int hw_base; + int port_base; /* Number of the first port */ + struct sx_port *ports; + int nports; + int flags; + int irq; + int poll; + int ta_type; + struct timer_list timer; + int locks; +}; + +struct vpd_prom { + unsigned short id; + char hwrev; + char hwass; + int uniqid; + char myear; + char mweek; + char hw_feature[5]; + char oem_id; + char identifier[16]; +}; + +#ifndef MOD_RS232DB25MALE +#define MOD_RS232DB25MALE 0x0a +#endif + + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_ISA_BOARD 0x00000002 +#define SX_PCI_BOARD 0x00000004 +#define SI_ISA_BOARD 0x00000008 +#define SX_BOARD_INITIALIZED 0x00000010 +#define SX_IRQ_ALLOCATED 0x00000020 + +#define SX_BOARD_TYPE (SX_ISA_BOARD|SX_PCI_BOARD|SI_ISA_BOARD) + +#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_ISA_BOARD)) +#define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD) + + +#define SERIAL_TYPE_NORMAL 1 + +/* The SI processor clock is required to calculate the cc_int_count register + value for the SI cards. */ +#define SI_PROCESSOR_CLOCK 25000000 + + +/* port flags */ +/* Make sure these don't clash with gs flags or async flags */ +#define SX_RX_THROTTLE 0x0000001 + + + +#define SX_PORT_TRANSMIT_LOCK 0 +#define SX_BOARD_INTR_LOCK 0 + + + +/* Debug flags. Add these together to get more debug info. */ + +#define SX_DEBUG_OPEN 0x00000001 +#define SX_DEBUG_SETTING 0x00000002 +#define SX_DEBUG_FLOW 0x00000004 +#define SX_DEBUG_MODEMSIGNALS 0x00000008 +#define SX_DEBUG_TERMIOS 0x00000010 +#define SX_DEBUG_TRANSMIT 0x00000020 +#define SX_DEBUG_RECEIVE 0x00000040 +#define SX_DEBUG_INTERRUPTS 0x00000080 +#define SX_DEBUG_PROBE 0x00000100 +#define SX_DEBUG_INIT 0x00000200 +#define SX_DEBUG_CLEANUP 0x00000400 +#define SX_DEBUG_CLOSE 0x00000800 +#define SX_DEBUG_FIRMWARE 0x00001000 +#define SX_DEBUG_MEMTEST 0x00002000 + +#define SX_DEBUG_ALL 0xffffffff + + +#define O_OTHER(tty) \ + ((O_OLCUC(tty)) ||\ + (O_ONLCR(tty)) ||\ + (O_OCRNL(tty)) ||\ + (O_ONOCR(tty)) ||\ + (O_ONLRET(tty)) ||\ + (O_OFILL(tty)) ||\ + (O_OFDEL(tty)) ||\ + (O_NLDLY(tty)) ||\ + (O_CRDLY(tty)) ||\ + (O_TABDLY(tty)) ||\ + (O_BSDLY(tty)) ||\ + (O_VTDLY(tty)) ||\ + (O_FFDLY(tty))) + +/* Same for input. */ +#define I_OTHER(tty) \ + ((I_INLCR(tty)) ||\ + (I_IGNCR(tty)) ||\ + (I_ICRNL(tty)) ||\ + (I_IUCLC(tty)) ||\ + (L_ISIG(tty))) + +#define MOD_TA ( TA>>4) +#define MOD_MTA (MTA_CD1400>>4) +#define MOD_SXDC ( SXDC>>4) + + +/* We copy the download code over to the card in chunks of ... bytes */ +#define SX_CHUNK_SIZE 128 + +#endif /* __KERNEL__ */ + + + +/* Specialix document 6210046-11 page 3 */ +#define SPX(X) (('S'<<24) | ('P' << 16) | (X)) + +/* Specialix-Linux specific IOCTLS. */ +#define SPXL(X) (SPX(('L' << 8) | (X))) + + +#define SXIO_SET_BOARD SPXL(0x01) +#define SXIO_GET_TYPE SPXL(0x02) +#define SXIO_DOWNLOAD SPXL(0x03) +#define SXIO_INIT SPXL(0x04) +#define SXIO_SETDEBUG SPXL(0x05) +#define SXIO_GETDEBUG SPXL(0x06) +#define SXIO_DO_RAMTEST SPXL(0x07) +#define SXIO_SETGSDEBUG SPXL(0x08) +#define SXIO_GETGSDEBUG SPXL(0x09) + + +#ifndef SXCTL_MISC_MINOR +/* Allow others to gather this into "major.h" or something like that */ +#define SXCTL_MISC_MINOR 167 +#endif + +#define SX_TYPE_SX 0x01 +#define SX_TYPE_SI 0x02 + diff --git a/drivers/char/sxboards.h b/drivers/char/sxboards.h new file mode 100644 index 000000000..45636c9e9 --- /dev/null +++ b/drivers/char/sxboards.h @@ -0,0 +1,181 @@ +/************************************************************************/ +/* */ +/* Title : SX/SI/XIO Board Hardware Definitions */ +/* */ +/* Author : N.P.Vassallo */ +/* */ +/* Creation : 16th March 1998 */ +/* */ +/* Version : 3.0.0 */ +/* */ +/* Copyright : (c) Specialix International Ltd. 1998 */ +/* */ +/* Description : Prototypes, structures and definitions */ +/* describing the SX/SI/XIO board hardware */ +/* */ +/************************************************************************/ + +/* History... + +3.0.0 16/03/98 NPV Creation. + +*/ + +#ifndef _sxboards_h /* If SXBOARDS.H not already defined */ +#define _sxboards_h 1 + +/***************************************************************************** +******************************* ****************************** +******************************* Board Types ****************************** +******************************* ****************************** +*****************************************************************************/ + +/* BUS types... */ +#define BUS_ISA 0 +#define BUS_MCA 1 +#define BUS_EISA 2 +#define BUS_PCI 3 + +/* Board phases... */ +#define SI1_Z280 1 +#define SI2_Z280 2 +#define SI3_T225 3 + +/* Board types... */ +#define CARD_TYPE(bus,phase) (bus<<4|phase) +#define CARD_BUS(type) ((type>>4)&0xF) +#define CARD_PHASE(type) (type&0xF) + +#define TYPE_SI2_ISA CARD_TYPE(BUS_ISA,SI2_Z280) +#define TYPE_SI2_EISA CARD_TYPE(BUS_EISA,SI2_Z280) +#define TYPE_SI2_PCI CARD_TYPE(BUS_PCI,SI2_Z280) + +#define TYPE_SX_ISA CARD_TYPE(BUS_ISA,SI3_T225) +#define TYPE_SX_PCI CARD_TYPE(BUS_PCI,SI3_T225) + +/***************************************************************************** +****************************** ****************************** +****************************** Phase 2 Z280 ****************************** +****************************** ****************************** +*****************************************************************************/ + +/* ISA board details... */ +#define SI2_ISA_WINDOW_LEN 0x8000 /* 32 Kbyte shared memory window */ +#define SI2_ISA_MEMORY_LEN 0x7FF8 /* Usable memory */ +#define SI2_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */ +#define SI2_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */ +#define SI2_ISA_ADDR_STEP SI2_ISA_WINDOW_LEN/* ISA board address step */ +#define SI2_ISA_IRQ_MASK 0x9800 /* IRQs 15,12,11 */ + +/* ISA board, register definitions... */ +#define SI2_ISA_ID_BASE 0x7FF8 /* READ: Board ID string */ +#define SI2_ISA_RESET SI2_ISA_ID_BASE /* WRITE: Host Reset */ +#define SI2_ISA_IRQ11 (SI2_ISA_ID_BASE+1) /* WRITE: Set IRQ11 */ +#define SI2_ISA_IRQ12 (SI2_ISA_ID_BASE+2) /* WRITE: Set IRQ12 */ +#define SI2_ISA_IRQ15 (SI2_ISA_ID_BASE+3) /* WRITE: Set IRQ15 */ +#define SI2_ISA_IRQSET (SI2_ISA_ID_BASE+4) /* WRITE: Set Host Interrupt */ +#define SI2_ISA_INTCLEAR (SI2_ISA_ID_BASE+5) /* WRITE: Enable Host Interrupt */ + +#define SI2_ISA_IRQ11_SET 0x10 +#define SI2_ISA_IRQ11_CLEAR 0x00 +#define SI2_ISA_IRQ12_SET 0x10 +#define SI2_ISA_IRQ12_CLEAR 0x00 +#define SI2_ISA_IRQ15_SET 0x10 +#define SI2_ISA_IRQ15_CLEAR 0x00 +#define SI2_ISA_INTCLEAR_SET 0x10 +#define SI2_ISA_INTCLEAR_CLEAR 0x00 +#define SI2_ISA_IRQSET_CLEAR 0x10 +#define SI2_ISA_IRQSET_SET 0x00 +#define SI2_ISA_RESET_SET 0x00 +#define SI2_ISA_RESET_CLEAR 0x10 + +/* PCI board details... */ +#define SI2_PCI_WINDOW_LEN 0x100000 /* 1 Mbyte memory window */ + +/* PCI board register definitions... */ +#define SI2_PCI_SET_IRQ 0x40001 /* Set Host Interrupt */ +#define SI2_PCI_RESET 0xC0001 /* Host Reset */ + +/***************************************************************************** +****************************** ****************************** +****************************** Phase 3 T225 ****************************** +****************************** ****************************** +*****************************************************************************/ + +/* General board details... */ +#define SX_WINDOW_LEN 64*1024 /* 64 Kbyte memory window */ + +/* ISA board details... */ +#define SX_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */ +#define SX_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */ +#define SX_ISA_ADDR_STEP SX_WINDOW_LEN /* ISA board address step */ +#define SX_ISA_IRQ_MASK 0x9E00 /* IRQs 15,12,11,10,9 */ + +/* Hardware register definitions... */ +#define SX_EVENT_STATUS 0x7800 /* READ: T225 Event Status */ +#define SX_EVENT_STROBE 0x7800 /* WRITE: T225 Event Strobe */ +#define SX_EVENT_ENABLE 0x7880 /* WRITE: T225 Event Enable */ +#define SX_VPD_ROM 0x7C00 /* READ: Vital Product Data ROM */ +#define SX_CONFIG 0x7C00 /* WRITE: Host Configuration Register */ +#define SX_IRQ_STATUS 0x7C80 /* READ: Host Interrupt Status */ +#define SX_SET_IRQ 0x7C80 /* WRITE: Set Host Interrupt */ +#define SX_RESET_STATUS 0x7D00 /* READ: Host Reset Status */ +#define SX_RESET 0x7D00 /* WRITE: Host Reset */ +#define SX_RESET_IRQ 0x7D80 /* WRITE: Reset Host Interrupt */ + +/* SX_VPD_ROM definitions... */ +#define SX_VPD_SLX_ID1 0x00 +#define SX_VPD_SLX_ID2 0x01 +#define SX_VPD_HW_REV 0x02 +#define SX_VPD_HW_ASSEM 0x03 +#define SX_VPD_UNIQUEID4 0x04 +#define SX_VPD_UNIQUEID3 0x05 +#define SX_VPD_UNIQUEID2 0x06 +#define SX_VPD_UNIQUEID1 0x07 +#define SX_VPD_MANU_YEAR 0x08 +#define SX_VPD_MANU_WEEK 0x09 +#define SX_VPD_IDENT 0x10 +#define SX_VPD_IDENT_STRING "JET HOST BY KEV#" + +/* SX unique identifiers... */ +#define SX_UNIQUEID_MASK 0xF0 +#define SX_ISA_UNIQUEID1 0x20 +#define SX_PCI_UNIQUEID1 0x50 + +/* SX_CONFIG definitions... */ +#define SX_CONF_BUSEN 0x02 /* Enable T225 memory and I/O */ +#define SX_CONF_HOSTIRQ 0x04 /* Enable board to host interrupt */ + +/* SX bootstrap... */ +#define SX_BOOTSTRAP "\x28\x20\x21\x02\x60\x0a" +#define SX_BOOTSTRAP_SIZE 6 +#define SX_BOOTSTRAP_ADDR (0x8000-SX_BOOTSTRAP_SIZE) + +/***************************************************************************** +********************************** ********************************** +********************************** EISA ********************************** +********************************** ********************************** +*****************************************************************************/ + +#define SI2_EISA_OFF 0x42 +#define SI2_EISA_VAL 0x01 + +/***************************************************************************** +*********************************** ********************************** +*********************************** PCI ********************************** +*********************************** ********************************** +*****************************************************************************/ + +/* General definitions... */ + +#define SPX_VENDOR_ID 0x11CB /* Assigned by the PCI SIG */ +#define SPX_DEVICE_ID 0x4000 /* SI/XIO boards */ +#define SPX_PLXDEVICE_ID 0x2000 /* SX boards */ + +#define SPX_SUB_VENDOR_ID SPX_VENDOR_ID /* Same as vendor id */ +#define SI2_SUB_SYS_ID 0x400 /* Phase 2 (Z280) board */ +#define SX_SUB_SYS_ID 0x200 /* Phase 3 (t225) board */ + +#endif /*_sxboards_h */ + +/* End of SXBOARDS.H */ diff --git a/drivers/char/sxwindow.h b/drivers/char/sxwindow.h new file mode 100644 index 000000000..cf01b662a --- /dev/null +++ b/drivers/char/sxwindow.h @@ -0,0 +1,393 @@ +/************************************************************************/ +/* */ +/* Title : SX Shared Memory Window Structure */ +/* */ +/* Author : N.P.Vassallo */ +/* */ +/* Creation : 16th March 1998 */ +/* */ +/* Version : 3.0.0 */ +/* */ +/* Copyright : (c) Specialix International Ltd. 1998 */ +/* */ +/* Description : Prototypes, structures and definitions */ +/* describing the SX/SI/XIO cards shared */ +/* memory window structure: */ +/* SXCARD */ +/* SXMODULE */ +/* SXCHANNEL */ +/* */ +/************************************************************************/ + +/* History... + +3.0.0 16/03/98 NPV Creation. (based on STRUCT.H) + +*/ + +#ifndef _sxwindow_h /* If SXWINDOW.H not already defined */ +#define _sxwindow_h 1 + +/***************************************************************************** +*************************** *************************** +*************************** Common Definitions *************************** +*************************** *************************** +*****************************************************************************/ + +typedef struct _SXCARD *PSXCARD; /* SXCARD structure pointer */ +typedef struct _SXMODULE *PMOD; /* SXMODULE structure pointer */ +typedef struct _SXCHANNEL *PCHAN; /* SXCHANNEL structure pointer */ + +/***************************************************************************** +********************************* ********************************* +********************************* SXCARD ********************************* +********************************* ********************************* +*****************************************************************************/ + +typedef struct _SXCARD +{ + BYTE cc_init_status; /* 0x00 Initialisation status */ + BYTE cc_mem_size; /* 0x01 Size of memory on card */ + WORD cc_int_count; /* 0x02 Interrupt count */ + WORD cc_revision; /* 0x04 Download code revision */ + BYTE cc_isr_count; /* 0x06 Count when ISR is run */ + BYTE cc_main_count; /* 0x07 Count when main loop is run */ + WORD cc_int_pending; /* 0x08 Interrupt pending */ + WORD cc_poll_count; /* 0x0A Count when poll is run */ + BYTE cc_int_set_count; /* 0x0C Count when host interrupt is set */ + BYTE cc_rfu[0x80 - 0x0D]; /* 0x0D Pad structure to 128 bytes (0x80) */ + +} SXCARD; + +/* SXCARD.cc_init_status definitions... */ +#define ADAPTERS_FOUND (BYTE)0x01 +#define NO_ADAPTERS_FOUND (BYTE)0xFF + +/* SXCARD.cc_mem_size definitions... */ +#define SX_MEMORY_SIZE (BYTE)0x40 + +/* SXCARD.cc_int_count definitions... */ +#define INT_COUNT_DEFAULT 100 /* Hz */ + +/***************************************************************************** +******************************** ******************************** +******************************** SXMODULE ******************************** +******************************** ******************************** +*****************************************************************************/ + +#define TOP_POINTER(a) ((a)|0x8000) /* Sets top bit of word */ +#define UNTOP_POINTER(a) ((a)&~0x8000) /* Clears top bit of word */ + +typedef struct _SXMODULE +{ + WORD mc_next; /* 0x00 Next module "pointer" (ORed with 0x8000) */ + BYTE mc_type; /* 0x02 Type of TA in terms of number of channels */ + BYTE mc_mod_no; /* 0x03 Module number on SI bus cable (0 closest to card) */ + BYTE mc_dtr; /* 0x04 Private DTR copy (TA only) */ + BYTE mc_rfu1; /* 0x05 Reserved */ + WORD mc_uart; /* 0x06 UART base address for this module */ + BYTE mc_chip; /* 0x08 Chip type / number of ports */ + BYTE mc_current_uart; /* 0x09 Current uart selected for this module */ +#ifdef DOWNLOAD + PCHAN mc_chan_pointer[8]; /* 0x0A Pointer to each channel structure */ +#else + WORD mc_chan_pointer[8]; /* 0x0A Define as WORD if not compiling into download */ +#endif + WORD mc_rfu2; /* 0x1A Reserved */ + BYTE mc_opens1; /* 0x1C Number of open ports on first four ports on MTA/SXDC */ + BYTE mc_opens2; /* 0x1D Number of open ports on second four ports on MTA/SXDC */ + BYTE mc_mods; /* 0x1E Types of connector module attached to MTA/SXDC */ + BYTE mc_rev1; /* 0x1F Revision of first CD1400 on MTA/SXDC */ + BYTE mc_rev2; /* 0x20 Revision of second CD1400 on MTA/SXDC */ + BYTE mc_mtaasic_rev; /* 0x21 Revision of MTA ASIC 1..4 -> A, B, C, D */ + BYTE mc_rfu3[0x100 - 0x22]; /* 0x22 Pad structure to 256 bytes (0x100) */ + +} SXMODULE; + +/* SXMODULE.mc_type definitions... */ +#define FOUR_PORTS (BYTE)4 +#define EIGHT_PORTS (BYTE)8 + +/* SXMODULE.mc_chip definitions... */ +#define CHIP_MASK 0xF0 +#define TA (BYTE)0 +#define TA4 (TA | FOUR_PORTS) +#define TA8 (TA | EIGHT_PORTS) +#define TA4_ASIC (BYTE)0x0A +#define TA8_ASIC (BYTE)0x0B +#define MTA_CD1400 (BYTE)0x28 +#define SXDC (BYTE)0x48 + +/* SXMODULE.mc_mods definitions... */ +#define MOD_RS232DB25 0x00 /* RS232 DB25 (socket/plug) */ +#define MOD_RS232RJ45 0x01 /* RS232 RJ45 (shielded/opto-isolated) */ +#define MOD_RESERVED_2 0x02 /* Reserved (RS485) */ +#define MOD_RS422DB25 0x03 /* RS422 DB25 Socket */ +#define MOD_RESERVED_4 0x04 /* Reserved */ +#define MOD_PARALLEL 0x05 /* Parallel */ +#define MOD_RESERVED_6 0x06 /* Reserved (RS423) */ +#define MOD_RESERVED_7 0x07 /* Reserved */ +#define MOD_2_RS232DB25 0x08 /* Rev 2.0 RS232 DB25 (socket/plug) */ +#define MOD_2_RS232RJ45 0x09 /* Rev 2.0 RS232 RJ45 */ +#define MOD_RESERVED_A 0x0A /* Rev 2.0 Reserved */ +#define MOD_2_RS422DB25 0x0B /* Rev 2.0 RS422 DB25 */ +#define MOD_RESERVED_C 0x0C /* Rev 2.0 Reserved */ +#define MOD_2_PARALLEL 0x0D /* Rev 2.0 Parallel */ +#define MOD_RESERVED_E 0x0E /* Rev 2.0 Reserved */ +#define MOD_BLANK 0x0F /* Blank Panel */ + +/***************************************************************************** +******************************** ******************************* +******************************** SXCHANNEL ******************************* +******************************** ******************************* +*****************************************************************************/ + +#define TX_BUFF_OFFSET 0x60 /* Transmit buffer offset in channel structure */ +#define BUFF_POINTER(a) (((a)+TX_BUFF_OFFSET)|0x8000) +#define UNBUFF_POINTER(a) (jet_channel*)(((a)&~0x8000)-TX_BUFF_OFFSET) +#define BUFFER_SIZE 256 +#define HIGH_WATER ((BUFFER_SIZE / 4) * 3) +#define LOW_WATER (BUFFER_SIZE / 4) + +typedef struct _SXCHANNEL +{ + WORD next_item; /* 0x00 Offset from window base of next channels hi_txbuf (ORred with 0x8000) */ + WORD addr_uart; /* 0x02 INTERNAL pointer to uart address. Includes FASTPATH bit */ + WORD module; /* 0x04 Offset from window base of parent SXMODULE structure */ + BYTE type; /* 0x06 Chip type / number of ports (copy of mc_chip) */ + BYTE chan_number; /* 0x07 Channel number on the TA/MTA/SXDC */ + WORD xc_status; /* 0x08 Flow control and I/O status */ + BYTE hi_rxipos; /* 0x0A Receive buffer input index */ + BYTE hi_rxopos; /* 0x0B Receive buffer output index */ + BYTE hi_txopos; /* 0x0C Transmit buffer output index */ + BYTE hi_txipos; /* 0x0D Transmit buffer input index */ + BYTE hi_hstat; /* 0x0E Command register */ + BYTE dtr_bit; /* 0x0F INTERNAL DTR control byte (TA only) */ + BYTE txon; /* 0x10 INTERNAL copy of hi_txon */ + BYTE txoff; /* 0x11 INTERNAL copy of hi_txoff */ + BYTE rxon; /* 0x12 INTERNAL copy of hi_rxon */ + BYTE rxoff; /* 0x13 INTERNAL copy of hi_rxoff */ + BYTE hi_mr1; /* 0x14 Mode Register 1 (databits,parity,RTS rx flow)*/ + BYTE hi_mr2; /* 0x15 Mode Register 2 (stopbits,local,CTS tx flow)*/ + BYTE hi_csr; /* 0x16 Clock Select Register (baud rate) */ + BYTE hi_op; /* 0x17 Modem Output Signal */ + BYTE hi_ip; /* 0x18 Modem Input Signal */ + BYTE hi_state; /* 0x19 Channel status */ + BYTE hi_prtcl; /* 0x1A Channel protocol (flow control) */ + BYTE hi_txon; /* 0x1B Transmit XON character */ + BYTE hi_txoff; /* 0x1C Transmit XOFF character */ + BYTE hi_rxon; /* 0x1D Receive XON character */ + BYTE hi_rxoff; /* 0x1E Receive XOFF character */ + BYTE close_prev; /* 0x1F INTERNAL channel previously closed flag */ + BYTE hi_break; /* 0x20 Break and error control */ + BYTE break_state; /* 0x21 INTERNAL copy of hi_break */ + BYTE hi_mask; /* 0x22 Mask for received data */ + BYTE mask; /* 0x23 INTERNAL copy of hi_mask */ + BYTE mod_type; /* 0x24 MTA/SXDC hardware module type */ + BYTE ccr_state; /* 0x25 INTERNAL MTA/SXDC state of CCR register */ + BYTE ip_mask; /* 0x26 Input handshake mask */ + BYTE hi_parallel; /* 0x27 Parallel port flag */ + BYTE par_error; /* 0x28 Error code for parallel loopback test */ + BYTE any_sent; /* 0x29 INTERNAL data sent flag */ + BYTE asic_txfifo_size; /* 0x2A INTERNAL SXDC transmit FIFO size */ + BYTE rfu1[2]; /* 0x2B Reserved */ + BYTE csr; /* 0x2D INTERNAL copy of hi_csr */ +#ifdef DOWNLOAD + PCHAN nextp; /* 0x2E Offset from window base of next channel structure */ +#else + WORD nextp; /* 0x2E Define as WORD if not compiling into download */ +#endif + BYTE prtcl; /* 0x30 INTERNAL copy of hi_prtcl */ + BYTE mr1; /* 0x31 INTERNAL copy of hi_mr1 */ + BYTE mr2; /* 0x32 INTERNAL copy of hi_mr2 */ + BYTE hi_txbaud; /* 0x33 Extended transmit baud rate (SXDC only if((hi_csr&0x0F)==0x0F) */ + BYTE hi_rxbaud; /* 0x34 Extended receive baud rate (SXDC only if((hi_csr&0xF0)==0xF0) */ + BYTE txbreak_state; /* 0x35 INTERNAL MTA/SXDC transmit break state */ + BYTE txbaud; /* 0x36 INTERNAL copy of hi_txbaud */ + BYTE rxbaud; /* 0x37 INTERNAL copy of hi_rxbaud */ + WORD err_framing; /* 0x38 Count of receive framing errors */ + WORD err_parity; /* 0x3A Count of receive parity errors */ + WORD err_overrun; /* 0x3C Count of receive overrun errors */ + WORD err_overflow; /* 0x3E Count of receive buffer overflow errors */ + BYTE rfu2[TX_BUFF_OFFSET - 0x40]; /* 0x40 Reserved until hi_txbuf */ + BYTE hi_txbuf[BUFFER_SIZE]; /* 0x060 Transmit buffer */ + BYTE hi_rxbuf[BUFFER_SIZE]; /* 0x160 Receive buffer */ + BYTE rfu3[0x300 - 0x260]; /* 0x260 Reserved until 768 bytes (0x300) */ + +} SXCHANNEL; + +/* SXCHANNEL.addr_uart definitions... */ +#define FASTPATH 0x1000 /* Set to indicate fast rx/tx processing (TA only) */ + +/* SXCHANNEL.xc_status definitions... */ +#define X_TANY 0x0001 /* XON is any character (TA only) */ +#define X_TION 0x0001 /* Tx interrupts on (MTA only) */ +#define X_TXEN 0x0002 /* Tx XON/XOFF enabled (TA only) */ +#define X_RTSEN 0x0002 /* RTS FLOW enabled (MTA only) */ +#define X_TXRC 0x0004 /* XOFF received (TA only) */ +#define X_RTSLOW 0x0004 /* RTS dropped (MTA only) */ +#define X_RXEN 0x0008 /* Rx XON/XOFF enabled */ +#define X_ANYXO 0x0010 /* XOFF pending/sent or RTS dropped */ +#define X_RXSE 0x0020 /* Rx XOFF sent */ +#define X_NPEND 0x0040 /* Rx XON pending or XOFF pending */ +#define X_FPEND 0x0080 /* Rx XOFF pending */ +#define C_CRSE 0x0100 /* Carriage return sent (TA only) */ +#define C_TEMR 0x0100 /* Tx empty requested (MTA only) */ +#define C_TEMA 0x0200 /* Tx empty acked (MTA only) */ +#define C_ANYP 0x0200 /* Any protocol bar tx XON/XOFF (TA only) */ +#define C_EN 0x0400 /* Cooking enabled (on MTA means port is also || */ +#define C_HIGH 0x0800 /* Buffer previously hit high water */ +#define C_CTSEN 0x1000 /* CTS automatic flow-control enabled */ +#define C_DCDEN 0x2000 /* DCD/DTR checking enabled */ +#define C_BREAK 0x4000 /* Break detected */ +#define C_RTSEN 0x8000 /* RTS automatic flow control enabled (MTA only) */ +#define C_PARITY 0x8000 /* Parity checking enabled (TA only) */ + +/* SXCHANNEL.hi_hstat definitions... */ +#define HS_IDLE_OPEN 0x00 /* Channel open state */ +#define HS_LOPEN 0x02 /* Local open command (no modem monitoring) */ +#define HS_MOPEN 0x04 /* Modem open command (wait for DCD signal) */ +#define HS_IDLE_MPEND 0x06 /* Waiting for DCD signal state */ +#define HS_CONFIG 0x08 /* Configuration command */ +#define HS_CLOSE 0x0A /* Close command */ +#define HS_START 0x0C /* Start transmit break command */ +#define HS_STOP 0x0E /* Stop transmit break command */ +#define HS_IDLE_CLOSED 0x10 /* Closed channel state */ +#define HS_IDLE_BREAK 0x12 /* Transmit break state */ +#define HS_FORCE_CLOSED 0x14 /* Force close command */ +#define HS_RESUME 0x16 /* Clear pending XOFF command */ +#define HS_WFLUSH 0x18 /* Flush transmit buffer command */ +#define HS_RFLUSH 0x1A /* Flush receive buffer command */ +#define HS_SUSPEND 0x1C /* Suspend output command (like XOFF received) */ +#define PARALLEL 0x1E /* Parallel port loopback test command (Diagnostics Only) */ +#define ENABLE_RX_INTS 0x20 /* Enable receive interrupts command (Diagnostics Only) */ +#define ENABLE_TX_INTS 0x22 /* Enable transmit interrupts command (Diagnostics Only) */ +#define ENABLE_MDM_INTS 0x24 /* Enable modem interrupts command (Diagnostics Only) */ +#define DISABLE_INTS 0x26 /* Disable interrupts command (Diagnostics Only) */ + +/* SXCHANNEL.hi_mr1 definitions... */ +#define MR1_BITS 0x03 /* Data bits mask */ +#define MR1_5_BITS 0x00 /* 5 data bits */ +#define MR1_6_BITS 0x01 /* 6 data bits */ +#define MR1_7_BITS 0x02 /* 7 data bits */ +#define MR1_8_BITS 0x03 /* 8 data bits */ +#define MR1_PARITY 0x1C /* Parity mask */ +#define MR1_ODD 0x04 /* Odd parity */ +#define MR1_EVEN 0x00 /* Even parity */ +#define MR1_WITH 0x00 /* Parity enabled */ +#define MR1_FORCE 0x08 /* Force parity */ +#define MR1_NONE 0x10 /* No parity */ +#define MR1_NOPARITY MR1_NONE /* No parity */ +#define MR1_ODDPARITY (MR1_WITH|MR1_ODD) /* Odd parity */ +#define MR1_EVENPARITY (MR1_WITH|MR1_EVEN) /* Even parity */ +#define MR1_MARKPARITY (MR1_FORCE|MR1_ODD) /* Mark parity */ +#define MR1_SPACEPARITY (MR1_FORCE|MR1_EVEN) /* Space parity */ +#define MR1_RTS_RXFLOW 0x80 /* RTS receive flow control */ + +/* SXCHANNEL.hi_mr2 definitions... */ +#define MR2_STOP 0x0F /* Stop bits mask */ +#define MR2_1_STOP 0x07 /* 1 stop bit */ +#define MR2_2_STOP 0x0F /* 2 stop bits */ +#define MR2_CTS_TXFLOW 0x10 /* CTS transmit flow control */ +#define MR2_RTS_TOGGLE 0x20 /* RTS toggle on transmit */ +#define MR2_NORMAL 0x00 /* Normal mode */ +#define MR2_AUTO 0x40 /* Auto-echo mode (TA only) */ +#define MR2_LOCAL 0x80 /* Local echo mode */ +#define MR2_REMOTE 0xC0 /* Remote echo mode (TA only) */ + +/* SXCHANNEL.hi_csr definitions... */ +#define CSR_75 0x0 /* 75 baud */ +#define CSR_110 0x1 /* 110 baud (TA), 115200 (MTA/SXDC) */ +#define CSR_38400 0x2 /* 38400 baud */ +#define CSR_150 0x3 /* 150 baud */ +#define CSR_300 0x4 /* 300 baud */ +#define CSR_600 0x5 /* 600 baud */ +#define CSR_1200 0x6 /* 1200 baud */ +#define CSR_2000 0x7 /* 2000 baud */ +#define CSR_2400 0x8 /* 2400 baud */ +#define CSR_4800 0x9 /* 4800 baud */ +#define CSR_1800 0xA /* 1800 baud */ +#define CSR_9600 0xB /* 9600 baud */ +#define CSR_19200 0xC /* 19200 baud */ +#define CSR_57600 0xD /* 57600 baud */ +#define CSR_EXTBAUD 0xF /* Extended baud rate (hi_txbaud/hi_rxbaud) */ + +/* SXCHANNEL.hi_op definitions... */ +#define OP_RTS 0x01 /* RTS modem output signal */ +#define OP_DTR 0x02 /* DTR modem output signal */ + +/* SXCHANNEL.hi_ip definitions... */ +#define IP_CTS 0x02 /* CTS modem input signal */ +#define IP_DCD 0x04 /* DCD modem input signal */ +#define IP_DSR 0x20 /* DTR modem input signal */ +#define IP_RI 0x40 /* RI modem input signal */ + +/* SXCHANNEL.hi_state definitions... */ +#define ST_BREAK 0x01 /* Break received (clear with config) */ +#define ST_DCD 0x02 /* DCD signal changed state */ + +/* SXCHANNEL.hi_prtcl definitions... */ +#define SP_TANY 0x01 /* Transmit XON/XANY (if SP_TXEN enabled) */ +#define SP_TXEN 0x02 /* Transmit XON/XOFF flow control */ +#define SP_CEN 0x04 /* Cooking enabled */ +#define SP_RXEN 0x08 /* Rx XON/XOFF enabled */ +#define SP_DCEN 0x20 /* DCD / DTR check */ +#define SP_DTR_RXFLOW 0x40 /* DTR receive flow control */ +#define SP_PAEN 0x80 /* Parity checking enabled */ + +/* SXCHANNEL.hi_break definitions... */ +#define BR_IGN 0x01 /* Ignore any received breaks */ +#define BR_INT 0x02 /* Interrupt on received break */ +#define BR_PARMRK 0x04 /* Enable parmrk parity error processing */ +#define BR_PARIGN 0x08 /* Ignore chars with parity errors */ +#define BR_ERRINT 0x80 /* Treat parity/framing/overrun errors as exceptions */ + +/* SXCHANNEL.par_error definitions.. */ +#define DIAG_IRQ_RX 0x01 /* Indicate serial receive interrupt (diags only) */ +#define DIAG_IRQ_TX 0x02 /* Indicate serial transmit interrupt (diags only) */ +#define DIAG_IRQ_MD 0x04 /* Indicate serial modem interrupt (diags only) */ + +/* SXCHANNEL.hi_txbaud/hi_rxbaud definitions... (SXDC only) */ +#define BAUD_75 0x00 /* 75 baud */ +#define BAUD_115200 0x01 /* 115200 baud */ +#define BAUD_38400 0x02 /* 38400 baud */ +#define BAUD_150 0x03 /* 150 baud */ +#define BAUD_300 0x04 /* 300 baud */ +#define BAUD_600 0x05 /* 600 baud */ +#define BAUD_1200 0x06 /* 1200 baud */ +#define BAUD_2000 0x07 /* 2000 baud */ +#define BAUD_2400 0x08 /* 2400 baud */ +#define BAUD_4800 0x09 /* 4800 baud */ +#define BAUD_1800 0x0A /* 1800 baud */ +#define BAUD_9600 0x0B /* 9600 baud */ +#define BAUD_19200 0x0C /* 19200 baud */ +#define BAUD_57600 0x0D /* 57600 baud */ +#define BAUD_230400 0x0E /* 230400 baud */ +#define BAUD_460800 0x0F /* 460800 baud */ +#define BAUD_921600 0x10 /* 921600 baud */ +#define BAUD_50 0x11 /* 50 baud */ +#define BAUD_110 0x12 /* 110 baud */ +#define BAUD_134_5 0x13 /* 134.5 baud */ +#define BAUD_200 0x14 /* 200 baud */ +#define BAUD_7200 0x15 /* 7200 baud */ +#define BAUD_56000 0x16 /* 56000 baud */ +#define BAUD_64000 0x17 /* 64000 baud */ +#define BAUD_76800 0x18 /* 76800 baud */ +#define BAUD_128000 0x19 /* 128000 baud */ +#define BAUD_150000 0x1A /* 150000 baud */ +#define BAUD_14400 0x1B /* 14400 baud */ +#define BAUD_256000 0x1C /* 256000 baud */ +#define BAUD_28800 0x1D /* 28800 baud */ + +/* SXCHANNEL.txbreak_state definiions... */ +#define TXBREAK_OFF 0 /* Not sending break */ +#define TXBREAK_START 1 /* Begin sending break */ +#define TXBREAK_START1 2 /* Begin sending break, part 1 */ +#define TXBREAK_ON 3 /* Sending break */ +#define TXBREAK_STOP 4 /* Stop sending break */ +#define TXBREAK_STOP1 5 /* Stop sending break, part 1 */ + +#endif /* _sxwindow_h */ + +/* End of SXWINDOW.H */ + diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 3b6c7eacf..d07532a17 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -3413,7 +3413,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) if ( info->params.mode == MGSL_MODE_HDLC ) { while (info->tx_active) { current->state = TASK_INTERRUPTIBLE; - current->counter = 0; /* make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; @@ -3424,7 +3423,6 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && info->tx_enabled) { current->state = TASK_INTERRUPTIBLE; - current->counter = 0; /* make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; @@ -3561,7 +3559,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, spin_unlock_irqrestore(&info->irq_spinlock,flags); } - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){ retval = (info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index e6f50ebf1..85e436565 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -40,7 +40,7 @@ static void send_sig_all(int sig, int even_init) struct task_struct *p; for_each_task(p) { - if (p->pid && p->mm != &init_mm) { /* Not swapper nor kernel thread */ + if (p->mm) { /* Not swapper nor kernel thread */ if (p->pid == 1 && even_init) /* Ugly hack to kill init */ p->pid = 0x8000; force_sig(sig, p); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 62ec86628..7d99d5b68 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -125,11 +125,15 @@ static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); static int tty_fasync(int fd, struct file * filp, int on); +#ifdef CONFIG_SX +extern int sx_init (void); +#endif #ifdef CONFIG_8xx extern long console_8xx_init(long, long); extern int rs_8xx_init(void); #endif /* CONFIG_8xx */ + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -1495,10 +1499,11 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, return 0; } -static int tioccons(struct tty_struct *tty, struct tty_struct *real_tty) +static int tioccons(struct inode *inode, + struct tty_struct *tty, struct tty_struct *real_tty) { - if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE || - tty->driver.type == TTY_DRIVER_TYPE_SYSCONS) { + if (inode->i_rdev == SYSCONS_DEV || + inode->i_rdev == CONSOLE_DEV) { if (!suser()) return -EPERM; redirect = NULL; @@ -1627,7 +1632,7 @@ static int tiocsetd(struct tty_struct *tty, int *arg) static int send_break(struct tty_struct *tty, int duration) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); tty->driver.break_ctl(tty, -1); if (!signal_pending(current)) @@ -1708,7 +1713,7 @@ int tty_ioctl(struct inode * inode, struct file * file, case TIOCSWINSZ: return tiocswinsz(tty, real_tty, (struct winsize *) arg); case TIOCCONS: - return tioccons(tty, real_tty); + return tioccons(inode, tty, real_tty); case FIONBIO: return fionbio(file, (int *) arg); case TIOCEXCL: @@ -2080,7 +2085,7 @@ static struct tty_driver dev_console_driver; * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ -__initfunc(int tty_init(void)) +int __init tty_init(void) { if (sizeof(struct tty_struct) > PAGE_SIZE) panic("size of tty structure > PAGE_SIZE!"); @@ -2148,6 +2153,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_SERIAL rs_init(); #endif +#ifdef CONFIG_COMPUTONE + ip2_init(); +#endif #ifdef CONFIG_MAC_SERIAL macserial_init(); #endif @@ -2178,6 +2186,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_SPECIALIX specialix_init(); #endif +#ifdef CONFIG_SX + sx_init(); +#endif #ifdef CONFIG_8xx rs_8xx_init(); #endif /* CONFIG_8xx */ diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 44dfbfcd8..c05b80392 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -59,7 +59,7 @@ void tty_wait_until_sent(struct tty_struct * tty, long timeout) printk("waiting %s...(%d)\n", tty_name(tty, buf), tty->driver.chars_in_buffer(tty)); #endif - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) goto stop_waiting; if (!tty->driver.chars_in_buffer(tty)) diff --git a/drivers/char/tuner.c b/drivers/char/tuner.c index 55ff8eacc..108ea55a7 100644 --- a/drivers/char/tuner.c +++ b/drivers/char/tuner.c @@ -84,9 +84,11 @@ static struct tunertype tuners[] = { // 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623}, {"Temic 4036 FY5 NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732}, - {"Alps HSBH1", TEMIC, NTSC, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, + 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732}, + {"Alps TSBH1",TEMIC,NTSC, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, + {"Alps TSBE1",TEMIC,PAL, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, }; /* ---------------------------------------------------------------------- */ @@ -192,12 +194,13 @@ static int tuner_attach(struct i2c_device *device) /* * For now we only try and attach these tuners to the BT848 - * bus. This same module will however work different species - * of card using these chips. Just change the constraints + * or ZORAN bus. This same module will however work different + * species of card using these chips. Just change the constraints * (i2c doesn't have a totally clash free 'address' space) */ - if(device->bus->id!=I2C_BUSID_BT848) + if(device->bus->id!=I2C_BUSID_BT848 && + device->bus->id!=I2C_BUSID_ZORAN) return -EINVAL; device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 4db4e6efb..a25013fea 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -310,7 +310,7 @@ static struct file_operations vcs_fops = { NULL /* fsync */ }; -__initfunc(int vcs_init(void)) +int __init vcs_init(void) { int error; diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 39bb5f256..eb76d446d 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -57,24 +57,12 @@ extern int init_bw_qcams(struct video_init *); #ifdef CONFIG_VIDEO_PLANB extern int init_planbs(struct video_init *); #endif -#ifdef CONFIG_RADIO_AZTECH -extern int aztech_init(struct video_init *); -#endif -#ifdef CONFIG_RADIO_RTRACK -extern int rtrack_init(struct video_init *); -#endif #ifdef CONFIG_RADIO_RTRACK2 extern int rtrack2_init(struct video_init *); #endif #ifdef CONFIG_RADIO_SF16FMI extern int fmi_init(struct video_init *); #endif -#ifdef CONFIG_RADIO_MIROPCM20 -extern int pcm20_init(struct video_init *); -#endif -#ifdef CONFIG_RADIO_GEMTEK -extern int gemtek_init(struct video_init *); -#endif #ifdef CONFIG_RADIO_TYPHOON extern int typhoon_init(struct video_init *); #endif @@ -84,9 +72,6 @@ extern int cadet_init(struct video_init *); #ifdef CONFIG_RADIO_TERRATEC extern int terratec_init(struct video_init *); #endif -#ifdef CONFIG_VIDEO_PMS -extern int init_pms_cards(struct video_init *); -#endif #ifdef CONFIG_VIDEO_ZORAN extern int init_zoran_cards(struct video_init *); #endif @@ -105,33 +90,18 @@ static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, #endif -#ifdef CONFIG_VIDEO_PMS - {"PMS", init_pms_cards}, -#endif #ifdef CONFIG_VIDEO_PLANB {"planb", init_planbs}, #endif -#ifdef CONFIG_RADIO_AZTECH - {"Aztech", aztech_init}, -#endif -#ifdef CONFIG_RADIO_RTRACK - {"RTrack", rtrack_init}, -#endif #ifdef CONFIG_RADIO_RTRACK2 {"RTrack2", rtrack2_init}, #endif #ifdef CONFIG_RADIO_SF16FMI {"SF16FMI", fmi_init}, #endif -#ifdef CONFIG_RADIO_MIROPCM20 - {"PCM20", pcm20_init}, -#endif #ifdef CONFIG_RADIO_CADET {"Cadet", cadet_init}, #endif -#ifdef CONFIG_RADIO_GEMTEK - {"GemTek", gemtek_init}, -#endif #ifdef CONFIG_RADIO_TYPHOON {"radio-typhoon", typhoon_init}, #endif @@ -144,7 +114,6 @@ static struct video_init video_init_list[]={ {"end", NULL} }; -#if LINUX_VERSION_CODE >= 0x020100 /* * Read will do some smarts later on. Buffer pin etc. */ @@ -160,7 +129,6 @@ static ssize_t video_read(struct file *file, } - /* * Write for now does nothing. No reason it shouldnt do overlay setting * for some boards I guess.. @@ -191,31 +159,6 @@ static unsigned int video_poll(struct file *file, poll_table * wait) } -#else -static int video_read(struct inode *ino,struct file *file, - char *buf, int count) -{ - int err; - struct video_device *vfl=video_device[MINOR(ino->i_rdev)]; - if (vfl->read) - return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); - else - return -EINVAL; -} - -static int video_write(struct inode *ino,struct file *file, const char *buf, - int count) -{ - int err; - struct video_device *vfl=video_device[MINOR(ino->i_rdev)]; - if (vfl->write) - return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK); - else - return 0; -} - -#endif - /* * Open a video device. */ diff --git a/drivers/char/vino.c b/drivers/char/vino.c index 6e0036786..a9ac3086b 100644 --- a/drivers/char/vino.c +++ b/drivers/char/vino.c @@ -1,4 +1,4 @@ -/* $Id: vino.c,v 1.3 1999/02/09 23:54:24 ulfc Exp $ +/* $Id: vino.c,v 1.4 1999/02/09 23:59:36 ulfc Exp $ * drivers/char/vino.c * * (incomplete) Driver for the Vino Video input system found in SGI Indys. @@ -9,7 +9,6 @@ * some more code. */ -#include <linux/config.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> @@ -241,7 +240,7 @@ static struct video_device vino_dev = { 0 }; -__initfunc(int init_vino(struct video_device *dev)) +int __init init_vino(struct video_device *dev) { int err; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index a3c00925d..020120d4d 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -62,7 +62,7 @@ struct vt_struct *vt_cons[MAX_NR_CONSOLES]; unsigned char keyboard_type = KB_101; #if !defined(__alpha__) && !defined(__mips__) -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); +asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); #endif unsigned int video_font_height; @@ -670,7 +670,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case KDSIGACCEPT: { extern int spawnpid, spawnsig; - if (!perm) + if (!perm || !capable(CAP_KILL)) return -EPERM; if (arg < 1 || arg > _NSIG || arg == SIGKILL) return -EINVAL; @@ -1113,7 +1113,7 @@ int vt_waitactive(int vt) add_wait_queue(&vt_activate_queue, &wait); for (;;) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); retval = 0; if (vt == fg_console) break; diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c index 174175808..1792bbac4 100644 --- a/drivers/char/wdt.c +++ b/drivers/char/wdt.c @@ -64,7 +64,7 @@ static int irq=11; * Setup options */ -__initfunc(void wdt_setup(char *str, int *ints)) +void __init wdt_setup(char *str, int *ints) { if(ints[0]>0) { @@ -365,7 +365,7 @@ void cleanup_module(void) #endif -__initfunc(int wdt_init(void)) +int __init wdt_init(void) { printk("WDT500/501-P driver 0.07 at %X (Interrupt %d)\n", io,irq); if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL)) |