diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/CREDITS linuxppc64_2_4/CREDITS
--- ../kernel.org/linux-2.4.19/CREDITS Wed May 15 08:29:33 2002
+++ linuxppc64_2_4/CREDITS Mon May 13 16:39:05 2002
@@ -986,6 +986,14 @@
S: 80050-430 - Curitiba - Paraná
S: Brazil
+N: Tom Gall
+E: tom_gall@vnet.ibm.com
+E: tgall@rochcivictheatre.org
+D: ppc64, ppc
+S: 710 Walnut St
+S: Mantorville, MN 55955
+S: USA
+
N: Nigel Gamble
E: nigel@nrg.org
E: nigel@sgi.com
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Documentation/Configure.help linuxppc64_2_4/Documentation/Configure.help
--- ../kernel.org/linux-2.4.19/Documentation/Configure.help Wed May 15 08:29:33 2002
+++ linuxppc64_2_4/Documentation/Configure.help Mon May 13 16:39:06 2002
@@ -232,6 +232,13 @@
CPU and the single-board computers built around it, targeted for
network and embedded applications. For more information see the
Axis Communication site, .
+PowerPC64 processor
+CONFIG_PPC64
+ The PowerPC architecture was designed for both 32 bit and 64 bit
+ processor implementations. 64 bit PowerPC processors are in many
+ ways a superset of their 32 bit PowerPC cousins. Each 64 bit PowerPC
+ processor also has a 32 bit mode to allow for 32 bit compatibility.
+ The home of the PowerPC 64 Linux project is at
Multiquad support for NUMA systems
CONFIG_MULTIQUAD
@@ -15358,6 +15365,20 @@
hard drives and ADFS-formatted floppy disks. This is experimental
codes, so if you're unsure, say N.
+JFS filesystem support
+CONFIG_JFS_FS
+ This is a port of IBM's Journaled Filesystem . More information is
+ available in the file Documentation/filesystems/jfs.txt.
+
+ If you do not intend to use the JFS filesystem, say N.
+
+JFS Debugging
+CONFIG_JFS_DEBUG
+ If you are experiencing any problems with the JFS filesystem, say
+ Y here. This will result in additional debugging messages to be
+ written to the system log. Under normal circumstances, this
+ results in very little overhead.
+
/dev/pts file system for Unix98 PTYs
CONFIG_DEVPTS_FS
You should say Y here if you said Y to "Unix98 PTY support" above.
@@ -16527,6 +16548,19 @@
. The module will be called
isicom.o.
+IBM Multiport Serial Adapter
+CONFIG_ICOM
+ This driver is for a family of multiport serial adapters including
+ 2 port RVX (iSeries 2745), 2 port modem (iSeries
+ 2772) and 1 port RVX + 1 port modem (iSeries 2771). The
+ module is called iCom.o
+CONFIG_ICOM_MODEM_CC
+ This field entry enables the device driver to configure the modem
+ for appropriate operations based on country code. If you do not
+ have an internal modem card then a blank entry is recommended.
+ If you do have an internal modem card, look for the comment in iCom.c
+ indicating which value relates to your country.
+
Unix98 PTY support
CONFIG_UNIX98_PTYS
A pseudo terminal (PTY) is a software device consisting of two
@@ -21333,6 +21367,12 @@
Select APUS if configuring for a PowerUP Amiga.
More information is available at:
.
+# Choice: i or p
+Platform support
+CONFIG_PPC_ISERIES
+ Linux runs on certain models of the IBM AS/400, now known as the
+ IBM iSeries. Generally if you can run LPAR (Logical Partitioning)
+ on your iSeries you can run Linux in a partition on your machine.
AltiVec kernel support
CONFIG_ALTIVEC
@@ -21396,6 +21436,16 @@
You may also want to compile the dma sound driver as a module and
have it autoloaded. The act of removing the module shuts down the
sound hardware for more power savings.
+Platform support
+CONFIG_PPC_PSERIES
+ Linux runs on most models of IBM pSeries hardware. (pSeries used
+ to be known as the RS/6000)
+
+ See for exact model information for the
+ 64 bit PowerPC kernel.
+
+ pSeries Linux information from IBM can be found at:
+
APM emulation
CONFIG_PMAC_APM_EMU
@@ -21684,6 +21734,12 @@
Date of Release: early 2001 (?)
End of life: -
URL:
+Support for Large Memory
+CONFIG_MSCHUNKS
+ MsChunks stands for Main Store Chunks and specifically allows the
+ 64 bit PowerPC Linux kernel to optimize for machines with sparse
+ discontiguous memory. iSeries kernels need to have this on.
+ It is recommended that for pSeries hardware that you answer N.
ADB raw keycode support
CONFIG_MAC_ADBKEYCODES
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Documentation/cachetlb.txt linuxppc64_2_4/Documentation/cachetlb.txt
--- ../kernel.org/linux-2.4.19/Documentation/cachetlb.txt Fri Apr 19 11:00:11 2002
+++ linuxppc64_2_4/Documentation/cachetlb.txt Mon Apr 22 10:24:05 2002
@@ -260,8 +260,9 @@
Here is the new interface:
- void copy_user_page(void *to, void *from, unsigned long address)
- void clear_user_page(void *to, unsigned long address)
+ void copy_user_page(struct page *to, struct page *from,
+ unsigned long address)
+ void clear_user_page(struct page *to, unsigned long address)
These two routines store data in user anonymous or COW
pages. It allows a port to efficiently avoid D-cache alias
@@ -279,6 +280,11 @@
If D-cache aliasing is not an issue, these two routines may
simply call memcpy/memset directly and do nothing more.
+
+ There are default versions of these procedures supplied in
+ include/linux/highmem.h. If a port does not want to use the
+ default versions it should declare them and define the symbol
+ __HAVE_ARCH_USER_PAGE in include/asm/page.h.
void flush_dcache_page(struct page *page)
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Documentation/filesystems/00-INDEX linuxppc64_2_4/Documentation/filesystems/00-INDEX
--- ../kernel.org/linux-2.4.19/Documentation/filesystems/00-INDEX Fri Apr 19 10:30:50 2002
+++ linuxppc64_2_4/Documentation/filesystems/00-INDEX Thu Sep 13 14:29:38 2001
@@ -22,6 +22,8 @@
- info and mount options for the OS/2 HPFS.
isofs.txt
- info and mount options for the ISO 9660 (CDROM) filesystem.
+jfs.txt
+ - info and mount options for the JFS filesystem.
ncpfs.txt
- info on Novell Netware(tm) filesystem using NCP protocol.
ntfs.txt
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Documentation/filesystems/changelog.jfs linuxppc64_2_4/Documentation/filesystems/changelog.jfs
--- ../kernel.org/linux-2.4.19/Documentation/filesystems/changelog.jfs Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/Documentation/filesystems/changelog.jfs Tue Apr 23 11:14:24 2002
@@ -0,0 +1,234 @@
+IBM's Journaled File System (JFS) for Linux version 1.0.17
+Team members
+Steve Best sbest@us.ibm.com
+Dave Kleikamp shaggy@austin.ibm.com
+Barry Arndt barndt@us.ibm.com
+Christoph Hellwig hch@infradead.org
+
+
+Release April 2, 2002 (version 1.0.17)
+
+This is our fifty-fifth release of IBM's Enterprise JFS technology port to Linux.
+Beta 1 was release 0.1.0 on 12/8/2000, Beta 2 was release 0.2.0 on 3/7/2001,
+Beta 3 was release 0.3.0 on 4/30/2001, and release 1.0.0 on 6/28/2001.
+
+
+Function and Fixes in drop 55 (1.0.17)
+ - Call sb_set_blocksize instead of set_blocksize in 2.5 (Christoph Hellwig)
+ - Replace strtok by strsep (Christoph Hellwig)
+ - Store entire device number in log superblock rather than just the minor.
+ - Include file clean (Christoph Hellwig)
+ - Fix race introduced by thread handling cleanups (Christoph Hellwig)
+ - Detect dtree corruption to avoid infinite loop
+ - JFS needs to include completion.h
+ - Support external log(journal) device file system work part 1 (Christoph Hellwig)
+
+Function and Fixes in drop 54 (1.0.16)
+ - Limit readdir offset to signed integer for NFSv2 (Christoph Hellwig)
+ - missing static in jfs_imap.c (Christoph Hellwig)
+ - Fix infinite loop in jfs_readdir
+ weren't updating the directory index table completely (bug # 2591)
+ - Sync up 2.4 tree with 2.5 -- (Christoph Hellwig & Shaggy)
+ move to completions, provide back-compact for pre-2.4.7
+ remove dead code
+ add kdev_t conversion, that should have been in 2.4 anyway
+ move one-time inode initialization into slab constructor
+ - Remove non-core files from CVS
+
+Function and Fixes in drop 53 (1.0.15)
+ - Fix trap when appending to very large file
+ - Moving jfs headers into fs/jfs at Linus' request
+ - Move up to linux-2.5.4
+ - Fix file size limit on 32-bit (Andi Kleen)
+ - make changelog more read-able and include only 1.0.0 and above (Christoph Hellwig)
+ - Don't allocate metadata pages from high memory. JFS keeps them kmapped too long causing deadlock.
+ - Fix xtree corruption when creating file with >= 64 GB of physically contiguous dasd
+ - Replace semaphore with struct completion for thread startup/shutdown (Benedikt Spranger)
+ - cleanup Tx alloc/free (Christoph Hellwig)
+ - Move up to linux-2.5.3
+ - thread cleanups (Christoph Hellwig)
+ - First step toward making tblocks and tlocks dynamically allocated. Intro tid_t and lid_t to
+ insulate the majority of the code from future changes. Also hide TxBlock and TxLock arrays
+ by using macros to get from tids and lids to real structures.
+ - minor list-handling cleanup (Christoph Hellwig)
+ - Replace altnext and altprev with struct list_head
+ - Clean up the debugging code and add support for collecting statistics (Christoph Hellwig)
+
+
+Function and Fixes in drop 52 (1.0.14)
+ - Fix hang in invalidate_metapages when jfs.o is built as a module
+ - Fix anon_list removal logic in txLock
+
+Function and Fixes in drop 51 (1.0.13)
+ - chmod changes on newly created directories are lost after umount (bug 2535)
+ - Page locking race fixes
+ - Improve metapage locking
+ - Fix timing window. Lock page while metapage is active to avoid page going
+ away before the metadata is released. (Fixed crash during mount/umount testing)
+ - Make changes for 2.5.2 kernel
+ - Fix race condition truncating large files
+
+Function and Fixes in drop50 (1.0.12)
+ - Add O_DIRECT support
+ - Add support for 2.4.17 kernel
+ - Make sure COMMIT_STALE gets reset before the inode is unlocked. Fixing
+ this gets rid of XT_GETPAGE errors
+ - Remove invalid __exit keyword from metapage_exit and txExit.
+ - fix assert(log->cqueue.head == NULL by waiting longer
+
+Function and Fixes in drop49 (1.0.11)
+ - Readdir was not handling multibyte codepages correctly.
+ - Make mount option parsing more robust.
+ - Add iocharset mount option.
+ - Journalling of symlinks incorrect, resulting in logredo failure of -265.
+ - Add jfsutils information to Changes file
+ - Improve recoverability of the file system when metadata corruption is detected.
+ - Fix kernel OOPS when root inode is corrupted
+
+Function and Fixes in drop48 (1.0.10)
+ - put inodes later on hash queues
+ - Fix boundary case in xtTruncate
+ - When invalidating metadata, try to flush the dirty buffers rather than sync them.
+ - Add another sanity check to avoid trapping when imap is corrupt
+ - Fix file truncate while removing large file (assert(cmp == 0))
+ - read_cache_page returns ERR_PTR, not NULL on error
+ - Add dtSearchNode and dtRelocate
+ - JFS needs to use generic_file_open & generic_file_llseek
+ - Remove lazyQwait, etc. It created an unnecessary bottleneck in TxBegin.
+
+Function and Fixes in drop47 (1.0.9)
+ - Fix data corruption problem when creating files while deleting others. (jitterbug 183)
+ - Make sure all metadata is written before finalizing the log
+ - Fix serialization problem in shutdown by setting i_size of directory sooner. (bugzilla #334)
+ - JFS should quit whining when special files are marked dirty during read-only mount.
+ - Must always check rc after DT_GETPAGE
+ - Add diExtendFS
+ - Removing defconfig form JFS source - not really needed
+
+Function and Fixes in drop46 (1.0.8)
+ - Synclist was being built backwards causing logredo to quit too early
+ - jfs_compat.h needs to include module.h
+ - uncomment EXPORTS_NO_SYMBOLS in super.c
+ - Minor code cleanup
+ - xtree of zero-truncated file not being logged
+ - Fix logging on file truncate
+ - remove unused metapage fields
+
+Function and Fixes in drop45 (1.0.7)
+ - cleanup remove IS_KIOBUFIO define.
+ - cleanup remove TRUNC_NO_TOSS define.
+ - have jFYI's use the name directly from dentry
+ - Remove nul _ALLOC and _FREE macros and also make spinlocks static.
+ - cleanup add externs where needed in the header files
+ - jfs_write_inode is a bad place to call iput. Also limit warnings.
+ - More truncate cleanup
+ - Truncate cleanup
+ - Add missing statics in jfs_metapage.c
+ - fsync fixes
+ - Clean up symlink code - use page_symlink_inode_operations
+ - unicode handling cleanup
+ - cleanup replace UniChar with wchar_t
+ - Get rid of CDLL_* macros - use list.h instead
+ - 2.4.11-prex mount problem Call new_inode instead of get_empty_inode
+ - use kernel min/max macros
+ - Add MODULE_LICENSE stub for older kernels
+ - IA64/gcc3 fixes
+ - Log Manager fixes, introduce __SLEEP_COND macro
+ - Mark superblock dirty when some errors detected (forcing fsck to be run).
+ - More robust remounting from r/o to r/w.
+ - Misc. cleanup add static where appropriate
+ - small cleanup in jfs_umount_rw
+ - add MODULE_ stuff
+ - Set *dropped_lock in alloc_metapage
+ - Get rid of unused log list
+ - cleanup jfs_imap.c to remove _OLD_STUFF and _NO_MORE_MOUNT_INODE defines
+ - Log manager cleanup
+ - Transaction manager cleanup
+ - correct memory allocations flags
+ - Better handling of iterative truncation
+ - Change continue to break, otherwise we don't re-acquire LAZY_LOCK
+
+Function and Fixes in drop44 (1.0.6)
+ - Create jfs_incore.h which merges linux/jfs_fs.h, linux/jfs_fs_i.h, and jfs_fs_sb.h
+ - Create a configuration option to handle JFS_DEBUG define
+ - Fixed a few cases where positive error codes were returned to the VFS.
+ - Replace jfs_dir_read by generic_read_dir.
+ - jfs_fsync_inode is only called by jfs_fsync_file, merge the two and rename to jfs_fsync.
+ - Add a bunch of missing externs.
+ - jfs_rwlock_lock is unused, nuke it.
+ - Always use atomic set/test_bit operations to protect jfs_ip->cflag
+ - Combine jfs_ip->flag with jfs_ip->cflag
+ - Fixed minor format errors reported by fsck
+ - cflags should be long so bitops always works correctly
+ - Use GFP_NOFS for runtime memory allocations
+ - Support VM changes in 2.4.10 of the kernel
+ - Remove ifdefs supporting older 2.4 kernels. JFS now requires at least 2.4.3 or 2.4.2-ac2
+ - Simplify and remove one use of IWRITE_TRYLOCK
+ - jfs_truncate was not passing tid to xtTruncate
+ - removed obsolete extent_page workaround
+ - correct recovery from failed diAlloc call (disk full)
+ - In write_metapage, don't call commit_write if prepare_write failed
+
+Function and Fixes in drop43 (1.0.5)
+ - Allow separate allocation of JFS-private superblock/inode data.
+ - Remove checks in namei.c that are already done by the VFS.
+ - Remove redundant mutex defines.
+ - Replace all occurrences of #include with #include
+ - Work around race condition in remount -fixes OOPS during shutdown
+ - Truncate large files incrementally ( affects directories too)
+
+Function and Fixes in drop42 (1.0.4)
+ - Fixed compiler warnings in the FS when building on 64 bits systems
+ - Fixed deadlock where jfsCommit hung in hold_metapage
+ - Fixed problems with remount
+ - Reserve metapages for jfsCommit thread
+ - Get rid of buggy invalidate_metapage & use discard_metapage
+ - Don't hand metapages to jfsIOthread (too many context switches) (jitterbug 125, bugzilla 238)
+ - Fix error message in jfs_strtoUCS
+
+Function and Fixes in drop41 (1.0.3)
+ - Patch to move from previous release to latest release needs to update the version number in super.c
+ - Jitterbug problems (134,140,152) removing files have been fixed
+ - Set rc=ENOSPC if ialloc fails in jfs_create and jfs_mkdir
+ - Fixed jfs_txnmgr.c 775! assert
+ - Fixed jfs_txnmgr.c 884! assert(mp->nohomeok==0)
+ - Fix hang - prevent tblocks from being exhausted
+ - Fix oops trying to mount reiserfs
+ - Fail more gracefully in jfs_imap.c
+ - Print more information when char2uni fails
+ - Fix timing problem between Block map and metapage cache - jitterbug 139
+ - Code Cleanup (removed many ifdef's, obsolete code, ran code through indent) Mostly 2.4 tree
+ - Split source tree (Now have a separate source tree for 2.2, 2.4, and jfsutils)
+
+Function and Fixes in drop40 (1.0.2)
+ - Fixed multiple truncate hang
+ - Fixed hang on unlink a file and sync happening at the same time
+ - Improved handling of kmalloc error conditions
+ - Fixed hang in blk_get_queue and SMP deadlock: bh_end_io call generic_make_request
+ (jitterbug 145 and 146)
+ - stbl was not set correctly set in dtDelete
+ - changed trap to printk in dbAllocAG to avoid system hang
+
+Function and Fixes in drop 39 (1.0.1)
+ - Fixed hang during copying files on 2.2.x series
+ - Fixed TxLock compile problem
+ - Fixed to correctly update the number of blocks for directories (this was causing the FS
+ to show fsck error after compiling mozilla).
+ - Fixed to prevent old data from being written to disk from the page cache.
+
+Function and Fixes in drop 38 (1.0.0)
+ - Fixed some general log problems
+
+Please send bugs, comments, cards and letters to linuxjfs@us.ibm.com.
+
+The JFS mailing list can be subscribed to by using the link labeled "Mail list Subscribe"
+at our web page http://oss.software.ibm.com/jfs/.
+
+
+
+
+
+
+
+
+
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Documentation/filesystems/jfs.txt linuxppc64_2_4/Documentation/filesystems/jfs.txt
--- ../kernel.org/linux-2.4.19/Documentation/filesystems/jfs.txt Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/Documentation/filesystems/jfs.txt Tue Apr 23 11:14:24 2002
@@ -0,0 +1,10 @@
+IBM's Journaled File System (JFS) for Linux
+
+The JFS utilities can be found at the JFS homepage at
+http://oss.software.ibm.com/jfs
+
+Team members
+Steve Best sbest@us.ibm.com
+Dave Kleikamp shaggy@austin.ibm.com
+Barry Arndt barndt@us.ibm.com
+Christoph Hellwig hch@infradead.org
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/MAINTAINERS linuxppc64_2_4/MAINTAINERS
--- ../kernel.org/linux-2.4.19/MAINTAINERS Wed May 15 08:29:34 2002
+++ linuxppc64_2_4/MAINTAINERS Mon May 13 16:39:05 2002
@@ -852,6 +852,13 @@
W: http://sources.redhat.com/jffs2/
S: Maintained
+JFS FILESYSTEM
+P: Dave Kleikamp
+M: shaggy@austin.ibm.com
+L: jfs-discussion@oss.software.ibm.com
+W: http://oss.software.ibm.com/developerworks/opensource/jfs/
+S: Supported
+
JOYSTICK DRIVER
P: Vojtech Pavlik
M: vojtech@suse.cz
@@ -925,6 +932,13 @@
W: http://www.linuxppc.org/
L: linuxppc-dev@lists.linuxppc.org
S: Maintained
+
+LINUX FOR 64BIT POWERPC
+P: David Engebretsen
+M: engebret@us.ibm.com
+W: http://linuxppc64.org
+L: linuxppc64-dev@lists.linuxppc.org
+S: Supported
LINUX FOR 64BIT POWERPC
P: David Engebretsen
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/Makefile linuxppc64_2_4/Makefile
--- ../kernel.org/linux-2.4.19/Makefile Wed May 15 08:29:34 2002
+++ linuxppc64_2_4/Makefile Mon May 13 16:39:05 2002
@@ -5,7 +5,8 @@
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
-ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+#ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+ARCH := ppc64
KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
@@ -19,7 +20,7 @@
HOSTCC = gcc
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
-CROSS_COMPILE =
+CROSS_COMPILE = /usr/local/ppc64-current3.0/bin/powerpc64-linux-
#
# Include the make variables (CC, etc...)
@@ -154,6 +155,7 @@
DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o
DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o
DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o
+DRIVERS-$(CONFIG_PPC_ISERIES) += drivers/iseries/iseries.o
ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
DRIVERS-y += drivers/cdrom/driver.o
@@ -170,7 +172,6 @@
DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus_all.o
DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/driver.o
DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
-DRIVERS-$(CONFIG_PPC) += drivers/macintosh/macintosh.o
DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o
DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/boot/zImage.c linuxppc64_2_4/arch/ppc64/boot/zImage.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/boot/zImage.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/boot/zImage.c Tue Apr 30 09:19:01 2002
@@ -225,7 +225,7 @@
rec = bi_rec_alloc(rec, 2);
rec->tag = BI_MACHTYPE;
- rec->data[0] = _MACH_pSeries;
+ rec->data[0] = PLATFORM_PSERIES;
rec->data[1] = 1;
if ( initrd.size > 0 ) {
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/config.in linuxppc64_2_4/arch/ppc64/config.in
--- ../kernel.org/linux-2.4.19/arch/ppc64/config.in Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/config.in Mon May 6 14:29:20 2002
@@ -37,6 +37,7 @@
define_bool CONFIG_MSCHUNKS y
else
bool 'MsChunks Physical to Absolute address translation support' CONFIG_MSCHUNKS
+tristate 'Firmware flash interface' CONFIG_RTAS_FLASH
fi
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig linuxppc64_2_4/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig
--- ../kernel.org/linux-2.4.19/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig Fri Apr 19 11:00:33 2002
+++ linuxppc64_2_4/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig Thu Apr 25 20:32:38 2002
@@ -67,7 +67,6 @@
#
# CONFIG_PNP is not set
# CONFIG_ISAPNP is not set
-# CONFIG_PNPBIOS is not set
#
# Block devices
@@ -77,6 +76,7 @@
# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
# CONFIG_BLK_DEV_DAC960 is not set
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
@@ -101,8 +101,6 @@
#
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-CONFIG_RTNETLINK=y
# CONFIG_NETLINK_DEV is not set
# CONFIG_NETFILTER is not set
CONFIG_FILTER=y
@@ -110,8 +108,6 @@
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_RTNETLINK=y
-CONFIG_NETLINK=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_NAT=y
CONFIG_IP_ROUTE_MULTIPATH=y
@@ -134,12 +130,18 @@
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
#
#
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_DEV_APPLETALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
@@ -155,6 +157,16 @@
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
# CONFIG_BLK_DEV_IDE_MODES is not set
# CONFIG_BLK_DEV_HD is not set
@@ -192,6 +204,7 @@
# CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_DPT_I2O is not set
@@ -213,6 +226,7 @@
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_SYM53C8XX is not set
# CONFIG_SCSI_PAS16 is not set
@@ -229,8 +243,6 @@
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_MESH is not set
-# CONFIG_SCSI_MAC53C94 is not set
#
# IEEE 1394 (FireWire) support (EXPERIMENTAL)
@@ -259,12 +271,10 @@
# CONFIG_MACE is not set
# CONFIG_BMAC is not set
# CONFIG_GMAC is not set
-# CONFIG_OAKNET is not set
# CONFIG_SUNLANCE is not set
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNBMAC is not set
# CONFIG_SUNQE is not set
-# CONFIG_SUNLANCE is not set
# CONFIG_SUNGEM is not set
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_LANCE is not set
@@ -278,6 +288,7 @@
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_TULIP is not set
+# CONFIG_TC35815 is not set
# CONFIG_DE4X5 is not set
# CONFIG_DGRS is not set
# CONFIG_DM9102 is not set
@@ -293,11 +304,13 @@
# CONFIG_8139TOO_PIO is not set
# CONFIG_8139TOO_TUNE_TWISTER is not set
# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_NEW_RX_RESET is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
# CONFIG_WINBOND_840 is not set
# CONFIG_NET_POCKET is not set
@@ -312,6 +325,7 @@
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PLIP is not set
@@ -329,6 +343,7 @@
CONFIG_TR=y
CONFIG_IBMOL=m
# CONFIG_IBMLS is not set
+# CONFIG_3C359 is not set
# CONFIG_TMS380TR is not set
# CONFIG_NET_FC is not set
# CONFIG_RCPCI is not set
@@ -380,6 +395,15 @@
# CONFIG_FB is not set
#
+# Input core support
+#
+# CONFIG_INPUT is not set
+# CONFIG_INPUT_KEYBDEV is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+
+#
# iSeries device drivers
#
CONFIG_VIOCONS=y
@@ -435,43 +459,20 @@
# Joysticks
#
# CONFIG_INPUT_GAMEPORT is not set
-# CONFIG_INPUT_NS558 is not set
-# CONFIG_INPUT_LIGHTNING is not set
-# CONFIG_INPUT_PCIGAME is not set
-# CONFIG_INPUT_CS461X is not set
-# CONFIG_INPUT_EMU10K1 is not set
-# CONFIG_INPUT_SERIO is not set
-# CONFIG_INPUT_SERPORT is not set
#
-# Joysticks
+# Input core support is needed for gameports
+#
+
+#
+# Input core support is needed for joysticks
#
-# CONFIG_INPUT_ANALOG is not set
-# CONFIG_INPUT_A3D is not set
-# CONFIG_INPUT_ADI is not set
-# CONFIG_INPUT_COBRA is not set
-# CONFIG_INPUT_GF2K is not set
-# CONFIG_INPUT_GRIP is not set
-# CONFIG_INPUT_INTERACT is not set
-# CONFIG_INPUT_TMDC is not set
-# CONFIG_INPUT_SIDEWINDER is not set
-# CONFIG_INPUT_IFORCE_USB is not set
-# CONFIG_INPUT_IFORCE_232 is not set
-# CONFIG_INPUT_WARRIOR is not set
-# CONFIG_INPUT_MAGELLAN is not set
-# CONFIG_INPUT_SPACEORB is not set
-# CONFIG_INPUT_SPACEBALL is not set
-# CONFIG_INPUT_STINGER is not set
-# CONFIG_INPUT_DB9 is not set
-# CONFIG_INPUT_GAMECON is not set
-# CONFIG_INPUT_TURBOGRAFX is not set
# CONFIG_QIC02_TAPE is not set
#
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
-# CONFIG_INTEL_RNG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
# CONFIG_DTLK is not set
@@ -484,7 +485,6 @@
# CONFIG_FTAPE is not set
# CONFIG_AGP is not set
# CONFIG_DRM is not set
-# CONFIG_MWAVE is not set
#
# File systems
@@ -494,11 +494,15 @@
CONFIG_AUTOFS4_FS=y
CONFIG_REISERFS_FS=y
# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
# CONFIG_ADFS_FS is not set
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
@@ -511,8 +515,10 @@
CONFIG_RAMFS=y
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
# CONFIG_VXFS_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS_RW is not set
@@ -536,6 +542,7 @@
# Network File Systems
#
# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_ROOT_NFS is not set
@@ -555,6 +562,8 @@
CONFIG_NCPFS_SMALLDOS=y
CONFIG_NCPFS_NLS=y
CONFIG_NCPFS_EXTRAS=y
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
#
# Partition Types
@@ -589,6 +598,7 @@
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
@@ -614,106 +624,6 @@
# USB support
#
# CONFIG_USB is not set
-
-#
-# USB Controllers
-#
-# CONFIG_USB_UHCI is not set
-# CONFIG_USB_UHCI_ALT is not set
-# CONFIG_USB_OHCI is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_BLUETOOTH is not set
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
-# CONFIG_USB_HID is not set
-# CONFIG_USB_HIDDEV is not set
-# CONFIG_USB_KBD is not set
-# CONFIG_USB_MOUSE is not set
-# CONFIG_USB_WACOM is not set
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
-# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-# CONFIG_USB_IBMCAM is not set
-# CONFIG_USB_OV511 is not set
-# CONFIG_USB_PWC is not set
-# CONFIG_USB_SE401 is not set
-# CONFIG_USB_DSBR is not set
-# CONFIG_USB_DABUSB is not set
-
-#
-# USB Network adaptors
-#
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
-# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
-# CONFIG_USB_USS720 is not set
-
-#
-# USB Serial Converter support
-#
-# CONFIG_USB_SERIAL is not set
-# CONFIG_USB_SERIAL_GENERIC is not set
-# CONFIG_USB_SERIAL_BELKIN is not set
-# CONFIG_USB_SERIAL_WHITEHEAT is not set
-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
-# CONFIG_USB_SERIAL_EMPEG is not set
-# CONFIG_USB_SERIAL_FTDI_SIO is not set
-# CONFIG_USB_SERIAL_VISOR is not set
-# CONFIG_USB_SERIAL_IR is not set
-# CONFIG_USB_SERIAL_EDGEPORT is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_PL2303 is not set
-# CONFIG_USB_SERIAL_CYBERJACK is not set
-# CONFIG_USB_SERIAL_XIRCOM is not set
-# CONFIG_USB_SERIAL_OMNINET is not set
-
-#
-# USB Miscellaneous drivers
-#
-# CONFIG_USB_RIO500 is not set
#
# Kernel hacking
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/configs/pSeries_defconfig linuxppc64_2_4/arch/ppc64/configs/pSeries_defconfig
--- ../kernel.org/linux-2.4.19/arch/ppc64/configs/pSeries_defconfig Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/configs/pSeries_defconfig Mon May 6 14:29:20 2002
@@ -25,6 +25,7 @@
CONFIG_IRQ_ALL_CPUS=y
# CONFIG_HMT is not set
# CONFIG_MSCHUNKS is not set
+CONFIG_RTAS_FLASH=m
#
# Loadable module support
@@ -80,6 +81,7 @@
# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
# CONFIG_BLK_DEV_DAC960 is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
@@ -128,6 +130,11 @@
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_DEV_APPLETALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
@@ -145,6 +152,11 @@
# CONFIG_NET_SCHED is not set
#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
# ATA/IDE/MFM/RLL support
#
CONFIG_IDE=y
@@ -161,6 +173,7 @@
# CONFIG_BLK_DEV_HD is not set
# CONFIG_BLK_DEV_IDEDISK is not set
# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
# CONFIG_BLK_DEV_IDEDISK_IBM is not set
@@ -175,6 +188,7 @@
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
#
# IDE chipset support/bugfixes
@@ -186,12 +200,15 @@
CONFIG_BLK_DEV_IDEPCI=y
# CONFIG_IDEPCI_SHARE_IRQ is not set
CONFIG_BLK_DEV_IDEDMA_PCI=y
-CONFIG_BLK_DEV_ADMA=y
# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_IDEDMA_ONLYDISK is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+CONFIG_BLK_DEV_ADMA=y
# CONFIG_BLK_DEV_AEC62XX is not set
# CONFIG_AEC62XX_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
@@ -199,6 +216,7 @@
# CONFIG_BLK_DEV_AMD74XX is not set
# CONFIG_AMD74XX_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CMD680 is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5530 is not set
# CONFIG_BLK_DEV_HPT34X is not set
@@ -349,6 +367,7 @@
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_TULIP is not set
+# CONFIG_TC35815 is not set
# CONFIG_DE4X5 is not set
# CONFIG_DGRS is not set
# CONFIG_DM9102 is not set
@@ -380,12 +399,12 @@
CONFIG_ACENIC=y
# CONFIG_ACENIC_OMIT_TIGON_I is not set
# CONFIG_DL2K is not set
-CONFIG_E1000=y
# CONFIG_MYRI_SBUS is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PLIP is not set
@@ -403,6 +422,7 @@
CONFIG_TR=y
CONFIG_IBMOL=y
# CONFIG_IBMLS is not set
+# CONFIG_3C359 is not set
# CONFIG_TMS380TR is not set
# CONFIG_NET_FC is not set
# CONFIG_RCPCI is not set
@@ -445,6 +465,7 @@
# CONFIG_FB_RIVA is not set
# CONFIG_FB_CLGEN is not set
# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
# CONFIG_FB_CYBER2000 is not set
CONFIG_FB_OF=y
# CONFIG_FB_CONTROL is not set
@@ -464,6 +485,7 @@
# CONFIG_FB_RADEON is not set
# CONFIG_FB_ATY128 is not set
# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_TRIDENT is not set
@@ -517,6 +539,7 @@
CONFIG_PSMOUSE=y
# CONFIG_82C710_MOUSE is not set
# CONFIG_PC110_PAD is not set
+# CONFIG_MK712_MOUSE is not set
#
# Joysticks
@@ -536,7 +559,6 @@
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
-# CONFIG_INTEL_RNG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
# CONFIG_RTC is not set
@@ -577,7 +599,7 @@
# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_TMPFS is not set
-# CONFIG_RAMFS is not set
+CONFIG_RAMFS=y
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_ZISOFS is not set
@@ -689,109 +711,6 @@
# USB support
#
# CONFIG_USB is not set
-
-#
-# USB Controllers
-#
-# CONFIG_USB_UHCI is not set
-# CONFIG_USB_UHCI_ALT is not set
-# CONFIG_USB_OHCI is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_BLUETOOTH is not set
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
-
-#
-# Input core support is needed for USB HID
-#
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
-# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-# CONFIG_USB_IBMCAM is not set
-# CONFIG_USB_OV511 is not set
-# CONFIG_USB_PWC is not set
-# CONFIG_USB_SE401 is not set
-# CONFIG_USB_STV680 is not set
-# CONFIG_USB_VICAM is not set
-# CONFIG_USB_DSBR is not set
-# CONFIG_USB_DABUSB is not set
-
-#
-# USB Network adaptors
-#
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
-# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
-# CONFIG_USB_USS720 is not set
-
-#
-# USB Serial Converter support
-#
-# CONFIG_USB_SERIAL is not set
-# CONFIG_USB_SERIAL_GENERIC is not set
-# CONFIG_USB_SERIAL_BELKIN is not set
-# CONFIG_USB_SERIAL_WHITEHEAT is not set
-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
-# CONFIG_USB_SERIAL_EMPEG is not set
-# CONFIG_USB_SERIAL_FTDI_SIO is not set
-# CONFIG_USB_SERIAL_VISOR is not set
-# CONFIG_USB_SERIAL_IPAQ is not set
-# CONFIG_USB_SERIAL_IR is not set
-# CONFIG_USB_SERIAL_EDGEPORT is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_KLSI is not set
-# CONFIG_USB_SERIAL_PL2303 is not set
-# CONFIG_USB_SERIAL_CYBERJACK is not set
-# CONFIG_USB_SERIAL_XIRCOM is not set
-# CONFIG_USB_SERIAL_OMNINET is not set
-
-#
-# USB Miscellaneous drivers
-#
-# CONFIG_USB_RIO500 is not set
#
# Kernel hacking
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/defconfig linuxppc64_2_4/arch/ppc64/defconfig
--- ../kernel.org/linux-2.4.19/arch/ppc64/defconfig Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/defconfig Mon May 13 16:39:10 2002
@@ -25,6 +25,7 @@
CONFIG_IRQ_ALL_CPUS=y
# CONFIG_HMT is not set
# CONFIG_MSCHUNKS is not set
+CONFIG_RTAS_FLASH=m
#
# Loadable module support
@@ -80,7 +81,9 @@
# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
@@ -128,6 +131,11 @@
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_DEV_APPLETALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
@@ -145,6 +153,11 @@
# CONFIG_NET_SCHED is not set
#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
# ATA/IDE/MFM/RLL support
#
CONFIG_IDE=y
@@ -161,6 +174,7 @@
# CONFIG_BLK_DEV_HD is not set
# CONFIG_BLK_DEV_IDEDISK is not set
# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
# CONFIG_BLK_DEV_IDEDISK_IBM is not set
@@ -175,6 +189,7 @@
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
#
# IDE chipset support/bugfixes
@@ -186,12 +201,15 @@
CONFIG_BLK_DEV_IDEPCI=y
# CONFIG_IDEPCI_SHARE_IRQ is not set
CONFIG_BLK_DEV_IDEDMA_PCI=y
-CONFIG_BLK_DEV_ADMA=y
# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_IDEDMA_ONLYDISK is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+CONFIG_BLK_DEV_ADMA=y
# CONFIG_BLK_DEV_AEC62XX is not set
# CONFIG_AEC62XX_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
@@ -199,6 +217,7 @@
# CONFIG_BLK_DEV_AMD74XX is not set
# CONFIG_AMD74XX_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CMD680 is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5530 is not set
# CONFIG_BLK_DEV_HPT34X is not set
@@ -349,6 +368,7 @@
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_TULIP is not set
+# CONFIG_TC35815 is not set
# CONFIG_DE4X5 is not set
# CONFIG_DGRS is not set
# CONFIG_DM9102 is not set
@@ -380,12 +400,12 @@
CONFIG_ACENIC=y
# CONFIG_ACENIC_OMIT_TIGON_I is not set
# CONFIG_DL2K is not set
-CONFIG_E1000=y
# CONFIG_MYRI_SBUS is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PLIP is not set
@@ -403,6 +423,7 @@
CONFIG_TR=y
CONFIG_IBMOL=y
# CONFIG_IBMLS is not set
+# CONFIG_3C359 is not set
# CONFIG_TMS380TR is not set
# CONFIG_NET_FC is not set
# CONFIG_RCPCI is not set
@@ -445,6 +466,7 @@
# CONFIG_FB_RIVA is not set
# CONFIG_FB_CLGEN is not set
# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
# CONFIG_FB_CYBER2000 is not set
CONFIG_FB_OF=y
# CONFIG_FB_CONTROL is not set
@@ -464,6 +486,7 @@
# CONFIG_FB_RADEON is not set
# CONFIG_FB_ATY128 is not set
# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_TRIDENT is not set
@@ -517,6 +540,7 @@
CONFIG_PSMOUSE=y
# CONFIG_82C710_MOUSE is not set
# CONFIG_PC110_PAD is not set
+# CONFIG_MK712_MOUSE is not set
#
# Joysticks
@@ -536,7 +560,6 @@
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
-# CONFIG_INTEL_RNG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
# CONFIG_RTC is not set
@@ -577,7 +600,7 @@
# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_TMPFS is not set
-# CONFIG_RAMFS is not set
+CONFIG_RAMFS=y
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_ZISOFS is not set
@@ -689,109 +712,6 @@
# USB support
#
# CONFIG_USB is not set
-
-#
-# USB Controllers
-#
-# CONFIG_USB_UHCI is not set
-# CONFIG_USB_UHCI_ALT is not set
-# CONFIG_USB_OHCI is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_BLUETOOTH is not set
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
-
-#
-# Input core support is needed for USB HID
-#
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
-# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-# CONFIG_USB_IBMCAM is not set
-# CONFIG_USB_OV511 is not set
-# CONFIG_USB_PWC is not set
-# CONFIG_USB_SE401 is not set
-# CONFIG_USB_STV680 is not set
-# CONFIG_USB_VICAM is not set
-# CONFIG_USB_DSBR is not set
-# CONFIG_USB_DABUSB is not set
-
-#
-# USB Network adaptors
-#
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
-# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
-# CONFIG_USB_USS720 is not set
-
-#
-# USB Serial Converter support
-#
-# CONFIG_USB_SERIAL is not set
-# CONFIG_USB_SERIAL_GENERIC is not set
-# CONFIG_USB_SERIAL_BELKIN is not set
-# CONFIG_USB_SERIAL_WHITEHEAT is not set
-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
-# CONFIG_USB_SERIAL_EMPEG is not set
-# CONFIG_USB_SERIAL_FTDI_SIO is not set
-# CONFIG_USB_SERIAL_VISOR is not set
-# CONFIG_USB_SERIAL_IPAQ is not set
-# CONFIG_USB_SERIAL_IR is not set
-# CONFIG_USB_SERIAL_EDGEPORT is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_KLSI is not set
-# CONFIG_USB_SERIAL_PL2303 is not set
-# CONFIG_USB_SERIAL_CYBERJACK is not set
-# CONFIG_USB_SERIAL_XIRCOM is not set
-# CONFIG_USB_SERIAL_OMNINET is not set
-
-#
-# USB Miscellaneous drivers
-#
-# CONFIG_USB_RIO500 is not set
#
# Kernel hacking
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/Makefile linuxppc64_2_4/arch/ppc64/kernel/Makefile
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/Makefile Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/Makefile Mon May 6 14:29:20 2002
@@ -40,6 +40,8 @@
obj-y += rtasd.o nvram.o
endif
+obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o
+
obj-$(CONFIG_KGDB) += ppc-stub.o
obj-$(CONFIG_SMP) += smp.o
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/chrp_setup.c linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/chrp_setup.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c Thu Apr 25 20:45:03 2002
@@ -259,11 +259,9 @@
if(naca->interrupt_controller == IC_OPEN_PIC) {
ppc_md.init_IRQ = openpic_init_IRQ;
ppc_md.get_irq = openpic_get_irq;
- ppc_md.post_irq = NULL;
} else {
ppc_md.init_IRQ = xics_init_IRQ;
ppc_md.get_irq = xics_get_irq;
- ppc_md.post_irq = NULL;
}
ppc_md.init_ras_IRQ = init_ras_IRQ;
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ioctl32.c linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ioctl32.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c Thu Apr 25 22:03:30 2002
@@ -44,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -102,6 +103,7 @@
#include
#include
#include
+#include
#include
#include
@@ -469,6 +471,7 @@
return -ENODEV;
strcpy(ifr32.ifr_name, dev->name);
+ dev_put(dev);
err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32));
return (err ? -EFAULT : 0);
@@ -1157,6 +1160,234 @@
return err;
}
+typedef struct sg_io_hdr32 {
+ s32 interface_id; /* [i] 'S' for SCSI generic (required) */
+ s32 dxfer_direction; /* [i] data transfer direction */
+ u8 cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
+ u8 mx_sb_len; /* [i] max length to write to sbp */
+ u16 iovec_count; /* [i] 0 implies no scatter gather */
+ u32 dxfer_len; /* [i] byte count of data transfer */
+ u32 dxferp; /* [i], [*io] points to data transfer memory
+ or scatter gather list */
+ u32 cmdp; /* [i], [*i] points to command to perform */
+ u32 sbp; /* [i], [*o] points to sense_buffer memory */
+ u32 timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
+ u32 flags; /* [i] 0 -> default, see SG_FLAG... */
+ s32 pack_id; /* [i->o] unused internally (normally) */
+ u32 usr_ptr; /* [i->o] unused internally */
+ u8 status; /* [o] scsi status */
+ u8 masked_status; /* [o] shifted, masked scsi status */
+ u8 msg_status; /* [o] messaging level data (optional) */
+ u8 sb_len_wr; /* [o] byte count actually written to sbp */
+ u16 host_status; /* [o] errors from host adapter */
+ u16 driver_status; /* [o] errors from software driver */
+ s32 resid; /* [o] dxfer_len - actual_transferred */
+ u32 duration; /* [o] time taken by cmd (unit: millisec) */
+ u32 info; /* [o] auxiliary information */
+} sg_io_hdr32_t; /* 64 bytes long (on sparc32) */
+
+typedef struct sg_iovec32 {
+ u32 iov_base;
+ u32 iov_len;
+} sg_iovec32_t;
+
+static int alloc_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
+{
+ sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
+ sg_iovec_t *kiov;
+ int i;
+
+ sgp->dxferp = kmalloc(sgp->iovec_count *
+ sizeof(sg_iovec_t), GFP_KERNEL);
+ if (!sgp->dxferp)
+ return -ENOMEM;
+ memset(sgp->dxferp, 0,
+ sgp->iovec_count * sizeof(sg_iovec_t));
+
+ kiov = (sg_iovec_t *) sgp->dxferp;
+ for (i = 0; i < sgp->iovec_count; i++) {
+ u32 iov_base32;
+ if (__get_user(iov_base32, &uiov->iov_base) ||
+ __get_user(kiov->iov_len, &uiov->iov_len))
+ return -EFAULT;
+
+ kiov->iov_base = kmalloc(kiov->iov_len, GFP_KERNEL);
+ if (!kiov->iov_base)
+ return -ENOMEM;
+ if (copy_from_user(kiov->iov_base,
+ (void *) A(iov_base32),
+ kiov->iov_len))
+ return -EFAULT;
+
+ uiov++;
+ kiov++;
+ }
+
+ return 0;
+}
+
+static int copy_back_sg_iovec(sg_io_hdr_t *sgp, u32 uptr32)
+{
+ sg_iovec32_t *uiov = (sg_iovec32_t *) A(uptr32);
+ sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
+ int i;
+
+ for (i = 0; i < sgp->iovec_count; i++) {
+ u32 iov_base32;
+
+ if (__get_user(iov_base32, &uiov->iov_base))
+ return -EFAULT;
+
+ if (copy_to_user((void *) A(iov_base32),
+ kiov->iov_base,
+ kiov->iov_len))
+ return -EFAULT;
+
+ uiov++;
+ kiov++;
+ }
+
+ return 0;
+}
+
+static void free_sg_iovec(sg_io_hdr_t *sgp)
+{
+ sg_iovec_t *kiov = (sg_iovec_t *) sgp->dxferp;
+ int i;
+
+ for (i = 0; i < sgp->iovec_count; i++) {
+ if (kiov->iov_base) {
+ kfree(kiov->iov_base);
+ kiov->iov_base = NULL;
+ }
+ kiov++;
+ }
+ kfree(sgp->dxferp);
+ sgp->dxferp = NULL;
+}
+
+static int sg_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ sg_io_hdr32_t *sg_io32;
+ sg_io_hdr_t sg_io64;
+ u32 dxferp32, cmdp32, sbp32;
+ mm_segment_t old_fs;
+ int err = 0;
+
+ sg_io32 = (sg_io_hdr32_t *)arg;
+ err = __get_user(sg_io64.interface_id, &sg_io32->interface_id);
+ err |= __get_user(sg_io64.dxfer_direction, &sg_io32->dxfer_direction);
+ err |= __get_user(sg_io64.cmd_len, &sg_io32->cmd_len);
+ err |= __get_user(sg_io64.mx_sb_len, &sg_io32->mx_sb_len);
+ err |= __get_user(sg_io64.iovec_count, &sg_io32->iovec_count);
+ err |= __get_user(sg_io64.dxfer_len, &sg_io32->dxfer_len);
+ err |= __get_user(sg_io64.timeout, &sg_io32->timeout);
+ err |= __get_user(sg_io64.flags, &sg_io32->flags);
+ err |= __get_user(sg_io64.pack_id, &sg_io32->pack_id);
+
+ sg_io64.dxferp = NULL;
+ sg_io64.cmdp = NULL;
+ sg_io64.sbp = NULL;
+
+ err |= __get_user(cmdp32, &sg_io32->cmdp);
+ sg_io64.cmdp = kmalloc(sg_io64.cmd_len, GFP_KERNEL);
+ if (!sg_io64.cmdp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.cmdp,
+ (void *) A(cmdp32),
+ sg_io64.cmd_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(sbp32, &sg_io32->sbp);
+ sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
+ if (!sg_io64.sbp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.sbp,
+ (void *) A(sbp32),
+ sg_io64.mx_sb_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err |= __get_user(dxferp32, &sg_io32->dxferp);
+ if (sg_io64.iovec_count) {
+ int ret;
+
+ if ((ret = alloc_sg_iovec(&sg_io64, dxferp32))) {
+ err = ret;
+ goto out;
+ }
+ } else {
+ sg_io64.dxferp = kmalloc(sg_io64.dxfer_len, GFP_KERNEL);
+ if (!sg_io64.dxferp) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(sg_io64.dxferp,
+ (void *) A(dxferp32),
+ sg_io64.dxfer_len)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Unused internally, do not even bother to copy it over. */
+ sg_io64.usr_ptr = NULL;
+
+ if (err)
+ return -EFAULT;
+
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long) &sg_io64);
+ set_fs (old_fs);
+
+ if (err < 0)
+ goto out;
+
+ err = __put_user(sg_io64.pack_id, &sg_io32->pack_id);
+ err |= __put_user(sg_io64.status, &sg_io32->status);
+ err |= __put_user(sg_io64.masked_status, &sg_io32->masked_status);
+ err |= __put_user(sg_io64.msg_status, &sg_io32->msg_status);
+ err |= __put_user(sg_io64.sb_len_wr, &sg_io32->sb_len_wr);
+ err |= __put_user(sg_io64.host_status, &sg_io32->host_status);
+ err |= __put_user(sg_io64.driver_status, &sg_io32->driver_status);
+ err |= __put_user(sg_io64.resid, &sg_io32->resid);
+ err |= __put_user(sg_io64.duration, &sg_io32->duration);
+ err |= __put_user(sg_io64.info, &sg_io32->info);
+ err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count)
+ err |= copy_back_sg_iovec(&sg_io64, dxferp32);
+ else
+ err |= copy_to_user((void *)A(dxferp32),
+ sg_io64.dxferp,
+ sg_io64.dxfer_len);
+ }
+ if (err)
+ err = -EFAULT;
+
+out:
+ if (sg_io64.cmdp)
+ kfree(sg_io64.cmdp);
+ if (sg_io64.sbp)
+ kfree(sg_io64.sbp);
+ if (sg_io64.dxferp) {
+ if (sg_io64.iovec_count) {
+ free_sg_iovec(&sg_io64);
+ } else {
+ kfree(sg_io64.dxferp);
+ }
+ }
+ return err;
+}
+
struct ppp_option_data32 {
__kernel_caddr_t32 ptr;
__u32 length;
@@ -2161,12 +2392,11 @@
if (l->lv_block_exception) {
lbe32 = (lv_block_exception32_t *)A(ptr2);
memset(lbe, 0, size);
- for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) {
- err |= get_user(lbe->rsector_org, &lbe32->rsector_org);
- err |= __get_user(lbe->rdev_org, &lbe32->rdev_org);
- err |= __get_user(lbe->rsector_new, &lbe32->rsector_new);
- err |= __get_user(lbe->rdev_new, &lbe32->rdev_new);
-
+ for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) {
+ err |= get_user(lbe->rsector_org, &lbe32->rsector_org);
+ err |= __get_user(lbe->rdev_org, &lbe32->rdev_org);
+ err |= __get_user(lbe->rsector_new, &lbe32->rsector_new);
+ err |= __get_user(lbe->rdev_new, &lbe32->rdev_new);
}
}
}
@@ -2222,16 +2452,17 @@
switch (cmd) {
case VG_STATUS:
v = kmalloc(sizeof(vg_t), GFP_KERNEL);
- if (!v) return -ENOMEM;
+ if (!v)
+ return -ENOMEM;
karg = v;
break;
case VG_CREATE_OLD:
case VG_CREATE:
v = kmalloc(sizeof(vg_t), GFP_KERNEL);
- if (!v) return -ENOMEM;
- if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) ||
- __get_user(v->proc, &((vg32_t *)arg)->proc)) {
+ if (!v)
+ return -ENOMEM;
+ if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc)) {
kfree(v);
return -EFAULT;
}
@@ -2248,39 +2479,46 @@
return -EPERM;
for (i = 0; i < v->pv_max; i++) {
err = __get_user(ptr, &((vg32_t *)arg)->pv[i]);
- if (err) break;
+ if (err)
+ break;
if (ptr) {
v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL);
if (!v->pv[i]) {
err = -ENOMEM;
break;
}
- err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1);
+ err = copy_from_user(v->pv[i], (void *)A(ptr),
+ sizeof(pv32_t) - 8 - UUID_LEN+1);
if (err) {
err = -EFAULT;
break;
}
- err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1);
+ err = copy_from_user(v->pv[i]->pv_uuid,
+ ((pv32_t *)A(ptr))->pv_uuid,
+ UUID_LEN+1);
if (err) {
err = -EFAULT;
break;
}
-
- v->pv[i]->pe = NULL; v->pv[i]->inode = NULL;
+ v->pv[i]->pe = NULL;
+ v->pv[i]->bd = NULL;
}
}
if (!err) {
for (i = 0; i < v->lv_max; i++) {
err = __get_user(ptr, &((vg32_t *)arg)->lv[i]);
- if (err) break;
+ if (err)
+ break;
if (ptr) {
v->lv[i] = get_lv_t(ptr, &err);
- if (err) break;
+ if (err)
+ break;
}
}
}
break;
+
case LV_CREATE:
case LV_EXTEND:
case LV_REDUCE:
@@ -2288,54 +2526,70 @@
case LV_RENAME:
case LV_STATUS_BYNAME:
err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name));
- if (err) return -EFAULT;
+ if (err)
+ return -EFAULT;
if (cmd != LV_REMOVE) {
err = __get_user(ptr, &((lv_req32_t *)arg)->lv);
- if (err) return err;
+ if (err)
+ return err;
u.lv_req.lv = get_lv_t(ptr, &err);
} else
u.lv_req.lv = NULL;
break;
-
case LV_STATUS_BYINDEX:
- err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index);
+ err = get_user(u.lv_byindex.lv_index,
+ &((lv_status_byindex_req32_t *)arg)->lv_index);
err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv);
- if (err) return err;
+ if (err)
+ return err;
u.lv_byindex.lv = get_lv_t(ptr, &err);
break;
+
case LV_STATUS_BYDEV:
err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev);
+ err |= __get_user(ptr, &((lv_status_bydev_req32_t *)arg)->lv);
+ if (err)
+ return err;
u.lv_bydev.lv = get_lv_t(ptr, &err);
- if (err) return err;
- u.lv_bydev.lv = &p;
- p.pe = NULL; p.inode = NULL;
- break;
+ break;
+
case VG_EXTEND:
err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1);
- if (err) return -EFAULT;
+ if (err)
+ return -EFAULT;
err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1);
- if (err) return -EFAULT;
- p.pe = NULL; p.inode = NULL;
+ if (err)
+ return -EFAULT;
+ p.pe = NULL;
+ p.bd = NULL;
karg = &p;
break;
+
case PV_CHANGE:
case PV_STATUS:
err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name));
- if (err) return -EFAULT;
+ if (err)
+ return -EFAULT;
err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv);
- if (err) return err;
+ if (err)
+ return err;
u.pv_status.pv = &p;
if (cmd == PV_CHANGE) {
- err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1);
- if (err) return -EFAULT;
- p.pe = NULL; p.inode = NULL;
+ err = copy_from_user(&p, (void *)A(ptr),
+ sizeof(pv32_t) - 8 - UUID_LEN+1);
+ if (err)
+ return -EFAULT;
+ p.pe = NULL;
+ p.bd = NULL;
}
break;
- }
+ };
+
old_fs = get_fs(); set_fs (KERNEL_DS);
err = sys_ioctl (fd, cmd, (unsigned long)karg);
set_fs (old_fs);
+
switch (cmd) {
case VG_STATUS:
if (!err) {
@@ -2361,17 +2615,23 @@
}
kfree(v);
break;
+
case LV_STATUS_BYNAME:
- if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv);
+ if (!err && u.lv_req.lv)
+ err = copy_lv_t(ptr, u.lv_req.lv);
/* Fall through */
+
case LV_CREATE:
case LV_EXTEND:
case LV_REDUCE:
- if (u.lv_req.lv) put_lv_t(u.lv_req.lv);
+ if (u.lv_req.lv)
+ put_lv_t(u.lv_req.lv);
break;
+
case LV_STATUS_BYINDEX:
if (u.lv_byindex.lv) {
- if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv);
+ if (!err)
+ err = copy_lv_t(ptr, u.lv_byindex.lv);
put_lv_t(u.lv_byindex.lv);
}
break;
@@ -2387,9 +2647,11 @@
case PV_STATUS:
if (!err) {
err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1);
- if (err) return -EFAULT;
+ if (err)
+ return -EFAULT;
err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1);
- if (err) return -EFAULT;
+ if (err)
+ return -EFAULT;
}
break;
};
@@ -3435,119 +3697,6 @@
return ((0 == ret) ? 0 : -EFAULT);
}
-struct sg_io_hdr_32
-{
- int interface_id;
- int dxfer_direction;
- unsigned char cmd_len;
- unsigned char mx_sb_len;
- unsigned short iovec_count;
- unsigned int dxfer_len;
- u32 dxferp;
- u32 cmdp;
- u32 sbp;
- unsigned int timeout;
- unsigned int flags;
- int pack_id;
- u32 usr_ptr;
- unsigned char status;
- unsigned char masked_status;
- unsigned char msg_status;
- unsigned char sb_len_wr;
- unsigned short host_status;
- unsigned short driver_status;
- int resid;
- unsigned int duration;
- unsigned int info;
-};
-
-static int do_sg_io(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
- struct sg_io_hdr *sg = kmalloc(sizeof(struct sg_io_hdr), GFP_KERNEL);
- struct sg_io_hdr_32 *sg_32 = (struct sg_io_hdr_32 *)arg;
- u32 dxferp_32;
- u32 cmdp_32;
- u32 sbp_32;
- u32 usr_ptr_32;
- int ret = -EFAULT;
- int err;
- mm_segment_t old_fs = get_fs();
-
- if (!sg)
- return -ENOMEM;
-
- memset(sg, 0, sizeof(*sg));
-
- err = copy_from_user(sg, sg_32, offsetof(struct sg_io_hdr, dxferp));
- err |= __get_user(dxferp_32, &sg_32->dxferp);
- err |= __get_user(cmdp_32, &sg_32->cmdp);
- err |= __get_user(sbp_32, &sg_32->sbp);
-
- if (err)
- goto error;
-
- sg->dxferp = (void *)A(dxferp_32);
- sg->cmdp = (void *)A(cmdp_32);
- sg->sbp = (void *)A(sbp_32);
-
- err = __copy_from_user(&sg->timeout, &sg_32->timeout,
- (long)&sg->usr_ptr - (long)&sg->timeout);
-
- err |= __get_user(usr_ptr_32, &sg_32->usr_ptr);
-
- if (err)
- goto error;
-
- sg->usr_ptr = (void *)A(usr_ptr_32);
-
- err = __copy_from_user(&sg->status, &sg_32->status,
- sizeof(struct sg_io_hdr) -
- offsetof(struct sg_io_hdr, status));
-
- if (err)
- goto error;
-
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, cmd, (unsigned long)sg);
- set_fs(old_fs);
-
- err = copy_to_user(sg_32, sg, offsetof(struct sg_io_hdr, dxferp));
-
- dxferp_32 = (unsigned long)sg->dxferp;
- cmdp_32 = (unsigned long)sg->cmdp;
- sbp_32 = (unsigned long)sg->sbp;
- err |= __put_user(dxferp_32, &sg_32->dxferp);
- err |= __put_user(cmdp_32, &sg_32->cmdp);
- err |= __put_user(sbp_32, &sg_32->sbp);
-
- if (err) {
- ret = -EFAULT;
- goto error;
- }
-
- err = __copy_to_user(&sg_32->timeout, &sg->timeout,
- (long)&sg->usr_ptr - (long)&sg->timeout);
-
- usr_ptr_32 = (unsigned long)sg->usr_ptr;
- err |= __put_user(usr_ptr_32, &sg_32->usr_ptr);
-
- if (err) {
- ret = -EFAULT;
- goto error;
- }
-
- err = __copy_to_user(&sg_32->status, &sg->status,
- sizeof(struct sg_io_hdr) -
- offsetof(struct sg_io_hdr, status));
-
- if (err)
- ret = -EFAULT;
-
-error:
- kfree(sg);
- return ret;
-}
-
struct ioctl_trans {
unsigned long cmd;
unsigned long handler;
@@ -3736,6 +3885,12 @@
COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE),
COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER),
COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND),
+/* Big T */
+COMPATIBLE_IOCTL(TUNSETNOCSUM),
+COMPATIBLE_IOCTL(TUNSETDEBUG),
+COMPATIBLE_IOCTL(TUNSETIFF),
+COMPATIBLE_IOCTL(TUNSETPERSIST),
+COMPATIBLE_IOCTL(TUNSETOWNER),
/* Big V */
COMPATIBLE_IOCTL(VT_SETMODE),
COMPATIBLE_IOCTL(VT_GETMODE),
@@ -3812,6 +3967,8 @@
COMPATIBLE_IOCTL(SIOCDRARP),
COMPATIBLE_IOCTL(SIOCADDDLCI),
COMPATIBLE_IOCTL(SIOCDELDLCI),
+COMPATIBLE_IOCTL(SIOCGIFVLAN),
+COMPATIBLE_IOCTL(SIOCSIFVLAN),
/* SG stuff */
COMPATIBLE_IOCTL(SG_SET_TIMEOUT),
COMPATIBLE_IOCTL(SG_GET_TIMEOUT),
@@ -4145,6 +4302,13 @@
COMPATIBLE_IOCTL(WDIOC_GETTEMP),
COMPATIBLE_IOCTL(WDIOC_SETOPTIONS),
COMPATIBLE_IOCTL(WDIOC_KEEPALIVE),
+/* Big R */
+COMPATIBLE_IOCTL(RNDGETENTCNT),
+COMPATIBLE_IOCTL(RNDADDTOENTCNT),
+COMPATIBLE_IOCTL(RNDGETPOOL),
+COMPATIBLE_IOCTL(RNDADDENTROPY),
+COMPATIBLE_IOCTL(RNDZAPENTCNT),
+COMPATIBLE_IOCTL(RNDCLEARPOOL),
/* Bluetooth ioctls */
COMPATIBLE_IOCTL(HCIDEVUP),
COMPATIBLE_IOCTL(HCIDEVDOWN),
@@ -4264,6 +4428,7 @@
HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans),
HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans),
HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans),
+HANDLE_IOCTL(SG_IO,sg_ioctl_trans),
HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans),
HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans),
HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans),
@@ -4358,7 +4523,6 @@
HANDLE_IOCTL(USBDEVFS_REAPURB32, do_usbdevfs_reapurb),
HANDLE_IOCTL(USBDEVFS_REAPURBNDELAY32, do_usbdevfs_reapurb),
HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal),
-HANDLE_IOCTL(SG_IO, do_sg_io),
};
unsigned long ioctl32_hash_table[1024];
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/irq.c linuxppc64_2_4/arch/ppc64/kernel/irq.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/irq.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/irq.c Fri May 3 03:15:52 2002
@@ -99,7 +99,7 @@
* this needs to be removed.
* -- Cort
*/
-#define IRQ_KMALLOC_ENTRIES 8
+#define IRQ_KMALLOC_ENTRIES 16
static int cache_bitmask = 0;
static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES];
extern int mem_init_done;
@@ -554,58 +554,55 @@
spin_unlock(&desc->lock);
}
-int do_IRQ(struct pt_regs *regs, int isfake)
+int do_IRQ(struct pt_regs *regs)
{
int cpu = smp_processor_id();
- int irq;
+ int irq, first = 1;
+#ifdef CONFIG_PPC_ISERIES
struct paca_struct *lpaca;
- struct ItLpQueue * lpq;
-
- /* if(cpu) udbg_printf("Entering do_IRQ\n"); */
+ struct ItLpQueue *lpq;
+#endif
- irq_enter(cpu);
+ irq_enter(cpu);
- if (naca->platform != PLATFORM_ISERIES_LPAR) {
-
- /* every arch is required to have a get_irq -- Cort */
- irq = ppc_md.get_irq( regs );
-
- if ( irq >= 0 ) {
- ppc_irq_dispatch_handler( regs, irq );
- if (ppc_md.post_irq)
- ppc_md.post_irq( regs, irq );
- } else {
- /* -2 means ignore, already handled */
- if (irq != -2) {
- printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
- irq, regs->nip);
- ppc_spurious_interrupts++;
- }
- }
- }
- /* if on iSeries partition */
- else {
- lpaca = get_paca();
+#ifdef CONFIG_PPC_ISERIES
+ lpaca = get_paca();
#ifdef CONFIG_SMP
- if ( lpaca->xLpPaca.xIntDword.xFields.xIpiCnt ) {
- lpaca->xLpPaca.xIntDword.xFields.xIpiCnt = 0;
- iSeries_smp_message_recv( regs );
- }
-#endif /* CONFIG_SMP */
- lpq = lpaca->lpQueuePtr;
- if ( lpq && ItLpQueue_isLpIntPending( lpq ) )
- lpEvent_count += ItLpQueue_process( lpq, regs );
+ if (lpaca->xLpPaca.xIntDword.xFields.xIpiCnt) {
+ lpaca->xLpPaca.xIntDword.xFields.xIpiCnt = 0;
+ iSeries_smp_message_recv(regs);
}
-
+#endif /* CONFIG_SMP */
+ lpq = lpaca->lpQueuePtr;
+ if (lpq && ItLpQueue_isLpIntPending(lpq))
+ lpEvent_count += ItLpQueue_process(lpq, regs);
+#else
+ /*
+ * Every arch is required to implement ppc_md.get_irq.
+ * This function will either return an irq number or -1 to
+ * indicate there are no more pending. But the first time
+ * through the loop this means there wasn't an IRQ pending.
+ * The value -2 is for buggy hardware and means that this IRQ
+ * has already been handled. -- Tom
+ */
+ while ((irq = ppc_md.get_irq(regs)) >= 0) {
+ ppc_irq_dispatch_handler(regs, irq);
+ first = 0;
+ }
+ if (irq != -2 && first)
+ /* That's not SMP safe ... but who cares ? */
+ ppc_spurious_interrupts++;
+#endif
+
irq_exit(cpu);
- if (naca->platform == PLATFORM_ISERIES_LPAR) {
- if ( lpaca->xLpPaca.xIntDword.xFields.xDecrInt ) {
- lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 0;
- /* Signal a fake decrementer interrupt */
- timer_interrupt( regs );
- }
+#ifdef CONFIG_PPC_ISERIES
+ if (lpaca->xLpPaca.xIntDword.xFields.xDecrInt) {
+ lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 0;
+ /* Signal a fake decrementer interrupt */
+ timer_interrupt(regs);
}
+#endif
if (softirq_pending(cpu))
do_softirq();
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/open_pic.c linuxppc64_2_4/arch/ppc64/kernel/open_pic.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/open_pic.c Fri Apr 19 11:00:33 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/open_pic.c Thu Apr 25 20:45:03 2002
@@ -47,7 +47,6 @@
OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU];
static void openpic_end_irq(unsigned int irq_nr);
-static void openpic_ack_irq(unsigned int irq_nr);
static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
struct hw_interrupt_type open_pic = {
@@ -56,14 +55,13 @@
NULL,
openpic_enable_irq,
openpic_disable_irq,
- openpic_ack_irq,
+ NULL,
openpic_end_irq,
openpic_set_affinity
};
#ifdef CONFIG_SMP
static void openpic_end_ipi(unsigned int irq_nr);
-static void openpic_ack_ipi(unsigned int irq_nr);
static void openpic_enable_ipi(unsigned int irq_nr);
static void openpic_disable_ipi(unsigned int irq_nr);
@@ -73,9 +71,9 @@
NULL,
openpic_enable_ipi,
openpic_disable_ipi,
- openpic_ack_ipi,
+ NULL,
openpic_end_ipi,
- 0
+ NULL
};
#endif /* CONFIG_SMP */
@@ -756,13 +754,6 @@
(sense ? OPENPIC_SENSE_LEVEL : 0));
}
-/* No spinlocks, should not be necessary with the OpenPIC
- * (1 register = 1 interrupt and we have the desc lock).
- */
-static void openpic_ack_irq(unsigned int irq_nr)
-{
-}
-
static void openpic_end_irq(unsigned int irq_nr)
{
if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
@@ -775,10 +766,6 @@
}
#ifdef CONFIG_SMP
-static void openpic_ack_ipi(unsigned int irq_nr)
-{
-}
-
static void openpic_end_ipi(unsigned int irq_nr)
{
/* IPIs are marked IRQ_PER_CPU. This has the side effect of
@@ -825,4 +812,3 @@
irq = -1;
return irq;
}
-
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ppc_ksyms.c linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ppc_ksyms.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c Mon May 6 14:29:20 2002
@@ -40,15 +40,15 @@
#include
#include
#include
-#ifdef CONFIG_SMP
#include
-#endif /* CONFIG_SMP */
#ifdef CONFIG_PPC_ISERIES
#include
#include
#include
#include
#include
+#else
+#include
#endif
/* Tell string.h we don't want memcpy etc. as cpp defines */
@@ -237,6 +237,13 @@
EXPORT_SYMBOL(machine_is_compatible);
EXPORT_SYMBOL(find_all_nodes);
EXPORT_SYMBOL(get_property);
+
+#ifdef CONFIG_PPC_PSERIES
+EXPORT_SYMBOL(rtas_proc_dir);
+EXPORT_SYMBOL(rtas_firmware_flash_list);
+EXPORT_SYMBOL(rtas_token);
+EXPORT_SYMBOL(rtas_call);
+#endif
#ifndef CONFIG_PPC_ISERIES
EXPORT_SYMBOL(kd_mksound);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/proc_pmc.c linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/proc_pmc.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c Mon May 6 14:29:20 2002
@@ -41,6 +41,7 @@
#include
#include
#include
+#include
/* pci Flight Recorder AHT */
extern void proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root);
@@ -115,6 +116,9 @@
for (i = 0; i < naca->processorCount; i++)
proc_ppc64_create_paca(i, ent);
}
+
+ /* Placeholder for rtas interfaces. */
+ rtas_proc_dir = proc_mkdir("rtas", proc_ppc64_root);
/* Create the /proc/ppc64/pcifr for the Pci Flight Recorder. */
proc_pciFr_init(proc_ppc64_root);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/process.c linuxppc64_2_4/arch/ppc64/kernel/process.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/process.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/process.c Thu Apr 25 20:09:58 2002
@@ -278,87 +278,45 @@
current->thread.fpscr = 0;
}
-asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
- struct pt_regs *regs)
+int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
+ struct pt_regs *regs)
{
- unsigned long clone_flags = p1;
- int res;
-
- PPCDBG(PPCDBG_SYS64, "sys_clone - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm);
-
- res = do_fork(clone_flags, regs->gpr[1], regs, 0);
-#ifdef CONFIG_SMP
- /* When we clone the idle task we keep the same pid but
- * the return value of 0 for both causes problems.
- * -- Cort
- */
- if ((current->pid == 0) && (current == &init_task))
- res = 1;
-#endif /* CONFIG_SMP */
-
- PPCDBG(PPCDBG_SYS64, "sys_clone - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm);
-
- return res;
+ return do_fork(p1, regs->gpr[1], regs, 0);
}
-asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
- struct pt_regs *regs)
+int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
+ struct pt_regs *regs)
{
- int res;
-
- PPCDBG(PPCDBG_SYS64, "sys_fork - entered - pid=%ld comm=%s \n", current->pid, current->comm);
-
- res = do_fork(SIGCHLD, regs->gpr[1], regs, 0);
-
-#ifdef CONFIG_SMP
- /* When we clone the idle task we keep the same pid but
- * the return value of 0 for both causes problems.
- * -- Cort
- */
- if ((current->pid == 0) && (current == &init_task))
- res = 1;
-#endif /* CONFIG_SMP */
-
- PPCDBG(PPCDBG_SYS64, "sys_fork - exited - pid=%ld comm=%s \n", current->pid, current->comm);
-
- return res;
+ return do_fork(SIGCHLD, regs->gpr[1], regs, 0);
}
-asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
+int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs)
{
- PPCDBG(PPCDBG_SYS64, "sys_vfork - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm);
-
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0);
}
-asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
- unsigned long a3, unsigned long a4, unsigned long a5,
- struct pt_regs *regs)
+int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs *regs)
{
int error;
char * filename;
-
- PPCDBG(PPCDBG_SYS64, "sys_execve - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm);
-
+
filename = getname((char *) a0);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
if (regs->msr & MSR_FP)
giveup_fpu(current);
-
- PPCDBG(PPCDBG_SYS64, "sys_execve - before do_execve : filename = %s\n", filename);
-
+
error = do_execve(filename, (char **) a1, (char **) a2, regs);
-
+
if (error == 0)
current->ptrace &= ~PT_DTRACE;
putname(filename);
- out:
- PPCDBG(PPCDBG_SYS64, "sys_execve - exited - pid=%ld current=%lx comm=%s error = %lx\n", current->pid, current, current->comm, error);
-
+out:
return error;
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/prom.c linuxppc64_2_4/arch/ppc64/kernel/prom.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/prom.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/prom.c Wed May 1 09:55:09 2002
@@ -912,8 +912,6 @@
(strstr(model, RELOC("peedwagon")) == NULL) &&
(strstr(model, RELOC("innipeg")) == NULL))
continue;
- } else {
- prom_print(RELOC("No known I/O bridge chip found.\n"));
}
if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) {
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ptrace.c linuxppc64_2_4/arch/ppc64/kernel/ptrace.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ptrace.c Fri Apr 19 11:00:33 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/ptrace.c Thu Apr 25 19:05:58 2002
@@ -126,14 +126,9 @@
ret = ptrace_attach(child);
goto out_tsk;
}
- ret = -ESRCH;
- if (!(child->ptrace & PT_PTRACED))
- goto out_tsk;
- if (child->state != TASK_STOPPED) {
- if (request != PTRACE_KILL)
- goto out_tsk;
- }
- if (child->p_pptr != current)
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
goto out_tsk;
switch (request) {
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ptrace32.c linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/ptrace32.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c Thu Apr 25 19:05:58 2002
@@ -115,14 +115,9 @@
ret = ptrace_attach(child);
goto out_tsk;
}
- ret = -ESRCH;
- if (!(child->ptrace & PT_PTRACED))
- goto out_tsk;
- if (child->state != TASK_STOPPED) {
- if (request != PTRACE_KILL)
- goto out_tsk;
- }
- if (child->p_pptr != current)
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
goto out_tsk;
switch (request)
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/rtas.c linuxppc64_2_4/arch/ppc64/kernel/rtas.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/rtas.c Wed May 15 08:29:35 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/rtas.c Mon May 6 14:29:20 2002
@@ -24,8 +24,12 @@
#include
#include
#include
+#include
#include
+struct proc_dir_entry *rtas_proc_dir; /* /proc/ppc64/rtas dir */
+struct flash_block_list_header rtas_firmware_flash_list = {0, 0};
+
/*
* prom_init() is called very early on, before the kernel text
* and data have been mapped to KERNELBASE. At this point the code
@@ -185,9 +189,87 @@
return (ulong)((nret > 0) ? rtas_args->rets[0] : 0);
}
+#define FLASH_BLOCK_LIST_VERSION (1UL)
+static void
+rtas_flash_firmware(void)
+{
+ unsigned long image_size;
+ struct flash_block_list *f, *next, *flist;
+ unsigned long rtas_block_list;
+ int i, status, update_token;
+
+ update_token = rtas_token("ibm,update-flash-64-and-reboot");
+ if (update_token == RTAS_UNKNOWN_SERVICE) {
+ printk(KERN_ALERT "FLASH: ibm,update-flash-64-and-reboot is not available -- not a service partition?\n");
+ printk(KERN_ALERT "FLASH: firmware will not be flashed\n");
+ return;
+ }
+
+ /* NOTE: the "first" block list is a global var with no data
+ * blocks in the kernel data segment. We do this because
+ * we want to ensure this block_list addr is under 4GB.
+ */
+ rtas_firmware_flash_list.num_blocks = 0;
+ flist = (struct flash_block_list *)&rtas_firmware_flash_list;
+ rtas_block_list = virt_to_absolute((unsigned long)flist);
+ if (rtas_block_list >= (4UL << 20)) {
+ printk(KERN_ALERT "FLASH: kernel bug...flash list header addr above 4GB\n");
+ return;
+ }
+
+ printk(KERN_ALERT "FLASH: preparing saved firmware image for flash\n");
+ /* Update the block_list in place. */
+ image_size = 0;
+ for (f = flist; f; f = next) {
+ /* Translate data addrs to absolute */
+ for (i = 0; i < f->num_blocks; i++) {
+ f->blocks[i].data = (char *)virt_to_absolute((unsigned long)f->blocks[i].data);
+ image_size += f->blocks[i].length;
+ }
+ next = f->next;
+ f->next = (struct flash_block_list *)virt_to_absolute((unsigned long)f->next);
+ /* make num_blocks into the version/length field */
+ f->num_blocks = (FLASH_BLOCK_LIST_VERSION << 56) | ((f->num_blocks+1)*16);
+ }
+
+ printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size);
+ printk(KERN_ALERT "FLASH: performing flash and reboot\n");
+ ppc_md.progress("Flashing \n", 0x0);
+ ppc_md.progress("Please Wait... ", 0x0);
+ printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n");
+ status = rtas_call(update_token, 1, 1, NULL, rtas_block_list);
+ switch (status) { /* should only get "bad" status */
+ case 0:
+ printk(KERN_ALERT "FLASH: success\n");
+ break;
+ case -1:
+ printk(KERN_ALERT "FLASH: hardware error. Firmware may not be not flashed\n");
+ break;
+ case -3:
+ printk(KERN_ALERT "FLASH: image is corrupt or not correct for this platform. Firmware not flashed\n");
+ break;
+ case -4:
+ printk(KERN_ALERT "FLASH: flash failed when partially complete. System may not reboot\n");
+ break;
+ default:
+ printk(KERN_ALERT "FLASH: unknown flash return code %d\n", status);
+ break;
+ }
+}
+
+void rtas_flash_bypass_warning(void)
+{
+ printk(KERN_ALERT "FLASH: firmware flash requires a reboot\n");
+ printk(KERN_ALERT "FLASH: the firmware image will NOT be flashed\n");
+}
+
+
void __chrp
rtas_restart(char *cmd)
{
+ if (rtas_firmware_flash_list.next)
+ rtas_flash_firmware();
+
printk("RTAS system-reboot returned %ld\n",
rtas_call(rtas_token("system-reboot"), 0, 1, NULL));
for (;;);
@@ -196,6 +278,8 @@
void __chrp
rtas_power_off(void)
{
+ if (rtas_firmware_flash_list.next)
+ rtas_flash_bypass_warning();
/* allow power on only with power button press */
printk("RTAS power-off returned %ld\n",
rtas_call(rtas_token("power-off"), 2, 1, NULL,0xffffffff,0xffffffff));
@@ -205,5 +289,7 @@
void __chrp
rtas_halt(void)
{
+ if (rtas_firmware_flash_list.next)
+ rtas_flash_bypass_warning();
rtas_power_off();
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/rtas_flash.c linuxppc64_2_4/arch/ppc64/kernel/rtas_flash.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/rtas_flash.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/arch/ppc64/kernel/rtas_flash.c Mon May 6 14:29:20 2002
@@ -0,0 +1,240 @@
+/*
+ * c 2001 PPC 64 Team, IBM Corp
+ *
+ * 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.
+ *
+ * /proc/ppc64/rtas/firmware_flash interface
+ *
+ * This file implements a firmware_flash interface to pump a firmware
+ * image into the kernel. At reboot time rtas_restart() will see the
+ * firmware image and flash it as it reboots (see rtas.c).
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_VERSION "1.0"
+#define MODULE_NAME "rtas_flash"
+
+#define FIRMWARE_FLASH_NAME "firmware_flash"
+
+/* Local copy of the flash block list.
+ * We only allow one open of the flash proc file and create this
+ * list as we go. This list will be put in the kernel's
+ * rtas_firmware_flash_list global var once it is fully read.
+ *
+ * For convenience as we build the list we use virtual addrs,
+ * we do not fill in the version number, and the length field
+ * is treated as the number of entries currently in the block
+ * (i.e. not a byte count). This is all fixed on release.
+ */
+static struct flash_block_list *flist;
+static char *flash_msg;
+static int flash_possible;
+
+static int rtas_flash_open(struct inode *inode, struct file *file)
+{
+ if ((file->f_mode & FMODE_WRITE) && flash_possible) {
+ if (flist)
+ return -EBUSY;
+ flist = (struct flash_block_list *)get_free_page(GFP_KERNEL);
+ if (!flist)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Do simple sanity checks on the flash image. */
+static int flash_list_valid(struct flash_block_list *flist)
+{
+ struct flash_block_list *f;
+ int i;
+ unsigned long block_size, image_size;
+
+ flash_msg = NULL;
+ /* Paranoid self test here. We also collect the image size. */
+ image_size = 0;
+ for (f = flist; f; f = f->next) {
+ for (i = 0; i < f->num_blocks; i++) {
+ if (f->blocks[i].data == NULL) {
+ flash_msg = "error: internal error null data\n";
+ return 0;
+ }
+ block_size = f->blocks[i].length;
+ if (block_size <= 0 || block_size > PAGE_SIZE) {
+ flash_msg = "error: internal error bad length\n";
+ return 0;
+ }
+ image_size += block_size;
+ }
+ }
+ if (image_size < (256 << 10)) {
+ if (image_size < 2)
+ flash_msg = NULL; /* allow "clear" of image */
+ else
+ flash_msg = "error: flash image short\n";
+ return 0;
+ }
+ printk(KERN_INFO "FLASH: flash image with %ld bytes stored for hardware flash on reboot\n", image_size);
+ return 1;
+}
+
+static void free_flash_list(struct flash_block_list *f)
+{
+ struct flash_block_list *next;
+ int i;
+
+ while (f) {
+ for (i = 0; i < f->num_blocks; i++)
+ free_page((unsigned long)(f->blocks[i].data));
+ next = f->next;
+ free_page((unsigned long)f);
+ f = next;
+ }
+}
+
+static int rtas_flash_release(struct inode *inode, struct file *file)
+{
+ if (flist) {
+ /* Always clear saved list on a new attempt. */
+ if (rtas_firmware_flash_list.next) {
+ free_flash_list(rtas_firmware_flash_list.next);
+ rtas_firmware_flash_list.next = NULL;
+ }
+
+ if (flash_list_valid(flist))
+ rtas_firmware_flash_list.next = flist;
+ else
+ free_flash_list(flist);
+ flist = NULL;
+ }
+ return 0;
+}
+
+/* Reading the proc file will show status (not the firmware contents) */
+static ssize_t rtas_flash_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int error;
+ char *msg;
+ int msglen;
+
+ if (!flash_possible) {
+ msg = "error: this partition does not have service authority\n";
+ } else if (flist) {
+ msg = "info: this file is busy for write by some process\n";
+ } else if (flash_msg) {
+ msg = flash_msg; /* message from last flash attempt */
+ } else if (rtas_firmware_flash_list.next) {
+ msg = "ready: firmware image ready for flash on reboot\n";
+ } else {
+ msg = "info: no firmware image for flash\n";
+ }
+ msglen = strlen(msg);
+ if (msglen > count)
+ msglen = count;
+
+ if (ppos && *ppos != 0)
+ return 0; /* be cheap */
+
+ error = verify_area(VERIFY_WRITE, buf, msglen);
+ if (error)
+ return -EINVAL;
+
+ copy_to_user(buf, msg, msglen);
+
+ if (ppos)
+ *ppos = msglen;
+ return msglen;
+}
+
+/* We could be much more efficient here. But to keep this function
+ * simple we allocate a page to the block list no matter how small the
+ * count is. If the system is low on memory it will be just as well
+ * that we fail....
+ */
+static ssize_t rtas_flash_write(struct file *file, const char *buffer,
+ size_t count, loff_t *off)
+{
+ size_t len = count;
+ char *p;
+ int next_free;
+ struct flash_block_list *fl = flist;
+
+ if (!flash_possible || len == 0)
+ return len; /* discard data */
+
+ while (fl->next)
+ fl = fl->next; /* seek to last block_list for append */
+ next_free = fl->num_blocks;
+ if (next_free == FLASH_BLOCKS_PER_NODE) {
+ /* Need to allocate another block_list */
+ fl->next = (struct flash_block_list *)get_free_page(GFP_KERNEL);
+ if (!fl->next)
+ return -ENOMEM;
+ fl = fl->next;
+ next_free = 0;
+ }
+
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ p = (char *)get_free_page(GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ if(copy_from_user(p, buffer, len)) {
+ free_page((unsigned long)p);
+ return -EFAULT;
+ }
+ fl->blocks[next_free].data = p;
+ fl->blocks[next_free].length = len;
+ fl->num_blocks++;
+
+ return len;
+}
+
+static struct file_operations rtas_flash_operations = {
+ read: rtas_flash_read,
+ write: rtas_flash_write,
+ open: rtas_flash_open,
+ release: rtas_flash_release,
+};
+
+
+int __init rtas_flash_init(void)
+{
+ struct proc_dir_entry *ent = NULL;
+
+ if (!rtas_proc_dir) {
+ printk(KERN_WARNING "rtas proc dir does not already exist");
+ return -ENOENT;
+ }
+
+ if (rtas_token("ibm,update-flash-64-and-reboot") != RTAS_UNKNOWN_SERVICE)
+ flash_possible = 1;
+
+ if ((ent = create_proc_entry(FIRMWARE_FLASH_NAME, S_IRUSR | S_IWUSR, rtas_proc_dir)) != NULL) {
+ ent->nlink = 1;
+ ent->proc_fops = &rtas_flash_operations;
+ ent->owner = THIS_MODULE;
+ }
+ return 0;
+}
+
+void __exit rtas_flash_cleanup(void)
+{
+ if (!rtas_proc_dir)
+ return;
+ remove_proc_entry(FIRMWARE_FLASH_NAME, rtas_proc_dir);
+}
+
+module_init(rtas_flash_init);
+module_exit(rtas_flash_cleanup);
+MODULE_LICENSE("GPL");
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/signal.c linuxppc64_2_4/arch/ppc64/kernel/signal.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/signal.c Wed May 15 08:29:36 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/signal.c Thu May 9 00:49:40 2002
@@ -637,11 +637,6 @@
*/
if (current->thread.flags & PPC_FLAG_32BIT)
return do_signal32(oldset, regs);
-
- PPCDBG(PPCDBG_SIGNAL, "do_signal - running - pid=%ld current=%lx comm=%s \n",
- current->pid, current, current->comm);
-
-
if (!oldset)
oldset = ¤t->blocked;
@@ -716,7 +711,7 @@
continue;
switch (signr) {
- case SIGCONT: case SIGCHLD: case SIGWINCH:
+ case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
continue;
case SIGTSTP: case SIGTTIN: case SIGTTOU:
@@ -740,10 +735,7 @@
/* FALLTHRU */
default:
- sigaddset(¤t->pending.signal, signr);
- recalc_sigpending(current);
- current->flags |= PF_SIGNALED;
- do_exit(exit_code);
+ sig_exit(signr, exit_code, &info);
/* NOTREACHED */
}
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/signal32.c linuxppc64_2_4/arch/ppc64/kernel/signal32.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/signal32.c Wed May 15 08:29:36 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/signal32.c Thu May 9 00:49:40 2002
@@ -1406,7 +1406,7 @@
continue;
switch (signr) {
- case SIGCONT: case SIGCHLD: case SIGWINCH:
+ case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
continue;
case SIGTSTP: case SIGTTIN: case SIGTTOU:
@@ -1430,10 +1430,7 @@
/* FALLTHRU */
default:
- sigaddset(¤t->pending.signal, signr);
- recalc_sigpending(current);
- current->flags |= PF_SIGNALED;
- do_exit(exit_code);
+ sig_exit(signr, exit_code, &info);
/* NOTREACHED */
}
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/smp.c linuxppc64_2_4/arch/ppc64/kernel/smp.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/smp.c Wed May 15 08:29:36 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/smp.c Thu Apr 25 20:16:39 2002
@@ -346,7 +346,7 @@
set_bit(msg, &xics_ipi_message[i]);
mb();
xics_cause_IPI(i);
- }
+ }
}
}
@@ -577,7 +577,7 @@
init_idle();
for (i = 0; i < NR_CPUS; i++) {
- paca[i].prof_counter=1;
+ paca[i].prof_counter = 1;
paca[i].prof_multiplier = 1;
if(i != 0) {
/*
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/kernel/xics.c linuxppc64_2_4/arch/ppc64/kernel/xics.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/kernel/xics.c Wed May 15 08:29:36 2002
+++ linuxppc64_2_4/arch/ppc64/kernel/xics.c Thu Apr 25 20:45:03 2002
@@ -231,9 +231,9 @@
}
} else if( vec == XICS_IRQ_SPURIOUS ) {
irq = -1;
- printk("spurious PPC interrupt!\n");
- } else
+ } else {
irq = real_irq_to_virt(vec) + XICS_IRQ_OFFSET;
+ }
return irq;
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/arch/ppc64/xmon/xmon.c linuxppc64_2_4/arch/ppc64/xmon/xmon.c
--- ../kernel.org/linux-2.4.19/arch/ppc64/xmon/xmon.c Wed May 15 08:29:36 2002
+++ linuxppc64_2_4/arch/ppc64/xmon/xmon.c Thu Apr 25 19:17:44 2002
@@ -123,12 +123,10 @@
static void mem_check(void);
static void mem_find_real(void);
static void mem_find_vsid(void);
-static void mem_check_full_group(void);
static void mem_check_pagetable_vsids (void);
static void mem_map_check_slab(void);
static void mem_map_lock_pages(void);
-static void mem_map_check_hash(void);
static void mem_check_dup_rpn (void);
static void debug_trace(void);
@@ -634,15 +632,9 @@
case 'c':
mem_check();
break;
- case 'g':
- mem_check_full_group();
- break;
case 'j':
mem_map_check_slab();
break;
- case 'h':
- mem_map_check_hash();
- break;
case 'f':
mem_find_real();
break;
@@ -2490,34 +2482,6 @@
printf(" count of locked pages = %d \n", lock_count);
}
-
-
-void mem_map_check_hash()
-{
- int i = max_mapnr;
-
- while (i-- > 0) {
- /* skip the reserved */
- if (!PageReserved(mem_map+i)) {
- if (((mem_map+i)->next_hash) != NULL) {
- if ( REGION_ID((mem_map+i)->next_hash) != KERNEL_REGION_ID ) {
- printf(" mem_map check hash - non c0 entry - "
- "address/value = %p %lx\n", mem_map+i,(mem_map+i)->next_hash);
- }
- if ((unsigned long)((mem_map+i)->next_hash) == KERNELBASE){
- printf(" mem_map check hash - 0x%lx entry = %p \n",
- KERNELBASE, mem_map+i);
- }
- }
- } else {
- if (page_count(mem_map+i) < 0) {
- printf(" reserved page with negative count- entry = %lx \n", mem_map+i);
- }
- }
- }
- printf(" mem_map check hash completed \n");
-}
-
void mem_check_dup_rpn ()
{
unsigned long htab_size_bytes;
@@ -2725,70 +2689,6 @@
printf("\nDone -------------------\n");
-}
-
-
-void mem_check_full_group()
-{
- unsigned long htab_size_bytes;
- unsigned count;
- unsigned count_array[] = {0,0,0,0,0,0,0,0,0};
- unsigned i;
- unsigned long htab_end;
- HPTE *hpte1, *hpte2, *hpte3;
- u64 rpn = 0;
-
- htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG
- htab_end = (unsigned long)htab_data.htab + htab_size_bytes;
-
- printf("\nHardware Page Find full groups \n-------------------\n");
- printf("htab base : %.16lx\n", htab_data.htab);
- printf("htab size : %.16lx\n", htab_size_bytes);
-
- for (hpte1 = htab_data.htab; (unsigned long)hpte1 < htab_end; hpte1= hpte1 + 8)
- {
- count = 0;
- hpte2 = hpte1;
- for (i=0; i<8; ++i)
- {
- if ( hpte2->dw0.dw0.v != 0 )
- {
- count++;
- }
- hpte2++;
- }
- if (count == 8 )
- {
- printf(" full group starting with entry %lx \n", hpte1);
- hpte3 = hpte1;
- for (i=0; i<8; ++i)
- {
- if ( hpte3->dw0.dw0.v != 0 )
- {
- printf(" entry number %d \n",i);
- printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n",
- (hpte3->dw0.dw0.avpn)>>5,
- (hpte3->dw0.dw0.avpn) & 0x1f,
- (hpte3->dw0.dw0.h));
- printf(" rpn: %.13lx \n", (hpte3->dw1.dw1.rpn));
- // Dump out the memmap array entry address, corresponding virtual address, and reference count.
- rpn = hpte3->dw1.dw1.rpn;
- printf(" mem_map+rpn=%p, virtual@=%p, count=%lx \n", mem_map+rpn, (mem_map+rpn)->virtual, (mem_map+rpn)->count);
- }
- hpte3++;
- }
- if (xmon_interrupted())
- return;
- }
-
- count_array[count]++;
- }
- for (i=1; i<9; ++i)
- {
- printf(" group count for size %i = %lx \n", i, count_array[i]);
- }
-
- printf("\nDone -------------------\n");
}
static void debug_trace(void) {
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/Makefile linuxppc64_2_4/drivers/Makefile
--- ../kernel.org/linux-2.4.19/drivers/Makefile Wed May 15 08:29:37 2002
+++ linuxppc64_2_4/drivers/Makefile Mon May 13 16:39:12 2002
@@ -8,7 +8,7 @@
mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \
message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
- fc4 net/hamradio i2c acpi bluetooth
+ fc4 net/hamradio i2c acpi bluetooth iseries
subdir-y := parport char block net sound misc media cdrom hotplug
subdir-m := $(subdir-y)
@@ -24,7 +24,8 @@
subdir-$(CONFIG_TC) += tc
subdir-$(CONFIG_VT) += video
subdir-$(CONFIG_MAC) += macintosh
-subdir-$(CONFIG_PPC) += macintosh
+subdir-$(CONFIG_PPC) += macintosh
+subdir-$(CONFIG_PPC_ISERIES) += iseries
subdir-$(CONFIG_USB) += usb
subdir-$(CONFIG_INPUT) += input
subdir-$(CONFIG_PHONE) += telephony
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/block/genhd.c linuxppc64_2_4/drivers/block/genhd.c
--- ../kernel.org/linux-2.4.19/drivers/block/genhd.c Wed May 15 08:29:37 2002
+++ linuxppc64_2_4/drivers/block/genhd.c Mon May 13 16:39:12 2002
@@ -220,6 +220,9 @@
#ifdef CONFIG_VT
console_map_init();
#endif
+#ifdef CONFIG_VIODASD
+ viodasd_init();
+#endif
return 0;
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/block/ll_rw_blk.c linuxppc64_2_4/drivers/block/ll_rw_blk.c
--- ../kernel.org/linux-2.4.19/drivers/block/ll_rw_blk.c Fri Apr 19 11:00:44 2002
+++ linuxppc64_2_4/drivers/block/ll_rw_blk.c Mon Apr 22 10:32:49 2002
@@ -1345,6 +1345,9 @@
#ifdef CONFIG_BLK_DEV_XD
xd_init();
#endif
+#ifdef CONFIG_VIOCD
+ viocd_init();
+#endif
#ifdef CONFIG_BLK_DEV_MFM
mfm_init();
#endif
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/cdrom/Makefile linuxppc64_2_4/drivers/cdrom/Makefile
--- ../kernel.org/linux-2.4.19/drivers/cdrom/Makefile Fri Apr 19 10:30:25 2002
+++ linuxppc64_2_4/drivers/cdrom/Makefile Thu Oct 11 11:10:49 2001
@@ -27,6 +27,7 @@
obj-$(CONFIG_BLK_DEV_IDECD) += cdrom.o
obj-$(CONFIG_BLK_DEV_SR) += cdrom.o
obj-$(CONFIG_PARIDE_PCD) += cdrom.o
+obj-$(CONFIG_VIOCD) += cdrom.o
obj-$(CONFIG_AZTCD) += aztcd.o
obj-$(CONFIG_CDU31A) += cdu31a.o cdrom.o
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/Config.in linuxppc64_2_4/drivers/char/Config.in
--- ../kernel.org/linux-2.4.19/drivers/char/Config.in Mon Apr 22 11:34:26 2002
+++ linuxppc64_2_4/drivers/char/Config.in Tue Apr 23 09:37:26 2002
@@ -45,6 +45,10 @@
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' Multi-Tech multiport card support (EXPERIMENTAL)' CONFIG_ISI m
fi
+ tristate ' IBM Multiport Serial Adapter' CONFIG_ICOM
+ if [ "$CONFIG_ICOM" = "y" ]; then
+ string 'Modem Country Code (Internal Modem Users only)' CONFIG_ICOM_MODEM_CC ""
+ fi
tristate ' Microgate SyncLink card support' CONFIG_SYNCLINK
tristate ' HDLC line discipline support' CONFIG_N_HDLC
tristate ' SDL RISCom/8 card support' CONFIG_RISCOM8
@@ -134,6 +138,7 @@
fi
dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT
fi
+dep_bool 'pSeries Hypervisor Virtual Console support' CONFIG_HVC_CONSOLE $CONFIG_PPC64
source drivers/i2c/Config.in
@@ -228,6 +233,9 @@
dep_tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG $CONFIG_PCI
fi
tristate '/dev/nvram support' CONFIG_NVRAM
+if [ "$CONFIG_PPC_ISERIES" != "y" ]; then
+ tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
+fi
tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
if [ "$CONFIG_IA64" = "y" ]; then
bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/Makefile linuxppc64_2_4/drivers/char/Makefile
--- ../kernel.org/linux-2.4.19/drivers/char/Makefile Mon Apr 22 11:34:26 2002
+++ linuxppc64_2_4/drivers/char/Makefile Tue Apr 23 09:37:26 2002
@@ -164,6 +164,7 @@
obj-$(CONFIG_COMPUTONE) += ip2.o ip2main.o
obj-$(CONFIG_RISCOM8) += riscom8.o
obj-$(CONFIG_ISI) += isicom.o
+obj-$(CONFIG_ICOM) += icom.o
obj-$(CONFIG_ESPSERIAL) += esp.o
obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_N_HDLC) += n_hdlc.o
@@ -177,6 +178,7 @@
obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o
+obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o
obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o
obj-$(CONFIG_TXX927_SERIAL) += serial_txx927.o
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/hvc_console.c linuxppc64_2_4/drivers/char/hvc_console.c
--- ../kernel.org/linux-2.4.19/drivers/char/hvc_console.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/char/hvc_console.c Mon Apr 22 10:32:50 2002
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2001 Anton Blanchard , IBM
+ * Copyright (C) 2001 Paul Mackerras , IBM
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern int hvc_count(int *);
+extern int hvc_get_chars(int index, char *buf, int count);
+extern int hvc_put_chars(int index, const char *buf, int count);
+
+#define HVC_MAJOR 229
+#define HVC_MINOR 0
+
+#define MAX_NR_HVC_CONSOLES 4
+
+#define TIMEOUT ((HZ + 99) / 100)
+
+struct tty_driver hvc_driver;
+static int hvc_refcount;
+static struct tty_struct *hvc_table[MAX_NR_HVC_CONSOLES];
+static struct termios *hvc_termios[MAX_NR_HVC_CONSOLES];
+static struct termios *hvc_termios_locked[MAX_NR_HVC_CONSOLES];
+static int hvc_offset;
+#ifdef CONFIG_MAGIC_SYSRQ
+static int sysrq_pressed;
+#endif
+
+#define N_OUTBUF 16
+
+#define __ALIGNED__ __attribute__((__aligned__(8)))
+
+struct hvc_struct {
+ spinlock_t lock;
+ int index;
+ struct tty_struct *tty;
+ unsigned int count;
+ int do_wakeup;
+ char outbuf[N_OUTBUF] __ALIGNED__;
+ int n_outbuf;
+};
+
+struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES];
+
+static int hvc_open(struct tty_struct *tty, struct file * filp)
+{
+ int line = MINOR(tty->device) - tty->driver.minor_start;
+ struct hvc_struct *hp;
+ unsigned long flags;
+
+ if (line < 0 || line >= MAX_NR_HVC_CONSOLES)
+ return -ENODEV;
+ hp = &hvc_struct[line];
+
+ tty->driver_data = hp;
+ spin_lock_irqsave(&hp->lock, flags);
+ hp->tty = tty;
+ hp->count++;
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ return 0;
+}
+
+static void hvc_close(struct tty_struct *tty, struct file * filp)
+{
+ struct hvc_struct *hp = tty->driver_data;
+ unsigned long flags;
+
+ if (tty_hung_up_p(filp))
+ return;
+ spin_lock_irqsave(&hp->lock, flags);
+ if (--hp->count == 0)
+ hp->tty = NULL;
+ else if (hp->count < 0)
+ printk(KERN_ERR "hvc_close %lu: oops, count is %d\n",
+ hp - hvc_struct, hp->count);
+ spin_unlock_irqrestore(&hp->lock, flags);
+}
+
+/* called with hp->lock held */
+static void hvc_push(struct hvc_struct *hp)
+{
+ int n;
+
+ n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf);
+ if (n <= 0) {
+ if (n == 0)
+ return;
+ /* throw away output on error; this happens when
+ there is no session connected to the vterm. */
+ hp->n_outbuf = 0;
+ } else
+ hp->n_outbuf -= n;
+ if (hp->n_outbuf > 0)
+ memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
+ else
+ hp->do_wakeup = 1;
+}
+
+static int hvc_write(struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct hvc_struct *hp = tty->driver_data;
+ char *p;
+ int todo, written = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hp->lock, flags);
+ while (count > 0 && (todo = N_OUTBUF - hp->n_outbuf) > 0) {
+ if (todo > count)
+ todo = count;
+ p = hp->outbuf + hp->n_outbuf;
+ if (from_user) {
+ todo -= copy_from_user(p, buf, todo);
+ if (todo == 0) {
+ if (written == 0)
+ written = -EFAULT;
+ break;
+ }
+ } else
+ memcpy(p, buf, todo);
+ count -= todo;
+ buf += todo;
+ hp->n_outbuf += todo;
+ written += todo;
+ hvc_push(hp);
+ }
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ return written;
+}
+
+static int hvc_write_room(struct tty_struct *tty)
+{
+ struct hvc_struct *hp = tty->driver_data;
+
+ return N_OUTBUF - hp->n_outbuf;
+}
+
+static int hvc_chars_in_buffer(struct tty_struct *tty)
+{
+ struct hvc_struct *hp = tty->driver_data;
+
+ return hp->n_outbuf;
+}
+
+static void hvc_poll(int index)
+{
+ struct hvc_struct *hp = &hvc_struct[index];
+ struct tty_struct *tty;
+ int i, n;
+ char buf[16] __ALIGNED__;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hp->lock, flags);
+
+ if (hp->n_outbuf > 0)
+ hvc_push(hp);
+
+ tty = hp->tty;
+ if (tty) {
+ for (;;) {
+ if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf))
+ break;
+ n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf));
+ if (n <= 0)
+ break;
+ for (i = 0; i < n; ++i) {
+#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
+ if (buf[i] == '\x0f') { /* ^O -- should support a sequence */
+ sysrq_pressed = 1;
+ continue;
+ } else if (sysrq_pressed) {
+ handle_sysrq(buf[i], NULL, NULL, tty);
+ sysrq_pressed = 0;
+ continue;
+ }
+#endif
+ tty_insert_flip_char(tty, buf[i], 0);
+ }
+ }
+ if (tty->flip.count)
+ tty_schedule_flip(tty);
+
+ if (hp->do_wakeup) {
+ hp->do_wakeup = 0;
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ }
+
+ spin_unlock_irqrestore(&hp->lock, flags);
+}
+
+int khvcd(void *unused)
+{
+ int i;
+
+ daemonize();
+ reparent_to_init();
+ strcpy(current->comm, "khvcd");
+ sigfillset(¤t->blocked);
+
+ for (;;) {
+ for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i)
+ hvc_poll(i);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(TIMEOUT);
+ }
+}
+
+int __init hvc_init(void)
+{
+ int i;
+
+ memset(&hvc_driver, 0, sizeof(struct tty_driver));
+
+ hvc_driver.magic = TTY_DRIVER_MAGIC;
+ hvc_driver.driver_name = "hvc";
+ hvc_driver.name = "hvc/%d";
+ hvc_driver.major = HVC_MAJOR;
+ hvc_driver.minor_start = HVC_MINOR;
+ hvc_driver.num = hvc_count(&hvc_offset);
+ if (hvc_driver.num > MAX_NR_HVC_CONSOLES)
+ hvc_driver.num = MAX_NR_HVC_CONSOLES;
+ hvc_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+ hvc_driver.init_termios = tty_std_termios;
+ hvc_driver.flags = TTY_DRIVER_REAL_RAW;
+ hvc_driver.refcount = &hvc_refcount;
+ hvc_driver.table = hvc_table;
+ hvc_driver.termios = hvc_termios;
+ hvc_driver.termios_locked = hvc_termios_locked;
+
+ hvc_driver.open = hvc_open;
+ hvc_driver.close = hvc_close;
+ hvc_driver.write = hvc_write;
+ hvc_driver.write_room = hvc_write_room;
+ hvc_driver.chars_in_buffer = hvc_chars_in_buffer;
+
+ for (i = 0; i < hvc_driver.num; i++) {
+ hvc_struct[i].lock = SPIN_LOCK_UNLOCKED;
+ hvc_struct[i].index = i;
+ tty_register_devfs(&hvc_driver, 0, hvc_driver.minor_start + i);
+ }
+
+ if (tty_register_driver(&hvc_driver))
+ panic("Couldn't register hvc console driver\n");
+
+ if (hvc_driver.num > 0)
+ kernel_thread(khvcd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+
+ return 0;
+}
+
+static void __exit hvc_exit(void)
+{
+}
+
+void hvc_console_print(struct console *co, const char *b, unsigned count)
+{
+ char c[16] __ALIGNED__;
+ unsigned i, n;
+ int r, donecr = 0;
+
+ i = n = 0;
+ while (count > 0 || i > 0) {
+ if (count > 0 && i < sizeof(c)) {
+ if (b[n] == '\n' && !donecr) {
+ c[i++] = '\r';
+ donecr = 1;
+ } else {
+ c[i++] = b[n++];
+ donecr = 0;
+ --count;
+ }
+ } else {
+ r = hvc_put_chars(co->index + hvc_offset, c, i);
+ if (r < 0) {
+ /* throw away chars on error */
+ i = 0;
+ } else if (r > 0) {
+ i -= r;
+ if (i > 0)
+ memmove(c, c+r, i);
+ }
+ }
+ }
+}
+
+static kdev_t hvc_console_device(struct console *c)
+{
+ return MKDEV(HVC_MAJOR, HVC_MINOR + c->index);
+}
+
+int hvc_wait_for_keypress(struct console *co)
+{
+ char c[16] __ALIGNED__;
+
+ while (hvc_get_chars(co->index, &c[0], 1) < 1)
+ ;
+ return 0;
+}
+
+static int __init hvc_console_setup(struct console *co, char *options)
+{
+ if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES
+ || co->index >= hvc_count(&hvc_offset))
+ return -1;
+ return 0;
+}
+
+struct console hvc_con_driver = {
+ name: "hvc",
+ write: hvc_console_print,
+ device: hvc_console_device,
+ setup: hvc_console_setup,
+ flags: CON_PRINTBUFFER,
+ index: -1,
+};
+
+int __init hvc_console_init(void)
+{
+ register_console(&hvc_con_driver);
+ return 0;
+}
+
+module_init(hvc_init);
+module_exit(hvc_exit);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/icom.c linuxppc64_2_4/drivers/char/icom.c
--- ../kernel.org/linux-2.4.19/drivers/char/icom.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/char/icom.c Mon Apr 15 11:14:27 2002
@@ -0,0 +1,3311 @@
+/*
+ * iCom.c
+ *
+ * Copyright (C) 2001 Michael Anderson, IBM Corporation
+ *
+ * Serial device driver.
+ *
+ * Based on code from serial.c
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * NOTE: Only for users with internal modem cards (eg. iSeries 2771, 2772)
+ * find the decimal number associated with your country below
+ * and set country code operations using module parameter at install
+ * time, eg. for Argentina the command would be (be sure number is in "")
+ * insmod iCom.o iCom_country_code="52"
+ *
+ * Module paramter values for
+ * iCom_country_code:
+ 52 * AR Argentina *
+ 52 * AW Aruba *
+ 1 * AU Australia *
+ 52 * AT Austria *
+ 52 * BH Bahrain *
+ 52 * BE Belgium *
+ 52 * BR Brazil *
+ 52 * BN Brunei Darussalam *
+ 52 * CA Canada *
+ 52 * KY Cayman Islands *
+ 52 * CL Chile *
+ 52 * CN China *
+ 52 * CO Colombia *
+ 52 * CR Costa Rica *
+ 52 * HR Croatia *
+ 52 * CY Cyprus *
+ 37 * CZ Czech Republic *
+ 52 * DK Denmark *
+ 52 * EC Ecuador *
+ 52 * EG Egypt *
+ 52 * FI Finland *
+ 52 * FR France *
+ 52 * DE Germany *
+ 52 * GR Greece *
+ 52 * GT Guatemala *
+ 48 * HK China (Hong Kong S.A.R.) *
+ 48 * HU Hungary *
+ 52 * IS Iceland *
+ 48 * IN India *
+ 48 * ID Indonesia *
+ 52 * IE Ireland *
+ 48 * IL Israel *
+ 52 * IT Italy *
+ 52 * JM Jamaica *
+ 16 * JP Japan *
+ 52 * KR Korea, Republic of *
+ 52 * LU Luxembourg *
+ 52 * MO China (Macau S.A.R.) *
+ 48 * MY Malaysia *
+ 52 * MX Mexico *
+ 52 * MA Morocco *
+ 52 * NL Netherlands *
+ 52 * AN Netherlands Antilles *
+ 9 * NZ New Zealand *
+ 52 * NO Norway *
+ 52 * PK Pakistan *
+ 52 * PA Panama *
+ 52 * PE Peru *
+ 48 * PH Philippines *
+ 48 * PL Poland *
+ 52 * PT Portugal *
+ 52 * QA Qatar *
+ 52 * RO Romania *
+ 52 * RU Russia *
+ 52 * SA Saudi Arabia *
+ 48 * SG Singapore *
+ 52 * SK Slovakia *
+ 48 * SI Slovenia *
+ 53 * ZA South Africa *
+ 52 * ES Spain *
+ 52 * LK Sri Lanka *
+ 52 * SE Sweden *
+ 52 * CH Switzerland *
+ 52 * TW Taiwan *
+ 52 * TH Thailand *
+ 52 * TT Trinidad and Tobago *
+ 52 * TR Turkey *
+ 52 * UA Ukraine *
+ 52 * AE United Arab Emirates *
+ 52 * GB United Kingdom *
+ 52 * US United States of America*
+ 52 * UY Uruguay *
+ 52 * VE Venezuela *
+ 48 * VN Vietnam *
+*/
+#define SERIAL_DO_RESTART
+#ifdef MODVERSIONS
+#include
+#endif
+#include
+
+MODULE_AUTHOR ("Michael Anderson ");
+MODULE_DESCRIPTION ("IBM iSeries Serial IOA driver");
+MODULE_SUPPORTED_DEVICE("IBM iSeries 2745, 2771, 2772 Communications adapters");
+MODULE_LICENSE("GPL");
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* adapter code loads */
+#include "icom.h"
+
+#ifdef MODULE
+#define CONFIG_ICOM_MODEM_CC ""
+#endif
+
+static char *iCom_country_code = CONFIG_ICOM_MODEM_CC;
+MODULE_PARM(iCom_country_code, "s");
+MODULE_PARM_DESC(iCom_country_code, "Modem country code configuration");
+
+#define ICOM_TRACE /* enable port trace capabalities */
+
+#define DRIVER_NAME "iCom"
+#define VENDOR_ID 0x1014
+#define DEVICE_ID 0x0031
+#define DEVICE_ID2 0x0219
+#define MAX_ADAPTERS 4
+#define NR_PORTS (active_adapters * 4)
+#define MAX_PORTS (MAX_ADAPTERS * 4)
+#define ASYNC_CLOSING 0x08000000 /* Serial port is closing */
+#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+
+#ifdef MODULE
+static const struct pci_device_id iCom_pci_table[] __initdata =
+{
+ {
+ vendor: VENDOR_ID,
+ device: DEVICE_ID,
+ subvendor: 0xFFFF,
+ subdevice: 0xFFFF,
+ },
+ {
+ vendor: VENDOR_ID,
+ device: DEVICE_ID2,
+ subvendor: VENDOR_ID,
+ subdevice: 0x021a,
+ },
+ {
+ vendor: VENDOR_ID,
+ device: DEVICE_ID2,
+ subvendor: VENDOR_ID,
+ subdevice: 0x0251,
+ },
+ {
+ vendor: VENDOR_ID,
+ device: DEVICE_ID2,
+ subvendor: VENDOR_ID,
+ subdevice: 0x0252,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, iCom_pci_table);
+#endif
+
+/*
+ * adapter defines and structures
+ */
+#define ICOM_CONTROL_START_A 0x00000008
+#define ICOM_CONTROL_STOP_A 0x00000004
+#define ICOM_CONTROL_START_B 0x00000002
+#define ICOM_CONTROL_STOP_B 0x00000001
+#define ICOM_CONTROL_START_C 0x00000008
+#define ICOM_CONTROL_STOP_C 0x00000004
+#define ICOM_CONTROL_START_D 0x00000002
+#define ICOM_CONTROL_STOP_D 0x00000001
+#define ICOM_IRAM_OFFSET 0x1000
+#define ICOM_DCE_IRAM_OFFSET 0x0A00
+#define ICOM_CABLE_ID_VALID 0x01
+#define ICOM_CABLE_ID_MASK 0xF0
+#define ICOM_DISABLE 0x80
+#define CMD_XMIT_RCV_ENABLE 0xC0
+#define CMD_XMIT_ENABLE 0x40
+#define CMD_RCV_DISABLE 0x00
+#define CMD_RCV_ENABLE 0x80
+#define CMD_RESTART 0x01
+#define CMD_HOLD_XMIT 0x02
+#define CMD_SND_BREAK 0x04
+#define RS232_CABLE 0x06
+#define V24_CABLE 0x0E
+#define V35_CABLE 0x0C
+#define V36_CABLE 0x02
+#define START_DOWNLOAD 0x80
+#define ICOM_INT_MASK_PRC_A 0x00003FFF
+#define ICOM_INT_MASK_PRC_B 0x3FFF0000
+#define ICOM_INT_MASK_PRC_C 0x00003FFF
+#define ICOM_INT_MASK_PRC_D 0x3FFF0000
+#define INT_RCV_COMPLETED 0x1000
+#define INT_XMIT_COMPLETED 0x2000
+#define INT_IDLE_DETECT 0x0800
+#define INT_RCV_DISABLED 0x0400
+#define INT_XMIT_DISABLED 0x0200
+#define INT_RCV_XMIT_SHUTDOWN 0x0100
+#define INT_FATAL_ERROR 0x0080
+#define INT_CABLE_PULL 0x0020
+#define INT_SIGNAL_CHANGE 0x0010
+#define HDLC_PPP_PURE_ASYNC 0x02
+#define HDLC_FF_FILL 0x00
+#define HDLC_HDW_FLOW 0x01
+#define START_XMIT 0x80
+#define ICOM_ACFG_DRIVE1 0x20
+#define ICOM_ACFG_NO_PARITY 0x00
+#define ICOM_ACFG_PARITY_ENAB 0x02
+#define ICOM_ACFG_PARITY_ODD 0x01
+#define ICOM_ACFG_8BPC 0x00
+#define ICOM_ACFG_7BPC 0x04
+#define ICOM_ACFG_6BPC 0x08
+#define ICOM_ACFG_5BPC 0x0C
+#define ICOM_ACFG_1STOP_BIT 0x00
+#define ICOM_ACFG_2STOP_BIT 0x10
+#define DTR 0x80
+#define RTS 0x40
+#define RI 0x08
+#define DSR 0x80
+#define DCD 0x20
+#define CTS 0x40
+
+#define BAUD_TABLE_LIMIT 20
+static int icom_acfg_baud[] = {
+ 300,
+ 600,
+ 900,
+ 1200,
+ 1800,
+ 2400,
+ 3600,
+ 4800,
+ 7200,
+ 9600,
+ 14400,
+ 19200,
+ 28800,
+ 38400,
+ 57600,
+ 76800,
+ 115200,
+ 153600,
+ 230400,
+ 307200,
+ 460800};
+
+static int active_adapters;
+static struct tty_driver serial_driver;
+static int serial_refcount = 0;
+static struct tty_struct *serial_table[MAX_PORTS];
+static struct termios *serial_termios[MAX_PORTS];
+static struct termios *serial_termios_locked[MAX_PORTS];
+
+struct iCom_regs {
+ u32 control; /* Adapter Control Register */
+ u32 interrupt; /* Adapter Interrupt Register */
+ u32 int_mask; /* Adapter Interrupt Mask Reg */
+ u32 int_pri; /* Adapter Interrupt Priority r */
+ u32 int_reg_b; /* Adapter non-masked Interrupt */
+ u32 resvd01;
+ u32 resvd02;
+ u32 resvd03;
+ u32 control_2; /* Adapter Control Register 2 */
+ u32 interrupt_2; /* Adapter Interrupt Register 2 */
+ u32 int_mask_2; /* Adapter Interrupt Mask 2 */
+ u32 int_pri_2; /* Adapter Interrupt Prior 2 */
+ u32 int_reg_2b; /* Adapter non-masked 2 */
+};
+
+struct func_dram {
+ u32 reserved[108]; /* 0-1B0 reserved by personality code */
+ u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */
+ u8 RcvStnAddr; /* 1B4 Receive Station Addr */
+ u8 IdleState; /* 1B5 Idle State */
+ u8 IdleMonitor; /* 1B6 Idle Monitor */
+ u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */
+ u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */
+ u8 StartXmitCmd; /* 1BC Start Xmit Command */
+ u8 HDLCConfigReg; /* 1BD Reserved */
+ u8 CauseCode; /* 1BE Cause code for fatal error */
+ u8 xchar; /* 1BF High priority send */
+ u32 reserved3; /* 1C0-1C3 Reserved */
+ u8 PrevCmdReg; /* 1C4 Reserved */
+ u8 CmdReg; /* 1C5 Command Register */
+ u8 async_config2; /* 1C6 Async Config Byte 2*/
+ u8 async_config3; /* 1C7 Async Config Byte 3*/
+ u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */
+ u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte*/
+ u8 misc_flags; /* 1DD misc flags */
+#define V2_HARDWARE 0x40
+ u8 call_length; /* 1DE Phone #/CFI buff ln*/
+ u8 call_length2; /* 1DF Upper byte (unused)*/
+ u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr*/
+ u16 timer_value; /* 1E4-1E5 general timer value*/
+ u8 timer_command; /* 1E6 general timer cmd */
+ u8 dce_command; /* 1E7 dce command reg */
+ u8 dce_cmd_status; /* 1E8 dce command stat */
+ u8 x21_r1_ioff; /* 1E9 dce ready counter */
+ u8 x21_r0_ioff; /* 1EA dce not ready ctr */
+ u8 x21_ralt_ioff; /* 1EB dce CNR counter */
+ u8 x21_r1_ion; /* 1EC dce ready I on ctr */
+ u8 rsvd_ier; /* 1ED Rsvd for IER (if ne*/
+ u8 ier; /* 1EE Interrupt Enable */
+ u8 isr; /* 1EF Input Signal Reg */
+ u8 osr; /* 1F0 Output Signal Reg */
+ u8 reset; /* 1F1 Reset/Reload Reg */
+ u8 disable; /* 1F2 Disable Reg */
+ u8 sync; /* 1F3 Sync Reg */
+ u8 error_stat; /* 1F4 Error Status */
+ u8 cable_id; /* 1F5 Cable ID */
+ u8 cs_length; /* 1F6 CS Load Length */
+ u8 mac_length; /* 1F7 Mac Load Length */
+ u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */
+ u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */
+};
+
+#define NUM_XBUFFS 1
+#define NUM_RBUFFS 2
+#define RCV_BUFF_SZ 0x0200
+#define XMIT_BUFF_SZ 0x1000
+struct statusArea
+{
+ /**********************************************/
+ /* Transmit Status Area */
+ /**********************************************/
+ struct {
+ u32 leNext; /* Next entry in Little Endian on Adapter */
+ u32 leNextASD;
+ u32 leBuffer; /* Buffer for entry in LE for Adapter */
+ u16 leLengthASD;
+ u16 leOffsetASD;
+ u16 leLength; /* Length of data in segment */
+ u16 flags;
+#define SA_FLAGS_DONE 0x0080 /* Done with Segment */
+#define SA_FLAGS_CONTINUED 0x8000 /* More Segments */
+#define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */
+#define SA_FLAGS_READY_TO_XMIT 0x0800
+#define SA_FLAGS_STAT_MASK 0x007F
+ } xmit[NUM_XBUFFS];
+
+ /**********************************************/
+ /* Receive Status Area */
+ /**********************************************/
+ struct {
+ u32 leNext; /* Next entry in Little Endian on Adapter */
+ u32 leNextASD;
+ u32 leBuffer; /* Buffer for entry in LE for Adapter */
+ u16 WorkingLength; /* size of segment */
+ u16 reserv01;
+ u16 leLength; /* Length of data in segment */
+ u16 flags;
+#define SA_FL_RCV_DONE 0x0010 /* Data ready */
+#define SA_FLAGS_OVERRUN 0x0040
+#define SA_FLAGS_PARITY_ERROR 0x0080
+#define SA_FLAGS_FRAME_ERROR 0x0001
+#define SA_FLAGS_FRAME_TRUNC 0x0002
+#define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */
+#define SA_FLAGS_RCV_MASK 0xFFE6
+ } rcv[NUM_RBUFFS];
+};
+
+struct iCom_port {
+ int open_active_count;
+ struct tty_struct *tty;
+ unsigned long int event;
+ struct tq_struct tqueue;
+ int flags;
+ int xmit_fifo_size;
+ int baud_base;
+ wait_queue_head_t close_wait;
+ wait_queue_head_t open_wait;
+ wait_queue_head_t delta_msr_wait;
+ int blocked_open;
+ unsigned short close_delay;
+ unsigned short closing_wait;
+ unsigned long int timeout;
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char read_status_mask;
+ unsigned char ignore_status_mask;
+ struct async_icount icount;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ unsigned long int int_reg;
+ struct iCom_regs *global_reg;
+ struct func_dram *dram;
+ int adapter;
+ int port;
+ struct statusArea *statStg;
+ dma_addr_t statStg_pci;
+ u32 *xmitRestart;
+ dma_addr_t xmitRestart_pci;
+ unsigned char *xmit_buf;
+ dma_addr_t xmit_buf_pci;
+ unsigned char *recv_buf;
+ dma_addr_t recv_buf_pci;
+ int next_rcv;
+ int put_length;
+ int status;
+#define ICOM_PORT_ACTIVE 1
+#define ICOM_PORT_OFF 0
+ unsigned long *trace_blk;
+};
+
+static struct iCom_adapter {
+ unsigned long int base_addr;
+ unsigned char irq_number;
+ struct pci_dev *pci_dev;
+ struct iCom_port port_info[4];
+ int version;
+#define ADAPTER_V1 0x0001
+#define ADAPTER_V2 0x0002
+ unsigned long int subsystem_id;
+#define FOUR_PORT_MODEL 0x02521014
+ int numb_ports;
+} *iCom_adapter_info;
+
+
+static DECLARE_MUTEX(tmp_buf_sem);
+
+static spinlock_t iComlock;
+
+/*
+ Utility functions
+*/
+static void return_port_memory(struct iCom_port *iCom_port_info);
+static void iCom_wait_until_sent(struct tty_struct *tty, int timeout);
+static void do_softint(void *);
+static void iCom_start(struct tty_struct * tty);
+static void iCom_flush_buffer(struct tty_struct * tty);
+static void iCom_set_code(struct iCom_port *iCom_port_info);
+#ifdef ICOM_TRACE
+static void TRACE(struct iCom_port *,u32 , u32);
+#else
+#define TRACE(x,y,z) /* nub out calls to TRACE function */
+#endif
+
+#ifdef CONFIG_PPC64
+extern int register_ioctl32_conversion(unsigned int cmd,
+ int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
+extern int unregister_ioctl32_conversion(unsigned int cmd);
+#else
+static inline int register_ioctl32_conversion(unsigned int cmd,
+ int (*handler)(unsigned int,
+ unsigned int, unsigned long, struct file *))
+{
+ return 0;
+}
+static inline int unregister_ioctl32_conversion(unsigned int cmd)
+{
+ return 0;
+}
+#endif
+
+static u8 iCom_readb(void *address)
+{
+ /* Issue a 'write memory barrier' prior to the mmio to ensure ordering */
+ /* This translates to an eieio instruction on ppc */
+ wmb();
+ return readb(address);
+}
+
+static void iCom_writeb(u8 value, void *address)
+{
+ /* Issue a 'memory barrier' prior to the mmio to ensure data is flushed
+ to memory. This translates to a sync instruction on ppc */
+ mb();
+ writeb(value, address);
+}
+
+static u16 iCom_readw(void *address)
+{
+ /* Issue a 'write memory barrier' prior to the mmio to ensure ordering */
+ /* This translates to an eieio instruction on ppc */
+ wmb();
+ return readw(address);
+}
+
+static void iCom_writew(u16 value, void *address)
+{
+ /* Issue a 'memory barrier' prior to the mmio to ensure data is flushed
+ to memory. This translates to a sync instruction on ppc */
+ mb();
+ writew(value, address);
+}
+
+static u32 iCom_readl(void *address)
+{
+ /* Issue a 'write memory barrier' prior to the mmio to ensure ordering */
+ /* This translates to an eieio instruction on ppc */
+ wmb();
+ return readl(address);
+}
+
+static void iCom_writel(u32 value, void *address)
+{
+ /* Issue a 'memory barrier' prior to the mmio to ensure data is flushed
+ to memory. This translates to a sync instruction on ppc */
+ mb();
+ writel(value, address);
+}
+
+static int get_port_memory(struct iCom_port *iCom_port_info)
+{
+ int index;
+ int number_of_buffs;
+ unsigned long int stgAddr;
+ unsigned long int startStgAddr;
+ unsigned long int offset;
+
+ TRACE(iCom_port_info,TRACE_GET_PORT_MEM,0);
+
+ iCom_port_info->xmit_buf = (unsigned char *)kmalloc(4096,GFP_KERNEL | GFP_DMA);
+ iCom_port_info->xmit_buf_pci = pci_map_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ (void *)iCom_port_info->xmit_buf,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+
+ if (!iCom_port_info->xmit_buf) {
+ printk("iCom: ERROR, Can not allocate Transmit buffer\n");
+ return -ENOMEM;
+ }
+ TRACE(iCom_port_info,TRACE_GET_PORT_MEM,(unsigned long)iCom_port_info->xmit_buf);
+
+ iCom_port_info->recv_buf = (unsigned char *)kmalloc(4096,GFP_KERNEL | GFP_DMA);
+ iCom_port_info->recv_buf_pci = pci_map_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ (void *)iCom_port_info->recv_buf,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+
+ if (!iCom_port_info->recv_buf) {
+ printk("iCom: ERROR, Can not allocate Receive buffer\n");
+ return_port_memory(iCom_port_info);
+ return -ENOMEM;
+ }
+ TRACE(iCom_port_info,TRACE_GET_PORT_MEM,(unsigned long)iCom_port_info->recv_buf);
+
+ iCom_port_info->statStg = (struct statusArea *)kmalloc(4096,GFP_KERNEL | GFP_DMA);
+ iCom_port_info->statStg_pci = pci_map_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ (void *)iCom_port_info->statStg,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+
+ if (!iCom_port_info->statStg) {
+ printk("iCom: ERROR, Can not allocate Status buffer\n");
+ return_port_memory(iCom_port_info);
+ return -ENOMEM;
+ }
+ TRACE(iCom_port_info,TRACE_GET_PORT_MEM,(unsigned long)iCom_port_info->statStg);
+
+ iCom_port_info->xmitRestart = (u32 *)kmalloc(sizeof(u32),GFP_KERNEL | GFP_DMA);
+ iCom_port_info->xmitRestart_pci = pci_map_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ iCom_port_info->xmitRestart,
+ 4,
+ PCI_DMA_BIDIRECTIONAL);
+
+ if (!iCom_port_info->xmitRestart) {
+ printk("iCom: ERROR, Can not allocate xmit Restart buffer\n");
+ return_port_memory(iCom_port_info);
+ return -ENOMEM;
+ }
+
+ memset(iCom_port_info->statStg, 0,4096);
+
+ /* FODs */
+ number_of_buffs = NUM_XBUFFS;
+ stgAddr = (unsigned long int)iCom_port_info->statStg;
+ startStgAddr = stgAddr;
+ for (index = 0; index < number_of_buffs; index++)
+ {
+ TRACE(iCom_port_info,TRACE_FOD_ADDR,stgAddr);
+ stgAddr = stgAddr + sizeof(iCom_port_info->statStg->xmit[0]);
+ if (index < (number_of_buffs - 1))
+ {
+ iCom_port_info->statStg->xmit[index].flags = 0;
+ iCom_port_info->statStg->xmit[index].leNext = 0;
+ iCom_port_info->statStg->xmit[index].leNextASD = 0;
+ iCom_port_info->statStg->xmit[index].leLengthASD = (unsigned short int)cpu_to_le16(XMIT_BUFF_SZ);
+ iCom_port_info->statStg->xmit[index].leOffsetASD = 0;
+ TRACE(iCom_port_info,TRACE_FOD_ADDR,stgAddr);
+ TRACE(iCom_port_info,TRACE_FOD_XBUFF,(unsigned long)iCom_port_info->xmit_buf);
+ iCom_port_info->statStg->xmit[index].leBuffer = cpu_to_le32(iCom_port_info->xmit_buf_pci);
+ }
+ else if (index == (number_of_buffs - 1))
+ {
+ iCom_port_info->statStg->xmit[index].flags = 0;
+ iCom_port_info->statStg->xmit[index].leNext = 0;
+ iCom_port_info->statStg->xmit[index].leNextASD = 0;
+ iCom_port_info->statStg->xmit[index].leLengthASD = (unsigned short int)cpu_to_le16(XMIT_BUFF_SZ);
+ iCom_port_info->statStg->xmit[index].leOffsetASD = 0;
+ TRACE(iCom_port_info,TRACE_FOD_XBUFF,(unsigned long)iCom_port_info->xmit_buf);
+ iCom_port_info->statStg->xmit[index].leBuffer = cpu_to_le32(iCom_port_info->xmit_buf_pci);
+ }
+ else
+ {
+ iCom_port_info->statStg->xmit[index].flags = 0;
+ iCom_port_info->statStg->xmit[index].leNext = 0;
+ iCom_port_info->statStg->xmit[index].leNextASD = 0;
+ iCom_port_info->statStg->xmit[index].leLengthASD = 0;
+ iCom_port_info->statStg->xmit[index].leOffsetASD = 0;
+ iCom_port_info->statStg->xmit[index].leBuffer = 0;
+ }
+ }
+ /* FIDs */
+ startStgAddr = stgAddr;
+
+ /* fill in every entry, even if no buffer */
+ number_of_buffs = NUM_RBUFFS;
+ for (index = 0; index < number_of_buffs; index++)
+ {
+ TRACE(iCom_port_info,TRACE_FID_ADDR,stgAddr);
+ stgAddr = stgAddr + sizeof(iCom_port_info->statStg->rcv[0]);
+ iCom_port_info->statStg->rcv[index].leLength = 0;
+ iCom_port_info->statStg->rcv[index].WorkingLength = (unsigned short int)cpu_to_le16(RCV_BUFF_SZ);
+ if (index < (number_of_buffs - 1))
+ {
+ offset = stgAddr - (unsigned long)iCom_port_info->statStg;
+ iCom_port_info->statStg->rcv[index].leNext = (unsigned long)cpu_to_le32(iCom_port_info->statStg_pci + offset);
+ TRACE(iCom_port_info,TRACE_FID_RBUFF,(unsigned long)iCom_port_info->recv_buf);
+ iCom_port_info->statStg->rcv[index].leBuffer = cpu_to_le32(iCom_port_info->recv_buf_pci);
+ }
+ else if (index == (number_of_buffs - 1))
+ {
+ offset = startStgAddr - (unsigned long)iCom_port_info->statStg;
+ iCom_port_info->statStg->rcv[index].leNext = (unsigned long)cpu_to_le32(iCom_port_info->statStg_pci + offset);
+ TRACE(iCom_port_info,TRACE_FID_RBUFF,(unsigned long)iCom_port_info->recv_buf + 2048);
+ iCom_port_info->statStg->rcv[index].leBuffer = cpu_to_le32(iCom_port_info->recv_buf_pci + 2048);
+ }
+ else
+ {
+ iCom_port_info->statStg->rcv[index].leNext = 0;
+ iCom_port_info->statStg->rcv[index].leBuffer = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void return_port_memory(struct iCom_port *iCom_port_info)
+{
+ TRACE(iCom_port_info, TRACE_RET_PORT_MEM,0);
+ if (iCom_port_info->recv_buf) {
+ pci_unmap_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ iCom_port_info->recv_buf_pci,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+ kfree((void *)iCom_port_info->recv_buf);
+ iCom_port_info->recv_buf = 0;
+ }
+ if (iCom_port_info->xmit_buf) {
+ pci_unmap_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ iCom_port_info->xmit_buf_pci,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+ kfree((void *)iCom_port_info->xmit_buf);
+ iCom_port_info->xmit_buf = 0;
+ }
+ if (iCom_port_info->statStg) {
+ pci_unmap_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ iCom_port_info->statStg_pci,
+ 4096,
+ PCI_DMA_BIDIRECTIONAL);
+ kfree((void *)iCom_port_info->statStg);
+ iCom_port_info->statStg = 0;
+ }
+
+ if (iCom_port_info->xmitRestart) {
+ pci_unmap_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,
+ iCom_port_info->xmitRestart_pci,
+ 4,
+ PCI_DMA_BIDIRECTIONAL);
+ kfree(iCom_port_info->xmitRestart);
+ iCom_port_info->xmitRestart = 0;
+ }
+ TRACE(iCom_port_info,TRACE_RET_MEM,0);
+}
+
+static void stop_processor(struct iCom_port *iCom_port_info)
+{
+ unsigned long temp;
+
+ switch (iCom_port_info->port) {
+ case 0:
+ temp = iCom_readl(&iCom_port_info->global_reg->control);
+ temp = (temp & ~ICOM_CONTROL_START_A) | ICOM_CONTROL_STOP_A;
+ iCom_writel(temp,&iCom_port_info->global_reg->control);
+ TRACE(iCom_port_info,TRACE_STOP_PROC_A,0);
+ break;
+ case 1:
+ temp = iCom_readl(&iCom_port_info->global_reg->control);
+ temp = (temp & ~ICOM_CONTROL_START_B) | ICOM_CONTROL_STOP_B;
+ iCom_writel(temp,&iCom_port_info->global_reg->control);
+ TRACE(iCom_port_info,TRACE_STOP_PROC_B,0);
+ break;
+ case 2:
+ temp = iCom_readl(&iCom_port_info->global_reg->control_2);
+ temp = (temp & ~ICOM_CONTROL_START_C) | ICOM_CONTROL_STOP_C;
+ iCom_writel(temp,&iCom_port_info->global_reg->control_2);
+ TRACE(iCom_port_info,TRACE_STOP_PROC_C,0);
+ break;
+ case 3:
+ temp = iCom_readl(&iCom_port_info->global_reg->control_2);
+ temp = (temp & ~ICOM_CONTROL_START_D) | ICOM_CONTROL_STOP_D;
+ iCom_writel(temp,&iCom_port_info->global_reg->control_2);
+ TRACE(iCom_port_info,TRACE_STOP_PROC_D,0);
+ break;
+ default:
+ printk("iCom: ERROR: invalid port assignment\n");
+ }
+}
+
+static void start_processor(struct iCom_port *iCom_port_info)
+{
+ unsigned long temp;
+
+ switch (iCom_port_info->port) {
+ case 0:
+ temp = iCom_readl(&iCom_port_info->global_reg->control);
+ temp = (temp & ~ICOM_CONTROL_STOP_A) | ICOM_CONTROL_START_A;
+ iCom_writel(temp,&iCom_port_info->global_reg->control);
+ TRACE(iCom_port_info,TRACE_START_PROC_A,0);
+ break;
+ case 1:
+ temp = iCom_readl(&iCom_port_info->global_reg->control);
+ temp = (temp & ~ICOM_CONTROL_STOP_B) | ICOM_CONTROL_START_B;
+ iCom_writel(temp,&iCom_port_info->global_reg->control);
+ TRACE(iCom_port_info,TRACE_START_PROC_B,0);
+ break;
+ case 2:
+ temp = iCom_readl(&iCom_port_info->global_reg->control_2);
+ temp = (temp & ~ICOM_CONTROL_STOP_C) | ICOM_CONTROL_START_C;
+ iCom_writel(temp,&iCom_port_info->global_reg->control_2);
+ TRACE(iCom_port_info,TRACE_START_PROC_C,0);
+ break;
+ case 3:
+ temp = iCom_readl(&iCom_port_info->global_reg->control_2);
+ temp = (temp & ~ICOM_CONTROL_STOP_D) | ICOM_CONTROL_START_D;
+ iCom_writel(temp,&iCom_port_info->global_reg->control_2);
+ TRACE(iCom_port_info,TRACE_START_PROC_D,0);
+ break;
+ default:
+ printk("iCom: ERROR: invalid port assignment\n");
+ }
+}
+
+/*
+ * irq = no lock
+ */
+static int loadCode (struct iCom_port *iCom_port_info)
+{
+ char *iram_ptr;
+ int index;
+ int status = 0;
+ char *dram_ptr = (char *)iCom_port_info->dram;
+ unsigned long int temp;
+ unsigned char *new_page;
+
+ TRACE(iCom_port_info,TRACE_GET_MEM,0); /* this really gets memory for trace */
+ TRACE(iCom_port_info,TRACE_LOAD_MEM,0);
+
+ /* Clear out any pending interrupts */
+ iCom_writew(0x3FFF,(void *)iCom_port_info->int_reg);
+
+ TRACE(iCom_port_info,TRACE_CLEAR_INTERRUPTS,0);
+
+ /* Stop processor */
+ stop_processor(iCom_port_info);
+
+ /* Zero out DRAM */
+ for (index = 0; index < 512; index++)
+ {
+ iCom_writeb(0x00,&dram_ptr[index]);
+ }
+
+ /* Load Call Setup into Adapter */
+ iram_ptr = (char *)iCom_port_info->dram + ICOM_IRAM_OFFSET;
+ for (index = 0; index < sizeof(callSetup); index++)
+ {
+ iCom_writeb(callSetup[index],&iram_ptr[index]);
+ }
+
+ /* Load Resident DCE portion of Adapter */
+ iram_ptr = (char *) iCom_port_info->dram + ICOM_IRAM_OFFSET +
+ ICOM_DCE_IRAM_OFFSET;
+
+ /* Load the RV dce code */
+ for (index = 0; index < sizeof(resRVdce); index++) {
+ iCom_writeb(resRVdce[index],&iram_ptr[index]);
+ }
+
+ /* Set Hardware level */
+ if ((iCom_adapter_info[iCom_port_info->adapter].version | ADAPTER_V2) == ADAPTER_V2) {
+ iCom_writeb(V2_HARDWARE,&(iCom_port_info->dram->misc_flags));
+ }
+
+ /* Start the processor in Adapter */
+ start_processor(iCom_port_info);
+
+ /* Wait 0.1 Sec for simple Init to complete */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ /*
+ * Verify Code is running
+ */
+ status = 0;
+
+ iCom_writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL),&(iCom_port_info->dram->HDLCConfigReg));
+ iCom_writeb(0x04,&(iCom_port_info->dram->FlagFillIdleTimer)); /* 0.5 seconds */
+ iCom_writeb(0x00,&(iCom_port_info->dram->CmdReg));
+ iCom_writeb(0x10,&(iCom_port_info->dram->async_config3));
+ iCom_writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC | ICOM_ACFG_1STOP_BIT),&(iCom_port_info->dram->async_config2));
+
+ /*Set up data in iCom DRAM to indicate where personality
+ *code is located and its length.
+ */
+ new_page = (unsigned char *)kmalloc(4096,GFP_KERNEL | GFP_DMA);
+ for (index = 0; index < sizeof(funcLoad); index++) {
+ new_page[index] = funcLoad[index];
+ }
+ temp = pci_map_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,new_page,4096,PCI_DMA_BIDIRECTIONAL);
+
+ iCom_writeb((char)(sizeof(funcLoad)/16),&iCom_port_info->dram->mac_length);
+ iCom_writel(temp,&iCom_port_info->dram->mac_load_addr);
+
+ /*Setting the syncReg to 0x80 causes adapter to start downloading
+ the personality code into adapter instruction RAM.
+ Once code is loaded, it will begin executing and, based on
+ information provided above, will start DMAing data from
+ shared memory to adapter DRAM.
+ */
+ iCom_writeb(START_DOWNLOAD,&iCom_port_info->dram->sync);
+
+ /* Wait 1 Sec for data download */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ);
+ pci_unmap_single(iCom_adapter_info[iCom_port_info->adapter].pci_dev,temp,4096,PCI_DMA_BIDIRECTIONAL);
+ kfree(new_page);
+
+ if (status != 0)
+ {
+ /* Clear out any pending interrupts */
+ iCom_writew(0x3FFF,(void *)iCom_port_info->int_reg);
+
+ /* Turn off port */
+ iCom_writeb(ICOM_DISABLE,&iCom_port_info->dram->disable);
+ }
+
+ return status;
+}
+
+/*
+ * This routine is called to set the port to match
+ * the specified baud rate for a serial port.
+ * irq = locked
+ */
+static void change_speed(struct iCom_port *iCom_port_info,
+ struct termios *old_termios, unsigned long flags)
+{
+ int baud;
+ unsigned cflag;
+ int bits;
+ char new_config2;
+ char new_config3;
+ char tmp_byte;
+ int index;
+ int rcv_buff,xmit_buff;
+ unsigned long int offset;
+
+ TRACE(iCom_port_info,TRACE_CHANGE_SPEED | TRACE_TIME,jiffies);
+
+ if (!iCom_port_info->tty || !iCom_port_info->tty->termios)
+ return;
+ cflag = iCom_port_info->tty->termios->c_cflag;
+
+ new_config2 = ICOM_ACFG_DRIVE1;
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5: /* 5 bits/char */
+ new_config2 |= ICOM_ACFG_5BPC;
+ bits = 7;
+ break;
+ case CS6: /* 6 bits/char */
+ new_config2 |= ICOM_ACFG_6BPC;
+ bits = 8;
+ break;
+ case CS7: /* 7 bits/char */
+ new_config2 |= ICOM_ACFG_7BPC;
+ bits = 9;
+ break;
+ case CS8: /* 8 bits/char */
+ new_config2 |= ICOM_ACFG_8BPC;
+ bits = 10;
+ break;
+ default: bits = 10; break;
+ }
+ if (cflag & CSTOPB) {
+ /* 2 stop bits */
+ new_config2 |= ICOM_ACFG_2STOP_BIT;
+ bits++;
+ }
+ if (cflag & PARENB) {
+ /* parity bit enabled */
+ new_config2 |= ICOM_ACFG_PARITY_ENAB;
+ TRACE(iCom_port_info, TRACE_PARENB,0);
+ bits++;
+ }
+ if (cflag & PARODD) {
+ /* odd parity */
+ new_config2 |= ICOM_ACFG_PARITY_ODD;
+ TRACE(iCom_port_info, TRACE_PARODD,0);
+ }
+
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(iCom_port_info->tty);
+ if (!baud)
+ baud = 9600; /* B0 transition handled in rs_set_termios */
+
+ for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
+ if (icom_acfg_baud[index] == baud) {
+ new_config3 = index;
+ break;
+ }
+ }
+
+ iCom_port_info->timeout = XMIT_BUFF_SZ*HZ*bits/baud;
+ iCom_port_info->timeout += HZ/50; /* Add .02 seconds of slop */
+
+ /* CTS flow control flag and modem status interrupts */
+ if (cflag & CRTSCTS) {
+ iCom_port_info->flags |= ASYNC_CTS_FLOW;
+ tmp_byte = iCom_readb(&(iCom_port_info->dram->HDLCConfigReg));
+ tmp_byte |= HDLC_HDW_FLOW;
+ iCom_writeb(tmp_byte, &(iCom_port_info->dram->HDLCConfigReg));
+ } else {
+ iCom_port_info->flags &= ~ASYNC_CTS_FLOW;
+ tmp_byte = iCom_readb(&(iCom_port_info->dram->HDLCConfigReg));
+ tmp_byte &= ~HDLC_HDW_FLOW;
+ iCom_writeb(tmp_byte, &(iCom_port_info->dram->HDLCConfigReg));
+ }
+ if (cflag & CLOCAL)
+ iCom_port_info->flags &= ~ASYNC_CHECK_CD;
+ else {
+ iCom_port_info->flags |= ASYNC_CHECK_CD;
+ }
+
+ /*
+ * Set up parity check flag
+ */
+ iCom_port_info->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE;
+ if (I_INPCK(iCom_port_info->tty))
+ iCom_port_info->read_status_mask |= SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR;
+
+ if (I_BRKINT(iCom_port_info->tty) || I_PARMRK(iCom_port_info->tty))
+ iCom_port_info->read_status_mask |= SA_FLAGS_BREAK_DET;
+
+ /*
+ * Characters to ignore
+ */
+ iCom_port_info->ignore_status_mask = 0;
+ if (I_IGNPAR(iCom_port_info->tty))
+ iCom_port_info->ignore_status_mask |= SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR;
+ if (I_IGNBRK(iCom_port_info->tty)) {
+ iCom_port_info->ignore_status_mask |= SA_FLAGS_BREAK_DET;
+ /*
+ * If we're ignore parity and break indicators, ignore
+ * overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(iCom_port_info->tty))
+ iCom_port_info->ignore_status_mask |= SA_FLAGS_OVERRUN;
+ }
+
+ /*
+ * !!! ignore all characters if CREAD is not set
+ */
+ if ((cflag & CREAD) == 0)
+ iCom_port_info->ignore_status_mask |= SA_FL_RCV_DONE;
+
+ /* Turn off Receiver to prepare for reset */
+ iCom_writeb(CMD_RCV_DISABLE,&iCom_port_info->dram->CmdReg);
+
+ spin_unlock_irqrestore(&iComlock,flags);
+ for (index = 0; index < 10; index++) {
+ /* Wait 0.1 Sec for receive operations to complete*/
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ if (iCom_readb(&iCom_port_info->dram->PrevCmdReg) == 0x00) {
+ break;
+ }
+ }
+ spin_lock_irqsave(&iComlock, flags);
+
+ /* clear all current buffers of data */
+ for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
+ iCom_port_info->statStg->rcv[rcv_buff].flags = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].leLength = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].WorkingLength = (unsigned short int)cpu_to_le16(RCV_BUFF_SZ);
+ }
+
+ for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) {
+ iCom_port_info->statStg->xmit[xmit_buff].flags = 0;
+ }
+
+ /* activate changes and start xmit and receiver here */
+ /* Enable the receiver */
+ iCom_writeb(new_config3,&(iCom_port_info->dram->async_config3));
+ iCom_writeb(new_config2,&(iCom_port_info->dram->async_config2));
+ tmp_byte = iCom_readb(&(iCom_port_info->dram->HDLCConfigReg));
+ tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL;
+ iCom_writeb(tmp_byte,&(iCom_port_info->dram->HDLCConfigReg));
+ iCom_writeb(0x04, &(iCom_port_info->dram->FlagFillIdleTimer)); /* 0.5 seconds */
+ iCom_writeb(0xFF, &(iCom_port_info->dram->ier)); /* enable modem signal interrupts */
+
+ /* reset processor */
+ iCom_writeb(CMD_RESTART,&iCom_port_info->dram->CmdReg);
+ spin_unlock_irqrestore(&iComlock, flags);
+ for (index = 0; index < 10; index++) {
+ /* Wait for reset operation */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ if (iCom_readb(&iCom_port_info->dram->CmdReg) == 0x00) {
+ break;
+ }
+ }
+ spin_lock_irqsave(&iComlock, flags);
+
+ /* Enable Transmitter and Reciever */
+ offset = (unsigned long int)&iCom_port_info->statStg->rcv[0] - (unsigned long int)iCom_port_info->statStg;
+ iCom_writel(iCom_port_info->statStg_pci + offset,&iCom_port_info->dram->RcvStatusAddr);
+ iCom_port_info->next_rcv = 0;
+ iCom_port_info->put_length = 0;
+ *iCom_port_info->xmitRestart = 0;
+ iCom_writel(iCom_port_info->xmitRestart_pci,&iCom_port_info->dram->XmitStatusAddr);
+ TRACE(iCom_port_info,TRACE_XR_ENAB,0);
+ iCom_writeb(CMD_XMIT_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+}
+
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct iCom_port *iCom_port_info, long int flags)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+
+ /*
+ * 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) ||
+ (iCom_port_info->flags & ASYNC_CLOSING)) {
+ if (iCom_port_info->flags & ASYNC_CLOSING) {
+ spin_unlock_irqrestore(&iComlock,flags);
+ interruptible_sleep_on(&iCom_port_info->close_wait);
+ spin_lock_irqsave(&iComlock,flags);
+ }
+#ifdef SERIAL_DO_RESTART
+ return ((iCom_port_info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (iCom_port_info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (iCom_port_info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (iCom_port_info->session != current->session))
+ return -EBUSY;
+ if ((iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (iCom_port_info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (iCom_port_info->pgrp != current->pgrp))
+ return -EBUSY;
+ iCom_port_info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ iCom_port_info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (iCom_port_info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, open_active_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(&iCom_port_info->open_wait, &wait);
+
+ if (!tty_hung_up_p(filp)) {
+ extra_count = 1;
+ iCom_port_info->open_active_count--;
+ }
+ iCom_port_info->blocked_open++;
+ while (1) {
+ if (!(iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ /* raise DTR and RTS */
+ TRACE(iCom_port_info,TRACE_RAISE_DTR_RTS,0);
+ iCom_writeb(0xC0,&iCom_port_info->dram->osr);
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(iCom_port_info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (iCom_port_info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+
+ if (!(iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(iCom_port_info->flags & ASYNC_CLOSING) &&
+ (do_clocal || (iCom_readb(&iCom_port_info->dram->isr) & 0x20))) /* 0x20 = Carrier Detect */
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ spin_unlock_irqrestore(&iComlock,flags);
+ printk("iCom: WAIT for CD\n");
+ schedule();
+ spin_lock_irqsave(&iComlock,flags);
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&iCom_port_info->open_wait, &wait);
+ if (extra_count)
+ iCom_port_info->open_active_count++;
+ iCom_port_info->blocked_open--;
+
+ if (retval)
+ return retval;
+ iCom_port_info->flags |= ASYNC_NORMAL_ACTIVE;
+
+ return 0;
+}
+
+static int startup(struct iCom_port *iCom_port_info, unsigned long flags)
+{
+ int retval=0;
+ unsigned long int temp;
+ unsigned char cable_id;
+
+ TRACE(iCom_port_info,TRACE_STARTUP,0);
+
+ if (iCom_port_info->flags & ASYNC_INITIALIZED) {
+ goto errout;
+ }
+
+ /*
+ * check Cable ID
+ */
+ cable_id = iCom_readb(&iCom_port_info->dram->cable_id);
+ TRACE(iCom_port_info,TRACE_CABLE_ID,cable_id);
+ if (cable_id & ICOM_CABLE_ID_VALID)
+ {
+ /* Get cable ID into the lower 4 bits (standard form) */
+ cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4;
+
+ /* Check Cable ID valid */
+ if ((cable_id == RS232_CABLE) || (cable_id == V24_CABLE) ||
+ (cable_id == V35_CABLE) || (cable_id == V36_CABLE))
+ {
+ ;
+ }
+ }
+
+ /*
+ * set appropriate modem signals
+ */
+ if (iCom_port_info->tty->termios->c_cflag & CBAUD) {
+ /* raise DTR and RTS */
+ TRACE(iCom_port_info,TRACE_RAISE_DTR_RTS,0);
+ iCom_writeb(0xC0,&iCom_port_info->dram->osr);
+ }
+
+ /*
+ * Finally, clear and enable interrupts
+ */
+ switch (iCom_port_info->port) {
+ case 0:
+ /* Clear out any pending interrupts */
+ iCom_writew(0x00FF,(void *)iCom_port_info->int_reg);
+
+ /* Enable interrupts for first port */
+ TRACE(iCom_port_info,TRACE_ENABLE_INTERRUPTS_PA,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask);
+ iCom_writel((temp & ~ICOM_INT_MASK_PRC_A),&iCom_port_info->global_reg->int_mask);
+ break;
+ case 1:
+ /* Clear out any pending interrupts */
+ iCom_writew(0x3F00,(void *)iCom_port_info->int_reg);
+
+ /* Enable interrupts for second port */
+ TRACE(iCom_port_info,TRACE_ENABLE_INTERRUPTS_PB,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask);
+ iCom_writel((temp & ~ICOM_INT_MASK_PRC_B),&iCom_port_info->global_reg->int_mask);
+ break;
+ case 2:
+ /* Clear out any pending interrupts */
+ iCom_writew(0x00FF,(void *)iCom_port_info->int_reg);
+
+ /* Enable interrupts for first port */
+ TRACE(iCom_port_info,TRACE_ENABLE_INTERRUPTS_PC,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask_2);
+ iCom_writel((temp & ~ICOM_INT_MASK_PRC_C),&iCom_port_info->global_reg->int_mask_2);
+ break;
+ case 3:
+ /* Clear out any pending interrupts */
+ iCom_writew(0x3F00,(void *)iCom_port_info->int_reg);
+
+ /* Enable interrupts for second port */
+ TRACE(iCom_port_info,TRACE_ENABLE_INTERRUPTS_PD,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask_2);
+ iCom_writel((temp & ~ICOM_INT_MASK_PRC_D),&iCom_port_info->global_reg->int_mask_2);
+ break;
+ default:
+ printk("iCom: ERROR: Invalid port defined\n");
+ }
+
+ if (iCom_port_info->tty)
+ clear_bit(TTY_IO_ERROR, &iCom_port_info->tty->flags);
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if (iCom_port_info->tty) {
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ iCom_port_info->tty->alt_speed = 57600;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ iCom_port_info->tty->alt_speed = 115200;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ iCom_port_info->tty->alt_speed = 230400;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ iCom_port_info->tty->alt_speed = 460800;
+ }
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(iCom_port_info, 0, flags);
+
+ iCom_port_info->flags |= ASYNC_INITIALIZED;
+ return 0;
+
+ errout:
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ *
+ * irq = locked
+ */
+static void shutdown(struct iCom_port * iCom_port_info)
+{
+ unsigned long int temp;
+ unsigned char cmdReg;
+
+ TRACE(iCom_port_info,TRACE_SHUTDOWN | TRACE_TIME,jiffies);
+
+ if (!(iCom_port_info->flags & ASYNC_INITIALIZED))
+ return;
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&iCom_port_info->delta_msr_wait);
+
+ /*
+ * disable all interrupts
+ */
+ switch (iCom_port_info->port) {
+ case 0:
+ TRACE(iCom_port_info,TRACE_DIS_INTERRUPTS_PA,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask);
+ iCom_writel((temp | ICOM_INT_MASK_PRC_A),&iCom_port_info->global_reg->int_mask);
+ break;
+ case 1:
+ TRACE(iCom_port_info,TRACE_DIS_INTERRUPTS_PB,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask);
+ iCom_writel((temp | ICOM_INT_MASK_PRC_B),&iCom_port_info->global_reg->int_mask);
+ break;
+ case 2:
+ TRACE(iCom_port_info,TRACE_DIS_INTERRUPTS_PC,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask_2);
+ iCom_writel((temp | ICOM_INT_MASK_PRC_C),&iCom_port_info->global_reg->int_mask_2);
+ break;
+ case 3:
+ TRACE(iCom_port_info,TRACE_DIS_INTERRUPTS_PD,0);
+ temp = iCom_readl(&iCom_port_info->global_reg->int_mask_2);
+ iCom_writel((temp | ICOM_INT_MASK_PRC_D),&iCom_port_info->global_reg->int_mask_2);
+ break;
+ default:
+ printk("iCom: ERROR: Invalid port assignment\n");
+ }
+
+ /*
+ * disable break condition
+ */
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ if ((cmdReg | CMD_SND_BREAK) == CMD_SND_BREAK) {
+ iCom_writeb(cmdReg & ~CMD_SND_BREAK,&iCom_port_info->dram->CmdReg);
+ }
+
+ if (!iCom_port_info->tty || (iCom_port_info->tty->termios->c_cflag & HUPCL)) {
+ /* drop DTR and RTS */
+ TRACE(iCom_port_info,TRACE_DROP_DTR_RTS,0);
+ iCom_writeb(0x00,&iCom_port_info->dram->osr);
+ }
+
+ if (iCom_port_info->tty)
+ set_bit(TTY_IO_ERROR, &iCom_port_info->tty->flags);
+
+ iCom_port_info->flags &= ~ASYNC_INITIALIZED;
+}
+
+/*
+ Primary interface routines to iCom Driver
+*/
+static int iCom_open(struct tty_struct * tty, struct file * filp)
+{
+ int line;
+ int adapter_entry;
+ int port_entry;
+ struct iCom_port *iCom_port_info;
+ int retval;
+ unsigned long flags;
+
+ /*
+ Minor Number
+ _ _ _ _ b (lower nibble)
+ ___ ___
+ | |
+ | - port number (lowest 2 bits is port identifier)
+ - adapter number (remaining higher order bits identify adapter #)
+ */
+
+ MOD_INC_USE_COUNT;
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS)) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ adapter_entry = (line & 0xFFFE) >> 2; /* shift adapter # into position */
+ port_entry = line & 0x0003; /* mask of port number */
+
+ if ((port_entry == 1) &&
+ (iCom_adapter_info[adapter_entry].version == ADAPTER_V2) &&
+ (iCom_adapter_info[adapter_entry].subsystem_id != FOUR_PORT_MODEL)) {
+ port_entry = 2;
+ }
+ iCom_port_info = &iCom_adapter_info[adapter_entry].port_info[port_entry];
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_DEVICE_NUMB,tty->device);
+ tty->driver_data = iCom_port_info;
+ iCom_port_info->tty = tty;
+ iCom_port_info->open_active_count++;
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (iCom_port_info->flags & ASYNC_CLOSING)) {
+
+ spin_unlock_irqrestore(&iComlock,flags);
+ if (iCom_port_info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&iCom_port_info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((iCom_port_info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(iCom_port_info, flags);
+ if (retval) {
+ /* reset open variables */
+ TRACE(iCom_port_info,TRACE_STARTUP_ERROR,0);
+ spin_unlock_irqrestore(&iComlock,flags);
+ return retval;
+ }
+
+ retval = block_til_ready(tty, filp, iCom_port_info, flags);
+ if (retval) {
+ spin_unlock_irqrestore(&iComlock,flags);
+ return retval;
+ }
+
+ if ((iCom_port_info->open_active_count == 1) &&
+ (iCom_port_info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = iCom_port_info->normal_termios;
+ else
+ *tty->termios = iCom_port_info->callout_termios;
+ change_speed(iCom_port_info, 0, flags);
+ }
+
+ iCom_port_info->session = current->session;
+ iCom_port_info->pgrp = current->pgrp;
+
+ spin_unlock_irqrestore(&iComlock,flags);
+ return 0;
+}
+
+/*
+ * ------------------------------------------------------------
+ * iCom_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary.
+ * ------------------------------------------------------------
+ */
+static void iCom_close(struct tty_struct * tty, struct file * filp)
+{
+ struct iCom_port *iCom_port_info;
+ unsigned long flags;
+ unsigned char cmdReg;
+
+
+ if (!tty) {
+ printk("iCom: iCom_close - no tty\n");
+ return;
+ }
+
+ iCom_port_info = (struct iCom_port *)tty->driver_data;
+ if (!iCom_port_info) {
+ printk("iCom: iCom_close - no tty->driver_data\n");
+ return;
+ }
+
+ TRACE(iCom_port_info,TRACE_CLOSE,0);
+ spin_lock_irqsave(&iComlock,flags);
+
+ if (tty_hung_up_p(filp)) {
+ TRACE(iCom_port_info,TRACE_CLOSE_HANGUP,0);
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&iComlock,flags);
+ return;
+ }
+
+ if ((tty->count == 1) && (iCom_port_info->open_active_count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. open_active_count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ iCom_port_info->open_active_count = 1;
+ }
+
+ if (--iCom_port_info->open_active_count < 0) {
+ iCom_port_info->open_active_count = 0;
+ }
+
+ if (iCom_port_info->open_active_count) {
+ TRACE(iCom_port_info,TRACE_OPEN_ACTIVE,0);
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&iComlock,flags);
+ return;
+ }
+ iCom_port_info->flags |= ASYNC_CLOSING;
+
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (iCom_port_info->flags & ASYNC_NORMAL_ACTIVE)
+ iCom_port_info->normal_termios = *tty->termios;
+ if (iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE)
+ iCom_port_info->callout_termios = *tty->termios;
+
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (iCom_port_info->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+ spin_unlock_irqrestore(&iComlock,flags);
+ tty_wait_until_sent(tty, iCom_port_info->closing_wait);
+ spin_lock_irqsave(&iComlock,flags);
+ }
+
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ if (iCom_port_info->flags & ASYNC_INITIALIZED) {
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg & (unsigned char)~CMD_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ spin_unlock_irqrestore(&iComlock,flags);
+ iCom_wait_until_sent(tty, iCom_port_info->timeout);
+ spin_lock_irqsave(&iComlock,flags);
+ }
+
+ shutdown(iCom_port_info);
+
+ spin_unlock_irqrestore(&iComlock,flags);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ spin_lock_irqsave(&iComlock,flags);
+ tty->closing = 0;
+ iCom_port_info->event = 0;
+ iCom_port_info->tty = 0;
+
+ if (iCom_port_info->blocked_open) {
+ if (iCom_port_info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ spin_unlock_irqrestore(&iComlock,flags);
+ schedule_timeout(iCom_port_info->close_delay);
+ spin_lock_irqsave(&iComlock,flags);
+ }
+ wake_up_interruptible(&iCom_port_info->open_wait);
+ }
+ iCom_port_info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+
+ wake_up_interruptible(&iCom_port_info->close_wait);
+
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static int iCom_write(struct tty_struct * tty, int from_user,
+ const unsigned char * buf, int count)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned long data_count = count;
+ unsigned char *data;
+ unsigned char cmdReg;
+ unsigned long int offset;
+ unsigned long int flags;
+
+
+ if (!tty) {
+ printk("iCom: iCom_write - no tty\n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&iComlock,flags);
+
+ iCom_port_info = (struct iCom_port *)tty->driver_data;
+ TRACE(iCom_port_info,TRACE_WRITE | TRACE_TIME,jiffies);
+
+ down(&tmp_buf_sem);
+
+ if (cpu_to_le16(iCom_port_info->statStg->xmit[0].flags) & SA_FLAGS_READY_TO_XMIT) {
+ TRACE(iCom_port_info,TRACE_WRITE_FULL,0);
+ up(&tmp_buf_sem);
+ spin_unlock_irqrestore(&iComlock,flags);
+ return 0;
+ }
+
+ if (data_count > XMIT_BUFF_SZ)
+ data_count = XMIT_BUFF_SZ;
+
+ if (from_user) {
+ data_count -= copy_from_user(iCom_port_info->xmit_buf, buf, data_count);
+ if (!data_count) {
+ TRACE(iCom_port_info,TRACE_WRITE_NODATA,0);
+ up(&tmp_buf_sem);
+ spin_unlock_irqrestore(&iComlock,flags);
+ return -EFAULT;
+ }
+ } else {
+ memcpy(iCom_port_info->xmit_buf, buf, data_count);
+ }
+
+ data = iCom_port_info->xmit_buf;
+
+ if (data_count) {
+ iCom_port_info->statStg->xmit[0].flags = (unsigned short int)cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
+ iCom_port_info->statStg->xmit[0].leLength = (unsigned short int)cpu_to_le16(data_count);
+ offset = (unsigned long int)&iCom_port_info->statStg->xmit[0] - (unsigned long int)iCom_port_info->statStg;
+ *iCom_port_info->xmitRestart = cpu_to_le32(iCom_port_info->statStg_pci + offset);
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg | CMD_XMIT_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+ iCom_writeb(START_XMIT,&iCom_port_info->dram->StartXmitCmd);
+ TRACE(iCom_port_info,TRACE_WRITE_START,data_count);
+ }
+
+ up(&tmp_buf_sem);
+ spin_unlock_irqrestore(&iComlock,flags);
+
+ return data_count;
+}
+
+static void iCom_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ /* iCom_put_char adds the character to the current buffer, the
+ * data is not actually sent until iCom_flush_chars is called.
+ * Per definition iCom_flush_chars MUST be called after
+ * iCom_put_char
+ */
+
+ unsigned char *data;
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_PUT_CHAR, ch);
+
+ down(&tmp_buf_sem);
+
+ if (cpu_to_le16(iCom_port_info->statStg->xmit[0].flags) & SA_FLAGS_READY_TO_XMIT) {
+ TRACE(iCom_port_info,TRACE_PUT_FULL,0);
+ up(&tmp_buf_sem);
+ spin_unlock_irqrestore(&iComlock,flags);
+ return;
+ }
+
+ data = iCom_port_info->xmit_buf;
+ data[iCom_port_info->put_length] = ch;
+
+ if (!tty->stopped && !tty->hw_stopped) {
+ iCom_port_info->put_length++;
+ }
+
+ up(&tmp_buf_sem);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static void iCom_flush_chars(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char cmdReg;
+ unsigned long int offset;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_FLUSH_CHAR | TRACE_TIME,jiffies);
+ if (iCom_port_info->put_length) {
+ TRACE(iCom_port_info,TRACE_START_FLUSH,iCom_port_info->put_length);
+ iCom_port_info->statStg->xmit[0].flags = (unsigned short int)cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
+ iCom_port_info->statStg->xmit[0].leLength = (unsigned short int)cpu_to_le16(iCom_port_info->put_length);
+ offset = (unsigned long int)&iCom_port_info->statStg->xmit[0] - (unsigned long int)iCom_port_info->statStg;
+ *iCom_port_info->xmitRestart = cpu_to_le32(iCom_port_info->statStg_pci + offset);
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg | CMD_XMIT_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+ iCom_writeb(START_XMIT,&iCom_port_info->dram->StartXmitCmd);
+ }
+ iCom_port_info->put_length = 0;
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static int iCom_write_room(struct tty_struct * tty)
+{
+ int bytes_avail;
+ struct iCom_port *iCom_port_info = tty->driver_data;
+
+ if (cpu_to_le16(iCom_port_info->statStg->xmit[0].flags) & SA_FLAGS_READY_TO_XMIT)
+ bytes_avail = 0;
+ else
+ bytes_avail = XMIT_BUFF_SZ;
+
+ TRACE(iCom_port_info,TRACE_WRITE_ROOM,bytes_avail);
+ return bytes_avail;
+}
+
+static int iCom_chars_in_buffer(struct tty_struct * tty)
+{
+ unsigned long int dram;
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ int number_remaining = 0;
+
+ TRACE(iCom_port_info,TRACE_CHARS_IN_BUFF,0);
+ if (cpu_to_le16(iCom_port_info->statStg->xmit[0].flags) & SA_FLAGS_READY_TO_XMIT) {
+ dram = (unsigned long int)iCom_port_info->dram;
+ number_remaining = iCom_readw((void *)(dram + 0x168));
+ TRACE(iCom_port_info,TRACE_CHARS_REMAIN,number_remaining);
+ }
+ return number_remaining;
+}
+
+static int get_modem_info(struct iCom_port * iCom_port_info, unsigned int *value)
+{
+ unsigned char status,control;
+ unsigned int result;
+
+ TRACE(iCom_port_info,TRACE_GET_MODEM,0);
+
+ status = iCom_readb(&iCom_port_info->dram->isr);
+ control = iCom_readb(&iCom_port_info->dram->osr);
+
+ result = ((control & 0x40) ? TIOCM_RTS : 0)
+ | ((control & DTR) ? TIOCM_DTR : 0)
+ | ((status & DCD) ? TIOCM_CAR : 0)
+ | ((status & RI ) ? TIOCM_RNG : 0)
+ | ((status & DSR) ? TIOCM_DSR : 0)
+ | ((status & CTS) ? TIOCM_CTS : 0);
+ return put_user(result,value);
+}
+
+static int set_modem_info(struct iCom_port * iCom_port_info, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+ unsigned char local_osr;
+
+ TRACE(iCom_port_info,TRACE_SET_MODEM,0);
+ local_osr = iCom_readb(&iCom_port_info->dram->osr);
+
+ error = get_user(arg, value);
+ if (error)
+ return error;
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS) {
+ TRACE(iCom_port_info,TRACE_RAISE_RTS,0);
+ local_osr |= RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ TRACE(iCom_port_info,TRACE_RAISE_DTR,0);
+ local_osr |= DTR;
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS) {
+ TRACE(iCom_port_info,TRACE_LOWER_RTS,0);
+ local_osr &= ~RTS;
+ }
+ if (arg & TIOCM_DTR) {
+ TRACE(iCom_port_info,TRACE_LOWER_DTR,0);
+ local_osr &= ~DTR;
+ }
+ break;
+ case TIOCMSET:
+ local_osr = ((local_osr & ~(RTS | DTR))
+ | ((arg & TIOCM_RTS) ? RTS : 0)
+ | ((arg & TIOCM_DTR) ? DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ iCom_writeb(local_osr,&iCom_port_info->dram->osr);
+ return 0;
+}
+
+static int get_serial_info(struct iCom_port * iCom_port_info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+
+ TRACE(iCom_port_info,TRACE_GET_SERIAL,0);
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = 0x00; /* device specific, PORT_UNKNOWN */
+ tmp.line = iCom_port_info->adapter; /* adapter number */
+ tmp.port = iCom_port_info->port; /* port number on adapter */
+ tmp.irq = iCom_adapter_info[iCom_port_info->adapter].irq_number;
+ tmp.flags = iCom_port_info->flags;
+ tmp.xmit_fifo_size = XMIT_BUFF_SZ;
+ tmp.baud_base = 0x00; /* device specific */
+ tmp.close_delay = iCom_port_info->close_delay;
+ tmp.closing_wait = iCom_port_info->closing_wait;
+ tmp.custom_divisor = 0x00; /* device specific */
+ tmp.hub6 = 0x00; /* device specific */
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_serial_info(struct iCom_port * iCom_port_info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ int old_flags;
+ int retval = 0;
+ unsigned long flags;
+
+ TRACE(iCom_port_info,TRACE_SET_SERIAL,0);
+
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+
+ old_flags = iCom_port_info->flags;
+ /* new_serial.irq --- irq of adapter will not change, PCI only */
+ /* new_serial.xmit_fifo_size -- can not change on this device */
+ /* new_serial.baud_base -- ??? */
+ /* new_serial.custom_divisor -- device specific */
+ /* new_serial.hub6 -- device specific */
+ /* new_serial.type -- device specific */
+ /* new_serial.port -- address of port will not change, PCI only */
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if ((new_serial.baud_base != iCom_port_info->baud_base) ||
+ (new_serial.close_delay != iCom_port_info->close_delay) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (iCom_port_info->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ iCom_port_info->flags = ((iCom_port_info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ goto check_and_exit;
+ }
+
+ if (new_serial.baud_base < 9600) {
+ return -EINVAL;
+ }
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+ iCom_port_info->baud_base = new_serial.baud_base;
+ iCom_port_info->flags = ((iCom_port_info->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
+ iCom_port_info->close_delay = new_serial.close_delay * HZ/100;
+ iCom_port_info->closing_wait = new_serial.closing_wait * HZ/100;
+ iCom_port_info->tty->low_latency = (iCom_port_info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+ spin_lock_irqsave(&iComlock,flags);
+ if (iCom_port_info->flags & ASYNC_INITIALIZED) {
+ if (((iCom_port_info->flags & ASYNC_SPD_MASK) !=
+ (old_flags & ASYNC_SPD_MASK))) {
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ iCom_port_info->tty->alt_speed = 57600;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ iCom_port_info->tty->alt_speed = 115200;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ iCom_port_info->tty->alt_speed = 230400;
+ if ((iCom_port_info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ iCom_port_info->tty->alt_speed = 460800;
+ change_speed(iCom_port_info, 0, flags);
+ }
+ } else
+ retval = startup(iCom_port_info, flags);
+
+ spin_unlock_irqrestore(&iComlock,flags);
+
+ return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct iCom_port * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+
+ TRACE(info,TRACE_SET_LSR,0);
+
+ status = cpu_to_le16(info->statStg->xmit[0].flags);
+ result = ((status & SA_FLAGS_DONE) ? TIOCSER_TEMT : 0);
+ return put_user(result,value);
+}
+
+static int iCom_ioctl(struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct iCom_port * iCom_port_info = (struct iCom_port *)tty->driver_data;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+ unsigned long flags;
+
+ TRACE(iCom_port_info,TRACE_IOCTL | TRACE_TIME,jiffies);
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case 0x4300:
+ if (copy_to_user((void *)arg,iCom_port_info->trace_blk,TRACE_BLK_SZ))
+ return -EFAULT;
+ return 0;
+ case TIOCMGET:
+ return get_modem_info(iCom_port_info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(iCom_port_info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(iCom_port_info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(iCom_port_info,
+ (struct serial_struct *) arg);
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(iCom_port_info, (unsigned int *) arg);
+
+ /*
+ * 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:
+ spin_lock_irqsave(&iComlock,flags);
+ /* note the counters on entry */
+ cprev = iCom_port_info->icount;
+ spin_unlock_irqrestore(&iComlock,flags);
+ while (1) {
+ interruptible_sleep_on(&iCom_port_info->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irqsave(&iComlock,flags);
+ cnow = iCom_port_info->icount; /* atomic copy */
+ spin_unlock_irqrestore(&iComlock,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:
+ spin_lock_irqsave(&iComlock,flags);
+ cnow = iCom_port_info->icount;
+ spin_unlock_irqrestore(&iComlock,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;
+ return 0;
+
+ case TIOCSERGWILD:
+ case TIOCSERSWILD:
+ /* "setserial -W" is called in Debian boot */
+ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+ return 0;
+
+ default:
+ TRACE(iCom_port_info,TRACE_IOCTL_IGNORE,cmd);
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void iCom_send_xchar(struct tty_struct * tty, char ch)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char xdata;
+ int index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_SEND_XCHAR,ch);
+ /* attempt sending char for a period of .1 second */
+ for (index = 0; index < 10; index++ ) {
+ xdata = iCom_readb(&iCom_port_info->dram->xchar);
+ if (xdata == 0x00) {
+ TRACE(iCom_port_info,TRACE_QUICK_WRITE,0);
+ iCom_writeb(ch,&iCom_port_info->dram->xchar);
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ spin_unlock_irqrestore(&iComlock,flags);
+ schedule_timeout(HZ/100);
+ spin_lock_irqsave(&iComlock,flags);
+ }
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static void iCom_throttle(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char osr;
+
+ TRACE(iCom_port_info,TRACE_THROTTLE,0);
+ if (I_IXOFF(tty))
+ iCom_send_xchar(tty, STOP_CHAR(tty));
+
+ if (tty->termios->c_cflag & CRTSCTS) {
+ osr = iCom_readb(&iCom_port_info->dram->osr);
+ iCom_writeb(osr & ~RTS,&iCom_port_info->dram->osr);
+ }
+}
+
+static void iCom_unthrottle(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char osr;
+
+ TRACE(iCom_port_info,TRACE_UNTHROTTLE,0);
+ if (I_IXOFF(tty)) {
+ iCom_send_xchar(tty, START_CHAR(tty));
+ }
+ if (tty->termios->c_cflag & CRTSCTS) {
+ osr = iCom_readb(&iCom_port_info->dram->osr);
+ iCom_writeb(osr | RTS,&iCom_port_info->dram->osr);
+ }
+}
+
+static void iCom_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned int cflag = tty->termios->c_cflag;
+ unsigned char osr;
+ unsigned long flags;
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_SET_TERMIOS,0);
+ if ((cflag == old_termios->c_cflag)
+ && (RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag))) {
+ spin_unlock_irqrestore(&iComlock,flags);
+ return;
+ }
+
+ change_speed(iCom_port_info, old_termios, flags);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(cflag & CBAUD)) {
+ osr = iCom_readb(&iCom_port_info->dram->osr);
+ TRACE(iCom_port_info,TRACE_DROP_DTR_RTS,0);
+ iCom_writeb(osr & ~(DTR|RTS),&iCom_port_info->dram->osr);
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (cflag & CBAUD)) {
+ osr = iCom_readb(&iCom_port_info->dram->osr);
+ TRACE(iCom_port_info,TRACE_RAISE_DTR,0);
+ osr |= DTR;
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
+ TRACE(iCom_port_info,TRACE_RAISE_RTS,0);
+ osr |= RTS;
+ }
+ iCom_writeb(osr,&iCom_port_info->dram->osr);
+ }
+
+ spin_unlock_irqrestore(&iComlock,flags);
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ iCom_start(tty);
+ }
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&iCom_port_info->open_wait);
+#endif
+}
+
+static void iCom_stop(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char cmdReg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_STOP,0);
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg | CMD_HOLD_XMIT,&iCom_port_info->dram->CmdReg);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static void iCom_start(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char cmdReg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_START,0);
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg & ~CMD_HOLD_XMIT,&iCom_port_info->dram->CmdReg);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static void iCom_hangup(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned long flags;
+
+ TRACE(iCom_port_info,TRACE_HANGUP,0);
+ iCom_flush_buffer(tty);
+ spin_lock_irqsave(&iComlock,flags);
+ shutdown(iCom_port_info);
+ iCom_port_info->open_active_count = 0;
+ iCom_port_info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ iCom_port_info->tty = 0;
+ wake_up_interruptible(&iCom_port_info->open_wait);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+static void iCom_break(struct tty_struct *tty, int break_state)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char cmdReg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_BREAK,0);
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ if (break_state == -1) {
+ iCom_writeb(cmdReg | CMD_SND_BREAK,&iCom_port_info->dram->CmdReg);
+ }
+ else{
+ iCom_writeb(cmdReg & ~CMD_SND_BREAK,&iCom_port_info->dram->CmdReg);
+ }
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+/*
+ * iCom_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void iCom_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+ int status;
+
+ TRACE(iCom_port_info,TRACE_WAIT_UNTIL_SENT,0);
+
+ orig_jiffies = jiffies;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (iCom_port_info->timeout - HZ/50) / iCom_port_info->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ char_time = 1;
+ if (timeout) {
+ if (timeout < char_time)
+ char_time = timeout;
+ }
+ /*
+ * If the transmitter hasn't cleared in twice the approximate
+ * amount of time to send the entire FIFO, it probably won't
+ * ever clear. This assumes the UART isn't doing flow
+ * control, which is currently the case. Hence, if it ever
+ * takes longer than iCom_port_info->timeout, this is probably due to a
+ * UART bug of some kind. So, we clamp the timeout parameter at
+ * 2*iCom_port_info->timeout.
+ */
+ if (!timeout || timeout > 2*iCom_port_info->timeout)
+ timeout = 2*iCom_port_info->timeout;
+
+ status = cpu_to_le16(iCom_port_info->statStg->xmit[0].flags);
+ while (status & SA_FLAGS_DONE ) { /*data still transmitting*/
+
+ current->state = TASK_INTERRUPTIBLE;
+ current->counter = 0; /* make us low-priority */
+ schedule_timeout(char_time);
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ status = cpu_to_le16(iCom_port_info->statStg->xmit[0].flags);
+ }
+ current->state = TASK_RUNNING;
+}
+
+/*
+ * /proc fs routines....
+ */
+static inline int line_info(char *buf, struct iCom_port *iCom_port_info)
+{
+ char stat_buf[30], control, status;
+ int ret, baud_index;
+ int port;
+
+ if ((iCom_port_info->port == 2) &&
+ (iCom_adapter_info[iCom_port_info->adapter].subsystem_id != FOUR_PORT_MODEL))
+ port = 1;
+ else
+ port = iCom_port_info->port;
+
+ ret = sprintf(buf, "%d: port:%X irq:%d",
+ iCom_port_info->adapter,
+ port,
+ iCom_adapter_info[iCom_port_info->adapter].irq_number);
+
+ status = iCom_readb(&iCom_port_info->dram->isr);
+ control = iCom_readb(&iCom_port_info->dram->osr);
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (control & RTS)
+ strcat(stat_buf, "|RTS");
+ if (status & CTS)
+ strcat(stat_buf, "|CTS");
+ if (control & DTR)
+ strcat(stat_buf, "|DTR");
+ if (status & DSR)
+ strcat(stat_buf, "|DSR");
+ if (status & DCD)
+ strcat(stat_buf, "|CD");
+ if (status & RI)
+ strcat(stat_buf, "|RI");
+
+ baud_index = iCom_readb(&iCom_port_info->dram->async_config3);
+ ret += sprintf(buf+ret, " baud:%d",icom_acfg_baud[baud_index]);
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ iCom_port_info->icount.tx, iCom_port_info->icount.rx);
+
+ if (iCom_port_info->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", iCom_port_info->icount.frame);
+
+ if (iCom_port_info->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", iCom_port_info->icount.parity);
+
+ if (iCom_port_info->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", iCom_port_info->icount.brk);
+
+ if (iCom_port_info->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", iCom_port_info->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+ return ret;
+}
+
+int iCom_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int i, j, len = 0, l;
+ off_t begin = 0;
+
+ len += sprintf(page, "iCom driver: %s\n", "1.0");
+ for (i = 0; i < active_adapters && len < 4000; i++) {
+ for (j= 0; j < 4 && len < 4000; j++) {
+ if (iCom_adapter_info[i].port_info[j].status == ICOM_PORT_ACTIVE) {
+ l = line_info(page + len, &iCom_adapter_info[i].port_info[j]);
+ len += l;
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ }
+ }
+ *eof = 1;
+ done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+static void iCom_flush_buffer(struct tty_struct * tty)
+{
+ struct iCom_port *iCom_port_info = (struct iCom_port *)tty->driver_data;
+ unsigned char cmdReg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+ TRACE(iCom_port_info,TRACE_FLUSH_BUFFER,0);
+ /*
+ * with no CMD_XMIT_ENABLE is same as disabling xmitter. This should
+ * result in an interrupt if currently transmitting
+ */
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg & ~CMD_XMIT_ENABLE,&iCom_port_info->dram->CmdReg);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static inline void rs_sched_event(struct iCom_port *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static inline void check_modem_status(struct iCom_port *iCom_port_info)
+{
+ static char old_status = 0;
+ char delta_status;
+ unsigned char status;
+
+ /*modem input register */
+ status = iCom_readb(&iCom_port_info->dram->isr);
+ TRACE(iCom_port_info,TRACE_CHECK_MODEM,status);
+ delta_status = status ^ old_status;
+ if (delta_status) {
+ if (delta_status & RI)
+ iCom_port_info->icount.rng++;
+ if (delta_status & DSR)
+ iCom_port_info->icount.dsr++;
+ if (delta_status & DCD)
+ iCom_port_info->icount.dcd++;
+ if (delta_status & CTS)
+ iCom_port_info->icount.cts++;
+
+ wake_up_interruptible(&iCom_port_info->delta_msr_wait);
+ old_status = status;
+ }
+
+ if ((iCom_port_info->flags & ASYNC_CHECK_CD) && (status & 0x20)) {
+ if (status & 0x20) /* Carrier Detect up */
+ wake_up_interruptible(&iCom_port_info->open_wait);
+ else if (!((iCom_port_info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (iCom_port_info->flags & ASYNC_CALLOUT_NOHUP))) {
+ if (iCom_port_info->tty)
+ tty_hangup(iCom_port_info->tty);
+ }
+ }
+
+ if (iCom_port_info->flags & ASYNC_CTS_FLOW) {
+ if (iCom_port_info->tty->hw_stopped) {
+ if (status & 0x40) { /* CTS up */
+ iCom_port_info->tty->hw_stopped = 0;
+ TRACE(iCom_port_info,TRACE_CTS_UP,0);
+ rs_sched_event(iCom_port_info, 0);
+ return;
+ }
+ } else {
+ if (!(status & 0x40)) { /* CTS down */
+ iCom_port_info->tty->hw_stopped = 1;
+ TRACE(iCom_port_info,TRACE_CTS_DOWN,0);
+ }
+ }
+ }
+}
+
+static void process_interrupt(u16 port_int_reg, struct iCom_port *iCom_port_info)
+{
+ short int count, rcv_buff;
+ struct tty_struct *tty = iCom_port_info->tty;
+ unsigned char *data;
+ unsigned short int status;
+ struct async_icount *icount;
+ unsigned long int offset;
+
+
+ TRACE(iCom_port_info,TRACE_INTERRUPT | TRACE_TIME,jiffies);
+
+ if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) {
+ if (port_int_reg & (INT_XMIT_COMPLETED))
+ TRACE(iCom_port_info,TRACE_XMIT_COMPLETE,0);
+ else
+ TRACE(iCom_port_info,TRACE_XMIT_DISABLED,0);
+
+ /* clear buffer in use bit */
+ iCom_port_info->statStg->xmit[0].flags &= cpu_to_le16(~SA_FLAGS_READY_TO_XMIT);
+ iCom_port_info->icount.tx += (unsigned short int)cpu_to_le16(iCom_port_info->statStg->xmit[0].leLength);
+
+ /* activate write queue */
+ rs_sched_event(iCom_port_info, 0);
+ }
+
+ if (port_int_reg & INT_RCV_COMPLETED) {
+
+ TRACE(iCom_port_info,TRACE_RCV_COMPLETE,0);
+ rcv_buff = iCom_port_info->next_rcv;
+
+ status = cpu_to_le16(iCom_port_info->statStg->rcv[rcv_buff].flags);
+ while (status & SA_FL_RCV_DONE) {
+
+ TRACE(iCom_port_info,TRACE_FID_STATUS,status);
+
+ count = cpu_to_le16(iCom_port_info->statStg->rcv[rcv_buff].leLength);
+
+ TRACE(iCom_port_info,TRACE_RCV_COUNT,count);
+ if (count > (TTY_FLIPBUF_SIZE - tty->flip.count))
+ count = TTY_FLIPBUF_SIZE - tty->flip.count;
+
+ TRACE(iCom_port_info,TRACE_REAL_COUNT,count);
+
+ offset = cpu_to_le32(iCom_port_info->statStg->rcv[rcv_buff].leBuffer) - iCom_port_info->recv_buf_pci;
+
+ memcpy(tty->flip.char_buf_ptr,(unsigned char *)((unsigned long int)iCom_port_info->recv_buf + offset),count);
+
+ data = (unsigned char *)tty->flip.char_buf_ptr;
+
+ if (count > 0) {
+ tty->flip.count += count - 1;
+ tty->flip.char_buf_ptr += count - 1;
+
+ memset(tty->flip.flag_buf_ptr, 0, count);
+ tty->flip.flag_buf_ptr += count - 1;
+ }
+
+ icount = &iCom_port_info->icount;
+ icount->rx += count;
+
+ /* Break detect logic */
+ if ((status & SA_FLAGS_FRAME_ERROR) && (tty->flip.char_buf_ptr[0] == 0x00)) {
+ status &= ~SA_FLAGS_FRAME_ERROR;
+ status |= SA_FLAGS_BREAK_DET;
+ TRACE(iCom_port_info,TRACE_BREAK_DET,0);
+ }
+
+ if (status & (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR |
+ SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) {
+
+ if (status & SA_FLAGS_BREAK_DET)
+ icount->brk++;
+ if (status & SA_FLAGS_PARITY_ERROR)
+ icount->parity++;
+ if (status & SA_FLAGS_FRAME_ERROR)
+ icount->frame++;
+ if (status & SA_FLAGS_OVERRUN)
+ icount->overrun++;
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ */
+ if (status & iCom_port_info->ignore_status_mask) {
+ TRACE(iCom_port_info,TRACE_IGNORE_CHAR,0);
+ goto ignore_char;
+ }
+
+ status &= iCom_port_info->read_status_mask;
+
+ if (status & SA_FLAGS_BREAK_DET) {
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ if (iCom_port_info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ } else if (status & SA_FLAGS_PARITY_ERROR) {
+ TRACE(iCom_port_info,TRACE_PARITY_ERROR,0);
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ }
+ else if (status & SA_FLAGS_FRAME_ERROR)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (status & SA_FLAGS_OVERRUN) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+ }
+ }
+
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ ignore_char:
+ iCom_port_info->statStg->rcv[rcv_buff].flags = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].leLength = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].WorkingLength = (unsigned short int)cpu_to_le16(RCV_BUFF_SZ);
+
+ rcv_buff++;
+ if (rcv_buff == NUM_RBUFFS) rcv_buff = 0;
+
+ status = cpu_to_le16(iCom_port_info->statStg->rcv[rcv_buff].flags);
+ }
+ iCom_port_info->next_rcv = rcv_buff;
+ tty_flip_buffer_push(tty);
+ }
+}
+
+static void iCom_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+ unsigned long int int_reg;
+ u32 adapter_interrupts;
+ u16 port_int_reg;
+ struct iCom_adapter *iCom_adapter_ptr;
+ struct iCom_port *iCom_port_info;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iComlock,flags);
+
+ /* find iCom_port_info for this interrupt */
+ iCom_adapter_ptr = (struct iCom_adapter *)dev_id;
+
+ if ((iCom_adapter_ptr->version | ADAPTER_V2) == ADAPTER_V2) {
+ int_reg = iCom_adapter_ptr->base_addr + 0x8024;
+
+ adapter_interrupts = iCom_readl((void *)int_reg);
+
+ if (adapter_interrupts & 0x00003FFF) {
+ /* port 2 interrupt, NOTE: for all ADAPTER_V2, port 2 will be active */
+ iCom_port_info = &iCom_adapter_ptr->port_info[2];
+ port_int_reg = (u16)adapter_interrupts;
+ process_interrupt(port_int_reg, iCom_port_info);
+ check_modem_status(iCom_port_info);
+ }
+ if (adapter_interrupts & 0x3FFF0000) {
+ /* port 3 interrupt */
+ iCom_port_info = &iCom_adapter_ptr->port_info[3];
+ if (iCom_port_info->status == ICOM_PORT_ACTIVE) {
+ port_int_reg = (u16)(adapter_interrupts >> 16);
+ process_interrupt(port_int_reg, iCom_port_info);
+ check_modem_status(iCom_port_info);
+ }
+ }
+
+ /* Clear out any pending interrupts */
+ iCom_writel(adapter_interrupts,(void *)int_reg);
+
+ int_reg = iCom_adapter_ptr->base_addr + 0x8004;
+ }
+ else {
+ int_reg = iCom_adapter_ptr->base_addr + 0x4004;
+ }
+
+ adapter_interrupts = iCom_readl((void *)int_reg);
+
+ if (adapter_interrupts & 0x00003FFF) {
+ /* port 0 interrupt, NOTE: for all adapters, port 0 will be active */
+ iCom_port_info = &iCom_adapter_ptr->port_info[0];
+ port_int_reg = (u16)adapter_interrupts;
+ process_interrupt(port_int_reg, iCom_port_info);
+ check_modem_status(iCom_port_info);
+ }
+ if (adapter_interrupts & 0x3FFF0000) {
+ /* port 1 interrupt */
+ iCom_port_info = &iCom_adapter_ptr->port_info[1];
+ if (iCom_port_info->status == ICOM_PORT_ACTIVE) {
+ port_int_reg = (u16)(adapter_interrupts >> 16);
+ process_interrupt(port_int_reg, iCom_port_info);
+ check_modem_status(iCom_port_info);
+ }
+ }
+
+ /* Clear out any pending interrupts */
+ iCom_writel(adapter_interrupts,(void *)int_reg);
+ spin_unlock_irqrestore(&iComlock,flags);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * iCom_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_softint(void *private_)
+{
+ struct iCom_port *info = (struct iCom_port *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(0, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ TRACE(info,TRACE_WAKEUP,0);
+ }
+}
+
+/*
+ Module operations
+*/
+int iCom_init(void)
+{
+ int index,
+ index_v2,
+ index2,
+ scan_index;
+ struct pci_dev *dev[MAX_ADAPTERS];
+ unsigned int irq_number[MAX_ADAPTERS];
+ unsigned long int base_addr[MAX_ADAPTERS];
+ unsigned char valid_indices[MAX_ADAPTERS];
+#define VALID 1
+#define INVALID 0
+ unsigned int command_reg;
+ struct iCom_port *iCom_port_info;
+ int retval;
+ int status;
+ int port_num;
+ int adapter_count = 0;
+ int duplicate;
+ unsigned int subsystem_id;
+
+
+ /*
+ * Find base addresses and IRQs for any/all installed cards
+ */
+ for (index=0; index < MAX_ADAPTERS; index++) {
+ valid_indices[index] = INVALID;
+ dev[index] = NULL;
+ }
+
+ /* check for Version 1 Adapters */
+ for (index = 0; index < MAX_ADAPTERS; index++){
+ if (index == 0) {
+ if (!(dev[index] = pci_find_device(VENDOR_ID, DEVICE_ID, dev[index])))
+ break;
+ }
+ else {
+ if (!(dev[index] = pci_find_device(VENDOR_ID, DEVICE_ID, dev[index-1])))
+ break;
+ }
+
+ adapter_count++;
+
+ if (pci_enable_device(dev[index])) {
+ printk("iCom: Device enable FAILED\n");
+ continue;
+ }
+
+ if (pci_read_config_dword(dev[index], PCI_COMMAND, &command_reg)) {
+ printk("iCom: PCI Config read FAILED\n");
+ continue;
+ }
+
+ pci_write_config_dword(dev[index],PCI_COMMAND, command_reg | 0x00000146);
+ pci_write_config_dword(dev[index],0x44, 0x8300830A);
+
+ base_addr[index] = pci_resource_start(dev[index],0);
+ base_addr[index] &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ duplicate = 0;
+ for (index2 = 0; index2 < index; index2++) {
+ if (base_addr[index] == base_addr[index2])
+ duplicate = 1;
+ }
+ if (duplicate) continue;
+
+ irq_number[index] = dev[index]->irq;
+
+ valid_indices[index] = ADAPTER_V1;
+ }
+
+ /* check for version 2 Adapters */
+ for (index_v2=0; index_v2 < (MAX_ADAPTERS - adapter_count); index_v2++){
+ if (index_v2 == 0) {
+ if (!(dev[index] = pci_find_device(VENDOR_ID, DEVICE_ID2, NULL)))
+ break;
+ }
+ else {
+ if (!(dev[index] = pci_find_device(VENDOR_ID, DEVICE_ID2, dev[index-1])))
+ break;
+ }
+
+ adapter_count++;
+
+ if (pci_enable_device(dev[index])) {
+ printk("iCom: Device enable FAILED\n");
+ continue;
+ }
+
+ if (pci_read_config_dword(dev[index], PCI_COMMAND, &command_reg)) {
+ printk("iCom: PCI Config read FAILED\n");
+ continue;
+ }
+
+ pci_write_config_dword(dev[index],PCI_COMMAND, command_reg | 0x00000146);
+ pci_write_config_dword(dev[index],0x44, 0x42004200);
+ pci_write_config_dword(dev[index],0x48, 0x42004200);
+
+ base_addr[index] = pci_resource_start(dev[index],0);
+ base_addr[index] &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ duplicate = 0;
+ for (index2 = 0; index2 < index; index2++) {
+ if (base_addr[index] == base_addr[index2])
+ duplicate = 1;
+ }
+ if (duplicate) continue;
+
+ irq_number[index] = dev[index]->irq;
+
+ valid_indices[index++] = ADAPTER_V2;
+ }
+
+ /* allocate memory for control blocks representing each adapter */
+ iCom_adapter_info = (struct iCom_adapter *)
+ kmalloc(adapter_count*sizeof(struct iCom_adapter),GFP_KERNEL);
+
+ if (!iCom_adapter_info) {
+ return -ENOMEM;
+ }
+
+ memset(iCom_adapter_info, 0,adapter_count*sizeof(struct iCom_adapter));
+
+ /* store information just obtained on base_addr and irq */
+ for (index = scan_index = 0; (scan_index < MAX_ADAPTERS) &
+ (index < adapter_count); scan_index++) {
+
+ if (valid_indices[scan_index]) {
+ iCom_adapter_info[index].base_addr = base_addr[scan_index];
+ iCom_adapter_info[index].irq_number = irq_number[scan_index];
+ iCom_adapter_info[index].pci_dev = dev[scan_index];
+ iCom_adapter_info[index].version = valid_indices[scan_index];
+ pci_read_config_dword(dev[index], PCI_SUBSYSTEM_VENDOR_ID, &subsystem_id);
+ iCom_adapter_info[index].subsystem_id = subsystem_id;
+
+ /* save off irq and request irq line */
+ if (request_irq(irq_number[scan_index], iCom_interrupt, SA_INTERRUPT |
+ SA_SHIRQ, DRIVER_NAME, (void *)&iCom_adapter_info[index])) {
+ printk("iCom: request_irq FAILED\n");
+ continue;
+ }
+
+ if (iCom_adapter_info[index].version == ADAPTER_V1) {
+ iCom_adapter_info[index].numb_ports = 2;
+ iCom_adapter_info[index].port_info[0].port = 0;
+ iCom_adapter_info[index].port_info[0].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[1].port = 1;
+ iCom_adapter_info[index].port_info[1].status = ICOM_PORT_ACTIVE;
+ }
+ else {
+ if (subsystem_id == FOUR_PORT_MODEL) {
+ iCom_adapter_info[index].numb_ports = 4;
+ iCom_adapter_info[index].port_info[0].port = 0;
+ iCom_adapter_info[index].port_info[0].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[1].port = 1;
+ iCom_adapter_info[index].port_info[1].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[2].port = 2;
+ iCom_adapter_info[index].port_info[2].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[3].port = 3;
+ iCom_adapter_info[index].port_info[3].status = ICOM_PORT_ACTIVE;
+ }
+ else {
+ iCom_adapter_info[index].numb_ports = 4;
+ iCom_adapter_info[index].port_info[0].port = 0;
+ iCom_adapter_info[index].port_info[0].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[1].status = ICOM_PORT_OFF;
+ iCom_adapter_info[index].port_info[2].port = 2;
+ iCom_adapter_info[index].port_info[2].status = ICOM_PORT_ACTIVE;
+ iCom_adapter_info[index].port_info[3].status = ICOM_PORT_OFF;
+ }
+ }
+
+ if (!request_mem_region(iCom_adapter_info[index].base_addr,
+ pci_resource_len(iCom_adapter_info[index].pci_dev,0),
+ "iCom")) {
+ printk("iCom: request_mem_region FAILED\n");
+ }
+
+ for (port_num = 0; port_num < iCom_adapter_info[index].numb_ports; port_num++) {
+ iCom_port_info = &iCom_adapter_info[index].port_info[port_num];
+
+ if (iCom_port_info->status == ICOM_PORT_ACTIVE) {
+ /* initialize wait queues */
+ init_waitqueue_head(&iCom_port_info->open_wait);
+ init_waitqueue_head(&iCom_port_info->close_wait);
+ init_waitqueue_head(&iCom_port_info->delta_msr_wait);
+
+ /* initialize port specific variables */
+ iCom_port_info->tqueue.routine = do_softint;
+ iCom_port_info->tqueue.data = iCom_port_info;
+ if (iCom_adapter_info[index].version == ADAPTER_V1) {
+ iCom_port_info->global_reg = (struct iCom_regs *)((char *)iCom_adapter_info[index].base_addr + 0x4000);
+ iCom_port_info->int_reg = (unsigned long)iCom_adapter_info[index].base_addr + 0x4004 + 2 - 2 * port_num;
+ }
+ else {
+ iCom_port_info->global_reg = (struct iCom_regs *)((char *)iCom_adapter_info[index].base_addr + 0x8000);
+ if (iCom_port_info->port < 2)
+ iCom_port_info->int_reg = (unsigned long)iCom_adapter_info[index].base_addr + 0x8004 + 2 - 2 * iCom_port_info->port;
+ else
+ iCom_port_info->int_reg = (unsigned long)iCom_adapter_info[index].base_addr + 0x8024 + 2 - 2 * (iCom_port_info->port - 2);
+ }
+ iCom_port_info->dram = (struct func_dram*)((char*)iCom_adapter_info[index].base_addr + 0x2000 * iCom_port_info->port);
+ iCom_port_info->close_delay = 5*HZ/10;
+ iCom_port_info->closing_wait = 30*HZ;
+ iCom_port_info->adapter = index;
+
+ /*
+ * Load and start processor
+ */
+ retval = loadCode(iCom_port_info);
+ if (retval != 0) {
+ printk("iCom%d: pico-code load of adapter FAILED\n",iCom_port_info->adapter);
+ return -ENODEV;
+ }
+
+ /* get port memory */
+ if ((status = get_port_memory(iCom_port_info)) != 0) {
+ return -ENODEV;
+ /* return status; *** -ENOMEM didn't work right for me */
+ }
+
+ /* Set Country Code */
+ iCom_set_code(iCom_port_info);
+ }
+ }
+ index++;
+ }
+ }
+ active_adapters = index;
+
+ printk("iCom: Adapter detection complete, %d adapters found with %d valid\n",adapter_count,active_adapters);
+
+ if (active_adapters > 0) {
+
+ /* Initialize the tty_driver structure */
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = DRIVER_NAME;
+#if defined(CONFIG_DEVFS_FS)
+ serial_driver.name = "ttyA%d";
+#else
+ serial_driver.name = "ttyA";
+#endif
+ serial_driver.major = 243;
+ serial_driver.minor_start = 0;
+ serial_driver.num = NR_PORTS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = iCom_open;
+ serial_driver.close = iCom_close;
+ serial_driver.write = iCom_write;
+ serial_driver.put_char = iCom_put_char;
+ serial_driver.flush_chars = iCom_flush_chars;
+ serial_driver.write_room = iCom_write_room;
+ serial_driver.chars_in_buffer = iCom_chars_in_buffer;
+ serial_driver.flush_buffer = iCom_flush_buffer;
+ serial_driver.ioctl = iCom_ioctl;
+ serial_driver.throttle = iCom_throttle;
+ serial_driver.unthrottle = iCom_unthrottle;
+ serial_driver.send_xchar = iCom_send_xchar;
+ serial_driver.set_termios = iCom_set_termios;
+ serial_driver.stop = iCom_stop;
+ serial_driver.start = iCom_start;
+ serial_driver.hangup = iCom_hangup;
+ serial_driver.break_ctl = iCom_break;
+ serial_driver.wait_until_sent = iCom_wait_until_sent;
+ serial_driver.read_proc = iCom_read_proc;
+
+
+ for (index=0; index < active_adapters; index++) {
+ iCom_adapter_info[index].port_info[0].callout_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[0].normal_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[1].callout_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[1].normal_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[2].callout_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[2].normal_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[3].callout_termios = serial_driver.init_termios;
+ iCom_adapter_info[index].port_info[3].normal_termios = serial_driver.init_termios;
+ }
+
+ if (tty_register_driver(&serial_driver)) {
+ for (index=0; index < active_adapters; index++) {
+ free_irq(iCom_adapter_info[index].irq_number, (void *)&iCom_adapter_info[index]);
+ }
+ kfree(iCom_adapter_info);
+ panic("Couldn't register serial driver\n");
+ }
+
+#if defined(CONFIG_DEVFS_FS)
+ for (index = 0; index < active_adapters; index++) {
+ tty_register_devfs(&serial_driver,
+ 0, index*4 + serial_driver.minor_start);
+ tty_register_devfs(&serial_driver,
+ 0, index*4 + serial_driver.minor_start + 1);
+
+ if ((iCom_adapter_info[index].version == ADAPTER_V2) &&
+ (iCom_adapter_info[index].subsystem_id == FOUR_PORT_MODEL)) {
+ tty_register_devfs(&serial_driver,
+ 0, index*4 + serial_driver.minor_start + 2);
+ tty_register_devfs(&serial_driver,
+ 0, index*4 + serial_driver.minor_start + 3);
+ }
+ }
+#endif
+
+ /* lastly, register unique ioctl */
+ register_ioctl32_conversion(0x4300,NULL);
+
+ return 0;
+ }
+ else {
+ if (adapter_count > 0) {
+ kfree(iCom_adapter_info);
+ }
+ }
+
+ return -ENODEV;
+}
+
+int init_module(void)
+{
+ return iCom_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+ int e1;
+ int index;
+ int port_num;
+ struct iCom_port *iCom_port_info;
+
+ /* remove registered ioctl */
+ unregister_ioctl32_conversion(0x4300);
+
+ spin_lock_irqsave(&iComlock,flags);
+ if ((e1 = tty_unregister_driver(&serial_driver)))
+ printk("iCom: failed to unregister serial driver (%d)\n",e1);
+
+#if defined(CONFIG_DEVFS_FS)
+ for (index = 0; index < active_adapters; index++) {
+ tty_unregister_devfs(&serial_driver,
+ index*4 + serial_driver.minor_start);
+ tty_unregister_devfs(&serial_driver,
+ index*4 + serial_driver.minor_start + 1);
+
+ if ((iCom_adapter_info[index].version == ADAPTER_V2) &&
+ (iCom_adapter_info[index].subsystem_id == FOUR_PORT_MODEL)) {
+ tty_unregister_devfs(&serial_driver,
+ index*4 + serial_driver.minor_start + 2);
+ tty_unregister_devfs(&serial_driver,
+ index*4 + serial_driver.minor_start + 3);
+ }
+ }
+#endif
+
+ for (index=0; index < active_adapters; index++) {
+
+ for (port_num = 0; port_num < iCom_adapter_info[index].numb_ports; port_num++) {
+ iCom_port_info = &iCom_adapter_info[index].port_info[port_num];
+
+ if (iCom_port_info->status == ICOM_PORT_ACTIVE) {
+
+ /* be sure that DTR and RTS are dropped */
+ iCom_writeb(0x00,&iCom_port_info->dram->osr);
+
+ /* Wait 0.1 Sec for simple Init to complete */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ /* Stop proccessor */
+ stop_processor(iCom_port_info);
+
+ return_port_memory(iCom_port_info);
+ }
+ }
+
+ free_irq(iCom_adapter_info[index].irq_number, (void *)&iCom_adapter_info[index]);
+ release_mem_region(iCom_adapter_info[index].base_addr,
+ pci_resource_len(iCom_adapter_info[index].pci_dev,0));
+ }
+ spin_unlock_irqrestore(&iComlock,flags);
+
+ kfree(iCom_adapter_info);
+ printk("iCom: Driver removed\n");
+}
+
+/* the interrupts should be disabled here so that no hardware
+ interrupts should occur, polling will be done to check received
+ data to avoid interrupt level processing */
+static int mdm_rcv(struct iCom_port *iCom_port_info, char *exp_str) {
+ int status = 0;
+ int loop_count = 0;
+ char *start_str;
+ int rcv_buff;
+
+ /* search expected string in received data */
+ while (!status && (loop_count++ < 10)) {
+ /* check buffer 1 */
+ start_str = (char *)iCom_port_info->recv_buf;
+ if (strstr(start_str, exp_str)) {
+ /* string found! */
+ status = 1;
+ break;
+ }
+
+ /* check buffer 2 */
+ start_str = (char *)iCom_port_info->recv_buf + 2048;
+ if (strstr(start_str, exp_str)) {
+ /* string found! */
+ status = 1;
+ break;
+ }
+
+ /* wait .5 seconds */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/2);
+
+ /* free up buffers if they had been used */
+ for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
+ iCom_port_info->statStg->rcv[rcv_buff].flags = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].leLength = 0;
+ iCom_port_info->statStg->rcv[rcv_buff].WorkingLength = (unsigned short int)cpu_to_le16(RCV_BUFF_SZ);
+ }
+ }
+
+ /* clear interrupts */
+ iCom_writew(0x3FFF,(void *)iCom_port_info->int_reg);
+
+ return status;
+}
+
+static void mdm_send(struct iCom_port *iCom_port_info, char *mdm_cmnd,
+ int cmnd_length) {
+
+ unsigned char cmdReg;
+ unsigned long int offset;
+
+
+ /* initialize transmit and receive operations */
+ offset = (unsigned long int)&iCom_port_info->statStg->rcv[0] - (unsigned long int)iCom_port_info->statStg;
+ iCom_writel(iCom_port_info->statStg_pci + offset,&iCom_port_info->dram->RcvStatusAddr);
+ iCom_port_info->next_rcv = 0;
+ iCom_port_info->put_length = 0;
+ *iCom_port_info->xmitRestart = 0;
+ iCom_writel(iCom_port_info->xmitRestart_pci,&iCom_port_info->dram->XmitStatusAddr);
+ iCom_writeb(CMD_XMIT_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+
+ /* clear target receive buffers 1 and 2 */
+ memset(iCom_port_info->recv_buf,0,4096);
+
+ memcpy(iCom_port_info->xmit_buf, mdm_cmnd, cmnd_length);
+
+ iCom_port_info->statStg->xmit[0].flags = (unsigned short int)cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
+ iCom_port_info->statStg->xmit[0].leLength = (unsigned short int)cpu_to_le16(cmnd_length);
+ offset = (unsigned long int)&iCom_port_info->statStg->xmit[0] - (unsigned long int)iCom_port_info->statStg;
+ *iCom_port_info->xmitRestart = cpu_to_le32(iCom_port_info->statStg_pci + offset);
+
+ cmdReg = iCom_readb(&iCom_port_info->dram->CmdReg);
+ iCom_writeb(cmdReg | CMD_XMIT_RCV_ENABLE,&iCom_port_info->dram->CmdReg);
+ iCom_writeb(START_XMIT,&iCom_port_info->dram->StartXmitCmd);
+ TRACE(iCom_port_info,TRACE_WRITE_START,cmnd_length);
+}
+
+static void iCom_set_code(struct iCom_port *iCom_port_info) {
+ char mdm_cmnd[15];
+ int index;
+
+ /* check if country code should be set at all */
+ if (strlen(iCom_country_code) == 0) return;
+
+ printk("iCom: Checking for country code on serial adapter %d port %d...",iCom_port_info->adapter, iCom_port_info->port);
+
+ /* sync up modems (if present) */
+ mdm_send(iCom_port_info,"AT\r\n",4);
+ mdm_rcv(iCom_port_info,"OK");
+
+ printk(".");
+
+ /* send ATI0 to check for internal modem */
+ mdm_send(iCom_port_info,"ATI0\r\n",6);
+
+ /* check returned data for internal modem identification */
+ if (!mdm_rcv(iCom_port_info,"SMI")) {
+ /* must not be internal modem - return */
+ printk("\n\tno internal modem on this port\n");
+ return;
+ }
+
+ /* send ATE0S0=0 to turn off command Echo and turn off Auto Answer */
+ mdm_send(iCom_port_info,"ATE0S0=0\r\n",10);
+
+ /* wait for OK */
+ if (!mdm_rcv(iCom_port_info,"OK")) {
+ /* unable to send command to modem - error */
+ printk("\niCom: Error, unable to set modem Country Code\n");
+ return;
+ }
+
+ printk(".");
+
+ /* Send new country code AT%T19,0, */
+ sprintf(mdm_cmnd,"AT%%T19,0,%s\r\n",iCom_country_code);
+ mdm_send(iCom_port_info,mdm_cmnd,strlen(mdm_cmnd));
+
+ /* wait for OK */
+ if (!mdm_rcv(iCom_port_info,"OK")) {
+ /* unable to set country code */
+ printk("\niCom: Error, unable to set modem Country Code\n");
+ return;
+ }
+
+ printk(".\n");
+
+ /* send ATE1S0=2 to enable command Echo and auto answer */
+ mdm_send(iCom_port_info,"ATE1S0=2\r\n",10);
+
+ /* wait for OK */
+ if (!mdm_rcv(iCom_port_info,"OK")) {
+ /* modem state unknown */
+ printk("iCom: Warning, modem state unknown\n");
+ }
+
+ /* print message that Country Code set appropriately */
+ printk("iCom: Modem country code for adapter %d port %d has been set to %s\n",iCom_port_info->adapter, iCom_port_info->port,iCom_country_code);
+
+ /* disable xmitter/recvr */
+ iCom_writeb(CMD_RCV_DISABLE,&iCom_port_info->dram->CmdReg);
+ for (index = 0; index < 10; index++) {
+ /* Wait 0.1 Sec for receive operations to complete*/
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+
+ if (iCom_readb(&iCom_port_info->dram->PrevCmdReg) == 0x00) {
+ break;
+ }
+ }
+
+ /* clear interrupts */
+ iCom_writew(0x3FFF,(void *)iCom_port_info->int_reg);
+}
+
+#ifdef ICOM_TRACE
+void TRACE(struct iCom_port *iCom_port_info, u32 trace_pt,
+ u32 trace_data) {
+
+ u32 *tp_start, *tp_end, **tp_next;
+
+ if (trace_pt == TRACE_GET_MEM) {
+ if (iCom_port_info->trace_blk != 0) return;
+ iCom_port_info->trace_blk = kmalloc(TRACE_BLK_SZ,GFP_KERNEL);
+ memset(iCom_port_info->trace_blk, 0,TRACE_BLK_SZ);
+ iCom_port_info->trace_blk[0] = (unsigned long)iCom_port_info->trace_blk + 3*sizeof(unsigned long);
+ iCom_port_info->trace_blk[1] = (unsigned long)iCom_port_info->trace_blk + TRACE_BLK_SZ;
+ iCom_port_info->trace_blk[2] = iCom_port_info->trace_blk[0];
+ }
+ if (iCom_port_info->trace_blk == 0) return;
+
+ if (trace_pt == TRACE_RET_MEM) {
+ kfree(iCom_port_info->trace_blk);
+ iCom_port_info->trace_blk = 0;
+ return;
+ }
+
+ tp_start = (u32 *)iCom_port_info->trace_blk[0];
+ tp_end = (u32 *)iCom_port_info->trace_blk[1];
+ tp_next = (u32 **)&iCom_port_info->trace_blk[2];
+
+ if (trace_data != 0) {
+ **tp_next = trace_data;
+ *tp_next = *tp_next + 1;
+ if (*tp_next == tp_end) *tp_next = tp_start;
+ **tp_next = TRACE_WITH_DATA | trace_pt;
+ }
+ else
+ **tp_next = trace_pt;
+
+ *tp_next = *tp_next + 1;
+ if (*tp_next == tp_end) *tp_next = tp_start;
+}
+#endif
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/icom.h linuxppc64_2_4/drivers/char/icom.h
--- ../kernel.org/linux-2.4.19/drivers/char/icom.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/char/icom.h Fri Dec 14 09:07:47 2001
@@ -0,0 +1,364 @@
+/*
+ * iCom.h
+ *
+ * Copyright (C) 2001 Michael Anderson, IBM Corporation
+ *
+ * Serial device driver include file.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define TRACE_BLK_SZ 1024
+#define TRACE_WITH_DATA 0x80000000
+#define TRACE_TIME 0x40000000
+#define TRACE_GET_MEM 0x20000000
+#define TRACE_RET_MEM 0x10000000
+#define TRACE_GET_PORT_MEM 0x00000001
+#define TRACE_FOD_ADDR 0x00000005
+#define TRACE_FOD_XBUFF 0x00000006
+#define TRACE_FID_ADDR 0x00000007
+#define TRACE_FID_RBUFF 0x00000008
+#define TRACE_RET_PORT_MEM 0x00000100
+#define TRACE_LOAD_MEM 0x00000200
+#define TRACE_CHANGE_SPEED 0x00000300
+#define TRACE_PARENB 0x00000301
+#define TRACE_PARODD 0x00000302
+#define TRACE_XR_ENAB 0x00000303
+#define TRACE_STARTUP 0x00000400
+#define TRACE_CABLE_ID 0x00000401
+#define TRACE_SHUTDOWN 0x00000500
+#define TRACE_DEVICE_NUMB 0x00000600
+#define TRACE_STARTUP_ERROR 0x00000601
+#define TRACE_CLOSE 0x00000700
+#define TRACE_CLOSE_HANGUP 0x00000701
+#define TRACE_OPEN_ACTIVE 0x00000702
+#define TRACE_WRITE 0x00000800
+#define TRACE_WRITE_FULL 0x00000801
+#define TRACE_WRITE_NODATA 0x00000802
+#define TRACE_WRITE_START 0x00000803
+#define TRACE_PUT_CHAR 0x00000900
+#define TRACE_PUT_FULL 0x00000901
+#define TRACE_FLUSH_CHAR 0x00000a00
+#define TRACE_START_FLUSH 0x00000a01
+#define TRACE_WRITE_ROOM 0x00000b00
+#define TRACE_CHARS_IN_BUFF 0x00000c00
+#define TRACE_CHARS_REMAIN 0x00000c01
+#define TRACE_GET_MODEM 0x00000d00
+#define TRACE_SET_MODEM 0x00000e00
+#define TRACE_RAISE_RTS 0x00000e01
+#define TRACE_RAISE_DTR 0x00000e02
+#define TRACE_LOWER_RTS 0x00000e03
+#define TRACE_LOWER_DTR 0x00000e04
+#define TRACE_GET_SERIAL 0x00000f00
+#define TRACE_SET_SERIAL 0x00001000
+#define TRACE_SET_LSR 0x00001100
+#define TRACE_IOCTL 0x00001200
+#define TRACE_IOCTL_IGNORE 0x00001201
+#define TRACE_SEND_XCHAR 0x00001300
+#define TRACE_QUICK_WRITE 0x00001301
+#define TRACE_THROTTLE 0x00001400
+#define TRACE_UNTHROTTLE 0x00001500
+#define TRACE_SET_TERMIOS 0x00001600
+#define TRACE_STOP 0x00001700
+#define TRACE_START 0x00001800
+#define TRACE_HANGUP 0x00001900
+#define TRACE_BREAK 0x00001a00
+#define TRACE_WAIT_UNTIL_SENT 0x00001b00
+#define TRACE_FLUSH_BUFFER 0x00001c00
+#define TRACE_CHECK_MODEM 0x00001d00
+#define TRACE_CTS_UP 0x00001d01
+#define TRACE_CTS_DOWN 0x00001d02
+#define TRACE_INTERRUPT 0x00001e00
+#define TRACE_XMIT_COMPLETE 0x00001e01
+#define TRACE_RCV_COMPLETE 0x00001e02
+#define TRACE_FID_STATUS 0x00001e03
+#define TRACE_RCV_COUNT 0x00001e04
+#define TRACE_REAL_COUNT 0x00001e05
+#define TRACE_BREAK_DET 0x00001e06
+#define TRACE_IGNORE_CHAR 0x00001e07
+#define TRACE_PARITY_ERROR 0x00001e08
+#define TRACE_XMIT_DISABLED 0x00001e09
+#define TRACE_WAKEUP 0x00001f00
+#define TRACE_CLEAR_INTERRUPTS 0x0000ff00
+#define TRACE_START_PROC_A 0x0000ff01
+#define TRACE_START_PROC_B 0x0000ff02
+#define TRACE_STOP_PROC_A 0x0000ff03
+#define TRACE_STOP_PROC_B 0x0000ff04
+#define TRACE_RAISE_DTR_RTS 0x0000ff05
+#define TRACE_START_PROC_C 0x0000ff06
+#define TRACE_START_PROC_D 0x0000ff07
+#define TRACE_STOP_PROC_C 0x0000ff08
+#define TRACE_STOP_PROC_D 0x0000ff09
+#define TRACE_ENABLE_INTERRUPTS_PA 0x0000ff0a
+#define TRACE_ENABLE_INTERRUPTS_PB 0x0000ff0b
+#define TRACE_ENABLE_INTERRUPTS_PC 0x0000ff0c
+#define TRACE_ENABLE_INTERRUPTS_PD 0x0000ff0d
+#define TRACE_DIS_INTERRUPTS_PA 0x0000ff0e
+#define TRACE_DIS_INTERRUPTS_PB 0x0000ff0f
+#define TRACE_DIS_INTERRUPTS_PC 0x0000ff10
+#define TRACE_DIS_INTERRUPTS_PD 0x0000ff11
+#define TRACE_DROP_DTR_RTS 0x0000ff12
+
+#ifndef TRACE_ONLY
+
+static unsigned char callSetup[1936] =
+ {0xBD,0xD9,0x23,0x00,0xDD,0xDD,0x18,0x05,0x23,0x80,0x3E,0x7F,0x23,0x00,0x3E,0x7C,
+ 0x23,0x10,0x3E,0xB7,0x3E,0xB5,0x23,0x20,0x3E,0xB6,0x3E,0xB4,0xA2,0x0A,0x86,0x0A,
+ 0xAE,0x0A,0x23,0x80,0x3E,0x01,0x23,0x2A,0x3E,0x02,0x3D,0xDA,0x23,0xFF,0x3E,0x03,
+ 0x22,0x86,0xD9,0xDD,0x10,0x1C,0x22,0x08,0x2F,0xF0,0xD9,0xF1,0x18,0x48,0xD9,0xDD,
+ 0x18,0x32,0x23,0x00,0x3E,0x06,0x3E,0x46,0x3E,0x86,0x3E,0xC6,0x21,0xF0,0x37,0xFA,
+ 0x2F,0xFA,0x3E,0x08,0x23,0x00,0x3E,0x07,0x3E,0x87,0x3E,0xC7,0x23,0xFF,0x3E,0x09,
+ 0x3E,0x47,0x00,0x39,0x23,0x00,0x3E,0x06,0x3E,0x08,0x23,0x13,0x3E,0x07,0x23,0x01,
+ 0x3E,0x09,0xA2,0xFC,0xA2,0xF6,0xCD,0xF0,0x10,0x3E,0x82,0xFC,0x23,0xFF,0x68,0x69,
+ 0x3B,0x00,0x10,0x3F,0x22,0x86,0xD9,0xDD,0x10,0x46,0x22,0x08,0x2F,0xF0,0x3D,0xF5,
+ 0x3B,0xB0,0x10,0x60,0x23,0x00,0x3E,0x02,0x23,0x01,0x3E,0x7C,0x23,0x14,0x3E,0x03,
+ 0x23,0x00,0x3E,0x7C,0x23,0x80,0xDD,0xF2,0x1B,0x95,0x68,0x69,0x3B,0x00,0x10,0x53,
+ 0x23,0x09,0x3E,0x02,0x3D,0xDA,0x23,0x08,0xDD,0xDD,0x18,0x5F,0x23,0x88,0x3E,0x7F,
+ 0x21,0xDA,0xC9,0xF0,0x10,0x64,0x33,0x04,0x3E,0x02,0x3D,0xDA,0xD9,0xF1,0x10,0x6B,
+ 0x23,0x00,0x3D,0xF1,0x00,0x93,0x21,0xF5,0x2F,0xF0,0x3B,0xE0,0x10,0x71,0x23,0x22,
+ 0x00,0x8A,0x3B,0x60,0x10,0x75,0x23,0x22,0x00,0x8A,0x3B,0xC0,0x10,0x79,0x23,0xEE,
+ 0x00,0x8A,0x3B,0xB0,0x10,0x7D,0x23,0x44,0x00,0x8A,0x3B,0x20,0x10,0x81,0x23,0xC4,
+ 0x00,0x8A,0x3B,0x30,0x10,0x85,0x23,0x22,0x00,0x8A,0x3B,0xF0,0x1B,0x99,0x00,0xB0,
+ 0xD9,0xDD,0x18,0x8B,0x3E,0x46,0x23,0x11,0x68,0x69,0x3B,0x00,0x10,0x8C,0x22,0x06,
+ 0x37,0xFF,0x3D,0xEF,0x81,0xF5,0x0A,0xD2,0x08,0xB6,0x08,0xD5,0x0A,0x4D,0x0B,0x4F,
+ 0xDD,0xF3,0x10,0x93,0xBD,0xF3,0x9D,0xD9,0x21,0xDE,0x3B,0x00,0x18,0xA7,0x5D,0x00,
+ 0x4B,0xC8,0x41,0xE0,0x22,0x7F,0x33,0x40,0x3E,0x7F,0x21,0xDE,0x4D,0xC8,0x58,0x00,
+ 0x4B,0xC8,0x41,0xFC,0x22,0x7F,0x2F,0xBF,0x3E,0x7F,0x21,0xF7,0x4D,0xC8,0x00,0xAF,
+ 0x23,0xDD,0x3D,0xF4,0x0B,0xB8,0x96,0xF4,0x9E,0xF6,0x00,0xB5,0xDD,0xE6,0x68,0x6E,
+ 0xC5,0xE6,0x68,0x6F,0xC1,0xE6,0x18,0xC6,0x81,0xE6,0x68,0x70,0x43,0xC8,0x45,0xCA,
+ 0x21,0xE4,0x68,0x62,0x21,0xE5,0x68,0x74,0x47,0xD4,0x68,0x6D,0x41,0xD2,0x43,0xC8,
+ 0x50,0x00,0x47,0xC8,0x41,0xC8,0x68,0x71,0x68,0x6E,0xC1,0xE6,0x68,0x6E,0x85,0xE6,
+ 0x8A,0xF4,0xC1,0xE6,0x68,0x6F,0xA5,0xE6,0x68,0x6D,0x21,0xE7,0x2F,0x0F,0x3D,0xCA,
+ 0x2F,0x0C,0x3B,0x0C,0x68,0x6E,0x21,0xE7,0xDE,0xF0,0x1A,0x41,0xDA,0xF0,0x18,0xFE,
+ 0xA2,0x0A,0x99,0xE7,0x23,0x00,0x3D,0xD6,0x3D,0xE8,0x21,0xCA,0x3B,0x0F,0x10,0xF0,
+ 0x23,0x81,0x3E,0x01,0x21,0xDA,0x3E,0x02,0x23,0x7E,0x3E,0x03,0x92,0xF1,0x00,0xFC,
+ 0x23,0x40,0x3E,0x01,0x21,0xC6,0x3E,0x02,0x21,0xC7,0x3E,0x03,0x21,0xCA,0x3B,0x0C,
+ 0x23,0x00,0x10,0xFB,0x23,0x01,0x3D,0xD7,0x82,0x0A,0x68,0x6D,0x21,0xCA,0x3B,0x0F,
+ 0x19,0x3C,0x21,0xE7,0xDE,0xF0,0x1A,0x41,0xD6,0xF0,0x11,0x22,0x68,0x70,0x43,0xC8,
+ 0x21,0xCA,0xC2,0xF0,0x21,0xC9,0x68,0x64,0xDD,0xD7,0x11,0x18,0x3D,0xC8,0x21,0xCA,
+ 0xC6,0xF0,0x21,0xC8,0x68,0x64,0x3D,0xC8,0x21,0xCA,0xCA,0xF0,0x21,0xC8,0x68,0x64,
+ 0x68,0x6B,0x29,0xD8,0x3B,0x7F,0xDA,0xF1,0x21,0xE7,0x2F,0x0F,0x3D,0xCA,0x11,0x22,
+ 0x91,0xE7,0x02,0x06,0xDE,0x04,0x68,0x6E,0x68,0x70,0x43,0xC8,0x21,0xCA,0xC2,0xF0,
+ 0x21,0xC9,0x68,0x64,0x3D,0xD8,0x21,0xCA,0x3D,0xCB,0xDD,0xD7,0x11,0x39,0x21,0xCA,
+ 0xC6,0xF0,0x21,0xD8,0x68,0x64,0x3D,0xD8,0x21,0xCA,0xCA,0xF0,0x21,0xD8,0x68,0x64,
+ 0x3D,0xD8,0x21,0xE7,0x2F,0x0F,0x3D,0xCA,0xDE,0x04,0x68,0x6E,0x22,0x00,0xCA,0x0A,
+ 0x11,0x48,0xA2,0x0A,0x99,0xE8,0x21,0xCA,0x3B,0x0F,0x1A,0x48,0x82,0x0A,0x68,0x6D,
+ 0x3D,0xC8,0x22,0x04,0x3D,0xC9,0x21,0xCA,0x3B,0x0F,0x11,0x58,0x21,0xC9,0x2F,0x38,
+ 0x3B,0x00,0x11,0x55,0x21,0xC8,0x3E,0xB0,0x01,0xEC,0xD5,0xE7,0x68,0x6E,0x02,0x06,
+ 0x21,0xC9,0xD6,0xF0,0x11,0x5C,0x95,0xE8,0xCE,0xF0,0x11,0x5F,0x9D,0xE8,0x21,0xD7,
+ 0x3B,0x00,0x19,0xEB,0xDE,0xF0,0x11,0x6C,0xDA,0xF0,0x19,0xAE,0x21,0xC8,0x3B,0x54,
+ 0x11,0xEC,0x23,0xC2,0x3D,0xD7,0x01,0xEC,0x3B,0x01,0x11,0x72,0x21,0xC8,0x3B,0x52,
+ 0x11,0xE9,0x01,0xE5,0x3B,0x02,0x11,0x78,0x21,0xC8,0x3B,0x4F,0x11,0xE9,0x01,0xE5,
+ 0x3B,0x03,0x11,0x7E,0x21,0xC8,0x3B,0x42,0x11,0xE9,0x01,0xE5,0x3B,0x04,0x11,0x84,
+ 0x21,0xC8,0x3B,0x47,0x11,0xE9,0x01,0xE5,0x3B,0x05,0x11,0x8A,0x21,0xC8,0x3B,0x20,
+ 0x11,0xE9,0x01,0xE5,0x3B,0x06,0x11,0x90,0x21,0xC8,0x3B,0x54,0x11,0xE9,0x01,0xE5,
+ 0x3B,0x07,0x11,0x96,0x21,0xC8,0x3B,0x4F,0x11,0xE9,0x01,0xE5,0x3B,0x08,0x11,0x9C,
+ 0x21,0xC8,0x3B,0x4E,0x11,0xE9,0x01,0xE5,0x3B,0x09,0x11,0xE9,0x21,0xC8,0x3B,0x59,
+ 0x11,0xE9,0x23,0x80,0x3D,0xD7,0x21,0xCB,0xC6,0xF0,0x21,0xD8,0x68,0x64,0x3D,0xD8,
+ 0x21,0xCB,0xCA,0xF0,0x21,0xD8,0x68,0x64,0x3D,0xD8,0x01,0xEC,0x2F,0x3F,0x3B,0x02,
+ 0x11,0xB5,0x21,0xC8,0x3B,0x4F,0x11,0xE2,0x01,0xE5,0x3B,0x03,0x11,0xBB,0x21,0xC8,
+ 0x3B,0x4E,0x11,0xE2,0x01,0xE5,0x3B,0x04,0x11,0xC1,0x21,0xC8,0x3B,0x59,0x11,0xE2,
+ 0x01,0xE5,0x3B,0x05,0x11,0xC7,0x21,0xC8,0x3B,0x20,0x11,0xE2,0x01,0xE5,0x3B,0x06,
+ 0x11,0xCD,0x21,0xC8,0x3B,0x52,0x11,0xE2,0x01,0xE5,0x3B,0x07,0x11,0xD3,0x21,0xC8,
+ 0x3B,0x4F,0x11,0xE2,0x01,0xE5,0x3B,0x08,0x11,0xD9,0x21,0xC8,0x3B,0x42,0x11,0xE2,
+ 0x01,0xE5,0x3B,0x09,0x11,0xE2,0x21,0xC8,0x3B,0x47,0x11,0xE2,0x21,0xE8,0x33,0x01,
+ 0x3D,0xE8,0x01,0xEC,0x23,0x80,0x3D,0xD7,0x01,0x66,0x21,0xD7,0x68,0x68,0x3D,0xD7,
+ 0x01,0xEB,0x23,0x00,0x3D,0xD7,0x95,0xE7,0xD1,0xE8,0x1A,0x15,0x45,0xDE,0xDE,0xF1,
+ 0x11,0xF3,0x91,0xE8,0x02,0x08,0x51,0xCC,0x21,0xD6,0x68,0x62,0x21,0xC8,0x68,0x67,
+ 0x45,0xDE,0x23,0x01,0x68,0x63,0x47,0xDE,0x21,0xD6,0x68,0x68,0x3D,0xD6,0x21,0xE8,
+ 0x2F,0x07,0x3B,0x01,0x1A,0x06,0x21,0xD6,0x3B,0x08,0x12,0x15,0xD1,0xE8,0x1A,0x15,
+ 0x21,0xD6,0x3B,0x00,0x1A,0x15,0x41,0xE0,0x57,0xCC,0xDD,0xF2,0x1B,0x95,0xC2,0xF1,
+ 0x12,0x0D,0x68,0x62,0x43,0xE0,0x23,0x00,0x3D,0xD6,0x21,0xCA,0x3B,0x0F,0x12,0x3A,
+ 0x21,0xC9,0x2F,0x38,0x3B,0x00,0x12,0x1E,0x95,0xE7,0x68,0x6D,0x3B,0x10,0x12,0x35,
+ 0x21,0xC8,0x3B,0x7E,0x12,0x32,0x22,0x74,0x3B,0x47,0x12,0x2F,0x22,0x75,0x3B,0x0F,
+ 0x12,0x2F,0x21,0xE7,0xDE,0xF0,0x1A,0x41,0xD6,0xF0,0x68,0x6E,0x02,0x48,0x23,0x04,
+ 0x3D,0xE8,0x02,0x48,0x23,0x03,0x3D,0xE8,0x02,0x48,0xCE,0xF0,0x68,0x6F,0x23,0x02,
+ 0x3D,0xE8,0x02,0x48,0x21,0xE8,0x2F,0x07,0x3B,0x01,0x1A,0x48,0xD1,0xE7,0x68,0x6E,
+ 0x02,0x48,0xA2,0x0A,0xCE,0xF4,0x68,0x6F,0xDD,0xE7,0x68,0x6E,0x23,0x07,0x3D,0xE8,
+ 0xA2,0x0A,0x23,0x00,0x3D,0xE7,0x8E,0xF4,0x68,0x6D,0x21,0xE7,0x2F,0x0F,0x3D,0xCA,
+ 0x2F,0x0C,0x3D,0xCB,0x3B,0x08,0x68,0x6E,0x21,0xE7,0xDE,0xF0,0x1A,0xC6,0xDA,0xF0,
+ 0x1A,0x72,0x99,0xE7,0x23,0x00,0x3D,0xD6,0x3D,0xE8,0x21,0xCA,0x3B,0x0B,0x12,0x69,
+ 0x23,0x81,0x3E,0x01,0x21,0xDA,0x3E,0x02,0x23,0x7E,0x3E,0x03,0x86,0x0A,0xAE,0x0A,
+ 0x68,0x6D,0x23,0x40,0x3E,0x01,0x21,0xC6,0x3E,0x02,0x21,0xC7,0x3E,0x03,0x86,0x0A,
+ 0xAE,0x0A,0x68,0x6D,0x21,0xD6,0x3B,0x00,0x12,0x96,0x45,0xDE,0xDE,0xF1,0x12,0x80,
+ 0x21,0xCA,0x3B,0x0B,0x12,0xCE,0xD1,0xE7,0x12,0xB2,0xC6,0x04,0x68,0x6E,0x02,0xCE,
+ 0x21,0xDF,0x3B,0x00,0x12,0x87,0x21,0xDE,0x3B,0x08,0xDA,0xF1,0x12,0x88,0x23,0x08,
+ 0x41,0xE0,0x55,0xCC,0xDD,0xF2,0x1B,0x95,0xC2,0xF1,0x12,0x8A,0x68,0x62,0x43,0xE0,
+ 0x45,0xDE,0x68,0x63,0x47,0xDE,0x3D,0xD6,0x23,0x00,0x3D,0xD8,0xC2,0x04,0x68,0x6E,
+ 0xD5,0xE7,0x1A,0xA3,0x21,0xCA,0x3B,0x0B,0x12,0xA3,0x23,0x7E,0x68,0x6A,0x3E,0x00,
+ 0x95,0xE7,0x96,0xF1,0xAE,0x0A,0x51,0xCC,0x21,0xD8,0x68,0x62,0x68,0x68,0x3D,0xD8,
+ 0x68,0x66,0x68,0x6B,0x3E,0x00,0x3E,0xB1,0xCE,0x0A,0x1A,0xC2,0x21,0xD6,0x68,0x69,
+ 0x3D,0xD6,0x02,0x72,0x22,0x05,0x2F,0x0F,0x3B,0x0C,0xDA,0xF1,0x68,0x6F,0x22,0x78,
+ 0x68,0x6B,0x3E,0x00,0x22,0x79,0x3E,0x00,0x23,0x7E,0x68,0x6A,0x3E,0x00,0x91,0xE7,
+ 0xCE,0x0A,0x68,0x6E,0x86,0x0A,0xAE,0x0A,0x99,0xE8,0x02,0xCE,0x86,0x0A,0xAE,0x0A,
+ 0xCE,0xF4,0x68,0x6F,0xDD,0xE7,0x68,0x6E,0x23,0x07,0x3D,0xE8,0x23,0x00,0x3D,0xE7,
+ 0x8E,0xF4,0x68,0x6D,0x22,0x86,0xD9,0xDD,0x12,0xD6,0x22,0x08,0x2F,0xF0,0x3B,0xB0,
+ 0x1B,0x05,0x22,0x86,0xD9,0xDD,0x12,0xDD,0x22,0x08,0x2F,0xF0,0x3B,0xF0,0x1B,0x99,
+ 0xD9,0xDD,0x1A,0xE7,0x21,0xF0,0x37,0xFA,0x2F,0xFA,0x3E,0x08,0x02,0xF0,0xDD,0xF0,
+ 0x68,0x6C,0xF2,0x06,0xD9,0xF0,0x68,0x6C,0xE6,0x06,0xC5,0xF0,0x68,0x6C,0xE2,0x06,
+ 0xDD,0xF2,0x1B,0x95,0xDD,0xF1,0x12,0xF7,0xDD,0xD9,0x1B,0x9F,0xBD,0xF1,0x22,0x06,
+ 0x37,0xFF,0x39,0xEF,0x12,0xFC,0x68,0x6D,0x3D,0xC8,0x35,0xEF,0x2D,0xEE,0x3B,0x00,
+ 0x21,0xC8,0x3D,0xEF,0x68,0x6F,0x92,0xF4,0x68,0x6D,0x22,0x86,0xD9,0xDD,0x13,0x09,
+ 0x22,0x08,0x2F,0xF0,0x3B,0xF0,0x1B,0x99,0xD9,0xDD,0x1B,0x12,0xDD,0xF0,0x68,0x6C,
+ 0xFE,0x08,0x03,0x15,0xDD,0xF0,0x68,0x6C,0xF2,0x06,0xDD,0xE9,0x13,0x1E,0xD9,0xE9,
+ 0x1B,0x1E,0x22,0xBC,0x3B,0x10,0x13,0x1F,0x99,0xE9,0x82,0xF4,0x3E,0xBC,0xDD,0xEA,
+ 0x13,0x28,0xD9,0xEA,0x1B,0x28,0x22,0xBD,0x3B,0x10,0x13,0x29,0x99,0xEA,0x82,0xF4,
+ 0x3E,0xBD,0xDD,0xEB,0x13,0x32,0xD9,0xEB,0x1B,0x32,0x22,0xBE,0x3B,0x10,0x13,0x33,
+ 0x99,0xEB,0x82,0xF4,0x3E,0xBE,0xDD,0xEC,0x13,0x3C,0xD9,0xEC,0x1B,0x3C,0x22,0xBF,
+ 0x3B,0x10,0x13,0x3D,0x99,0xEC,0x82,0xF4,0x3E,0xBF,0xDD,0xF2,0x1B,0x95,0xDD,0xF1,
+ 0x13,0x44,0xDD,0xD9,0x1B,0x9F,0xBD,0xF1,0x22,0x06,0x37,0xFF,0x2F,0x80,0x39,0xEF,
+ 0x13,0x4A,0x68,0x6D,0x3D,0xEF,0xDD,0xEE,0x68,0x6E,0x92,0xF4,0x68,0x6D,0x21,0xE7,
+ 0x2F,0x0F,0x3B,0x04,0x68,0x6E,0x21,0xE7,0xDE,0xF0,0x1B,0x75,0xDA,0xF0,0x1B,0x6A,
+ 0x68,0x70,0x43,0xC8,0x21,0xCA,0xC2,0xF0,0x21,0xC9,0x68,0x64,0x3D,0xD8,0x23,0x81,
+ 0x3E,0x01,0x21,0xDA,0x33,0x20,0x3E,0x02,0x23,0x80,0x3E,0x01,0x23,0x00,0x3E,0xF5,
+ 0x99,0xE7,0x68,0x6D,0x68,0x70,0x43,0xC8,0x21,0xCA,0xC2,0xF0,0x21,0xC9,0x68,0x64,
+ 0x68,0x6B,0x29,0xD8,0x3B,0x3B,0xDA,0xF1,0x13,0x80,0xCE,0xF4,0x68,0x6F,0xDD,0xE7,
+ 0x68,0x6E,0x21,0xDA,0x2F,0xDF,0x3E,0x02,0x68,0x6A,0xFE,0x08,0xBD,0xF0,0x03,0x91,
+ 0xD6,0xF0,0x1B,0x8D,0x22,0xF5,0x3B,0x08,0xDA,0xF1,0x68,0x6E,0x21,0xDA,0x2F,0xDF,
+ 0x3E,0x02,0x68,0x6A,0xFE,0x08,0xBD,0xF0,0x95,0xE7,0x22,0xF5,0x3B,0x22,0xDA,0xF1,
+ 0x68,0x6E,0x23,0x00,0x3D,0xE7,0x8E,0xF4,0x68,0x6D,0x0B,0xB8,0xBD,0xF2,0x9E,0xF6,
+ 0x03,0x96,0x23,0xCC,0x3D,0xF4,0x0B,0xB8,0x96,0xF4,0x9E,0xF6,0x03,0x9E,0xBD,0xD9,
+ 0xA2,0x0A,0x86,0x0A,0xAE,0x0A,0xBD,0xF1,0x0B,0x4F,0xD9,0xF1,0x1B,0xAF,0x0A,0xD2,
+ 0xDD,0xF3,0x13,0xA3,0x23,0x00,0x3D,0xF1,0xBD,0xF3,0x9D,0xD9,0x00,0x00,0x58,0x00,
+ 0x4B,0xC8,0x41,0xF8,0x22,0x7F,0x2F,0xBF,0x3E,0x7F,0x21,0xF6,0x4D,0xC8,0x03,0xB7,
+ 0xA2,0x0A,0x86,0x0A,0xAE,0x0A,0xD9,0xDD,0x1B,0xBF,0x23,0x00,0x3E,0x46,0x23,0x00,
+ 0x3D,0xF5,0x3D,0xF0,0x3D,0xEF,0x23,0x12,0x3E,0x06,0x23,0xC0,0x3E,0x08,0x68,0x6D};
+
+static unsigned char resRVdce[192] =
+ {0x22,0x86,0xD9,0xDD,0x15,0x04,0x22,0x08,0x2F,0xF0,0x3B,0xF0,0x1D,0x30,0xD9,0xDD,
+ 0x1D,0x0E,0x21,0xF0,0x37,0xFA,0x2F,0xFA,0x3E,0x08,0x05,0x17,0xDD,0xF0,0x68,0x6C,
+ 0xF2,0x06,0xD9,0xF0,0x68,0x6C,0xE6,0x06,0xC5,0xF0,0x68,0x6C,0xE2,0x06,0xDD,0xF2,
+ 0x1D,0x2C,0xDD,0xF1,0x15,0x1E,0xDD,0xD9,0x1D,0x36,0xBD,0xF1,0x22,0x06,0x37,0xFF,
+ 0x39,0xEF,0x15,0x23,0x68,0x6D,0x3D,0xC8,0x35,0xEF,0x2D,0xEE,0x3B,0x00,0x21,0xC8,
+ 0x3D,0xEF,0x68,0x6F,0x92,0xF4,0x68,0x6D,0x0D,0x4F,0xBD,0xF2,0x9E,0xF6,0x05,0x2D,
+ 0x23,0xCC,0x3D,0xF4,0x0D,0x4F,0x96,0xF4,0x9E,0xF6,0x05,0x35,0xBD,0xD9,0xA2,0x0A,
+ 0x86,0x0A,0xAE,0x0A,0xBD,0xF1,0x0D,0x5F,0xD9,0xF1,0x1D,0x46,0x0D,0x00,0xDD,0xF3,
+ 0x15,0x3A,0x23,0x00,0x3D,0xF1,0xBD,0xF3,0x9D,0xD9,0x00,0x00,0x58,0x00,0x4B,0xC8,
+ 0x41,0xF8,0x22,0x7F,0x2F,0xBF,0x3E,0x7F,0x21,0xF6,0x4D,0xC8,0x05,0x4E,0xA2,0x0A,
+ 0x86,0x0A,0xAE,0x0A,0xD9,0xDD,0x1D,0x56,0x23,0x00,0x3E,0x46,0x23,0x00,0x3D,0xF5,
+ 0x3D,0xF0,0x3D,0xEF,0x23,0x12,0x3E,0x06,0x23,0xC0,0x3E,0x08,0x68,0x6D,0x68,0x6D};
+
+static unsigned char funcLoad[1760] =
+ {0xA1,0xC5,0x23,0x40,0x3E,0x01,0x21,0xC6,0x3E,0x02,0x21,0xC7,0x3E,0x03,0x23,0x10,
+ 0x3E,0xB7,0x3E,0xB5,0x23,0x20,0x3E,0xB6,0x3E,0xB4,0xCD,0xBD,0x18,0x12,0x23,0x7E,
+ 0x3D,0x9F,0x00,0x14,0x23,0xC0,0x3D,0x9F,0x50,0x00,0x43,0x84,0x23,0x00,0x3D,0x98,
+ 0x0B,0x1E,0x09,0xAE,0xC1,0xC5,0x18,0x00,0x49,0x8C,0x68,0x6D,0x21,0xBF,0x3B,0x00,
+ 0x18,0x26,0xC2,0x04,0x10,0x32,0x3E,0x00,0x23,0x00,0x3D,0xBF,0xC5,0xC5,0x18,0x32,
+ 0xC9,0xC5,0x10,0x30,0xC2,0x04,0x10,0x32,0x23,0x00,0x68,0x6A,0x3E,0x00,0x00,0x32,
+ 0x49,0x8E,0x68,0x6D,0x21,0xC5,0x39,0xC4,0x13,0x3A,0xC2,0xF1,0x10,0x59,0xD5,0x98,
+ 0x18,0x44,0x95,0x98,0x45,0x8A,0xDE,0xF1,0x10,0x4F,0x21,0x42,0x39,0x40,0x10,0x54,
+ 0x45,0x88,0xDE,0xF1,0x10,0x55,0x00,0x59,0xB5,0x98,0x45,0x88,0xDE,0xF1,0x10,0x55,
+ 0x45,0x8A,0xDE,0xF1,0x10,0x4F,0x21,0x42,0x39,0x40,0x10,0x54,0x00,0x59,0x49,0x8A,
+ 0x45,0x84,0x47,0x8A,0x68,0x6D,0x00,0x59,0x00,0xD9,0x49,0x88,0x45,0x84,0x47,0x88,
+ 0x68,0x6D,0x0D,0x00,0x00,0x1A,0x00,0x1E,0xDE,0x04,0x10,0x1E,0x78,0x60,0x67,0x8C,
+ 0xD9,0x97,0x10,0x64,0xB9,0x97,0x00,0x97,0xDE,0x04,0x10,0xB6,0x68,0x70,0x43,0x80,
+ 0x45,0x82,0x21,0xB7,0x68,0x62,0x47,0x82,0x22,0x00,0x3D,0x90,0xCA,0x0A,0x18,0x97,
+ 0x22,0x04,0x2F,0x28,0x3D,0x91,0x3B,0x00,0x18,0x7E,0xD6,0xF0,0x10,0x7A,0x95,0xA4,
+ 0x8D,0xA4,0x00,0x7E,0xCE,0xF0,0x10,0x7E,0x9D,0xA4,0x8D,0xA4,0x45,0x70,0x21,0x90,
+ 0x68,0x67,0x68,0x60,0x47,0x70,0x21,0x99,0x68,0x68,0x3D,0x99,0x53,0x74,0x18,0xD7,
+ 0xCD,0xA4,0xAD,0xA4,0x18,0x97,0x3B,0x20,0x68,0x6E,0x21,0x70,0x3B,0x60,0xDA,0xF1,
+ 0x10,0x95,0x23,0x00,0x3D,0x70,0x39,0x74,0x18,0xD7,0x23,0x00,0x00,0xBA,0x78,0x5C,
+ 0x67,0x8C,0x21,0x70,0x3B,0x60,0xDA,0xF1,0x10,0xA1,0x23,0x00,0x3D,0x70,0x39,0x74,
+ 0x18,0xA5,0xCA,0x0A,0x18,0xA5,0xC1,0x97,0x10,0xAA,0xAA,0x0A,0xA1,0x97,0xA2,0x0A,
+ 0x23,0x50,0x00,0xB3,0xDD,0xA4,0x10,0xAE,0x23,0x90,0x00,0xB3,0xD5,0xA4,0x10,0xB2,
+ 0x23,0x11,0x00,0xB3,0x23,0x10,0xBD,0xA4,0xB5,0xA4,0x00,0xBA,0x41,0x80,0x68,0x71,
+ 0x68,0x6E,0x00,0x97,0x45,0x40,0x68,0x67,0x68,0x60,0x21,0x99,0x68,0x67,0x23,0x00,
+ 0x3D,0x99,0x21,0x40,0x68,0x6B,0x27,0x02,0x3B,0x40,0x10,0xC7,0x23,0x00,0x39,0x42,
+ 0x18,0xCB,0x3D,0x40,0x68,0x6D,0x45,0x40,0x3D,0x40,0x23,0x50,0x68,0x67,0x68,0x60,
+ 0x68,0x66,0x33,0x80,0x68,0x67,0xA2,0x0A,0x78,0x5C,0x67,0x8C,0x00,0x1E,0x81,0x97,
+ 0x00,0x97,0x45,0x42,0x68,0x66,0x3D,0x94,0x68,0x60,0x68,0x66,0x2F,0x3F,0x3D,0x95,
+ 0x3B,0x00,0x19,0x1E,0xDD,0x97,0x18,0xFE,0xD9,0x97,0x10,0xE8,0x9D,0x97,0x00,0xFE,
+ 0x45,0x50,0x43,0x90,0x50,0x00,0x21,0x95,0x68,0x62,0x53,0x90,0xDA,0xF1,0x19,0x59,
+ 0x45,0x50,0x68,0x63,0x47,0x50,0x41,0x4C,0x21,0x95,0x4F,0x74,0x68,0x62,0x43,0x4C,
+ 0x45,0x54,0x68,0x62,0x47,0x54,0x79,0x02,0x67,0x8A,0x00,0x59,0x21,0x95,0x45,0x54,
+ 0x68,0x62,0x47,0x54,0x21,0x74,0x68,0x6B,0x25,0x95,0x3B,0x60,0xDA,0xF1,0x11,0x09,
+ 0x23,0x00,0x3D,0x74,0x21,0x94,0xDA,0xF0,0x11,0x0E,0x82,0x0A,0x21,0x94,0xD2,0xF0,
+ 0x19,0x1E,0x45,0x50,0xDE,0xF1,0x11,0x86,0xDD,0x97,0x19,0x86,0x41,0x48,0xDE,0xF1,
+ 0x19,0x1C,0x23,0x0C,0x55,0x48,0x01,0x86,0x99,0x97,0x01,0x86,0xCD,0x94,0x11,0x22,
+ 0x8E,0xF2,0x01,0x86,0x41,0x44,0xDE,0xF1,0x19,0x43,0x45,0x42,0x68,0x60,0x68,0x66,
+ 0xDE,0xF0,0x11,0x2B,0x82,0x0A,0x21,0x94,0xDD,0x97,0x11,0x30,0x33,0x02,0xBD,0x97,
+ 0xB9,0x97,0x3D,0x56,0x41,0xB0,0x23,0x10,0x68,0x62,0x23,0x04,0x57,0x54,0x79,0x3A,
+ 0x67,0x8A,0x00,0x59,0x92,0xF2,0x23,0x00,0x3D,0x54,0x3D,0x55,0x41,0x44,0x43,0xB0,
+ 0x23,0x10,0x55,0x44,0x01,0x86,0x41,0xB0,0x23,0x10,0x55,0x44,0x79,0x49,0x67,0x8A,
+ 0x00,0x59,0x41,0x44,0xDE,0xF1,0x11,0x25,0x68,0x70,0x23,0x0E,0x68,0x62,0x43,0x78,
+ 0x79,0x53,0x67,0x8A,0x00,0x59,0x41,0x78,0x68,0x71,0x19,0x43,0x79,0x53,0x67,0x8A,
+ 0x00,0x59,0x21,0x95,0x68,0x6B,0x29,0x50,0x3D,0x95,0x41,0x4C,0x21,0x50,0x4F,0x74,
+ 0x45,0x54,0x68,0x62,0x47,0x54,0x79,0x66,0x67,0x8A,0x00,0x59,0x21,0x74,0x68,0x6B,
+ 0x25,0x50,0x3D,0x74,0x41,0x48,0xDE,0xF1,0x11,0x75,0x9D,0x97,0x21,0x95,0x45,0x54,
+ 0x68,0x62,0x47,0x54,0x50,0x00,0x47,0x50,0x01,0x02,0x23,0x0C,0x55,0x48,0x79,0x7A,
+ 0x67,0x8A,0x00,0x59,0x45,0x50,0x43,0x90,0x50,0x00,0x21,0x95,0x68,0x62,0x53,0x90,
+ 0xDA,0xF1,0x19,0x59,0x45,0x50,0x68,0x63,0x47,0x50,0x00,0xF3,0x21,0x42,0x68,0x6B,
+ 0x27,0x02,0x3B,0x40,0x11,0x8C,0x23,0x00,0x3D,0x42,0x00,0x59,0x4B,0x92,0x09,0xAE,
+ 0x41,0xB0,0xDE,0xF1,0x11,0x99,0x79,0x97,0x67,0x8A,0x49,0x92,0x68,0x6D,0x4B,0x92,
+ 0x01,0x90,0x23,0x10,0x55,0x44,0x79,0x9F,0x67,0x8A,0x49,0x92,0x68,0x6D,0x82,0x0A,
+ 0x78,0x5C,0x67,0x8C,0x50,0x00,0x43,0x70,0x43,0x74,0x23,0x00,0x3D,0xB5,0x3D,0x99,
+ 0x3D,0x54,0x3D,0x55,0x3D,0x97,0x00,0x53,0x81,0x98,0x01,0xAF,0xA1,0x98,0xA2,0x0A,
+ 0x51,0x00,0x47,0x40,0x47,0x42,0x45,0x84,0x47,0x8A,0x78,0x5B,0x67,0x8C,0xC1,0x98,
+ 0x68,0x6E,0x8A,0xF2,0x68,0x6D,0x00,0x32,0x96,0xF2,0xB1,0x98,0xDD,0xBC,0x10,0x32,
+ 0xBD,0xBC,0x79,0xC6,0x67,0x8E,0x7A,0x5D,0x67,0x88,0x00,0x32,0x00,0x32,0xC2,0x04,
+ 0x10,0x32,0x4B,0x92,0x0A,0x4D,0x49,0x92,0xC1,0x9D,0x11,0xCF,0x00,0x32,0x79,0xD2,
+ 0x67,0x8E,0x68,0x6D,0xC2,0x04,0x10,0x32,0x4B,0x92,0x0A,0x4D,0x49,0x92,0xC1,0x9D,
+ 0x11,0xDA,0x00,0x32,0x45,0x6E,0x68,0x66,0x68,0x6B,0x3E,0x00,0x21,0x6E,0x68,0x68,
+ 0x3B,0x00,0x11,0xE3,0x23,0x80,0x3D,0x6E,0x68,0x6B,0x29,0x6C,0x11,0xEA,0xC9,0x98,
+ 0x19,0xF7,0x01,0xEC,0xC9,0x98,0x19,0xF1,0x3B,0x00,0x68,0x6E,0x7A,0x0E,0x67,0x8E,
+ 0x68,0x6D,0x3B,0x1F,0xDA,0xF1,0x19,0xF7,0x3B,0x00,0x19,0xFD,0x68,0x6D,0x45,0x88,
+ 0xDE,0xF1,0x68,0x6E,0x7A,0x80,0x67,0x88,0x68,0x6D,0x7A,0x00,0x67,0x8E,0x68,0x6D,
+ 0xC2,0x04,0x10,0x32,0x4B,0x92,0x0A,0x4D,0x49,0x92,0xC1,0x9D,0x12,0x08,0x00,0x32,
+ 0x21,0x6C,0x39,0x6E,0x18,0x32,0x79,0xD2,0x67,0x8E,0x01,0xDA,0xC2,0x04,0x10,0x32,
+ 0x4B,0x92,0x0A,0x4D,0x49,0x92,0xC1,0x9D,0x12,0x16,0x00,0x32,0xBD,0x98,0x91,0x98,
+ 0xAD,0x98,0x7A,0x3D,0x67,0x8E,0x7A,0x1E,0x67,0x88,0x68,0x6D,0x41,0x58,0xDE,0xF1,
+ 0x12,0x27,0x41,0x7C,0x23,0x14,0x55,0x58,0x7A,0x27,0x67,0x88,0x00,0x59,0x23,0x80,
+ 0x3D,0x6A,0x41,0x7C,0x23,0x12,0x68,0x62,0x23,0x01,0x57,0x6A,0x7A,0x31,0x67,0x88,
+ 0x00,0x59,0x8D,0x98,0x41,0x58,0xDE,0xF1,0x12,0x37,0x9D,0x98,0x68,0x6D,0x96,0xF2,
+ 0xBD,0xBC,0xB1,0x98,0x79,0xC6,0x67,0x8E,0x02,0x6B,0xDD,0x98,0x10,0x32,0xDD,0xBC,
+ 0x19,0xBC,0xD5,0x6B,0x1A,0x48,0xD1,0x6B,0x1A,0x46,0x91,0x6B,0xC6,0x04,0x10,0x32,
+ 0x96,0xF2,0xB1,0x98,0x79,0xBE,0x67,0x8E,0x00,0x32,0xC1,0xBD,0x12,0x54,0x22,0x06,
+ 0xDA,0xF0,0x1A,0x55,0xBE,0x0A,0xA1,0x9D,0x68,0x6D,0x81,0x9D,0x22,0x05,0x2F,0x0F,
+ 0x3B,0x01,0xDA,0xF1,0x68,0x6E,0x9E,0x0A,0x68,0x6D,0x41,0xB8,0x23,0x04,0x55,0x7C,
+ 0x7A,0x63,0x67,0x88,0x00,0x59,0x41,0x7C,0xDE,0xF1,0x12,0x6B,0x23,0x88,0x3D,0xBE,
+ 0x79,0xBE,0x67,0x8E,0x00,0x59,0x23,0x14,0x55,0x58,0x43,0x7C,0x7A,0x72,0x67,0x88,
+ 0x99,0xB6,0x00,0x59,0x41,0x58,0xDE,0xF1,0x12,0x7B,0x41,0xB8,0x23,0x04,0x57,0x84,
+ 0x7A,0x7B,0x67,0x88,0x00,0x59,0x99,0x98,0x89,0x98,0x50,0x80,0x47,0x6C,0x47,0x6E,
+ 0x45,0x68,0xDE,0xF1,0x12,0xA5,0x23,0x80,0xDD,0x6B,0x13,0x17,0x23,0x10,0x3D,0x6A,
+ 0x41,0x7C,0x23,0x12,0x68,0x62,0x23,0x01,0x57,0x6A,0x7A,0x90,0x67,0x88,0x00,0x59,
+ 0x41,0x58,0x23,0x82,0xDE,0xF1,0x1B,0x17,0x23,0x14,0x55,0x58,0x43,0x7C,0x7A,0x9A,
+ 0x67,0x88,0x00,0x59,0xDD,0x6B,0x1A,0xA5,0x41,0x58,0xDE,0xF1,0x12,0xA5,0x41,0xB8,
+ 0x23,0x04,0x57,0x84,0x7A,0xA5,0x67,0x88,0x00,0x59,0x45,0x66,0xDE,0xF1,0x1A,0xC0,
+ 0x65,0x66,0x63,0x90,0x45,0x64,0x53,0x90,0xDA,0xF1,0x1A,0xB9,0x68,0x5D,0x67,0x66,
+ 0x41,0x5C,0x23,0x84,0xDE,0xF1,0x1B,0x17,0x23,0x0A,0x55,0x5C,0x7A,0xA8,0x67,0x88,
+ 0x00,0x59,0x68,0x7D,0x47,0x64,0x41,0x60,0x68,0x7C,0x43,0x60,0x70,0x00,0x67,0x66,
+ 0x45,0x64,0xDE,0xF1,0x12,0xCC,0x41,0x5C,0x23,0x86,0xDE,0xF1,0x1B,0x17,0x23,0x0A,
+ 0x55,0x5C,0x7A,0xCC,0x67,0x88,0x00,0x59,0x21,0x6E,0x68,0x6B,0x29,0x6C,0x1A,0xD3,
+ 0x3B,0x00,0x1A,0xD3,0x02,0xD6,0x23,0x00,0x68,0x6B,0x29,0x6C,0x3D,0x96,0x45,0x68,
+ 0x43,0x90,0x70,0x00,0x68,0x42,0x73,0x90,0xDA,0xF1,0x12,0xE5,0x45,0x64,0x53,0x90,
+ 0xDA,0xF1,0x21,0x64,0x12,0xEB,0x21,0x68,0x02,0xEB,0x63,0x90,0x45,0x64,0x53,0x90,
+ 0xDA,0xF1,0x1A,0xEC,0x21,0x64,0x3D,0x96,0x21,0x96,0x41,0x60,0x4D,0x6C,0x68,0x62,
+ 0x43,0x60,0x45,0x64,0x68,0x63,0x47,0x64,0x45,0x68,0x68,0x63,0x47,0x68,0x7A,0xFA,
+ 0x67,0x88,0x00,0x59,0x21,0x6C,0x68,0x6B,0x25,0x96,0x3B,0x00,0x13,0x00,0x23,0x80,
+ 0x3D,0x6C,0x45,0x68,0xDE,0xF1,0x13,0x0A,0xDD,0x6B,0x1B,0x0A,0xA9,0x98,0xD9,0x98,
+ 0x10,0x59,0x03,0x0F,0xD9,0x98,0x10,0x59,0x21,0x6C,0x3B,0x80,0x12,0x80,0xB9,0x98,
+ 0x79,0xC7,0x67,0x8E,0xC2,0x04,0x10,0x59,0x49,0x8E,0x68,0x6D,0x03,0x12,0x3D,0xBE,
+ 0x0B,0x1E,0x09,0xAE,0x9E,0xF4,0x00,0x1A,0x81,0x98,0x03,0x1F,0xA1,0x98,0x86,0x0A,
+ 0xAE,0x0A,0xC5,0x98,0x13,0x26,0x22,0x01,0x2F,0xFC,0x3E,0x01,0x45,0x84,0x47,0x88,
+ 0x79,0xBB,0x67,0x8E,0xC1,0x98,0x68,0x6E,0x86,0xF2,0x68,0x6D,0x79,0xBE,0x67,0x8E,
+ 0xB1,0x98,0xC5,0x98,0x13,0x5D,0x22,0x01,0x2F,0xFD,0xD5,0xBD,0x13,0x38,0x33,0x03,
+ 0x3E,0x01,0x03,0x5D,0x3D,0x90,0x35,0xC4,0x3D,0x91,0xD1,0x90,0x1B,0x60,0xD5,0x90,
+ 0x13,0x48,0xD5,0x91,0x10,0x35,0x09,0xAE,0x0B,0x1E,0x82,0xF2,0x95,0xC4,0x00,0x35,
+ 0xDD,0x91,0x13,0x4F,0xDD,0x90,0x13,0x4E,0x09,0x8E,0x03,0x4F,0x09,0xAC,0xD9,0x91,
+ 0x13,0x5D,0xD9,0x90,0x1B,0x2E,0xD1,0x98,0x13,0x5C,0xCD,0x98,0x1B,0x5A,0x21,0x90,
+ 0x33,0x40,0x03,0x5E,0x96,0xF2,0xB1,0x98,0x0B,0x1C,0x21,0x90,0x3D,0xC4,0x00,0x35,
+ 0x91,0xC4,0x58,0x00,0x4B,0xC8,0x41,0xFC,0x22,0x7F,0x2F,0xBF,0x3E,0x7F,0x21,0xF7,
+ 0x4D,0xC8,0x03,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+#endif
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/lp.c linuxppc64_2_4/drivers/char/lp.c
--- ../kernel.org/linux-2.4.19/drivers/char/lp.c Fri Apr 19 10:59:54 2002
+++ linuxppc64_2_4/drivers/char/lp.c Mon Apr 22 10:32:50 2002
@@ -611,10 +611,13 @@
return -EFAULT;
break;
case LPGETSTATUS:
- lp_claim_parport_or_block (&lp_table[minor]);
+ if (down_interruptible (&lp_table[minor].port_mutex))
+ return -EINTR;
+ parport_claim_or_block (lp_table[minor].dev);
status = r_str(minor);
- lp_release_parport (&lp_table[minor]);
-
+ parport_release (lp_table[minor].dev);
+
+ up (&lp_table[minor].port_mutex);
if (copy_to_user((int *) arg, &status, sizeof(int)))
return -EFAULT;
break;
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/char/tty_io.c linuxppc64_2_4/drivers/char/tty_io.c
--- ../kernel.org/linux-2.4.19/drivers/char/tty_io.c Wed May 15 08:29:38 2002
+++ linuxppc64_2_4/drivers/char/tty_io.c Mon May 13 16:39:12 2002
@@ -2184,6 +2184,11 @@
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
+
+#ifdef CONFIG_VIOCONS
+ viocons_init();
+#endif
+
#ifdef CONFIG_VT
con_init();
#endif
@@ -2245,6 +2250,9 @@
#ifdef CONFIG_SERIAL_TX3912_CONSOLE
tx3912_console_init();
#endif
+#ifdef CONFIG_HVC_CONSOLE
+ hvc_console_init();
+#endif
#ifdef CONFIG_TXX927_SERIAL_CONSOLE
txx927_console_init();
#endif
@@ -2301,6 +2309,10 @@
/* console calls tty_register_driver() before kmalloc() works.
* Thus, we can't devfs_register() then. Do so now, instead.
*/
+#ifdef CONFIG_VIOCONS
+ viocons_init2();
+#endif
+
#ifdef CONFIG_VT
con_init_devfs();
#endif
@@ -2389,5 +2401,8 @@
#endif
#ifdef CONFIG_A2232
a2232board_init();
+#endif
+#ifdef CONFIG_ICOM
+ iCom_init();
#endif
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/ide/sl82c105.c linuxppc64_2_4/drivers/ide/sl82c105.c
--- ../kernel.org/linux-2.4.19/drivers/ide/sl82c105.c Fri Apr 19 10:30:27 2002
+++ linuxppc64_2_4/drivers/ide/sl82c105.c Mon Dec 17 15:04:57 2001
@@ -156,6 +156,29 @@
}
/*
+ * Reset the controller.
+ * If we are using INTC under a w83c553 we need to use a magic test
+ * bit to do this. Return zero if successful (or applicable).
+ *
+ */
+static int sl82c105_hard_reset(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned int reg;
+
+ pci_read_config_dword(dev, 0x40, ®); /* LEGIRQ register */
+ if (reg & (1<<11)) { /* Using INTC? */
+ printk("sl82c105: resetting device\n");
+ pci_read_config_dword(dev, 0x7e, ®);
+ pci_write_config_word(dev, 0x7e, reg | (1<<2));
+ pci_write_config_word(dev, 0x7e, reg & (~(1<<2)));
+ return 0;
+ }
+ return 1;
+}
+
+/*
* Our own dmaproc, only to intercept ide_dma_check
*/
static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
@@ -171,6 +194,11 @@
case ide_dma_off:
config_for_pio(drive, 4, 0);
break;
+ case ide_dma_lostirq:
+ case ide_dma_timeout:
+ if (sl82c105_hard_reset(drive) == 0)
+ return 0;
+ break;
default:
break;
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/Makefile linuxppc64_2_4/drivers/iseries/Makefile
--- ../kernel.org/linux-2.4.19/drivers/iseries/Makefile Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/Makefile Thu Oct 11 11:10:49 2001
@@ -0,0 +1,43 @@
+#
+# Makefile for the iSeries-specific device drivers.
+#
+# 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..
+#
+
+# The target object and module list name.
+
+# O_TARGET := macintosh.o
+
+O_TARGET := iseries.o
+
+# Objects that export symbols.
+
+# export-objs := adb.o rtc.o mac_hid.o via-pmu.o
+
+export-objs := veth.o viocons.o viotape.o viodasd.o viocd.o viopath.o
+
+# Object file lists.
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_VETH) += veth.o
+obj-$(CONFIG_VIOCONS) += viocons.o
+obj-$(CONFIG_VIOPATH) += viopath.o
+obj-$(CONFIG_VIOTAPE) += viotape.o
+obj-$(CONFIG_VIODASD) += viodasd.o
+obj-$(CONFIG_VIOCD) += viocd.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
+
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/veth.c linuxppc64_2_4/drivers/iseries/veth.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/veth.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/veth.c Mon May 6 10:47:06 2002
@@ -0,0 +1,1731 @@
+/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */
+
+/**************************************************************************/
+/* */
+/* IBM eServer iSeries Virtual Ethernet Device Driver */
+/* Copyright (C) 2001 Kyle A. Lucke (klucke@us.ibm.com), IBM Corp. */
+/* */
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */
+/* USA */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with iSeries LPAR Linux. It utilizes low-level message passing*/
+/* provided by the hypervisor to enable an ethernet-like network device */
+/* that can be used to enable inter-partition communications on the same */
+/* physical iSeries. */
+/* */
+/* The iSeries LPAR hypervisor has currently defined the ability for a */
+/* partition to communicate on up to 16 different virtual ethernets, all */
+/* dynamically configurable, at least for an OS/400 partition. The */
+/* dynamic nature is not supported for Linux yet. */
+/* */
+/* Each virtual ethernet a given Linux partition participates in will */
+/* cause a network device with the form ethXX to be created, */
+/* */
+/* The virtual ethernet a given ethXX virtual ethernet device talks on */
+/* can be determined either by dumping /proc/iSeries/veth/vethX, where */
+/* X is the virtual ethernet number, and the netdevice name will be */
+/* printed out. The virtual ethernet a given ethX device communicates on */
+/* is also printed to the printk() buffer at module load time. */
+/* */
+/* This driver (and others like it on other partitions) is responsible for*/
+/* routing packets to and from other partitions. The MAC addresses used */
+/* by the virtual ethernets contain meaning, and should not be modified. */
+/* Doing so could disable the ability of your Linux partition to */
+/* communicate with the other OS/400 partitions on your physical iSeries. */
+/* Similarly, setting the MAC address to something other than the */
+/* "virtual burned-in" address is not allowed, for the same reason. */
+/* */
+/* Notes: */
+/* */
+/* 1. Although there is the capability to talk on multiple shared */
+/* ethernets to communicate to the same partition, each shared */
+/* ethernet to a given partition X will use a finite, shared amount */
+/* of hypervisor messages to do the communication. So having 2 shared */
+/* ethernets to the same remote partition DOES NOT double the */
+/* available bandwidth. Each of the 2 shared ethernets will share the */
+/* same bandwidth available to another. */
+/* */
+/* 2. It is allowed to have a virtual ethernet that does not communicate */
+/* with any other partition. It won't do anything, but it's allowed. */
+/* */
+/* 3. There is no "loopback" mode for a virtual ethernet device. If you */
+/* send a packet to your own mac address, it will just be dropped, you */
+/* won't get it on the receive side. Such a thing could be done, */
+/* but my default driver DOES NOT do so. */
+/* */
+/* 4. Multicast addressing is implemented via broadcasting the multicast */
+/* frames to other partitions. It is the responsibility of the */
+/* receiving partition to filter the addresses desired. */
+/* */
+/* 5. This module utilizes several different bottom half handlers for */
+/* non-high-use path function (setup, error handling, etc.). Multiple */
+/* bottom halves were used because only one would not keep up to the */
+/* much faster iSeries device drivers this Linux driver is talking to. */
+/* All hi-priority work (receiving frames, handling frame acks) is done*/
+/* in the interrupt handler for maximum performance. */
+/* */
+/* Tunable parameters: */
+/* */
+/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/
+/* be safely changed to something greater or less than the default. It */
+/* controls how much memory Linux will allocate per remote partition it is*/
+/* communicating with. The user can play with this to see how it affects */
+/* performance, packets dropped, etc. Without trying to understand the */
+/* complete driver, it can be thought of as the maximum number of packets */
+/* outstanding to a remote partition at a time. */
+/* */
+/**************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef SIOCETHTOOL
+#include
+#endif
+#include
+#include
+
+#ifndef _VETH_H
+#include "veth.h"
+#endif
+#ifndef _HVLPCONFIG_H
+#include
+#endif
+#ifndef _VETH_PROC_H
+#include
+#endif
+#ifndef _HVTYPES_H
+#include
+#endif
+#ifndef _ISERIES_PROC_H
+#include
+#endif
+#include
+#include
+
+
+#define veth_printk(fmt, args...) \
+printk(KERN_INFO "%s: " fmt, __FILE__, ## args)
+
+#define veth_error_printk(fmt, args...) \
+printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args)
+
+static const char *version __initdata = "v1.0 03/11/2002 Kyle Lucke, klucke@us.ibm.com\n";
+
+static int probed __initdata = 0;
+#define VethBuffersToAllocate 120
+
+static struct VethFabricMgr *mFabricMgr = NULL;
+static struct proc_dir_entry *veth_proc_root = NULL;
+
+DECLARE_MUTEX_LOCKED(VethProcSemaphore);
+
+static int veth_open(struct net_device *dev);
+static int veth_close(struct net_device *dev);
+static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void veth_handleEvent(struct HvLpEvent *, struct pt_regs *);
+static void veth_handleAck(struct HvLpEvent *);
+static void veth_handleInt(struct HvLpEvent *);
+static void veth_openConnections(void);
+static void veth_openConnection(u8, int lockMe);
+static void veth_closeConnection(u8, int lockMe);
+static void veth_intFinishOpeningConnections(void *, int number);
+static void veth_finishOpeningConnections(void *);
+static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *);
+static int veth_multicast_wanted(struct VethPort *port, u64 dest);
+static void veth_set_multicast_list(struct net_device *dev);
+
+static void veth_sendCap(struct VethLpConnection *);
+static void veth_sendMonitor(struct VethLpConnection *);
+static void veth_takeCap(struct VethLpConnection *, struct VethLpEvent *);
+static void veth_takeCapAck(struct VethLpConnection *, struct VethLpEvent *);
+static void veth_takeMonitorAck(struct VethLpConnection *, struct VethLpEvent *);
+static void veth_msgsInit(struct VethLpConnection *connection);
+static void veth_recycleMsg(struct VethLpConnection *, u16);
+static void veth_capBh(struct VethLpConnection *);
+static void veth_capAckBh(struct VethLpConnection *);
+static void veth_monitorAckBh(struct VethLpConnection *);
+static void veth_takeFrames(struct VethLpConnection *, struct VethLpEvent *);
+static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev);
+static struct net_device_stats *veth_get_stats(struct net_device *dev);
+static void veth_intFinishMsgsInit(void *, int);
+static void veth_finishMsgsInit(struct VethLpConnection *connection);
+static void veth_intFinishCapBh(void *, int);
+static void veth_finishCapBh(struct VethLpConnection *connection);
+static void veth_finishCapBhLocked(struct VethLpConnection *connection);
+static void veth_finishSendCap(struct VethLpConnection *connection);
+static void veth_timedAck(unsigned long connectionPtr);
+#ifdef MODULE
+static void veth_waitForEnd(void);
+#endif
+static void veth_failMe(struct VethLpConnection *connection);
+
+extern struct pci_dev *iSeries_veth_dev;
+
+int __init veth_probe(void)
+{
+ struct net_device *dev = NULL;
+ struct VethPort *port = NULL;
+ int vlansFound = 0;
+ int displayVersion = 0;
+
+ u16 vlanMap = HvLpConfig_getVirtualLanIndexMap();
+ int vlanIndex = 0;
+
+ if (probed)
+ return -ENODEV;
+ probed = 1;
+
+ while (vlanMap != 0) {
+ int bitOn = vlanMap & 0x8000;
+
+ if (bitOn) {
+ vlansFound++;
+
+ dev = init_etherdev(NULL, sizeof(struct VethPort));
+
+ if (dev == NULL) {
+ veth_error_printk("Unable to allocate net_device structure!\n");
+ break;
+ }
+
+ if (!dev->priv)
+ dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL);
+ if (!dev->priv) {
+ veth_error_printk("Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev);
+ port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *) dev->priv;
+ memset(port, 0, sizeof(struct VethPort));
+ rwlock_init(&(port->mMcastGate));
+ mFabricMgr->mPorts[vlanIndex]->mDev = dev;
+
+ dev->dev_addr[0] = 0x02;
+ dev->dev_addr[1] = 0x01;
+ dev->dev_addr[2] = 0xFF;
+ dev->dev_addr[3] = vlanIndex;
+ dev->dev_addr[4] = 0xFF;
+ dev->dev_addr[5] = HvLpConfig_getLpIndex_outline();
+ dev->mtu = 9000;
+
+ memcpy(&(port->mMyAddress), dev->dev_addr, 6);
+
+ dev->open = &veth_open;
+ dev->hard_start_xmit = &veth_start_xmit;
+ dev->stop = &veth_close;
+ dev->get_stats = veth_get_stats;
+ dev->set_multicast_list = &veth_set_multicast_list;
+ dev->do_ioctl = &veth_ioctl;
+ dev->features |= NETIF_F_SG;
+
+ /* display version info if adapter is found */
+ if (!displayVersion) {
+ /* set display flag to TRUE so that */
+ /* we only display this string ONCE */
+ displayVersion = 1;
+ veth_printk("%s", version);
+ }
+
+ }
+
+ ++vlanIndex;
+ vlanMap = vlanMap << 1;
+ }
+
+ if (vlansFound > 0)
+ return 0;
+ else
+ return -ENODEV;
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("Kyle Lucke ");
+MODULE_DESCRIPTION("iSeries Virtual ethernet driver");
+MODULE_LICENSE("GPL");
+
+DECLARE_MUTEX_LOCKED(VethModuleBhDone);
+int VethModuleReopen = 1;
+
+void veth_proc_delete(struct proc_dir_entry *iSeries_proc)
+{
+ int i = 0;
+ HvLpIndex thisLp = HvLpConfig_getLpIndex_outline();
+ u16 vlanMap = HvLpConfig_getVirtualLanIndexMap();
+ int vlanIndex = 0;
+
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ if (i != thisLp) {
+ if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) {
+ char name[10] = "";
+ sprintf(name, "lpar%d", i);
+ remove_proc_entry(name, veth_proc_root);
+ }
+ }
+ }
+
+ while (vlanMap != 0) {
+ int bitOn = vlanMap & 0x8000;
+
+ if (bitOn) {
+ char name[10] = "";
+ sprintf(name, "veth%d", vlanIndex);
+ remove_proc_entry(name, veth_proc_root);
+ }
+
+ ++vlanIndex;
+ vlanMap = vlanMap << 1;
+ }
+
+ remove_proc_entry("veth", iSeries_proc);
+
+ up(&VethProcSemaphore);
+}
+
+void veth_waitForEnd(void)
+{
+ up(&VethModuleBhDone);
+}
+
+void __exit veth_module_cleanup(void)
+{
+ int i;
+ struct VethFabricMgr *myFm = mFabricMgr;
+ struct tq_struct myBottomHalf;
+ struct net_device *thisOne = NULL;
+
+ VethModuleReopen = 0;
+
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ veth_closeConnection(i, 1);
+ }
+
+ myBottomHalf.routine = (void *) (void *) veth_waitForEnd;
+
+ queue_task(&myBottomHalf, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ down(&VethModuleBhDone);
+
+ HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan);
+
+ mb();
+ mFabricMgr = NULL;
+ mb();
+
+ down(&VethProcSemaphore);
+
+ iSeries_proc_callback(&veth_proc_delete);
+
+ down(&VethProcSemaphore);
+
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) {
+ mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs,
+ NULL, NULL);
+ }
+
+ if (myFm->mConnection[i].mMsgs != NULL) {
+ kfree(myFm->mConnection[i].mMsgs);
+ }
+ }
+
+ for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) {
+ if (myFm->mPorts[i] != NULL) {
+ thisOne = myFm->mPorts[i]->mDev;
+ myFm->mPorts[i] = NULL;
+
+ mb();
+
+ if (thisOne != NULL) {
+ veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i);
+ unregister_netdev(thisOne);
+ }
+ }
+ }
+
+ kfree(myFm);
+}
+
+module_exit(veth_module_cleanup);
+#endif
+
+
+void veth_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ long i = 0;
+ HvLpIndex thisLp = HvLpConfig_getLpIndex_outline();
+ u16 vlanMap = HvLpConfig_getVirtualLanIndexMap();
+ long vlanIndex = 0;
+
+
+ veth_proc_root = proc_mkdir("veth", iSeries_proc);
+ if (!veth_proc_root)
+ return;
+
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ if (i != thisLp) {
+ if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) {
+ struct proc_dir_entry *ent;
+ char name[10] = "";
+ sprintf(name, "lpar%d", (int) i);
+ ent = create_proc_entry(name, S_IFREG | S_IRUSR, veth_proc_root);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = (void *) i;
+ ent->read_proc = proc_veth_dump_connection;
+ ent->write_proc = NULL;
+ }
+ }
+ }
+
+ while (vlanMap != 0) {
+ int bitOn = vlanMap & 0x8000;
+
+ if (bitOn) {
+ struct proc_dir_entry *ent;
+ char name[10] = "";
+ sprintf(name, "veth%d", (int) vlanIndex);
+ ent = create_proc_entry(name, S_IFREG | S_IRUSR, veth_proc_root);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = (void *) vlanIndex;
+ ent->read_proc = proc_veth_dump_port;
+ ent->write_proc = NULL;
+ }
+
+ ++vlanIndex;
+ vlanMap = vlanMap << 1;
+ }
+
+ up(&VethProcSemaphore);
+}
+
+int __init veth_module_init(void)
+{
+ int status;
+ int i;
+
+ mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL);
+ memset(mFabricMgr, 0, sizeof(struct VethFabricMgr));
+ veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr);
+
+ mFabricMgr->mEyecatcher = 0x56455448464D4752ULL;
+ mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline();
+
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL;
+ veth_failMe(mFabricMgr->mConnection + i);
+ spin_lock_init(&mFabricMgr->mConnection[i].mAckGate);
+ spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate);
+ }
+
+ status = veth_probe();
+
+ if (status == 0) {
+ veth_openConnections();
+ iSeries_proc_callback(&veth_proc_init);
+ }
+
+ return status;
+}
+
+module_init(veth_module_init);
+
+static void veth_failMe(struct VethLpConnection *connection)
+{
+ connection->mConnectionStatus.mSentCap = 0;
+ connection->mConnectionStatus.mCapAcked = 0;
+ connection->mConnectionStatus.mGotCap = 0;
+ connection->mConnectionStatus.mGotCapAcked = 0;
+ connection->mConnectionStatus.mSentMonitor = 0;
+ connection->mConnectionStatus.mFailed = 1;
+}
+
+static int veth_open(struct net_device *dev)
+{
+ struct VethPort *port = (struct VethPort *) dev->priv;
+
+ memset(&port->mStats, 0, sizeof(port->mStats));
+ MOD_INC_USE_COUNT;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int veth_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct net_device_stats *veth_get_stats(struct net_device *dev)
+{
+ struct VethPort *port = (struct VethPort *) dev->priv;
+
+ return (&port->mStats);
+}
+
+
+static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned char *frame = skb->data;
+ HvLpIndex remoteLp = frame[5];
+ int i = 0;
+ int clone = 0;
+
+ if (mFabricMgr == NULL) {
+ veth_error_printk("NULL fabric manager with active ports!\n");
+ netif_stop_queue(dev);
+ return 1;
+ }
+
+ mb();
+
+ if ((*frame & 0x01) != 0x01) { /* broadcast or multicast */
+ if ((remoteLp != mFabricMgr->mThisLp) && (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, remoteLp)))
+ veth_pTransmit(skb, remoteLp, dev);
+ } else {
+ for (i = 0; i < HvMaxArchitectedLps; ++i) {
+ if (i != mFabricMgr->mThisLp) {
+ if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) {
+ if (clone)
+ skb = skb_clone(skb, GFP_ATOMIC);
+ else
+ clone = 1;
+
+ /* the ack handles deleting the skb */
+ veth_pTransmit(skb, i, dev);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev)
+{
+ struct VethLpConnection *connection = mFabricMgr->mConnection + remoteLp;
+ HvLpEvent_Rc returnCode;
+ struct scatterlist sg[VethMaxFramesPerMsg];
+ int nfrags = 0;
+ int nsg;
+
+ if (connection->mConnectionStatus.mFailed != 1) {
+ int rc = 0;
+ struct VethMsg *msg = NULL;
+ VETHSTACKPOP(&(connection->mMsgStack), msg);
+
+ /* We can't handle a fragmented frame if it has
+ more than VethMaxFramesPerMsg fragments.
+ Attempt to coalesce the fragments if possible,
+ otherwise drop the frame */
+
+ if ((skb_shinfo(skb)->nr_frags + 1) > VethMaxFramesPerMsg) {
+ veth_printk("Linearizing frame to handle > 6 frags\n");
+ rc = skb_linearize(skb, GFP_ATOMIC);
+ }
+
+ if (msg != NULL && rc == 0 && ((skb->len - 14) <= 9000)) {
+ /* Use a scatterlist for both the fraged un-fraged
+ case. pci_map_sg has a fastpast for the single
+ case so we can simplify this code and not take
+ too big of a perf hit.
+ */
+
+ if (skb_shinfo(skb)->nr_frags) { /* fragmented frame */
+ int i = 0;
+
+ sg[nfrags].address = skb->data;
+ sg[nfrags].length = skb->len - skb->data_len;
+ ++nfrags;
+
+ do {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ sg[nfrags].address = page_address(frag->page) + frag->page_offset;
+ sg[nfrags].length = frag->size;
+ ++nfrags;
+ ++i;
+ } while (i < skb_shinfo(skb)->nr_frags);
+ } else { /* unfragmented frame */
+ sg[nfrags].address = skb->data;
+ sg[nfrags].length = skb->len;
+ ++nfrags;
+ }
+ /*
+ Note: nsg may be less than nfrags. Each frag entry
+ in the skb can only be a page in size, so they can
+ be contigous in memory but be spread over multiple
+ frag entries in the skb. pci_map_sg will coalesce
+ contiguous fragments and into a single dma address.
+ */
+
+ nsg = pci_map_sg(iSeries_veth_dev, sg, nfrags, PCI_DMA_TODEVICE);
+
+ /* Is it really necessary to check the length and address fields of the
+ first entry here? */
+ if (nsg) {
+ int i = 0;
+ msg->mSkb = skb;
+ do {
+ msg->mEvent.mSendData.mAddress[i] = sg[i].dma_address;
+ msg->mEvent.mSendData.mLength[i] = sg[i].dma_length;
+ ++i;
+ } while (i < nsg);
+
+ msg->mEvent.mSendData.mEofMask = (1 << (nsg - 1));
+ test_and_set_bit(0, &(msg->mInUse));
+
+ returnCode = HvCallEvent_signalLpEventFast(remoteLp,
+ HvLpEvent_Type_VirtualLan,
+ VethEventTypeFrames,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ msg->mIndex,
+ msg->mEvent.mFpData.mData1,
+ msg->mEvent.mFpData.mData2,
+ msg->mEvent.mFpData.mData3,
+ msg->mEvent.mFpData.mData4,
+ msg->mEvent.mFpData.mData5);
+ } else {
+ returnCode = -1; /* Bad return code */
+ }
+
+ if (returnCode != HvLpEvent_Rc_Good) {
+ struct VethPort *port = (struct VethPort *) dev->priv;
+ if (nsg)
+ pci_unmap_sg(iSeries_veth_dev, sg, nsg, PCI_DMA_TODEVICE);
+
+ dev_kfree_skb_any(skb);
+
+ msg->mSkb = NULL;
+ memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData));
+ VETHSTACKPUSH(&(connection->mMsgStack), msg);
+ port->mStats.tx_dropped++;
+ } else {
+ struct VethPort *port = (struct VethPort *) dev->priv;
+ port->mStats.tx_packets++;
+ port->mStats.tx_bytes += skb->len;
+ }
+ } else {
+ struct VethPort *port = (struct VethPort *) dev->priv;
+ port->mStats.tx_dropped++;
+ if (rc)
+ port->mLinearized++;
+ dev_kfree_skb_any(skb);
+ }
+ } else {
+ struct VethPort *port = (struct VethPort *) dev->priv;
+ port->mStats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+#ifdef SIOCETHTOOL
+ struct ethtool_cmd ecmd;
+ u32 link, speed;
+
+ if (cmd != SIOCETHTOOL)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&ecmd, ifr->ifr_data, sizeof(ecmd)))
+ return -EFAULT;
+ switch (ecmd.cmd) {
+ case ETHTOOL_GSET:
+ ecmd.supported =
+ (SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_FIBRE);
+ ecmd.advertising =
+ (SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_FIBRE);
+
+ ecmd.port = PORT_FIBRE;
+ ecmd.transceiver = XCVR_INTERNAL;
+ ecmd.phy_address = 0;
+ ecmd.speed = SPEED_1000;
+ ecmd.duplex = DUPLEX_FULL;
+ ecmd.autoneg = AUTONEG_ENABLE;
+ ecmd.maxtxpkt = 120;
+ ecmd.maxrxpkt = 120;
+ if(copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd)))
+ return -EFAULT;
+ return 0;
+
+ case ETHTOOL_GDRVINFO: {
+ struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+ strncpy(info.driver, "veth", sizeof(info.driver) - 1);
+ info.driver[sizeof(info.driver) - 1] = '\0';
+ strncpy(info.version, "1.0", sizeof(info.version) - 1);
+ if (copy_to_user(ifr->ifr_data, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ /* get link status */
+ case ETHTOOL_GLINK: {
+ struct ethtool_value edata = {ETHTOOL_GLINK};
+ edata.data = 1;
+ if (copy_to_user(ifr->ifr_data, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+#endif
+ return -EOPNOTSUPP;
+}
+
+static void veth_set_multicast_list(struct net_device *dev)
+{
+ char *addrs;
+ struct VethPort *port = (struct VethPort *) dev->priv;
+ u64 newAddress = 0;
+ unsigned long flags;
+
+ write_lock_irqsave(&port->mMcastGate, flags);
+
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ port->mPromiscuous = 1;
+ } else {
+ struct dev_mc_list *dmi = dev->mc_list;
+
+ if (dev->flags & IFF_ALLMULTI) {
+ port->mAllMcast = 1;
+ } else {
+ int i;
+ /* Update table */
+ port->mNumAddrs = 0;
+
+ for (i = 0; ((i < dev->mc_count) && (i < 12)); i++) { /* for each address in the list */
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ memcpy(&newAddress, addrs, 6);
+ newAddress &= 0xFFFFFFFFFFFF0000;
+
+ port->mMcasts[port->mNumAddrs] = newAddress;
+ mb();
+ port->mNumAddrs = port->mNumAddrs + 1;
+ }
+ }
+ }
+ }
+
+ write_unlock_irqrestore(&port->mMcastGate, flags);
+}
+
+
+static void veth_handleEvent(struct HvLpEvent *event, struct pt_regs *regs)
+{
+ if (event->xFlags.xFunction == HvLpEvent_Function_Ack) {
+ veth_handleAck(event);
+ } else if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ veth_handleInt(event);
+ }
+}
+
+static void veth_handleAck(struct HvLpEvent *event)
+{
+ struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xTargetLp]);
+ struct VethLpEvent *vethEvent = (struct VethLpEvent *) event;
+
+ switch (event->xSubtype) {
+ case VethEventTypeCap:
+ {
+ veth_takeCapAck(connection, vethEvent);
+ break;
+ }
+ case VethEventTypeMonitor:
+ {
+ veth_takeMonitorAck(connection, vethEvent);
+ break;
+ }
+ default:
+ {
+ veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp);
+ }
+ };
+}
+
+static void veth_handleInt(struct HvLpEvent *event)
+{
+ int i = 0;
+ struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xSourceLp]);
+ struct VethLpEvent *vethEvent = (struct VethLpEvent *) event;
+
+ switch (event->xSubtype) {
+ case VethEventTypeCap:
+ {
+ veth_takeCap(connection, vethEvent);
+ break;
+ }
+ case VethEventTypeMonitor:
+ {
+ /* do nothing... this'll hang out here til we're dead, and the hypervisor will return it for us. */
+ break;
+ }
+ case VethEventTypeFramesAck:
+ {
+ for (i = 0; i < VethMaxFramesMsgsAcked; ++i) {
+ u16 msg = vethEvent->mDerivedData.mFramesAckData.mToken[i];
+ veth_recycleMsg(connection, msg);
+ }
+ break;
+ }
+ case VethEventTypeFrames:
+ {
+ veth_takeFrames(connection, vethEvent);
+ break;
+ }
+ default:
+ {
+ veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp);
+ }
+ };
+}
+
+static void veth_openConnections()
+{
+ int i = 0;
+
+ HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handleEvent);
+
+ /* Now I need to run through the active lps and open connections to the ones I'm supposed to
+ open to. */
+
+ for (i = HvMaxArchitectedLps - 1; i >= 0; --i) {
+ if (i != mFabricMgr->mThisLp) {
+ if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) {
+ veth_openConnection(i, 1);
+ } else {
+ veth_closeConnection(i, 1);
+ }
+ }
+ }
+}
+
+static void veth_intFinishOpeningConnections(void *parm, int number)
+{
+ struct VethLpConnection *connection = (struct VethLpConnection *) parm;
+ connection->mAllocBhTq.data = parm;
+ connection->mNumberAllocated = number;
+ queue_task(&connection->mAllocBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void veth_finishOpeningConnections(void *parm)
+{
+ unsigned long flags;
+ struct VethLpConnection *connection = (struct VethLpConnection *) parm;
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+ veth_finishOpeningConnectionsLocked(connection);
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection)
+{
+ if (connection->mNumberAllocated >= 2) {
+ connection->mConnectionStatus.mCapMonAlloced = 1;
+ veth_sendCap(connection);
+ } else {
+ veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp,
+ connection->mNumberAllocated);
+ veth_failMe(connection);
+ }
+}
+
+static void veth_openConnection(u8 remoteLp, int lockMe)
+{
+ unsigned long flags;
+ unsigned long flags2;
+ HvLpInstanceId source;
+ HvLpInstanceId target;
+ u64 i = 0;
+ struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]);
+
+ memset(&connection->mCapBhTq, 0, sizeof(connection->mCapBhTq));
+ connection->mCapBhTq.routine = (void *) (void *) veth_capBh;
+
+ memset(&connection->mCapAckBhTq, 0, sizeof(connection->mCapAckBhTq));
+ connection->mCapAckBhTq.routine = (void *) (void *) veth_capAckBh;
+
+ memset(&connection->mMonitorAckBhTq, 0, sizeof(connection->mMonitorAckBhTq));
+ connection->mMonitorAckBhTq.routine = (void *) (void *) veth_monitorAckBh;
+
+ memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq));
+ connection->mAllocBhTq.routine = (void *) (void *) veth_finishOpeningConnections;
+
+ if (lockMe)
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+
+ connection->mRemoteLp = remoteLp;
+
+ spin_lock_irqsave(&connection->mAckGate, flags2);
+
+ memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData));
+ connection->mNumAcks = 0;
+
+ HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan);
+
+ /* clean up non-acked msgs */
+ for (i = 0; i < connection->mNumMsgs; ++i) {
+ veth_recycleMsg(connection, i);
+ }
+
+ connection->mConnectionStatus.mOpen = 1;
+
+ source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan);
+ target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan);
+
+ if (connection->mConnectionStatus.mCapMonAlloced != 1) {
+ connection->mAllocBhTq.routine = (void *) (void *) veth_finishOpeningConnections;
+ mf_allocateLpEvents(remoteLp,
+ HvLpEvent_Type_VirtualLan,
+ sizeof(struct VethLpEvent), 2, &veth_intFinishOpeningConnections, connection);
+ } else {
+ veth_finishOpeningConnectionsLocked(connection);
+ }
+
+ spin_unlock_irqrestore(&connection->mAckGate, flags2);
+
+ if (lockMe)
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_closeConnection(u8 remoteLp, int lockMe)
+{
+ struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]);
+ unsigned long flags;
+ unsigned long flags2;
+ if (lockMe)
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+
+ del_timer(&connection->mAckTimer);
+
+ if (connection->mConnectionStatus.mOpen == 1) {
+ HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan);
+ connection->mConnectionStatus.mOpen = 0;
+ veth_failMe(connection);
+
+ /* reset ack data */
+ spin_lock_irqsave(&connection->mAckGate, flags2);
+
+ memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData));
+ connection->mNumAcks = 0;
+
+ spin_unlock_irqrestore(&connection->mAckGate, flags2);
+ }
+
+ if (lockMe)
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_msgsInit(struct VethLpConnection *connection)
+{
+ connection->mAllocBhTq.routine = (void *) (void *) veth_finishMsgsInit;
+ mf_allocateLpEvents(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ sizeof(struct VethLpEvent),
+ connection->mMyCap.mUnionData.mFields.mNumberBuffers, &veth_intFinishMsgsInit, connection);
+}
+
+static void veth_intFinishMsgsInit(void *parm, int number)
+{
+ struct VethLpConnection *connection = (struct VethLpConnection *) parm;
+ connection->mAllocBhTq.data = parm;
+ connection->mNumberRcvMsgs = number;
+ queue_task(&connection->mAllocBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void veth_intFinishCapBh(void *parm, int number)
+{
+ struct VethLpConnection *connection = (struct VethLpConnection *) parm;
+ connection->mAllocBhTq.data = parm;
+ if (number > 0)
+ connection->mNumberLpAcksAlloced += number;
+
+ queue_task(&connection->mAllocBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static void veth_finishMsgsInit(struct VethLpConnection *connection)
+{
+ int i = 0;
+ unsigned int numberGotten = 0;
+ u64 amountOfHeapToGet = connection->mMyCap.mUnionData.mFields.mNumberBuffers * sizeof(struct VethMsg);
+ char *msgs = NULL;
+ unsigned long flags;
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+
+ if (connection->mNumberRcvMsgs >= connection->mMyCap.mUnionData.mFields.mNumberBuffers) {
+ msgs = kmalloc(amountOfHeapToGet, GFP_ATOMIC);
+
+ connection->mMsgs = (struct VethMsg *) msgs;
+
+ if (msgs != NULL) {
+ memset(msgs, 0, amountOfHeapToGet);
+
+ for (i = 0; i < connection->mMyCap.mUnionData.mFields.mNumberBuffers; ++i) {
+ connection->mMsgs[i].mIndex = i;
+ ++numberGotten;
+ VETHSTACKPUSH(&(connection->mMsgStack), (connection->mMsgs + i));
+ }
+ if (numberGotten > 0) {
+ connection->mNumMsgs = numberGotten;
+ }
+ } else {
+ kfree(msgs);
+ connection->mMsgs = NULL;
+ }
+ }
+
+ connection->mMyCap.mUnionData.mFields.mNumberBuffers = connection->mNumMsgs;
+
+ if (connection->mNumMsgs < 10)
+ connection->mMyCap.mUnionData.mFields.mThreshold = 1;
+ else if (connection->mNumMsgs < 20)
+ connection->mMyCap.mUnionData.mFields.mThreshold = 4;
+ else if (connection->mNumMsgs < 40)
+ connection->mMyCap.mUnionData.mFields.mThreshold = 10;
+ else
+ connection->mMyCap.mUnionData.mFields.mThreshold = 20;
+
+ connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec;
+
+ veth_finishSendCap(connection);
+
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_sendCap(struct VethLpConnection *connection)
+{
+ if (connection->mMsgs == NULL) {
+ connection->mMyCap.mUnionData.mFields.mNumberBuffers = VethBuffersToAllocate;
+ veth_msgsInit(connection);
+ } else {
+ veth_finishSendCap(connection);
+ }
+}
+
+static void veth_finishSendCap(struct VethLpConnection *connection)
+{
+ HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ VethEventTypeCap,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ 0,
+ connection->mMyCap.mUnionData.mNoFields.mReserved1,
+ connection->mMyCap.mUnionData.mNoFields.mReserved2,
+ connection->mMyCap.mUnionData.mNoFields.mReserved3,
+ connection->mMyCap.mUnionData.mNoFields.mReserved4,
+ connection->mMyCap.mUnionData.mNoFields.mReserved5);
+
+ if ((returnCode == HvLpEvent_Rc_PartitionDead) || (returnCode == HvLpEvent_Rc_PathClosed)) {
+ connection->mConnectionStatus.mSentCap = 0;
+ } else if (returnCode != HvLpEvent_Rc_Good) {
+ veth_error_printk("Couldn't send cap to lpar %d, rc %x\n", connection->mRemoteLp, (int) returnCode);
+ veth_failMe(connection);
+ } else {
+ connection->mConnectionStatus.mSentCap = 1;
+ }
+}
+
+static void veth_takeCap(struct VethLpConnection *connection, struct VethLpEvent *event)
+{
+ if (!test_and_set_bit(0, &(connection->mCapBhPending))) {
+ connection->mCapBhTq.data = connection;
+ memcpy(&connection->mCapEvent, event, sizeof(connection->mCapEvent));
+ queue_task(&connection->mCapBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ } else {
+ veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp);
+ event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable;
+ HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
+ }
+}
+
+static void veth_takeCapAck(struct VethLpConnection *connection, struct VethLpEvent *event)
+{
+ if (!test_and_set_bit(0, &(connection->mCapAckBhPending))) {
+ connection->mCapAckBhTq.data = connection;
+ memcpy(&connection->mCapAckEvent, event, sizeof(connection->mCapAckEvent));
+ queue_task(&connection->mCapAckBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ } else {
+ veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n",
+ connection->mRemoteLp);
+ }
+}
+
+static void veth_takeMonitorAck(struct VethLpConnection *connection, struct VethLpEvent *event)
+{
+ if (!test_and_set_bit(0, &(connection->mMonitorAckBhPending))) {
+ connection->mMonitorAckBhTq.data = connection;
+ memcpy(&connection->mMonitorAckEvent, event, sizeof(connection->mMonitorAckEvent));
+ queue_task(&connection->mMonitorAckBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ } else {
+ veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp);
+ }
+}
+
+static void veth_recycleMsg(struct VethLpConnection *connection, u16 msg)
+{
+ struct scatterlist sg[VethMaxFramesPerMsg];
+ if (msg < connection->mNumMsgs) {
+ struct VethMsg *myMsg = connection->mMsgs + msg;
+ if (test_and_clear_bit(0, &(myMsg->mInUse))) {
+ int i;
+ int nsg = 0;
+ for (i = 0; i < VethMaxFramesPerMsg; i++) {
+ if (myMsg->mEvent.mSendData.mAddress[i] != 0) {
+ sg[nsg].dma_address = myMsg->mEvent.mSendData.mAddress[i];
+ sg[nsg].dma_length = myMsg->mEvent.mSendData.mLength[i];
+ nsg++;
+ }
+ }
+ pci_unmap_sg(iSeries_veth_dev, sg, nsg, PCI_DMA_TODEVICE);
+
+ dev_kfree_skb_any(myMsg->mSkb);
+
+ myMsg->mSkb = NULL;
+ memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData));
+ VETHSTACKPUSH(&connection->mMsgStack, myMsg);
+ } else {
+ if (connection->mConnectionStatus.mOpen) {
+ veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg,
+ connection->mRemoteLp);
+ }
+ }
+ }
+}
+
+static void veth_capBh(struct VethLpConnection *connection)
+{
+ struct VethLpEvent *event = &connection->mCapEvent;
+ unsigned long flags;
+ struct VethCapData *remoteCap = &(connection->mRemoteCap);
+ u64 numAcks = 0;
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+ connection->mConnectionStatus.mGotCap = 1;
+
+ memcpy(remoteCap, &(event->mDerivedData.mCapabilitiesData), sizeof(connection->mRemoteCap));
+
+ if ((remoteCap->mUnionData.mFields.mNumberBuffers <= VethMaxFramesMsgs) &&
+ (remoteCap->mUnionData.mFields.mNumberBuffers != 0) &&
+ (remoteCap->mUnionData.mFields.mThreshold <= VethMaxFramesMsgsAcked) &&
+ (remoteCap->mUnionData.mFields.mThreshold != 0)) {
+ numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1;
+
+ if (connection->mNumberLpAcksAlloced < numAcks) {
+ numAcks = numAcks - connection->mNumberLpAcksAlloced;
+ connection->mAllocBhTq.routine = (void *) (void *) veth_finishCapBh;
+ mf_allocateLpEvents(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ sizeof(struct VethLpEvent), numAcks, &veth_intFinishCapBh, connection);
+ } else
+ veth_finishCapBhLocked(connection);
+ } else {
+ veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp);
+ event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData;
+ HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
+ }
+
+ clear_bit(0, &(connection->mCapBhPending));
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_capAckBh(struct VethLpConnection *connection)
+{
+ struct VethLpEvent *event = &connection->mCapAckEvent;
+ unsigned long flags;
+
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+
+ if (event->mBaseEvent.xRc == HvLpEvent_Rc_Good) {
+ connection->mConnectionStatus.mCapAcked = 1;
+
+ if ((connection->mConnectionStatus.mGotCap == 1) && (connection->mConnectionStatus.mGotCapAcked == 1)) {
+ if (connection->mConnectionStatus.mSentMonitor != 1)
+ veth_sendMonitor(connection);
+ }
+ } else {
+ veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp);
+ veth_failMe(connection);
+ }
+
+ clear_bit(0, &(connection->mCapAckBhPending));
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_monitorAckBh(struct VethLpConnection *connection)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+
+ veth_failMe(connection);
+
+ veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp);
+
+ if (connection->mConnectionStatus.mOpen) {
+ veth_closeConnection(connection->mRemoteLp, 0);
+
+ udelay(100);
+
+ queue_task(&connection->mMonitorAckBhTq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ } else {
+#ifdef MODULE
+ if (VethModuleReopen)
+#endif
+ veth_openConnection(connection->mRemoteLp, 0);
+#ifdef MODULE
+ else {
+ int i = 0;
+
+ for (i = 0; i < connection->mNumMsgs; ++i) {
+ veth_recycleMsg(connection, i);
+ }
+ }
+#endif
+ clear_bit(0, &(connection->mMonitorAckBhPending));
+ }
+
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+#define number_of_pages(v, l) ((((unsigned long)(v) & ((1 << 12) - 1)) + (l) + 4096 - 1) / 4096)
+#define page_offset(v) ((unsigned long)(v) & ((1 << 12) - 1))
+
+static void veth_takeFrames(struct VethLpConnection *connection, struct VethLpEvent *event)
+{
+ int i = 0;
+ struct VethPort *port = NULL;
+ struct BufList {
+ union {
+ struct {
+ u32 token2;
+ u32 garbage;
+ } token1;
+ u64 address;
+ } addr;
+ u64 size;
+ };
+
+ struct BufList myBufList[4]; /* max pages per frame */
+ struct BufList remoteList[VethMaxFramesPerMsg]; /* max frags per frame */
+
+ do {
+ int nfrags = 0;
+ u16 length = 0;
+
+ /* a 0 address marks the end of the valid entries */
+ if (event->mDerivedData.mSendData.mAddress[i] == 0)
+ break;
+
+ /* make sure that we have at least 1 EOF entry in the remaining entries */
+ if (!(event->mDerivedData.mSendData.mEofMask >> i)) {
+ veth_printk("bad lp event: missing EOF frag in event mEofMask 0x%x i %d\n",
+ event->mDerivedData.mSendData.mEofMask, i);
+ break;
+ }
+
+ /* add up length of non-EOF frags */
+ do {
+ remoteList[nfrags].addr.token1.token2 = event->mDerivedData.mSendData.mAddress[i + nfrags];
+ remoteList[nfrags].addr.token1.garbage = 0;
+ length += remoteList[nfrags].size = event->mDerivedData.mSendData.mLength[i + nfrags];
+ }
+ while (!(event->mDerivedData.mSendData.mEofMask & (1 << (i + nfrags++))));
+
+
+ /* length == total length of all framgents */
+ /* nfrags == # of fragments in this frame */
+
+ if ((length - 14) <= 9000) { /* save as 13 < length <= 9014 */
+ struct sk_buff *skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb != NULL) {
+ HvLpDma_Rc returnCode = HvLpDma_Rc_Good;
+
+ /* build the buffer list for the dma operation */
+ int numPages = number_of_pages((skb->data), length); /* number of pages in this fragment of the complete buffer */
+ myBufList[0].addr.address =
+ (0x8000000000000000LL | (virt_to_absolute((unsigned long) skb->data)));
+ myBufList[0].size = (numPages > 1) ? (4096 - page_offset(skb->data)) : length;
+ if (numPages > 1) {
+ myBufList[1].addr.address =
+ (0x8000000000000000LL |
+ (virt_to_absolute((unsigned long) skb->data + myBufList[0].size)));
+ myBufList[1].size = (numPages > 2) ? 4096 : length - myBufList[0].size;
+ if (numPages > 2) {
+ myBufList[2].addr.address =
+ (0x8000000000000000LL |
+ (virt_to_absolute
+ ((unsigned long) skb->data + myBufList[0].size + myBufList[1].size)));
+ myBufList[2].size =
+ (numPages > 3) ? 4096 : length - myBufList[0].size - myBufList[1].size;
+ if (numPages > 3) {
+ myBufList[3].addr.address =
+ 0x8000000000000000LL |
+ (virt_to_absolute
+ ((unsigned long) skb->data + myBufList[0].size + myBufList[1].size +
+ myBufList[2].size));
+ myBufList[3].size =
+ length - myBufList[0].size - myBufList[1].size - myBufList[2].size;
+ }
+ }
+ }
+ returnCode = HvCallEvent_dmaBufList(HvLpEvent_Type_VirtualLan,
+ event->mBaseEvent.xSourceLp,
+ HvLpDma_Direction_RemoteToLocal,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ HvLpDma_AddressType_RealAddress,
+ HvLpDma_AddressType_TceIndex,
+ 0x8000000000000000LL |
+ (virt_to_absolute((unsigned long) &myBufList)),
+ 0x8000000000000000LL |
+ (virt_to_absolute((unsigned long) &remoteList)), length);
+
+ if (returnCode == HvLpDma_Rc_Good) {
+ HvLpVirtualLanIndex vlan = skb->data[9];
+ u64 dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000;
+
+ if (((vlan < HvMaxArchitectedVirtualLans) && ((port = mFabricMgr->mPorts[vlan]) != NULL)) && ((dest == port->mMyAddress) || /* it's for me */
+ (dest == 0xFFFFFFFFFFFF0000) || /* it's a broadcast */
+ (veth_multicast_wanted(port, dest)) || /* it's one of my multicasts */
+ (port->mPromiscuous == 1))) { /* I'm promiscuous */
+ skb_put(skb, length);
+ skb->dev = port->mDev;
+ skb->protocol = eth_type_trans(skb, port->mDev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_rx(skb); /* send it up */
+ port->mStats.rx_packets++;
+ port->mStats.rx_bytes += length;
+
+ } else {
+ dev_kfree_skb_irq(skb);
+ }
+ } else {
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ } else {
+ break;
+ }
+ i += nfrags;
+ } while (i < VethMaxFramesPerMsg);
+
+ /* Ack it */
+
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&connection->mAckGate, flags);
+
+ if (connection->mNumAcks < VethMaxFramesMsgsAcked) {
+ connection->mEventData.mAckData.mToken[connection->mNumAcks] = event->mBaseEvent.xCorrelationToken;
+ ++connection->mNumAcks;
+
+ if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) {
+ HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ VethEventTypeFramesAck,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ 0,
+ connection->mEventData.mFpData.mData1,
+ connection->mEventData.mFpData.mData2,
+ connection->mEventData.mFpData.mData3,
+ connection->mEventData.mFpData.mData4,
+ connection->mEventData.mFpData.mData5);
+
+ if (rc != HvLpEvent_Rc_Good) {
+ veth_error_printk("Bad lp event return code(%x) acking frames from lpar %d\n", (int) rc,
+ connection->mRemoteLp);
+ }
+
+ connection->mNumAcks = 0;
+
+ memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData));
+ }
+
+ }
+
+ spin_unlock_irqrestore(&connection->mAckGate, flags);
+ }
+}
+
+#undef number_of_pages
+#undef page_offset
+
+static void veth_timedAck(unsigned long connectionPtr)
+{
+ unsigned long flags;
+ HvLpEvent_Rc rc;
+ struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr;
+ /* Ack all the events */
+ spin_lock_irqsave(&connection->mAckGate, flags);
+
+ if (connection->mNumAcks > 0) {
+ rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ VethEventTypeFramesAck,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ 0,
+ connection->mEventData.mFpData.mData1,
+ connection->mEventData.mFpData.mData2,
+ connection->mEventData.mFpData.mData3,
+ connection->mEventData.mFpData.mData4, connection->mEventData.mFpData.mData5);
+
+ if (rc != HvLpEvent_Rc_Good) {
+ veth_error_printk("Bad lp event return code(%x) acking frames from lpar %d!\n", (int) rc,
+ connection->mRemoteLp);
+ }
+
+ connection->mNumAcks = 0;
+
+ memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData));
+ }
+
+ spin_unlock_irqrestore(&connection->mAckGate, flags);
+
+ /* Reschedule the timer */
+ connection->mAckTimer.expires = jiffies + connection->mTimeout;
+ add_timer(&connection->mAckTimer);
+}
+
+static int veth_multicast_wanted(struct VethPort *port, u64 thatAddr)
+{
+ int returnParm = 0;
+ int i;
+ unsigned long flags;
+
+ if ((*((char *) &thatAddr) & 0x01) != 1)
+ return 0;
+
+ read_lock_irqsave(&port->mMcastGate, flags);
+ if (port->mAllMcast) {
+ read_unlock_irqrestore(&port->mMcastGate, flags);
+ return 1;
+ }
+
+ for (i = 0; i < port->mNumAddrs; ++i) {
+ u64 thisAddr = port->mMcasts[i];
+
+ if (thisAddr == thatAddr) {
+ returnParm = 1;
+ break;
+ }
+ }
+ read_unlock_irqrestore(&port->mMcastGate, flags);
+
+ return returnParm;
+}
+
+static void veth_sendMonitor(struct VethLpConnection *connection)
+{
+ HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp,
+ HvLpEvent_Type_VirtualLan,
+ VethEventTypeMonitor,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_DeferredAck,
+ connection->mSourceInst,
+ connection->mTargetInst,
+ 0, 0, 0, 0, 0, 0);
+
+ if (returnCode == HvLpEvent_Rc_Good) {
+ connection->mConnectionStatus.mSentMonitor = 1;
+ connection->mConnectionStatus.mFailed = 0;
+
+ /* Start the ACK timer */
+ init_timer(&connection->mAckTimer);
+ connection->mAckTimer.function = veth_timedAck;
+ connection->mAckTimer.data = (unsigned long) connection;
+ connection->mAckTimer.expires = jiffies + connection->mTimeout;
+ add_timer(&connection->mAckTimer);
+
+ } else {
+ veth_error_printk("Monitor send to lpar %d failed with rc %x\n", connection->mRemoteLp, (int) returnCode);
+ veth_failMe(connection);
+ }
+}
+
+static void veth_finishCapBh(struct VethLpConnection *connection)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&connection->mStatusGate, flags);
+ veth_finishCapBhLocked(connection);
+ spin_unlock_irqrestore(&connection->mStatusGate, flags);
+}
+
+static void veth_finishCapBhLocked(struct VethLpConnection *connection)
+{
+ struct VethLpEvent *event = &connection->mCapEvent;
+ struct VethCapData *remoteCap = &(connection->mRemoteCap);
+ int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1;
+
+ /* Convert timer to jiffies */
+ if (connection->mMyCap.mUnionData.mFields.mTimer)
+ connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000;
+ else
+ connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000;
+
+ if (connection->mNumberLpAcksAlloced >= numAcks) {
+ HvLpEvent_Rc returnCode = HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
+
+ if (returnCode == HvLpEvent_Rc_Good) {
+ connection->mConnectionStatus.mGotCapAcked = 1;
+
+ if (connection->mConnectionStatus.mSentCap != 1) {
+ connection->mTargetInst =
+ HvCallEvent_getTargetLpInstanceId(connection->mRemoteLp, HvLpEvent_Type_VirtualLan);
+
+ veth_sendCap(connection);
+ } else if (connection->mConnectionStatus.mCapAcked == 1) {
+ if (connection->mConnectionStatus.mSentMonitor != 1)
+ veth_sendMonitor(connection);
+ }
+ } else {
+ veth_error_printk("Failed to ack remote cap for lpar %d with rc %x\n", connection->mRemoteLp,
+ (int) returnCode);
+ veth_failMe(connection);
+ }
+ } else {
+ veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp);
+ event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable;
+ HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
+ }
+}
+
+int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data) {
+ char *out = page;
+ long whichConnection = (long) data;
+ int len = 0;
+ struct VethLpConnection *connection = NULL;
+
+ if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) {
+ veth_error_printk("Got bad data from /proc file system\n");
+ len = sprintf(page, "ERROR\n");
+ } else {
+ int thereWasStuffBefore = 0;
+ connection = &(mFabricMgr->mConnection[whichConnection]);
+
+ out += sprintf(out, "Remote Lp:\t%d\n", connection->mRemoteLp);
+ out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst);
+ out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst);
+ out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs);
+ out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced);
+ out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks);
+
+ if (connection->mConnectionStatus.mOpen) {
+ out += sprintf(out, "mConnectionStatus.mCapMonAlloced) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "CapMonAlloced");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mBaseMsgsAlloced) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "BaseMsgsAlloced");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mSentCap) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "SentCap");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mCapAcked) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "CapAcked");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mGotCap) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "GotCap");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mGotCapAcked) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "GotCapAcked");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mSentMonitor) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "SentMonitor");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mPopulatedRings) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "PopulatedRings");
+ thereWasStuffBefore = 1;
+ }
+
+ if (connection->mConnectionStatus.mFailed) {
+ if (thereWasStuffBefore)
+ out += sprintf(out, "/");
+ else
+ out += sprintf(out, "<");
+ out += sprintf(out, "Failed");
+ thereWasStuffBefore = 1;
+ }
+
+ if (thereWasStuffBefore)
+ out += sprintf(out, ">");
+
+ out += sprintf(out, "\n");
+
+ out += sprintf(out, "Capabilities (System:):\n");
+ out += sprintf(out, "\tLocal:<");
+ out += sprintf(out, "%d/%d/%d/%d>\n",
+ connection->mMyCap.mUnionData.mFields.mVersion,
+ connection->mMyCap.mUnionData.mFields.mNumberBuffers,
+ connection->mMyCap.mUnionData.mFields.mThreshold, connection->mMyCap.mUnionData.mFields.mTimer);
+ out += sprintf(out, "\tRemote:<");
+ out += sprintf(out, "%d/%d/%d/%d>\n",
+ connection->mRemoteCap.mUnionData.mFields.mVersion,
+ connection->mRemoteCap.mUnionData.mFields.mNumberBuffers,
+ connection->mRemoteCap.mUnionData.mFields.mThreshold,
+ connection->mRemoteCap.mUnionData.mFields.mTimer);
+ len = out - page;
+ }
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0)
+ return 0;
+ } else
+ len = count;
+ *start = page + off;
+ return len;
+}
+
+int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data) {
+ char *out = page;
+ long whichPort = (long) data;
+ int len = 0;
+ struct VethPort *port = NULL;
+
+ if ((whichPort < 0) || (whichPort > HvMaxArchitectedVirtualLans) || (mFabricMgr == NULL))
+ len = sprintf(page, "Virtual ethernet is not configured.\n");
+ else {
+ int i = 0;
+ u32 *myAddr;
+ u16 *myEndAddr;
+ port = mFabricMgr->mPorts[whichPort];
+
+ if (port != NULL) {
+ myAddr = (u32 *) & (port->mMyAddress);
+ myEndAddr = (u16 *) (myAddr + 1);
+ out += sprintf(out, "Net device:\t%p\n", port->mDev);
+ out += sprintf(out, "Net device name:\t%s\n", port->mDev->name);
+ out += sprintf(out, "Address:\t%08X%04X\n", myAddr[0], myEndAddr[0]);
+ out += sprintf(out, "Promiscuous:\t%d\n", port->mPromiscuous);
+ out += sprintf(out, "All multicast:\t%d\n", port->mAllMcast);
+ out += sprintf(out, "Number sk_buffs linearized:\t%u\n", port->mLinearized);
+ out += sprintf(out, "Number multicast:\t%d\n", port->mNumAddrs);
+
+ for (i = 0; i < port->mNumAddrs; ++i) {
+ u32 *multi = (u32 *) & (port->mMcasts[i]);
+ u16 *multiEnd = (u16 *) (multi + 1);
+ out += sprintf(out, " %08X%04X\n", multi[0], multiEnd[0]);
+ }
+ } else {
+ out += sprintf(page, "veth%d is not configured.\n", (int) whichPort);
+ }
+
+ len = out - page;
+ }
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0)
+ return 0;
+ } else
+ len = count;
+ *start = page + off;
+ return len;
+}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/veth.h linuxppc64_2_4/drivers/iseries/veth.h
--- ../kernel.org/linux-2.4.19/drivers/iseries/veth.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/veth.h Tue Mar 12 08:51:03 2002
@@ -0,0 +1,242 @@
+/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */
+
+/* Change Activity: */
+/* End Change Activity */
+
+#ifndef _VETH_H
+#define _VETH_H
+
+#ifndef _HVTYPES_H
+#include
+#endif
+#ifndef _HVLPEVENT_H
+#include
+#endif
+#include
+
+#define VethEventNumTypes (4)
+#define VethEventTypeCap (0)
+#define VethEventTypeFrames (1)
+#define VethEventTypeMonitor (2)
+#define VethEventTypeFramesAck (3)
+
+#define VethMaxFramesMsgsAcked (20)
+#define VethMaxFramesMsgs (0xFFFF)
+#define VethMaxFramesPerMsg (6)
+#define VethAckTimeoutUsec (1000000)
+
+#define VETHSTACKTYPE(T) struct VethStack##T
+#define VETHSTACK(T) \
+VETHSTACKTYPE(T) \
+{ \
+struct T *head; \
+spinlock_t lock; \
+}
+#define VETHSTACKCTOR(s) do { (s)->head = NULL; spin_lock_init(&(s)->lock); } while(0)
+#define VETHSTACKPUSH(s, p) \
+do { \
+unsigned long flags; \
+spin_lock_irqsave(&(s)->lock,flags); \
+(p)->next = (s)->head; \
+(s)->head = (p); \
+spin_unlock_irqrestore(&(s)->lock, flags); \
+} while(0)
+
+#define VETHSTACKPOP(s,p) \
+do { \
+unsigned long flags; \
+spin_lock_irqsave(&(s)->lock,flags); \
+(p) = (s)->head; \
+if ((s)->head != NULL) \
+{ \
+(s)->head = (s)->head->next; \
+} \
+spin_unlock_irqrestore(&(s)->lock, flags); \
+} while(0)
+
+#define VETHQUEUE(T) \
+struct VethQueue##T \
+{ \
+T *head; \
+T *tail; \
+spinlock_t lock; \
+}
+#define VETHQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; spin_lock_init(&(q)->lock); } while(0)
+#define VETHQUEUEENQ(q, p) \
+do { \
+unsigned long flags; \
+spin_lock_irqsave(&(q)->lock,flags); \
+(p)->next = NULL; \
+if ((q)->head != NULL) \
+{ \
+(q)->head->next = (p); \
+(q)->head = (p); \
+} \
+else \
+{ \
+(q)->tail = (q)->head = (p); \
+} \
+spin_unlock_irqrestore(&(q)->lock, flags); \
+} while(0)
+
+#define VETHQUEUEDEQ(q,p) \
+do { \
+unsigned long flags; \
+spin_lock_irqsave(&(q)->lock,flags); \
+(p) = (q)->tail; \
+if ((p) != NULL) \
+{ \
+(q)->tail = (p)->next; \
+(p)->next = NULL; \
+} \
+if ((q)->tail == NULL) \
+(q)->head = NULL; \
+spin_unlock_irqrestore(&(q)->lock, flags); \
+} while(0)
+
+struct VethFramesData {
+ u32 mAddress[6];
+ u16 mLength[6];
+ u32 mEofMask:6;
+ u32 mReserved:26;
+};
+
+struct VethFramesAckData {
+ u16 mToken[VethMaxFramesMsgsAcked];
+};
+
+struct VethCapData {
+ union {
+ struct Fields {
+ u8 mVersion;
+ u8 mReserved1;
+ u16 mNumberBuffers;
+ u16 mThreshold;
+ u16 mReserved2;
+ u32 mTimer;
+ u32 mReserved3;
+ u64 mReserved4;
+ u64 mReserved5;
+ u64 mReserved6;
+ } mFields;
+ struct NoFields {
+ u64 mReserved1;
+ u64 mReserved2;
+ u64 mReserved3;
+ u64 mReserved4;
+ u64 mReserved5;
+ } mNoFields;
+ } mUnionData;
+};
+
+struct VethFastPathData {
+ u64 mData1;
+ u64 mData2;
+ u64 mData3;
+ u64 mData4;
+ u64 mData5;
+};
+
+struct VethLpEvent {
+ struct HvLpEvent mBaseEvent;
+ union {
+ struct VethFramesData mSendData;
+ struct VethCapData mCapabilitiesData;
+ struct VethFramesAckData mFramesAckData;
+ struct VethFastPathData mFastPathData;
+ } mDerivedData;
+
+};
+
+struct VethMsg {
+ struct VethMsg *next;
+ union {
+ struct VethFramesData mSendData;
+ struct VethFastPathData mFpData;
+ } mEvent;
+ int mIndex;
+ unsigned long mInUse;
+ struct sk_buff *mSkb;
+};
+
+
+struct VethControlBlock {
+ struct net_device *mDev;
+ struct VethControlBlock *mNext;
+ HvLpVirtualLanIndex mVlanId;
+};
+
+struct VethLpConnection {
+ u64 mEyecatcher;
+ HvLpIndex mRemoteLp;
+ HvLpInstanceId mSourceInst;
+ HvLpInstanceId mTargetInst;
+ u32 mNumMsgs;
+ struct VethMsg *mMsgs;
+ int mNumberRcvMsgs;
+ int mNumberLpAcksAlloced;
+ union {
+ struct VethFramesAckData mAckData;
+ struct VethFastPathData mFpData;
+ } mEventData;
+ spinlock_t mAckGate;
+ u32 mNumAcks;
+ spinlock_t mStatusGate;
+ struct {
+ u64 mOpen:1;
+ u64 mCapMonAlloced:1;
+ u64 mBaseMsgsAlloced:1;
+ u64 mSentCap:1;
+ u64 mCapAcked:1;
+ u64 mGotCap:1;
+ u64 mGotCapAcked:1;
+ u64 mSentMonitor:1;
+ u64 mPopulatedRings:1;
+ u64 mReserved:54;
+ u64 mFailed:1;
+ } mConnectionStatus;
+ struct VethCapData mMyCap;
+ struct VethCapData mRemoteCap;
+ unsigned long mCapAckBhPending;
+ struct tq_struct mCapAckBhTq;
+ struct VethLpEvent mCapAckEvent;
+ unsigned long mCapBhPending;
+ struct tq_struct mCapBhTq;
+ struct VethLpEvent mCapEvent;
+ unsigned long mMonitorAckBhPending;
+ struct tq_struct mMonitorAckBhTq;
+ struct VethLpEvent mMonitorAckEvent;
+ unsigned long mAllocBhPending;
+ struct tq_struct mAllocBhTq;
+ int mNumberAllocated;
+ struct timer_list mAckTimer;
+ u32 mTimeout;
+ VETHSTACK(VethMsg) mMsgStack;
+};
+#define HVMAXARCHITECTEDVIRTUALLANS 16
+struct VethPort {
+ struct net_device *mDev;
+ struct net_device_stats mStats;
+ int mLock;
+ u64 mMyAddress;
+ int mPromiscuous;
+ int mAllMcast;
+ rwlock_t mMcastGate;
+ int mNumAddrs;
+ u64 mMcasts[12];
+ u32 mLinearized;
+};
+
+struct VethFabricMgr {
+ u64 mEyecatcher;
+ HvLpIndex mThisLp;
+ struct VethLpConnection mConnection[HVMAXARCHITECTEDLPS];
+ spinlock_t mPortListGate;
+ u64 mNumPorts;
+ struct VethPort *mPorts[HVMAXARCHITECTEDVIRTUALLANS];
+};
+
+int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data);
+int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+#endif /* _VETH_H */
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/vio.h linuxppc64_2_4/drivers/iseries/vio.h
--- ../kernel.org/linux-2.4.19/drivers/iseries/vio.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/vio.h Wed Feb 27 11:09:05 2002
@@ -0,0 +1,130 @@
+/* -*- linux-c -*-
+ * drivers/char/vio.h
+ *
+ * iSeries Virtual I/O Message Path header
+ *
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * This header file is used by the iSeries virtual I/O device
+ * drivers. It defines the interfaces to the common functions
+ * (implemented in drivers/char/viopath.h) as well as defining
+ * common functions and structures. Currently (at the time I
+ * wrote this comment) the iSeries virtual I/O device drivers
+ * that use this are
+ * drivers/block/viodasd.c
+ * drivers/char/viocons.c
+ * drivers/char/viotape.c
+ * drivers/cdrom/viocd.c
+ *
+ * The iSeries virtual ethernet support (veth.c) uses a whole
+ * different set of functions.
+ *
+ * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VIO_H
+#define _VIO_H
+
+#include
+#include
+
+/* iSeries virtual I/O events use the subtype field in
+ * HvLpEvent to figure out what kind of vio event is coming
+ * in. We use a table to route these, and this defines
+ * the maximum number of distinct subtypes
+ */
+#define VIO_MAX_SUBTYPES 7
+
+/* Each subtype can register a handler to process their events.
+ * The handler must have this interface.
+ */
+typedef void (vio_event_handler_t) (struct HvLpEvent * event);
+
+int viopath_open(HvLpIndex remoteLp, int subtype, int numReq);
+int viopath_close(HvLpIndex remoteLp, int subtype, int numReq);
+int vio_setHandler(int subtype, vio_event_handler_t * beh);
+int vio_clearHandler(int subtype);
+int viopath_isactive(HvLpIndex lp);
+HvLpInstanceId viopath_sourceinst(HvLpIndex lp);
+HvLpInstanceId viopath_targetinst(HvLpIndex lp);
+void vio_set_hostlp(void);
+void *vio_get_event_buffer(int subtype);
+void vio_free_event_buffer(int subtype, void *buffer);
+
+extern HvLpIndex viopath_hostLp;
+extern HvLpIndex viopath_ourLp;
+
+#define VIO_MESSAGE "iSeries virtual I/O: "
+#define KERN_DEBUG_VIO KERN_DEBUG VIO_MESSAGE
+#define KERN_INFO_VIO KERN_INFO VIO_MESSAGE
+#define KERN_WARNING_VIO KERN_WARNING VIO_MESSAGE
+
+#define VIOCHAR_MAX_DATA 200
+
+#define VIOMAJOR_SUBTYPE_MASK 0xff00
+#define VIOMINOR_SUBTYPE_MASK 0x00ff
+#define VIOMAJOR_SUBTYPE_SHIFT 8
+
+#define VIOVERSION 0x0101
+
+/*
+This is the general structure for VIO errors; each module should have a table
+of them, and each table should be terminated by an entry of { 0, 0, NULL }.
+Then, to find a specific error message, a module should pass its local table
+and the return code.
+*/
+struct vio_error_entry {
+ u16 rc;
+ int errno;
+ const char *msg;
+};
+const struct vio_error_entry *vio_lookup_rc(const struct vio_error_entry
+ *local_table, u16 rc);
+
+enum viosubtypes {
+ viomajorsubtype_monitor = 0x0100,
+ viomajorsubtype_blockio = 0x0200,
+ viomajorsubtype_chario = 0x0300,
+ viomajorsubtype_config = 0x0400,
+ viomajorsubtype_cdio = 0x0500,
+ viomajorsubtype_tape = 0x0600
+};
+
+
+enum vioconfigsubtype {
+ vioconfigget = 0x0001,
+};
+
+enum viorc {
+ viorc_good = 0x0000,
+ viorc_noConnection = 0x0001,
+ viorc_noReceiver = 0x0002,
+ viorc_noBufferAvailable = 0x0003,
+ viorc_invalidMessageType = 0x0004,
+ viorc_invalidRange = 0x0201,
+ viorc_invalidToken = 0x0202,
+ viorc_DMAError = 0x0203,
+ viorc_useError = 0x0204,
+ viorc_releaseError = 0x0205,
+ viorc_invalidDisk = 0x0206,
+ viorc_openRejected = 0x0301
+};
+
+
+#endif /* _VIO_H */
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/viocd.c linuxppc64_2_4/drivers/iseries/viocd.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/viocd.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/viocd.c Fri Mar 29 11:51:14 2002
@@ -0,0 +1,818 @@
+/* -*- linux-c -*-
+ * drivers/cdrom/viocd.c
+ *
+ ***************************************************************************
+ * iSeries Virtual CD Rom
+ *
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************
+ * This routine provides access to CD ROM drives owned and managed by an
+ * OS/400 partition running on the same box as this Linux partition.
+ *
+ * All operations are performed by sending messages back and forth to
+ * the OS/400 partition.
+ *
+ *
+ * This device driver can either use it's own major number, or it can
+ * pretend to be an AZTECH drive. This is controlled with a
+ * CONFIG option. You can either call this an elegant solution to the
+ * fact that a lot of software doesn't recognize a new CD major number...
+ * or you can call this a really ugly hack. Your choice.
+ *
+ */
+
+#include
+#include
+
+/* Decide on the proper naming convention to use for our device */
+#ifdef CONFIG_DEVFS_FS
+#define VIOCD_DEVICE "cdroms/cdrom%d"
+#define VIOCD_DEVICE_OFFSET 0
+#else
+#ifdef CONFIG_VIOCD_AZTECH
+#define VIOCD_DEVICE "aztcd"
+#define VIOCD_DEVICE_OFFSET 0
+#else
+#define VIOCD_DEVICE "iseries/vcd%c"
+#define VIOCD_DEVICE_OFFSET 'a'
+#endif
+#endif
+
+/***************************************************************************
+ * Decide if we are using our own major or pretending to be an AZTECH drive
+ ***************************************************************************/
+#ifdef CONFIG_VIOCD_AZTECH
+#define MAJOR_NR AZTECH_CDROM_MAJOR
+#define do_viocd_request do_aztcd_request
+#else
+#define MAJOR_NR VIOCD_MAJOR
+#endif
+
+#define VIOCD_VERS "1.04"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "vio.h"
+#include
+
+extern struct pci_dev * iSeries_vio_dev;
+
+#define signalLpEventFast HvCallEvent_signalLpEventFast
+
+struct viocdlpevent {
+ struct HvLpEvent event;
+ u32 mReserved1;
+ u16 mVersion;
+ u16 mSubTypeRc;
+ u16 mDisk;
+ u16 mFlags;
+ u32 mToken;
+ u64 mOffset; // On open, the max number of disks
+ u64 mLen; // On open, the size of the disk
+ u32 mBlockSize; // Only set on open
+ u32 mMediaSize; // Only set on open
+};
+
+enum viocdsubtype {
+ viocdopen = 0x0001,
+ viocdclose = 0x0002,
+ viocdread = 0x0003,
+ viocdwrite = 0x0004,
+ viocdlockdoor = 0x0005,
+ viocdgetinfo = 0x0006,
+ viocdcheck = 0x0007
+};
+
+/* Should probably make this a module parameter....sigh
+ */
+#define VIOCD_MAX_CD 8
+int viocd_blocksizes[VIOCD_MAX_CD];
+static u64 viocd_size_in_bytes[VIOCD_MAX_CD];
+
+static const struct vio_error_entry viocd_err_table[] = {
+ {0x0201, EINVAL, "Invalid Range"},
+ {0x0202, EINVAL, "Invalid Token"},
+ {0x0203, EIO, "DMA Error"},
+ {0x0204, EIO, "Use Error"},
+ {0x0205, EIO, "Release Error"},
+ {0x0206, EINVAL, "Invalid CD"},
+ {0x020C, EROFS, "Read Only Device"},
+ {0x020D, EIO, "Changed or Missing Volume (or Varied Off?)"},
+ {0x020E, EIO, "Optical System Error (Varied Off?)"},
+ {0x02FF, EIO, "Internal Error"},
+ {0x3010, EIO, "Changed Volume"},
+ {0xC100, EIO, "Optical System Error"},
+ {0x0000, 0, NULL},
+};
+
+/* This is the structure we use to exchange info between driver and interrupt
+ * handler
+ */
+struct viocd_waitevent {
+ struct semaphore *sem;
+ int rc;
+ u16 subtypeRc;
+ int changed;
+};
+
+/* this is a lookup table for the true capabilities of a device */
+struct capability_entry {
+ char *type;
+ int capability;
+};
+
+static struct capability_entry capability_table[] = {
+ { "6330", CDC_LOCK | CDC_DVD_RAM },
+ { "6321", CDC_LOCK },
+ { "632B", 0 },
+ { NULL , CDC_LOCK },
+};
+
+struct block_device_operations viocd_fops =
+{
+ owner: THIS_MODULE,
+ open: cdrom_open,
+ release: cdrom_release,
+ ioctl: cdrom_ioctl,
+ check_media_change: cdrom_media_changed,
+};
+
+/* These are our internal structures for keeping track of devices
+ */
+static int viocd_numdev;
+
+struct cdrom_info {
+ char rsrcname[10];
+ char type[4];
+ char model[3];
+};
+static struct cdrom_info *viocd_unitinfo = NULL;
+
+struct disk_info{
+ u32 useCount;
+ u32 blocksize;
+ u32 mediasize;
+};
+static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];
+
+static struct cdrom_device_info viocd_info[VIOCD_MAX_CD];
+
+static spinlock_t viocd_lock = SPIN_LOCK_UNLOCKED;
+
+#define MAX_CD_REQ 1
+static LIST_HEAD(reqlist);
+
+/* End a request
+ */
+static int viocd_end_request(struct request *req, int uptodate)
+{
+ if (end_that_request_first(req, uptodate, DEVICE_NAME))
+ return 0;
+ end_that_request_last(req);
+ return 1;
+}
+
+
+/* Get info on CD devices from OS/400
+ */
+static void get_viocd_info(void)
+{
+ dma_addr_t dmaaddr;
+ HvLpEvent_Rc hvrc;
+ int i;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ struct viocd_waitevent we;
+
+ // If we don't have a host, bail out
+ if (viopath_hostLp == HvLpIndexInvalid)
+ return;
+
+ if (viocd_unitinfo == NULL)
+ viocd_unitinfo =
+ kmalloc(sizeof(struct cdrom_info) * VIOCD_MAX_CD,
+ GFP_KERNEL);
+
+ memset(viocd_unitinfo, 0x00,
+ sizeof(struct cdrom_info) * VIOCD_MAX_CD);
+
+ dmaaddr = pci_map_single(iSeries_vio_dev, viocd_unitinfo,
+ sizeof(struct cdrom_info) * VIOCD_MAX_CD,
+ PCI_DMA_FROMDEVICE);
+ if (dmaaddr == 0xFFFFFFFF) {
+ printk(KERN_WARNING_VIO "error allocating tce\n");
+ return;
+ }
+
+ we.sem = &Semaphore;
+
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | viocdgetinfo,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ dmaaddr,
+ 0,
+ sizeof(struct cdrom_info) * VIOCD_MAX_CD,
+ 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk(KERN_WARNING_VIO "cdrom error sending event. rc %d\n", (int) hvrc);
+ return;
+ }
+
+ down(&Semaphore);
+
+ if (we.rc) {
+ const struct vio_error_entry *err = vio_lookup_rc(viocd_err_table, we.subtypeRc);
+ printk(KERN_WARNING_VIO "bad rc %d:0x%04X on getinfo: %s\n", we.rc, we.subtypeRc, err->msg);
+ return;
+ }
+
+
+ for (i = 0; (i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0]); i++) {
+ viocd_numdev++;
+ }
+}
+
+/* Open a device
+ */
+static int viocd_open(struct cdrom_device_info *cdi, int purpose)
+{
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ int device_no = MINOR(cdi->dev);
+ HvLpEvent_Rc hvrc;
+ struct viocd_waitevent we;
+ struct disk_info *diskinfo = &viocd_diskinfo[device_no];
+
+ // If we don't have a host, bail out
+ if (viopath_hostLp == HvLpIndexInvalid || device_no >= viocd_numdev)
+ return -ENODEV;
+
+ we.sem = &Semaphore;
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | viocdopen,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ ((u64) device_no << 48),
+ 0, 0, 0);
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEventFast %d\n",
+ (int) hvrc);
+ return -EIO;
+ }
+
+ down(&Semaphore);
+
+ if (we.rc) {
+ const struct vio_error_entry *err = vio_lookup_rc(viocd_err_table, we.subtypeRc);
+ printk(KERN_WARNING_VIO "bad rc %d:0x%04X on open: %s\n", we.rc, we.subtypeRc, err->msg);
+ return -err->errno;
+ }
+
+ if (diskinfo->useCount == 0) {
+ if(diskinfo->blocksize > 0) {
+ viocd_blocksizes[device_no] = diskinfo->blocksize;
+ viocd_size_in_bytes[device_no] = diskinfo->blocksize * diskinfo->mediasize;
+ } else {
+ viocd_size_in_bytes[device_no] = 0xFFFFFFFFFFFFFFFF;
+ }
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Release a device
+ */
+static void viocd_release(struct cdrom_device_info *cdi)
+{
+ int device_no = MINOR(cdi->dev);
+ HvLpEvent_Rc hvrc;
+
+ /* If we don't have a host, bail out */
+ if (viopath_hostLp == HvLpIndexInvalid
+ || device_no >= viocd_numdev)
+ return;
+
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | viocdclose,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ 0,
+ VIOVERSION << 16,
+ ((u64) device_no << 48),
+ 0, 0, 0);
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEventFast %d\n", (int) hvrc);
+ return;
+ }
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* Send a read or write request to OS/400
+ */
+static int send_request(struct request *req)
+{
+ HvLpEvent_Rc hvrc;
+ dma_addr_t dmaaddr;
+ int device_no = DEVICE_NR(req->rq_dev);
+ u64 start = req->sector * 512,
+ len = req->current_nr_sectors * 512;
+ char reading = req->cmd == READ;
+ u16 command = reading ? viocdread : viocdwrite;
+
+
+ if(start + len > viocd_size_in_bytes[device_no]) {
+ printk(KERN_WARNING_VIO "viocd%d; access position %lx, past size %lx\n",
+ device_no, start + len, viocd_size_in_bytes[device_no]);
+ return -1;
+ }
+
+ dmaaddr = pci_map_single(iSeries_vio_dev, req->buffer, len,
+ reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+ if (dmaaddr == 0xFFFFFFFF) {
+ printk(KERN_WARNING_VIO "error allocating tce for address %p len %ld\n",
+ req->buffer, len);
+ return -1;
+ }
+
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | command,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ (u64) (unsigned long) req->buffer,
+ VIOVERSION << 16,
+ ((u64) device_no << 48) | dmaaddr,
+ start, len, 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk(KERN_WARNING_VIO "hv error on op %d\n", (int) hvrc);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Do a request
+ */
+static int rwreq;
+static void do_viocd_request(request_queue_t * q)
+{
+ for (;;) {
+ struct request *req;
+ char err_str[80] = "";
+ int device_no;
+
+ INIT_REQUEST;
+ if (rwreq >= MAX_CD_REQ) {
+ return;
+ }
+
+ device_no = CURRENT_DEV;
+
+ /* remove the current request from the queue */
+ req = CURRENT;
+ blkdev_dequeue_request(req);
+
+ /* check for any kind of error */
+ if (device_no > viocd_numdev)
+ sprintf(err_str, "Invalid device number %d", device_no);
+ else if (send_request(req) < 0)
+ strcpy(err_str, "unable to send message to OS/400!");
+
+ /* if we had any sort of error, log it and cancel the request */
+ if (*err_str) {
+ printk(KERN_WARNING_VIO "%s\n", err_str);
+ viocd_end_request(req, 0);
+ } else {
+ spin_lock(&viocd_lock);
+ list_add_tail(&req->queue, &reqlist);
+ ++rwreq;
+ spin_unlock(&viocd_lock);
+ }
+ }
+}
+
+/* Check if the CD changed
+ */
+static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
+{
+ struct viocd_waitevent we;
+ HvLpEvent_Rc hvrc;
+ int device_no = MINOR(cdi->dev);
+
+ /* This semaphore is raised in the interrupt handler */
+ DECLARE_MUTEX_LOCKED(Semaphore);
+
+ /* Check that we are dealing with a valid hosting partition */
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ printk(KERN_WARNING_VIO "Invalid hosting partition\n");
+ return -EIO;
+ }
+
+ we.sem = &Semaphore;
+
+ /* Send the open event to OS/400 */
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | viocdcheck,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ ((u64) device_no << 48),
+ 0, 0, 0);
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEventFast %d\n", (int) hvrc);
+ return -EIO;
+ }
+
+ /* Wait for the interrupt handler to get the response */
+ down(&Semaphore);
+
+ /* Check the return code. If bad, assume no change */
+ if (we.rc) {
+ const struct vio_error_entry *err = vio_lookup_rc(viocd_err_table, we.subtypeRc);
+ printk(KERN_WARNING_VIO "bad rc %d:0x%04X on check_change: %s; Assuming no change\n", we.rc, we.subtypeRc, err->msg);
+ return 0;
+ }
+
+ return we.changed;
+}
+
+static int viocd_lock_door(struct cdrom_device_info *cdi, int locking)
+{
+ HvLpEvent_Rc hvrc;
+ u64 device_no = MINOR(cdi->dev);
+ /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */
+ u64 flags = !!locking;
+ /* This semaphore is raised in the interrupt handler */
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ struct viocd_waitevent we = { sem:&Semaphore };
+
+ /* Check that we are dealing with a valid hosting partition */
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ printk(KERN_WARNING_VIO "Invalid hosting partition\n");
+ return -EIO;
+ }
+
+ we.sem = &Semaphore;
+
+ /* Send the lockdoor event to OS/400 */
+ hvrc = signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_cdio | viocdlockdoor,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst(viopath_hostLp),
+ viopath_targetinst(viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ (device_no << 48) | (flags << 32),
+ 0, 0, 0);
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEventFast %d\n", (int) hvrc);
+ return -EIO;
+ }
+
+ /* Wait for the interrupt handler to get the response */
+ down(&Semaphore);
+
+ /* Check the return code. If bad, assume no change */
+ if (we.rc != 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* This routine handles incoming CD LP events
+ */
+static void vioHandleCDEvent(struct HvLpEvent *event)
+{
+ struct viocdlpevent *bevent = (struct viocdlpevent *) event;
+ struct viocd_waitevent *pwe;
+
+ if (event == NULL) {
+ /* Notification that a partition went away! */
+ return;
+ }
+ /* First, we should NEVER get an int here...only acks */
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ printk(KERN_WARNING_VIO "Yikes! got an int in viocd event handler!\n");
+ if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ }
+
+ switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
+ case viocdopen:
+ viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize;
+ viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize;
+ /* FALLTHROUGH !! */
+ case viocdgetinfo:
+ case viocdlockdoor:
+ pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken;
+ pwe->rc = event->xRc;
+ pwe->subtypeRc = bevent->mSubTypeRc;
+ up(pwe->sem);
+ break;
+
+ case viocdclose:
+ break;
+
+ case viocdwrite:
+ case viocdread:{
+ unsigned long flags;
+ int reading = ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread);
+ struct request *req = blkdev_entry_to_request(reqlist.next);
+ /* Since this is running in interrupt mode, we need to make sure we're not
+ * stepping on any global I/O operations
+ */
+ spin_lock_irqsave(&io_request_lock, flags);
+
+ pci_unmap_single(iSeries_vio_dev,
+ bevent->mToken,
+ bevent->mLen,
+ reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+
+ /* find the event to which this is a response */
+ while ((&req->queue != &reqlist) &&
+ ((u64) (unsigned long) req->buffer != bevent->event.xCorrelationToken))
+ req = blkdev_entry_to_request(req->queue.next);
+
+ /* if the event was not there, then what are we responding to?? */
+ if (&req->queue == &reqlist) {
+ printk(KERN_WARNING_VIO "Yikes! we never enqueued this guy!\n");
+ spin_unlock_irqrestore(&io_request_lock,
+ flags);
+ break;
+ }
+
+ /* we don't need to keep it around anymore... */
+ spin_lock(&viocd_lock);
+ list_del(&req->queue);
+ --rwreq;
+ spin_unlock(&viocd_lock);
+ {
+ char stat = event->xRc == HvLpEvent_Rc_Good;
+ int nsect = bevent->mLen >> 9;
+
+ if (!stat) {
+ const struct vio_error_entry *err =
+ vio_lookup_rc(viocd_err_table, bevent->mSubTypeRc);
+ printk(KERN_WARNING_VIO "request %p failed with rc %d:0x%04X: %s\n",
+ req->buffer, event->xRc, bevent->mSubTypeRc, err->msg);
+ }
+ while ((nsect > 0) && (req->bh)) {
+ nsect -= req->current_nr_sectors;
+ viocd_end_request(req, stat);
+ }
+ /* we weren't done yet */
+ if (req->bh) {
+ if (send_request(req) < 0) {
+ printk(KERN_WARNING_VIO
+ "couldn't re-submit req %p\n", req->buffer);
+ viocd_end_request(req, 0);
+ } else {
+ spin_lock(&viocd_lock);
+ list_add_tail(&req->queue, &reqlist);
+ ++rwreq;
+ spin_unlock(&viocd_lock);
+ }
+ }
+ }
+
+ /* restart handling of incoming requests */
+ do_viocd_request(NULL);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ break;
+ }
+ case viocdcheck:
+ pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken;
+ pwe->rc = event->xRc;
+ pwe->subtypeRc = bevent->mSubTypeRc;
+ pwe->changed = bevent->mFlags;
+ up(pwe->sem);
+ break;
+
+ default:
+ printk(KERN_WARNING_VIO "message with invalid subtype %0x04X!\n", event->xSubtype & VIOMINOR_SUBTYPE_MASK);
+ if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ }
+}
+
+/* Our file operations table
+ */
+static struct cdrom_device_ops viocd_dops = {
+ open:viocd_open,
+ release:viocd_release,
+ media_changed:viocd_media_changed,
+ lock_door:viocd_lock_door,
+ capability:CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM
+};
+
+/* Handle reads from the proc file system
+ */
+static int proc_read(char *buf, char **start, off_t offset,
+ int blen, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; i < viocd_numdev; i++) {
+ len +=
+ sprintf(buf + len,
+ "viocd device %d is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ i, viocd_unitinfo[i].rsrcname,
+ viocd_unitinfo[i].type,
+ viocd_unitinfo[i].model);
+ }
+ *eof = 1;
+ return len;
+}
+
+
+/* setup our proc file system entries
+ */
+void viocd_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ struct proc_dir_entry *ent;
+ ent = create_proc_entry("viocd", S_IFREG | S_IRUSR, iSeries_proc);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = NULL;
+ ent->read_proc = proc_read;
+}
+
+/* clean up our proc file system entries
+ */
+void viocd_proc_delete(struct proc_dir_entry *iSeries_proc)
+{
+ remove_proc_entry("viocd", iSeries_proc);
+}
+
+static int find_capability(const char *type)
+{
+ struct capability_entry *entry;
+ for(entry = capability_table; entry->type; ++entry)
+ if(!strncmp(entry->type, type, 4))
+ break;
+ return entry->capability;
+}
+
+/* Initialize the whole device driver. Handle module and non-module
+ * versions
+ */
+__init int viocd_init(void)
+{
+ int i, rc;
+
+ if (viopath_hostLp == HvLpIndexInvalid)
+ vio_set_hostlp();
+
+ /* If we don't have a host, bail out */
+ if (viopath_hostLp == HvLpIndexInvalid)
+ return -ENODEV;
+
+ rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
+ if (rc) {
+ printk(KERN_WARNING_VIO "error opening path to host partition %d\n",
+ viopath_hostLp);
+ return rc;
+ }
+
+ /* Initialize our request handler
+ */
+ rwreq = 0;
+ vio_setHandler(viomajorsubtype_cdio, vioHandleCDEvent);
+
+ memset(&viocd_diskinfo, 0x00, sizeof(viocd_diskinfo));
+
+ get_viocd_info();
+
+ if (viocd_numdev == 0) {
+ vio_clearHandler(viomajorsubtype_cdio);
+ viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
+ return 0;
+ }
+
+ printk(KERN_INFO_VIO
+ "%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n",
+ DEVICE_NAME, VIOCD_VERS, MAJOR_NR, VIOCD_MAX_CD, viopath_hostLp);
+
+ if (devfs_register_blkdev(MAJOR_NR, "viocd", &viocd_fops) != 0) {
+ printk(KERN_WARNING_VIO "Unable to get major %d for viocd CD-ROM\n", MAJOR_NR);
+ return -EIO;
+ }
+
+ blksize_size[MAJOR_NR] = viocd_blocksizes;
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+ read_ahead[MAJOR_NR] = 4;
+
+ memset(&viocd_info, 0x00, sizeof(viocd_info));
+ for (i = 0; i < viocd_numdev; i++) {
+ viocd_info[i].dev = MKDEV(MAJOR_NR, i);
+ viocd_info[i].ops = &viocd_dops;
+ viocd_info[i].speed = 4;
+ viocd_info[i].capacity = 1;
+ viocd_info[i].mask = ~find_capability(viocd_unitinfo[i].type);
+ sprintf(viocd_info[i].name, VIOCD_DEVICE, VIOCD_DEVICE_OFFSET + i);
+ if (register_cdrom(&viocd_info[i]) != 0) {
+ printk(KERN_WARNING_VIO "Cannot register viocd CD-ROM %s!\n", viocd_info[i].name);
+ } else {
+ printk(KERN_INFO_VIO
+ "cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ viocd_info[i].name,
+ viocd_unitinfo[i].rsrcname,
+ viocd_unitinfo[i].type,
+ viocd_unitinfo[i].model);
+ }
+ }
+
+ /*
+ * Create the proc entry
+ */
+ iSeries_proc_callback(&viocd_proc_init);
+
+ return 0;
+}
+
+#ifdef MODULE
+void viocd_exit(void)
+{
+ int i;
+ for (i = 0; i < viocd_numdev; i++) {
+ if (unregister_cdrom(&viocd_info[i]) != 0) {
+ printk(KERN_WARNING_VIO "Cannot unregister viocd CD-ROM %s!\n", viocd_info[i].name);
+ }
+ }
+ if ((devfs_unregister_blkdev(MAJOR_NR, "viocd") == -EINVAL)) {
+ printk(KERN_WARNING_VIO "can't unregister viocd\n");
+ return;
+ }
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+ if (viocd_unitinfo)
+ kfree(viocd_unitinfo);
+
+ iSeries_proc_callback(&viocd_proc_delete);
+
+ viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
+ vio_clearHandler(viomajorsubtype_cdio);
+}
+#endif
+
+#ifdef MODULE
+module_init(viocd_init);
+module_exit(viocd_exit);
+MODULE_LICENSE("GPL");
+#endif
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/viocons.c linuxppc64_2_4/drivers/iseries/viocons.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/viocons.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/viocons.c Thu Apr 25 19:59:10 2002
@@ -0,0 +1,1390 @@
+/* -*- linux-c -*-
+ * drivers/char/viocons.c
+ *
+ * iSeries Virtual Terminal
+ *
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "vio.h"
+
+#include
+#include "asm/iSeries/HvCallEvent.h"
+#include "asm/iSeries/HvLpConfig.h"
+#include "asm/iSeries/HvCall.h"
+#include
+
+/* Check that the tty_driver_data actually points to our stuff
+ */
+#define VIOTTY_PARANOIA_CHECK 1
+#define VIOTTY_MAGIC (0x0DCB)
+
+static int debug;
+
+static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue);
+
+#define VTTY_PORTS 10
+#define VIOTTY_SERIAL_START 65
+
+static u64 sndMsgSeq[VTTY_PORTS];
+static u64 sndMsgAck[VTTY_PORTS];
+
+static spinlock_t consolelock = SPIN_LOCK_UNLOCKED;
+
+/* THe structure of the events that flow between us and OS/400. You can't
+ * mess with this unless the OS/400 side changes too
+ */
+struct viocharlpevent {
+ struct HvLpEvent event;
+ u32 mReserved1;
+ u16 mVersion;
+ u16 mSubTypeRc;
+ u8 virtualDevice;
+ u8 immediateDataLen;
+ u8 immediateData[VIOCHAR_MAX_DATA];
+};
+
+#define viochar_window (10)
+#define viochar_highwatermark (3)
+
+enum viocharsubtype {
+ viocharopen = 0x0001,
+ viocharclose = 0x0002,
+ viochardata = 0x0003,
+ viocharack = 0x0004,
+ viocharconfig = 0x0005
+};
+
+enum viochar_rc {
+ viochar_rc_ebusy = 1
+};
+
+/* When we get writes faster than we can send it to the partition,
+ * buffer the data here. There is one set of buffers for each virtual
+ * port.
+ * Note that bufferUsed is a bit map of used buffers.
+ * It had better have enough bits to hold NUM_BUF
+ * the bitops assume it is a multiple of unsigned long
+ */
+#define NUM_BUF (8)
+#define OVERFLOW_SIZE VIOCHAR_MAX_DATA
+
+static struct overflowBuffers {
+ unsigned long bufferUsed;
+ u8 *buffer[NUM_BUF];
+ int bufferBytes[NUM_BUF];
+ int curbuf;
+ int bufferOverflow;
+ int overflowMessage;
+} overflow[VTTY_PORTS];
+
+static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp);
+
+static struct tty_driver viotty_driver;
+static struct tty_driver viottyS_driver;
+static int viotty_refcount;
+
+static struct tty_struct *viotty_table[VTTY_PORTS];
+static struct tty_struct *viottyS_table[VTTY_PORTS];
+static struct termios *viotty_termios[VTTY_PORTS];
+static struct termios *viottyS_termios[VTTY_PORTS];
+static struct termios *viotty_termios_locked[VTTY_PORTS];
+static struct termios *viottyS_termios_locked[VTTY_PORTS];
+
+void hvlog(char *fmt, ...)
+{
+ int i;
+ static char buf[256];
+ va_list args;
+ va_start(args, fmt);
+ i = vsprintf(buf, fmt, args);
+ va_end(args);
+ HvCall_writeLogBuffer(buf, i);
+ HvCall_writeLogBuffer("\r", 1);
+
+}
+
+/* Our port information. We store a pointer to one entry in the
+ * tty_driver_data
+ */
+static struct port_info_tag {
+ int magic;
+ struct tty_struct *tty;
+ HvLpIndex lp;
+ u8 vcons;
+ u8 port;
+} port_info[VTTY_PORTS];
+
+/* Make sure we're pointing to a valid port_info structure. Shamelessly
+ * plagerized from serial.c
+ */
+static inline int viotty_paranoia_check(struct port_info_tag *pi,
+ kdev_t device, const char *routine)
+{
+#ifdef VIOTTY_PARANOIA_CHECK
+ static const char *badmagic =
+ "%s Warning: bad magic number for port_info struct (%s) in %s\n";
+ static const char *badinfo =
+ "%s Warning: null port_info for (%s) in %s\n";
+
+ if (!pi) {
+ printk(badinfo, KERN_WARNING_VIO, kdevname(device),
+ routine);
+ return 1;
+ }
+ if (pi->magic != VIOTTY_MAGIC) {
+ printk(badmagic, KERN_WARNING_VIO, kdevname(device),
+ routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Handle reads from the proc file system. Right now we just dump the
+ * state of the first TTY
+ */
+static int proc_read(char *buf, char **start, off_t offset,
+ int blen, int *eof, void *data)
+{
+ int len = 0;
+ struct tty_struct *tty = viotty_table[0];
+ struct termios *termios;
+ if (tty == NULL) {
+ len += sprintf(buf + len, "no tty\n");
+ *eof = 1;
+ return len;
+ }
+
+ len +=
+ sprintf(buf + len,
+ "tty info: COOK_OUT %ld COOK_IN %ld, NO_WRITE_SPLIT %ld\n",
+ tty->flags & TTY_HW_COOK_OUT,
+ tty->flags & TTY_HW_COOK_IN,
+ tty->flags & TTY_NO_WRITE_SPLIT);
+
+ termios = tty->termios;
+ if (termios == NULL) {
+ len += sprintf(buf + len, "no termios\n");
+ *eof = 1;
+ return len;
+ }
+ len += sprintf(buf + len, "INTR_CHAR %2.2x\n", INTR_CHAR(tty));
+ len += sprintf(buf + len, "QUIT_CHAR %2.2x\n", QUIT_CHAR(tty));
+ len +=
+ sprintf(buf + len, "ERASE_CHAR %2.2x\n", ERASE_CHAR(tty));
+ len += sprintf(buf + len, "KILL_CHAR %2.2x\n", KILL_CHAR(tty));
+ len += sprintf(buf + len, "EOF_CHAR %2.2x\n", EOF_CHAR(tty));
+ len += sprintf(buf + len, "TIME_CHAR %2.2x\n", TIME_CHAR(tty));
+ len += sprintf(buf + len, "MIN_CHAR %2.2x\n", MIN_CHAR(tty));
+ len += sprintf(buf + len, "SWTC_CHAR %2.2x\n", SWTC_CHAR(tty));
+ len +=
+ sprintf(buf + len, "START_CHAR %2.2x\n", START_CHAR(tty));
+ len += sprintf(buf + len, "STOP_CHAR %2.2x\n", STOP_CHAR(tty));
+ len += sprintf(buf + len, "SUSP_CHAR %2.2x\n", SUSP_CHAR(tty));
+ len += sprintf(buf + len, "EOL_CHAR %2.2x\n", EOL_CHAR(tty));
+ len +=
+ sprintf(buf + len, "REPRINT_CHAR %2.2x\n", REPRINT_CHAR(tty));
+ len +=
+ sprintf(buf + len, "DISCARD_CHAR %2.2x\n", DISCARD_CHAR(tty));
+ len +=
+ sprintf(buf + len, "WERASE_CHAR %2.2x\n", WERASE_CHAR(tty));
+ len +=
+ sprintf(buf + len, "LNEXT_CHAR %2.2x\n", LNEXT_CHAR(tty));
+ len += sprintf(buf + len, "EOL2_CHAR %2.2x\n", EOL2_CHAR(tty));
+
+ len += sprintf(buf + len, "I_IGNBRK %4.4x\n", I_IGNBRK(tty));
+ len += sprintf(buf + len, "I_BRKINT %4.4x\n", I_BRKINT(tty));
+ len += sprintf(buf + len, "I_IGNPAR %4.4x\n", I_IGNPAR(tty));
+ len += sprintf(buf + len, "I_PARMRK %4.4x\n", I_PARMRK(tty));
+ len += sprintf(buf + len, "I_INPCK %4.4x\n", I_INPCK(tty));
+ len += sprintf(buf + len, "I_ISTRIP %4.4x\n", I_ISTRIP(tty));
+ len += sprintf(buf + len, "I_INLCR %4.4x\n", I_INLCR(tty));
+ len += sprintf(buf + len, "I_IGNCR %4.4x\n", I_IGNCR(tty));
+ len += sprintf(buf + len, "I_ICRNL %4.4x\n", I_ICRNL(tty));
+ len += sprintf(buf + len, "I_IUCLC %4.4x\n", I_IUCLC(tty));
+ len += sprintf(buf + len, "I_IXON %4.4x\n", I_IXON(tty));
+ len += sprintf(buf + len, "I_IXANY %4.4x\n", I_IXANY(tty));
+ len += sprintf(buf + len, "I_IXOFF %4.4x\n", I_IXOFF(tty));
+ len += sprintf(buf + len, "I_IMAXBEL %4.4x\n", I_IMAXBEL(tty));
+
+ len += sprintf(buf + len, "O_OPOST %4.4x\n", O_OPOST(tty));
+ len += sprintf(buf + len, "O_OLCUC %4.4x\n", O_OLCUC(tty));
+ len += sprintf(buf + len, "O_ONLCR %4.4x\n", O_ONLCR(tty));
+ len += sprintf(buf + len, "O_OCRNL %4.4x\n", O_OCRNL(tty));
+ len += sprintf(buf + len, "O_ONOCR %4.4x\n", O_ONOCR(tty));
+ len += sprintf(buf + len, "O_ONLRET %4.4x\n", O_ONLRET(tty));
+ len += sprintf(buf + len, "O_OFILL %4.4x\n", O_OFILL(tty));
+ len += sprintf(buf + len, "O_OFDEL %4.4x\n", O_OFDEL(tty));
+ len += sprintf(buf + len, "O_NLDLY %4.4x\n", O_NLDLY(tty));
+ len += sprintf(buf + len, "O_CRDLY %4.4x\n", O_CRDLY(tty));
+ len += sprintf(buf + len, "O_TABDLY %4.4x\n", O_TABDLY(tty));
+ len += sprintf(buf + len, "O_BSDLY %4.4x\n", O_BSDLY(tty));
+ len += sprintf(buf + len, "O_VTDLY %4.4x\n", O_VTDLY(tty));
+ len += sprintf(buf + len, "O_FFDLY %4.4x\n", O_FFDLY(tty));
+
+ len += sprintf(buf + len, "C_BAUD %4.4x\n", C_BAUD(tty));
+ len += sprintf(buf + len, "C_CSIZE %4.4x\n", C_CSIZE(tty));
+ len += sprintf(buf + len, "C_CSTOPB %4.4x\n", C_CSTOPB(tty));
+ len += sprintf(buf + len, "C_CREAD %4.4x\n", C_CREAD(tty));
+ len += sprintf(buf + len, "C_PARENB %4.4x\n", C_PARENB(tty));
+ len += sprintf(buf + len, "C_PARODD %4.4x\n", C_PARODD(tty));
+ len += sprintf(buf + len, "C_HUPCL %4.4x\n", C_HUPCL(tty));
+ len += sprintf(buf + len, "C_CLOCAL %4.4x\n", C_CLOCAL(tty));
+ len += sprintf(buf + len, "C_CRTSCTS %4.4x\n", C_CRTSCTS(tty));
+
+ len += sprintf(buf + len, "L_ISIG %4.4x\n", L_ISIG(tty));
+ len += sprintf(buf + len, "L_ICANON %4.4x\n", L_ICANON(tty));
+ len += sprintf(buf + len, "L_XCASE %4.4x\n", L_XCASE(tty));
+ len += sprintf(buf + len, "L_ECHO %4.4x\n", L_ECHO(tty));
+ len += sprintf(buf + len, "L_ECHOE %4.4x\n", L_ECHOE(tty));
+ len += sprintf(buf + len, "L_ECHOK %4.4x\n", L_ECHOK(tty));
+ len += sprintf(buf + len, "L_ECHONL %4.4x\n", L_ECHONL(tty));
+ len += sprintf(buf + len, "L_NOFLSH %4.4x\n", L_NOFLSH(tty));
+ len += sprintf(buf + len, "L_TOSTOP %4.4x\n", L_TOSTOP(tty));
+ len += sprintf(buf + len, "L_ECHOCTL %4.4x\n", L_ECHOCTL(tty));
+ len += sprintf(buf + len, "L_ECHOPRT %4.4x\n", L_ECHOPRT(tty));
+ len += sprintf(buf + len, "L_ECHOKE %4.4x\n", L_ECHOKE(tty));
+ len += sprintf(buf + len, "L_FLUSHO %4.4x\n", L_FLUSHO(tty));
+ len += sprintf(buf + len, "L_PENDIN %4.4x\n", L_PENDIN(tty));
+ len += sprintf(buf + len, "L_IEXTEN %4.4x\n", L_IEXTEN(tty));
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * Handle writes to our proc file system. Right now just turns on and off
+ * our debug flag
+ */
+static int proc_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ if (count) {
+ if (buffer[0] == '1') {
+ printk("viocons: debugging on\n");
+ debug = 1;
+ } else {
+ printk("viocons: debugging off\n");
+ debug = 0;
+ }
+ }
+ return count;
+}
+
+/*
+ * setup our proc file system entries
+ */
+void viocons_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ struct proc_dir_entry *ent;
+ ent =
+ create_proc_entry("viocons", S_IFREG | S_IRUSR, iSeries_proc);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = NULL;
+ ent->read_proc = proc_read;
+ ent->write_proc = proc_write;
+}
+
+/*
+ * clean up our proc file system entries
+ */
+void viocons_proc_delete(struct proc_dir_entry *iSeries_proc)
+{
+ remove_proc_entry("viocons", iSeries_proc);
+}
+
+/*
+ * Add data to our pending-send buffers.
+ *
+ * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be
+ * used to log to the hypervisor buffer
+ */
+static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag)
+{
+ size_t bleft = len;
+ size_t curlen;
+ char *cbuf = (char *) buf;
+ int nextbuf;
+ struct overflowBuffers *pov = &overflow[port];
+ while (bleft > 0) {
+ /* If there is no space left in the current buffer, we have
+ * filled everything up, so return. If we filled the previous
+ * buffer we would already have moved to the next one.
+ */
+ if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) {
+ hvlog("buffer %d full. no more space\n",
+ pov->curbuf);
+ pov->bufferOverflow++;
+ pov->overflowMessage = 1;
+ return len - bleft;
+ }
+
+ /* Turn on the "used" bit for this buffer. If it's already on, that's
+ * fine.
+ */
+ set_bit(pov->curbuf, &pov->bufferUsed);
+
+ /*
+ * See if this buffer has been allocated. If not, allocate it
+ */
+ if (pov->buffer[pov->curbuf] == NULL)
+ pov->buffer[pov->curbuf] =
+ kmalloc(OVERFLOW_SIZE, GFP_ATOMIC);
+
+ /*
+ * Figure out how much we can copy into this buffer
+ */
+ if (bleft <
+ (OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf]))
+ curlen = bleft;
+ else
+ curlen =
+ OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf];
+
+ /*
+ * Copy the data into the buffer
+ */
+ if (userFlag)
+ copy_from_user(pov->buffer[pov->curbuf] +
+ pov->bufferBytes[pov->curbuf], cbuf,
+ curlen);
+ else
+ memcpy(pov->buffer[pov->curbuf] +
+ pov->bufferBytes[pov->curbuf], cbuf,
+ curlen);
+
+ pov->bufferBytes[pov->curbuf] += curlen;
+ cbuf += curlen;
+ bleft -= curlen;
+
+ /*
+ * Now see if we've filled this buffer
+ */
+ if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) {
+ nextbuf = (pov->curbuf + 1) % NUM_BUF;
+
+ /*
+ * Move to the next buffer if it hasn't been used yet
+ */
+ if (test_bit(nextbuf, &pov->bufferUsed) == 0) {
+ pov->curbuf = nextbuf;
+ }
+ }
+ }
+ return len;
+}
+
+/* Send pending data
+ *
+ * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be
+ * used to log to the hypervisor buffer
+ */
+void sendBuffers(u8 port, HvLpIndex lp)
+{
+ HvLpEvent_Rc hvrc;
+ int nextbuf;
+ struct viocharlpevent *viochar;
+ unsigned long flags;
+ struct overflowBuffers *pov = &overflow[port];
+
+ spin_lock_irqsave(&consolelock, flags);
+
+ viochar = (struct viocharlpevent *)
+ vio_get_event_buffer(viomajorsubtype_chario);
+
+ /* Make sure we got a buffer
+ */
+ if (viochar == NULL) {
+ hvlog("Yikes...can't get viochar buffer");
+ spin_unlock_irqrestore(&consolelock, flags);
+ return;
+ }
+
+ if (pov->bufferUsed == 0) {
+ hvlog("in sendbuffers, but no buffers used\n");
+ vio_free_event_buffer(viomajorsubtype_chario, viochar);
+ spin_unlock_irqrestore(&consolelock, flags);
+ return;
+ }
+
+ /*
+ * curbuf points to the buffer we're filling. We want to start sending AFTER
+ * this one.
+ */
+ nextbuf = (pov->curbuf + 1) % NUM_BUF;
+
+ /*
+ * Loop until we find a buffer with the bufferUsed bit on
+ */
+ while (test_bit(nextbuf, &pov->bufferUsed) == 0)
+ nextbuf = (nextbuf + 1) % NUM_BUF;
+
+ initDataEvent(viochar, lp);
+
+ /*
+ * While we have buffers with data, and our send window is open, send them
+ */
+ while ((test_bit(nextbuf, &pov->bufferUsed)) &&
+ ((sndMsgSeq[port] - sndMsgAck[port]) < viochar_window)) {
+ viochar->immediateDataLen = pov->bufferBytes[nextbuf];
+ viochar->event.xCorrelationToken = sndMsgSeq[port]++;
+ viochar->event.xSizeMinus1 =
+ offsetof(struct viocharlpevent,
+ immediateData) + viochar->immediateDataLen;
+
+ memcpy(viochar->immediateData, pov->buffer[nextbuf],
+ viochar->immediateDataLen);
+
+ hvrc = HvCallEvent_signalLpEvent(&viochar->event);
+ if (hvrc) {
+ /*
+ * MUST unlock the spinlock before doing a printk
+ */
+ vio_free_event_buffer(viomajorsubtype_chario,
+ viochar);
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ printk(KERN_WARNING_VIO
+ "console error sending event! return code %d\n",
+ (int) hvrc);
+ return;
+ }
+
+ /*
+ * clear the bufferUsed bit, zero the number of bytes in this buffer,
+ * and move to the next buffer
+ */
+ clear_bit(nextbuf, &pov->bufferUsed);
+ pov->bufferBytes[nextbuf] = 0;
+ nextbuf = (nextbuf + 1) % NUM_BUF;
+ }
+
+
+ /*
+ * If we have emptied all the buffers, start at 0 again.
+ * this will re-use any allocated buffers
+ */
+ if (pov->bufferUsed == 0) {
+ pov->curbuf = 0;
+
+ if (pov->overflowMessage)
+ pov->overflowMessage = 0;
+
+ if (port_info[port].tty) {
+ if ((port_info[port].tty->
+ flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && (port_info[port].tty->ldisc.write_wakeup))
+ (port_info[port].tty->ldisc.
+ write_wakeup) (port_info[port].tty);
+ wake_up_interruptible(&port_info[port].tty->
+ write_wait);
+ }
+ }
+
+ vio_free_event_buffer(viomajorsubtype_chario, viochar);
+ spin_unlock_irqrestore(&consolelock, flags);
+
+}
+
+/* Our internal writer. Gets called both from the console device and
+ * the tty device. the tty pointer will be NULL if called from the console.
+ *
+ * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be
+ * used to log to the hypervisor buffer
+ */
+static int internal_write(struct tty_struct *tty, const char *buf,
+ size_t len, int userFlag)
+{
+ HvLpEvent_Rc hvrc;
+ size_t bleft = len;
+ size_t curlen;
+ const char *curbuf = buf;
+ struct viocharlpevent *viochar;
+ unsigned long flags;
+ struct port_info_tag *pi = NULL;
+ HvLpIndex lp;
+ u8 port;
+
+ if (tty) {
+ pi = (struct port_info_tag *) tty->driver_data;
+
+ if (!pi
+ || viotty_paranoia_check(pi, tty->device,
+ "viotty_internal_write"))
+ return -ENODEV;
+
+ lp = pi->lp;
+ port = pi->port;
+ } else {
+ /* If this is the console device, use the lp from the first port entry
+ */
+ port = 0;
+ lp = port_info[0].lp;
+ }
+
+ /* Always put console output in the hypervisor console log
+ */
+ if (port == 0)
+ HvCall_writeLogBuffer(buf, len);
+
+ /* If the path to this LP is closed, don't bother doing anything more.
+ * just dump the data on the floor
+ */
+ if (!viopath_isactive(lp))
+ return len;
+
+ /*
+ * If there is already data queued for this port, send it
+ */
+ if (overflow[port].bufferUsed)
+ sendBuffers(port, lp);
+
+ spin_lock_irqsave(&consolelock, flags);
+
+ viochar = (struct viocharlpevent *)
+ vio_get_event_buffer(viomajorsubtype_chario);
+ /* Make sure we got a buffer
+ */
+ if (viochar == NULL) {
+ hvlog("Yikes...can't get viochar buffer");
+ spin_unlock_irqrestore(&consolelock, flags);
+ return -1;
+ }
+
+ initDataEvent(viochar, lp);
+
+ /* Got the lock, don't cause console output */
+ while ((bleft > 0) &&
+ (overflow[port].bufferUsed == 0) &&
+ ((sndMsgSeq[port] - sndMsgAck[port]) < viochar_window)) {
+ if (bleft > VIOCHAR_MAX_DATA)
+ curlen = VIOCHAR_MAX_DATA;
+ else
+ curlen = bleft;
+
+ viochar->immediateDataLen = curlen;
+ viochar->event.xCorrelationToken = sndMsgSeq[port]++;
+
+ if (userFlag)
+ copy_from_user(viochar->immediateData, curbuf,
+ curlen);
+ else
+ memcpy(viochar->immediateData, curbuf, curlen);
+
+ viochar->event.xSizeMinus1 =
+ offsetof(struct viocharlpevent,
+ immediateData) + curlen;
+
+ hvrc = HvCallEvent_signalLpEvent(&viochar->event);
+ if (hvrc) {
+ /*
+ * MUST unlock the spinlock before doing a printk
+ */
+ vio_free_event_buffer(viomajorsubtype_chario,
+ viochar);
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ hvlog("viocons: error sending event! %d\n",
+ (int) hvrc);
+ return len - bleft;
+ }
+
+ curbuf += curlen;
+ bleft -= curlen;
+ }
+
+ /*
+ * If we didn't send it all, buffer it
+ */
+ if (bleft > 0) {
+ bleft -= bufferAdd(port, curbuf, bleft, userFlag);
+ }
+ vio_free_event_buffer(viomajorsubtype_chario, viochar);
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ return len - bleft;
+}
+
+/* Initialize the common fields in a charLpEvent
+ */
+static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp)
+{
+ memset(viochar, 0x00, sizeof(struct viocharlpevent));
+
+ viochar->event.xFlags.xValid = 1;
+ viochar->event.xFlags.xFunction = HvLpEvent_Function_Int;
+ viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck;
+ viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck;
+ viochar->event.xType = HvLpEvent_Type_VirtualIo;
+ viochar->event.xSubtype = viomajorsubtype_chario | viochardata;
+ viochar->event.xSourceLp = HvLpConfig_getLpIndex();
+ viochar->event.xTargetLp = lp;
+ viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent);
+ viochar->event.xSourceInstanceId = viopath_sourceinst(lp);
+ viochar->event.xTargetInstanceId = viopath_targetinst(lp);
+}
+
+
+/* console device write
+ */
+static void viocons_write(struct console *co, const char *s,
+ unsigned count)
+{
+ /* This parser will ensure that all single instances of either \n or \r are
+ * matched into carriage return/line feed combinations. It also allows for
+ * instances where there already exist \n\r combinations as well as the
+ * reverse, \r\n combinations.
+ */
+
+ int index;
+ char charptr[1];
+ int foundcr;
+ int slicebegin;
+ int sliceend;
+
+ foundcr = 0;
+ slicebegin = 0;
+ sliceend = 0;
+
+ for (index = 0; index < count; index++) {
+ if (!foundcr && s[index] == 0x0a) {
+ if ((slicebegin - sliceend > 0)
+ && sliceend < count) {
+ internal_write(NULL, &s[slicebegin],
+ sliceend - slicebegin, 0);
+ slicebegin = sliceend;
+ }
+ charptr[0] = '\r';
+ internal_write(NULL, charptr, 1, 0);
+ }
+ if (foundcr && s[index] != 0x0a) {
+ if ((index - 2) >= 0) {
+ if (s[index - 2] != 0x0a) {
+ internal_write(NULL,
+ &s[slicebegin],
+ sliceend -
+ slicebegin, 0);
+ slicebegin = sliceend;
+ charptr[0] = '\n';
+ internal_write(NULL, charptr, 1,
+ 0);
+ }
+ }
+ }
+ sliceend++;
+
+ if (s[index] == 0x0d)
+ foundcr = 1;
+ else
+ foundcr = 0;
+ }
+
+ internal_write(NULL, &s[slicebegin], sliceend - slicebegin, 0);
+
+ if (count > 1) {
+ if (foundcr == 1 && s[count - 1] != 0x0a) {
+ charptr[0] = '\n';
+ internal_write(NULL, charptr, 1, 0);
+ } else if (s[count - 1] == 0x0a && s[count - 2] != 0x0d) {
+
+ charptr[0] = '\r';
+ internal_write(NULL, charptr, 1, 0);
+ }
+ }
+}
+
+/* Work out a the device associate with this console
+ */
+static kdev_t viocons_device(struct console *c)
+{
+ return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start);
+}
+
+/* console device read method
+ */
+static int viocons_read(struct console *co, const char *s, unsigned count)
+{
+ printk(KERN_DEBUG_VIO "viocons_read\n");
+ // Implement me
+ interruptible_sleep_on(&viocons_wait_queue);
+ return 0;
+}
+
+/* Do console device setup
+ */
+static int __init viocons_setup(struct console *co, char *options)
+{
+ return 0;
+}
+
+/* console device I/O methods
+ */
+static struct console viocons = {
+ name:"ttyS",
+ write:viocons_write,
+ read:viocons_read,
+ device:viocons_device,
+ setup:viocons_setup,
+ flags:CON_PRINTBUFFER,
+};
+
+
+/* TTY Open method
+ */
+static int viotty_open(struct tty_struct *tty, struct file *filp)
+{
+ int port;
+ unsigned long flags;
+ MOD_INC_USE_COUNT;
+ port = MINOR(tty->device) - tty->driver.minor_start;
+
+ if (port >= VIOTTY_SERIAL_START)
+ port -= VIOTTY_SERIAL_START;
+
+ if ((port < 0) || (port >= VTTY_PORTS)) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&consolelock, flags);
+
+ /*
+ * If some other TTY is already connected here, reject the open
+ */
+ if ((port_info[port].tty) && (port_info[port].tty != tty)) {
+ spin_unlock_irqrestore(&consolelock, flags);
+ MOD_DEC_USE_COUNT;
+ printk(KERN_WARNING_VIO
+ "console attempt to open device twice from different ttys\n");
+ return -EBUSY;
+ }
+ tty->driver_data = &port_info[port];
+ port_info[port].tty = tty;
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ return 0;
+}
+
+/* TTY Close method
+ */
+static void viotty_close(struct tty_struct *tty, struct file *filp)
+{
+ unsigned long flags;
+ struct port_info_tag *pi =
+ (struct port_info_tag *) tty->driver_data;
+
+ if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close"))
+ return;
+
+ spin_lock_irqsave(&consolelock, flags);
+ if (tty->count == 1) {
+ pi->tty = NULL;
+ }
+
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* TTY Write method
+ */
+static int viotty_write(struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ return internal_write(tty, buf, count, from_user);
+}
+
+/* TTY put_char method
+ */
+static void viotty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ internal_write(tty, &ch, 1, 0);
+}
+
+/* TTY flush_chars method
+ */
+static void viotty_flush_chars(struct tty_struct *tty)
+{
+}
+
+/* TTY write_room method
+ */
+static int viotty_write_room(struct tty_struct *tty)
+{
+ int i;
+ int room = 0;
+ struct port_info_tag *pi =
+ (struct port_info_tag *) tty->driver_data;
+
+ if (!pi
+ || viotty_paranoia_check(pi, tty->device,
+ "viotty_sendbuffers"))
+ return 0;
+
+ // If no buffers are used, return the max size
+ if (overflow[pi->port].bufferUsed == 0)
+ return VIOCHAR_MAX_DATA * NUM_BUF;
+
+ for (i = 0; ((i < NUM_BUF) && (room < VIOCHAR_MAX_DATA)); i++) {
+ room +=
+ (OVERFLOW_SIZE - overflow[pi->port].bufferBytes[i]);
+ }
+
+ if (room > VIOCHAR_MAX_DATA)
+ return VIOCHAR_MAX_DATA;
+ else
+ return room;
+}
+
+/* TTY chars_in_buffer_room method
+ */
+static int viotty_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static void viotty_flush_buffer(struct tty_struct *tty)
+{
+}
+
+static int viotty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ /* the ioctls below read/set the flags usually shown in the leds */
+ /* don't use them - they will go away without warning */
+ case KDGETLED:
+ case KDGKBLED:
+ return put_user(0, (char *) arg);
+
+ case KDSKBLED:
+ return 0;
+ }
+
+ return n_tty_ioctl(tty, file, cmd, arg);
+}
+
+static void viotty_throttle(struct tty_struct *tty)
+{
+}
+
+static void viotty_unthrottle(struct tty_struct *tty)
+{
+}
+
+static void viotty_set_termios(struct tty_struct *tty,
+ struct termios *old_termios)
+{
+}
+
+static void viotty_stop(struct tty_struct *tty)
+{
+}
+
+static void viotty_start(struct tty_struct *tty)
+{
+}
+
+static void viotty_hangup(struct tty_struct *tty)
+{
+}
+
+static void viotty_break(struct tty_struct *tty, int break_state)
+{
+}
+
+static void viotty_send_xchar(struct tty_struct *tty, char ch)
+{
+}
+
+static void viotty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+}
+
+/* Handle an open charLpEvent. Could be either interrupt or ack
+ */
+static void vioHandleOpenEvent(struct HvLpEvent *event)
+{
+ unsigned long flags;
+ u8 eventRc;
+ u16 eventSubtypeRc;
+ struct viocharlpevent *cevent = (struct viocharlpevent *) event;
+ u8 port = cevent->virtualDevice;
+
+ if (event->xFlags.xFunction == HvLpEvent_Function_Ack) {
+ if (port >= VTTY_PORTS)
+ return;
+
+ spin_lock_irqsave(&consolelock, flags);
+ /* Got the lock, don't cause console output */
+
+ if (event->xRc == HvLpEvent_Rc_Good) {
+ sndMsgSeq[port] = sndMsgAck[port] = 0;
+ }
+
+ port_info[port].lp = event->xTargetLp;
+
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ if (event->xCorrelationToken != 0) {
+ unsigned long semptr = event->xCorrelationToken;
+ up((struct semaphore *) semptr);
+ } else
+ printk(KERN_WARNING_VIO
+ "console: wierd...got open ack without semaphore\n");
+ } else {
+ /* This had better require an ack, otherwise complain
+ */
+ if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) {
+ printk(KERN_WARNING_VIO
+ "console: viocharopen without ack bit!\n");
+ return;
+ }
+
+ spin_lock_irqsave(&consolelock, flags);
+ /* Got the lock, don't cause console output */
+
+ /* Make sure this is a good virtual tty */
+ if (port >= VTTY_PORTS) {
+ eventRc = HvLpEvent_Rc_SubtypeError;
+ eventSubtypeRc = viorc_openRejected;
+ }
+
+ /* If this is tty is already connected to a different
+ partition, fail */
+ else if ((port_info[port].lp != HvLpIndexInvalid) &&
+ (port_info[port].lp != event->xSourceLp)) {
+ eventRc = HvLpEvent_Rc_SubtypeError;
+ eventSubtypeRc = viorc_openRejected;
+ } else {
+ port_info[port].lp = event->xSourceLp;
+ eventRc = HvLpEvent_Rc_Good;
+ eventSubtypeRc = viorc_good;
+ sndMsgSeq[port] = sndMsgAck[port] = 0;
+ }
+
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ /* Return the acknowledgement */
+ HvCallEvent_ackLpEvent(event);
+ }
+}
+
+/* Handle a close open charLpEvent. Could be either interrupt or ack
+ */
+static void vioHandleCloseEvent(struct HvLpEvent *event)
+{
+ unsigned long flags;
+ struct viocharlpevent *cevent = (struct viocharlpevent *) event;
+ u8 port = cevent->virtualDevice;
+
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ if (port >= VTTY_PORTS)
+ return;
+
+ /* For closes, just mark the console partition invalid */
+ spin_lock_irqsave(&consolelock, flags);
+ /* Got the lock, don't cause console output */
+
+ if (port_info[port].lp == event->xSourceLp)
+ port_info[port].lp = HvLpIndexInvalid;
+
+ spin_unlock_irqrestore(&consolelock, flags);
+ printk(KERN_INFO_VIO
+ "console close from %d\n", event->xSourceLp);
+ } else {
+ printk(KERN_WARNING_VIO
+ "console got unexpected close acknowlegement\n");
+ }
+}
+
+/* Handle a config charLpEvent. Could be either interrupt or ack
+ */
+static void vioHandleConfig(struct HvLpEvent *event)
+{
+ struct viocharlpevent *cevent = (struct viocharlpevent *) event;
+ int len;
+
+ len = cevent->immediateDataLen;
+ HvCall_writeLogBuffer(cevent->immediateData,
+ cevent->immediateDataLen);
+
+ if (cevent->immediateData[0] == 0x01) {
+ printk(KERN_INFO_VIO
+ "console window resized to %d: %d: %d: %d\n",
+ cevent->immediateData[1],
+ cevent->immediateData[2],
+ cevent->immediateData[3], cevent->immediateData[4]);
+ } else {
+ printk(KERN_WARNING_VIO "console unknown config event\n");
+ }
+ return;
+}
+
+/* Handle a data charLpEvent.
+ */
+static void vioHandleData(struct HvLpEvent *event)
+{
+ struct tty_struct *tty;
+ struct viocharlpevent *cevent = (struct viocharlpevent *) event;
+ struct port_info_tag *pi;
+ int len;
+ u8 port = cevent->virtualDevice;
+
+ if (port >= VTTY_PORTS) {
+ printk(KERN_WARNING_VIO
+ "console data on invalid virtual device %d\n",
+ port);
+ return;
+ }
+
+ tty = port_info[port].tty;
+
+ if (tty == NULL) {
+ printk(KERN_WARNING_VIO
+ "no tty for virtual device %d\n", port);
+ return;
+ }
+
+ if (tty->magic != TTY_MAGIC) {
+ printk(KERN_WARNING_VIO "tty bad magic\n");
+ return;
+ }
+
+ /*
+ * Just to be paranoid, make sure the tty points back to this port
+ */
+ pi = (struct port_info_tag *) tty->driver_data;
+
+ if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData"))
+ return;
+
+ len = cevent->immediateDataLen;
+
+ if (len == 0)
+ return;
+
+ /*
+ * Log port 0 data to the hypervisor log
+ */
+ if (port == 0)
+ HvCall_writeLogBuffer(cevent->immediateData,
+ cevent->immediateDataLen);
+
+ /* Don't copy more bytes than there is room for in the buffer */
+ if (tty->flip.count + len > TTY_FLIPBUF_SIZE) {
+ len = TTY_FLIPBUF_SIZE - tty->flip.count;
+ printk(KERN_WARNING_VIO
+ "console input buffer overflow!\n");
+ }
+
+ memcpy(tty->flip.char_buf_ptr, cevent->immediateData, len);
+ memset(tty->flip.flag_buf_ptr, TTY_NORMAL, len);
+
+ /* Update the kernel buffer end */
+ tty->flip.count += len;
+ tty->flip.char_buf_ptr += len;
+
+ tty->flip.flag_buf_ptr += len;
+
+ tty_flip_buffer_push(tty);
+}
+
+/* Handle an ack charLpEvent.
+ */
+static void vioHandleAck(struct HvLpEvent *event)
+{
+ struct viocharlpevent *cevent = (struct viocharlpevent *) event;
+ unsigned long flags;
+ u8 port = cevent->virtualDevice;
+
+ if (port >= VTTY_PORTS) {
+ printk(KERN_WARNING_VIO
+ "viocons: data on invalid virtual device\n");
+ return;
+ }
+
+ spin_lock_irqsave(&consolelock, flags);
+ sndMsgAck[port] = event->xCorrelationToken;
+ spin_unlock_irqrestore(&consolelock, flags);
+
+ if (overflow[port].bufferUsed)
+ sendBuffers(port, port_info[port].lp);
+}
+
+/* Handle charLpEvents and route to the appropriate routine
+ */
+static void vioHandleCharEvent(struct HvLpEvent *event)
+{
+ int charminor;
+
+ if (event == NULL) {
+ return;
+ }
+ charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK;
+ switch (charminor) {
+ case viocharopen:
+ vioHandleOpenEvent(event);
+ break;
+ case viocharclose:
+ vioHandleCloseEvent(event);
+ break;
+ case viochardata:
+ vioHandleData(event);
+ break;
+ case viocharack:
+ vioHandleAck(event);
+ break;
+ case viocharconfig:
+ vioHandleConfig(event);
+ break;
+ default:
+ if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
+ (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ }
+}
+
+/* Send an open event
+ */
+static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem)
+{
+ return HvCallEvent_signalLpEventFast(remoteLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_chario
+ | viocharopen,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (remoteLp),
+ viopath_targetinst
+ (remoteLp),
+ (u64) (unsigned long)
+ sem, VIOVERSION << 16,
+ ((u64) port << 48), 0, 0, 0);
+
+}
+
+int __init viocons_init2(void)
+{
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ int rc;
+
+ /*
+ * Now open to the primary LP
+ */
+ printk(KERN_INFO_VIO "console open path to primary\n");
+ rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario, viochar_window + 2); /* +2 for fudge */
+ if (rc) {
+ printk(KERN_WARNING_VIO
+ "console error opening to primary %d\n", rc);
+ }
+
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ vio_set_hostlp();
+ }
+
+ /*
+ * And if the primary is not the same as the hosting LP, open to the
+ * hosting lp
+ */
+ if ((viopath_hostLp != HvLpIndexInvalid) &&
+ (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) {
+ printk(KERN_INFO_VIO
+ "console open path to hosting (%d)\n",
+ viopath_hostLp);
+ rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, viochar_window + 2); /* +2 for fudge */
+ if (rc) {
+ printk(KERN_WARNING_VIO
+ "console error opening to partition %d: %d\n",
+ viopath_hostLp, rc);
+ }
+ }
+
+ if (vio_setHandler(viomajorsubtype_chario, vioHandleCharEvent) < 0) {
+ printk(KERN_WARNING_VIO
+ "Error seting handler for console events!\n");
+ }
+
+ printk(KERN_INFO_VIO "console major number is %d\n", TTY_MAJOR);
+
+ /* First, try to open the console to the hosting lp.
+ * Wait on a semaphore for the response.
+ */
+ if ((viopath_isactive(viopath_hostLp)) &&
+ (viocons_sendOpen(viopath_hostLp, 0, &Semaphore) == 0)) {
+ printk(KERN_INFO_VIO
+ "opening console to hosting partition %d\n",
+ viopath_hostLp);
+ down(&Semaphore);
+ }
+
+ /*
+ * If we don't have an active console, try the primary
+ */
+ if ((!viopath_isactive(port_info[0].lp)) &&
+ (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) &&
+ (viocons_sendOpen
+ (HvLpConfig_getPrimaryLpIndex(), 0, &Semaphore) == 0)) {
+ printk(KERN_INFO_VIO
+ "opening console to primary partition\n");
+ down(&Semaphore);
+ }
+
+ /* Initialize the tty_driver structure */
+ memset(&viotty_driver, 0, sizeof(struct tty_driver));
+ viotty_driver.magic = TTY_DRIVER_MAGIC;
+ viotty_driver.driver_name = "vioconsole";
+#if defined(CONFIG_DEVFS_FS)
+ viotty_driver.name = "tty%d";
+#else
+ viotty_driver.name = "tty";
+#endif
+ viotty_driver.major = TTY_MAJOR;
+ viotty_driver.minor_start = 1;
+ viotty_driver.name_base = 1;
+ viotty_driver.num = VTTY_PORTS;
+ viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE;
+ viotty_driver.subtype = 1;
+ viotty_driver.init_termios = tty_std_termios;
+ viotty_driver.flags =
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+ viotty_driver.refcount = &viotty_refcount;
+ viotty_driver.table = viotty_table;
+ viotty_driver.termios = viotty_termios;
+ viotty_driver.termios_locked = viotty_termios_locked;
+
+ viotty_driver.open = viotty_open;
+ viotty_driver.close = viotty_close;
+ viotty_driver.write = viotty_write;
+ viotty_driver.put_char = viotty_put_char;
+ viotty_driver.flush_chars = viotty_flush_chars;
+ viotty_driver.write_room = viotty_write_room;
+ viotty_driver.chars_in_buffer = viotty_chars_in_buffer;
+ viotty_driver.flush_buffer = viotty_flush_buffer;
+ viotty_driver.ioctl = viotty_ioctl;
+ viotty_driver.throttle = viotty_throttle;
+ viotty_driver.unthrottle = viotty_unthrottle;
+ viotty_driver.set_termios = viotty_set_termios;
+ viotty_driver.stop = viotty_stop;
+ viotty_driver.start = viotty_start;
+ viotty_driver.hangup = viotty_hangup;
+ viotty_driver.break_ctl = viotty_break;
+ viotty_driver.send_xchar = viotty_send_xchar;
+ viotty_driver.wait_until_sent = viotty_wait_until_sent;
+
+ viottyS_driver = viotty_driver;
+#if defined(CONFIG_DEVFS_FS)
+ viottyS_driver.name = "ttyS%d";
+#else
+ viottyS_driver.name = "ttyS";
+#endif
+ viottyS_driver.major = TTY_MAJOR;
+ viottyS_driver.minor_start = VIOTTY_SERIAL_START;
+ viottyS_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ viottyS_driver.table = viottyS_table;
+ viottyS_driver.termios = viottyS_termios;
+ viottyS_driver.termios_locked = viottyS_termios_locked;
+
+ if (tty_register_driver(&viotty_driver)) {
+ printk(KERN_WARNING_VIO
+ "Couldn't register console driver\n");
+ }
+
+ if (tty_register_driver(&viottyS_driver)) {
+ printk(KERN_WARNING_VIO
+ "Couldn't register console S driver\n");
+ }
+ /* Now create the vcs and vcsa devfs entries so mingetty works */
+#if defined(CONFIG_DEVFS_FS)
+ {
+ struct tty_driver temp_driver = viotty_driver;
+ int i;
+
+ temp_driver.name = "vcs%d";
+ for (i = 0; i < VTTY_PORTS; i++)
+ tty_register_devfs(&temp_driver,
+ 0, i + temp_driver.minor_start);
+
+ temp_driver.name = "vcsa%d";
+ for (i = 0; i < VTTY_PORTS; i++)
+ tty_register_devfs(&temp_driver,
+ 0, i + temp_driver.minor_start);
+
+ // For compatibility with some earlier code only!
+ // This will go away!!!
+ temp_driver.name = "viocons/%d";
+ temp_driver.name_base = 0;
+ for (i = 0; i < VTTY_PORTS; i++)
+ tty_register_devfs(&temp_driver,
+ 0, i + temp_driver.minor_start);
+ }
+#endif
+
+ /*
+ * Create the proc entry
+ */
+ iSeries_proc_callback(&viocons_proc_init);
+
+ return 0;
+}
+
+void __init viocons_init(void)
+{
+ int i;
+ printk(KERN_INFO_VIO "registering console\n");
+
+ memset(&port_info, 0x00, sizeof(port_info));
+ for (i = 0; i < VTTY_PORTS; i++) {
+ sndMsgSeq[i] = sndMsgAck[i] = 0;
+ port_info[i].port = i;
+ port_info[i].lp = HvLpIndexInvalid;
+ port_info[i].magic = VIOTTY_MAGIC;
+ }
+
+ register_console(&viocons);
+ memset(overflow, 0x00, sizeof(overflow));
+ debug = 0;
+
+ HvCall_setLogBufferFormatAndCodepage(HvCall_LogBuffer_ASCII, 437);
+}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/viodasd.c linuxppc64_2_4/drivers/iseries/viodasd.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/viodasd.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/viodasd.c Wed Apr 3 12:27:16 2002
@@ -0,0 +1,1623 @@
+/* -*- linux-c -*-
+ * viodasd.c
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************
+ * This routine provides access to disk space (termed "DASD" in historical
+ * IBM terms) owned and managed by an OS/400 partition running on the
+ * same box as this Linux partition.
+ *
+ * All disk operations are performed by sending messages back and forth to
+ * the OS/400 partition.
+ *
+ * This device driver can either use its own major number, or it can
+ * pretend to be an IDE drive (grep 'IDE[0-9]_MAJOR' ../../include/linux/major.h).
+ * This is controlled with a CONFIG option. You can either call this an
+ * elegant solution to the fact that a lot of software doesn't recognize
+ * a new disk major number...or you can call this a really ugly hack.
+ * Your choice.
+ */
+
+#include
+#include
+
+/* Changelog:
+ 2001-11-27 devilbis Added first pass at complete IDE emulation
+ */
+
+/* Decide if we are using our own major or pretending to be an IDE drive
+ *
+ * If we are using our own major, we only support 7 partitions per physical
+ * disk....so with minor numbers 0-255 we get a maximum of 32 disks. If we
+ * are emulating IDE, we get 63 partitions per disk, with a maximum of 4
+ * disks per major, but common practice is to place only 2 devices in /dev
+ * for each IDE major, for a total of 20 (since there are 10 IDE majors).
+ */
+
+#ifdef CONFIG_VIODASD_IDE
+static const int major_table[] = {
+ IDE0_MAJOR,
+ IDE1_MAJOR,
+ IDE2_MAJOR,
+ IDE3_MAJOR,
+ IDE4_MAJOR,
+ IDE5_MAJOR,
+ IDE6_MAJOR,
+ IDE7_MAJOR,
+ IDE8_MAJOR,
+ IDE9_MAJOR,
+};
+enum {
+ DEV_PER_MAJOR = 2,
+ PARTITION_SHIFT = 6,
+};
+static int major_to_index(int major)
+{
+ switch(major) {
+ case IDE0_MAJOR: return 0;
+ case IDE1_MAJOR: return 1;
+ case IDE2_MAJOR: return 2;
+ case IDE3_MAJOR: return 3;
+ case IDE4_MAJOR: return 4;
+ case IDE5_MAJOR: return 5;
+ case IDE6_MAJOR: return 6;
+ case IDE7_MAJOR: return 7;
+ case IDE8_MAJOR: return 8;
+ case IDE9_MAJOR: return 9;
+ default:
+ return -1;
+ }
+}
+#define do_viodasd_request do_hd_request
+#define VIOD_DEVICE_NAME "hd"
+#define VIOD_GENHD_NAME "hd"
+#else /* !CONFIG_VIODASD_IDE */
+static const int major_table[] = {
+ VIODASD_MAJOR,
+};
+enum {
+ DEV_PER_MAJOR = 32,
+ PARTITION_SHIFT = 3,
+};
+static int major_to_index(int major)
+{
+ if(major != VIODASD_MAJOR)
+ return -1;
+ return 0;
+}
+#define VIOD_DEVICE_NAME "viod"
+#ifdef CONFIG_DEVFS_FS
+#define VIOD_GENHD_NAME "viod"
+#else
+#define VIOD_GENHD_NAME "iSeries/vd"
+#endif
+#endif /* CONFIG_VIODASD_IDE */
+
+#define DEVICE_NR(dev) (devt_to_diskno(dev))
+#define LOCAL_END_REQUEST
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include "vio.h"
+#include
+
+MODULE_DESCRIPTION("iSeries Virtual DASD");
+MODULE_AUTHOR("Dave Boutcher");
+MODULE_LICENSE("GPL");
+
+#define VIODASD_VERS "1.50"
+
+enum {
+ NUM_MAJORS = sizeof(major_table) / sizeof(major_table[0]),
+ MAX_DISKNO = DEV_PER_MAJOR * NUM_MAJORS,
+ MAX_MAJOR_NAME = 4 + 1, /* maximum length of a gendisk->name */
+};
+
+static volatile int viodasd_max_disk = MAX_DISKNO - 1;
+
+static int diskno_to_major(int diskno)
+{
+ if (diskno >= MAX_DISKNO)
+ return -1;
+ return major_table[diskno / DEV_PER_MAJOR];
+}
+static int devt_to_diskno(kdev_t dev)
+{
+ return major_to_index(MAJOR(dev)) * DEV_PER_MAJOR +
+ (MINOR(dev) >> PARTITION_SHIFT);
+}
+static int diskno_to_devt(int diskno, int partition)
+{
+ return MKDEV(diskno_to_major(diskno),
+ ((diskno % DEV_PER_MAJOR) << PARTITION_SHIFT) +
+ partition);
+}
+
+#define VIOMAXREQ 16
+#define VIOMAXBLOCKDMA 12
+
+extern struct pci_dev *iSeries_vio_dev;
+
+struct openData {
+ u64 mDiskLen;
+ u16 mMaxDisks;
+ u16 mCylinders;
+ u16 mTracks;
+ u16 mSectors;
+ u16 mBytesPerSector;
+};
+
+struct rwData { // Used during rw
+ u64 mOffset;
+ struct {
+ u32 mToken;
+ u32 reserved;
+ u64 mLen;
+ } dmaInfo[VIOMAXBLOCKDMA];
+};
+
+struct vioblocklpevent {
+ struct HvLpEvent event;
+ u32 mReserved1;
+ u16 mVersion;
+ u16 mSubTypeRc;
+ u16 mDisk;
+ u16 mFlags;
+ union {
+ struct openData openData;
+ struct rwData rwData;
+ struct {
+ u64 changed;
+ } check;
+ } u;
+};
+
+#define vioblockflags_ro 0x0001
+
+enum vioblocksubtype {
+ vioblockopen = 0x0001,
+ vioblockclose = 0x0002,
+ vioblockread = 0x0003,
+ vioblockwrite = 0x0004,
+ vioblockflush = 0x0005,
+ vioblockcheck = 0x0007
+};
+
+/* In a perfect world we will perform better if we get page-aligned I/O
+ * requests, in multiples of pages. At least peg our block size to the
+ * actual page size.
+ */
+static int blksize = HVPAGESIZE; /* in bytes */
+
+static DECLARE_WAIT_QUEUE_HEAD(viodasd_wait);
+struct viodasd_waitevent {
+ struct semaphore *sem;
+ int rc;
+ union {
+ int changed; /* Used only for check_change */
+ u16 subRC;
+ } data;
+};
+
+static const struct vio_error_entry viodasd_err_table[] = {
+ {0x0201, EINVAL, "Invalid Range"},
+ {0x0202, EINVAL, "Invalid Token"},
+ {0x0203, EIO, "DMA Error"},
+ {0x0204, EIO, "Use Error"},
+ {0x0205, EIO, "Release Error"},
+ {0x0206, EINVAL, "Invalid Disk"},
+ {0x0207, EBUSY, "Cant Lock"},
+ {0x0208, EIO, "Already Locked"},
+ {0x0209, EIO, "Already Unlocked"},
+ {0x020A, EIO, "Invalid Arg"},
+ {0x020B, EIO, "Bad IFS File"},
+ {0x020C, EROFS, "Read Only Device"},
+ {0x02FF, EIO, "Internal Error"},
+ {0x0000, 0, NULL},
+};
+
+/* Our gendisk table
+ */
+static struct gendisk viodasd_gendisk[NUM_MAJORS];
+
+static struct gendisk *major_to_gendisk(int major)
+{
+ int index = major_to_index(major);
+ return index < 0 ? NULL : &viodasd_gendisk[index];
+}
+static struct hd_struct *devt_to_partition(kdev_t dev)
+{
+ return &major_to_gendisk(MAJOR(dev))->part[MINOR(dev)];
+}
+
+/* Figure out the biggest I/O request (in sectors) we can accept
+ */
+#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA)
+
+/* Keep some statistics on what's happening for the PROC file system
+ */
+static struct {
+ long tot;
+ long nobh;
+ long ntce[VIOMAXBLOCKDMA];
+} viod_stats[MAX_DISKNO][2];
+
+/* Number of disk I/O requests we've sent to OS/400
+ */
+static int num_req_outstanding;
+
+/* This is our internal structure for keeping track of disk devices
+ */
+struct viodasd_device {
+ int useCount;
+ u16 cylinders;
+ u16 tracks;
+ u16 sectors;
+ u16 bytesPerSector;
+ u64 size;
+ int readOnly;
+} *viodasd_devices;
+
+/* When we get a disk I/O request we take it off the general request queue
+ * and put it here.
+ */
+static LIST_HEAD(reqlist);
+
+/* Handle reads from the proc file system
+ */
+static int proc_read(char *buf, char **start, off_t offset,
+ int blen, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+ int j;
+
+#if defined(MODULE)
+ len +=
+ sprintf(buf + len,
+ "viod Module opened %d times. Major number %d\n",
+ MOD_IN_USE, major_table[0]);
+#endif
+ len +=
+ sprintf(buf + len, "viod %d possible devices\n", MAX_DISKNO);
+
+ for (i = 0; i < 16; i++) {
+ if (viod_stats[i][0].tot || viod_stats[i][1].tot) {
+ len +=
+ sprintf(buf + len,
+ "DISK %2.2d: rd %-10.10ld wr %-10.10ld (no buffer list rd %-10.10ld wr %-10.10ld\n",
+ i, viod_stats[i][0].tot,
+ viod_stats[i][1].tot,
+ viod_stats[i][0].nobh,
+ viod_stats[i][1].nobh);
+
+ len += sprintf(buf + len, "rd DMA: ");
+
+ for (j = 0; j < VIOMAXBLOCKDMA; j++)
+ len += sprintf(buf + len, " [%2.2d] %ld",
+ j,
+ viod_stats[i][0].ntce[j]);
+
+ len += sprintf(buf + len, "\nwr DMA: ");
+
+ for (j = 0; j < VIOMAXBLOCKDMA; j++)
+ len += sprintf(buf + len, " [%2.2d] %ld",
+ j,
+ viod_stats[i][1].ntce[j]);
+ len += sprintf(buf + len, "\n");
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+/* Handle writes to our proc file system
+ */
+static int proc_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ return count;
+}
+
+/* setup our proc file system entries
+ */
+void viodasd_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ struct proc_dir_entry *ent;
+ ent =
+ create_proc_entry("viodasd", S_IFREG | S_IRUSR, iSeries_proc);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = NULL;
+ ent->read_proc = proc_read;
+ ent->write_proc = proc_write;
+}
+
+/* clean up our proc file system entries
+ */
+void viodasd_proc_delete(struct proc_dir_entry *iSeries_proc)
+{
+ remove_proc_entry("viodasd", iSeries_proc);
+}
+
+/* End a request
+ */
+static void viodasd_end_request(struct request *req, int uptodate)
+{
+ if (end_that_request_first(req, uptodate, VIOD_DEVICE_NAME))
+ return;
+
+ end_that_request_last(req);
+}
+
+/* This rebuilds the partition information for a single disk device
+ */
+static int viodasd_revalidate(kdev_t dev)
+{
+ int i;
+ int device_no = DEVICE_NR(dev);
+ int dev_within_major = device_no % DEV_PER_MAJOR;
+ int part0 = (dev_within_major << PARTITION_SHIFT);
+ int npart = (1 << PARTITION_SHIFT);
+ int major = MAJOR(dev);
+ struct gendisk *gendisk = major_to_gendisk(major);
+
+ if (viodasd_devices[device_no].size == 0)
+ return 0;
+
+ for (i = npart - 1; i >= 0; i--) {
+ int minor = part0 + i;
+ struct hd_struct *partition = &gendisk->part[minor];
+
+ if (partition->nr_sects != 0) {
+ kdev_t devp = MKDEV(major, minor);
+ struct super_block *sb;
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb)
+ invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ }
+
+ partition->start_sect = 0;
+ partition->nr_sects = 0;
+ }
+
+ grok_partitions(gendisk, dev_within_major, npart,
+ viodasd_devices[device_no].size >> 9);
+
+ return 0;
+}
+
+
+static u16 access_flags(mode_t mode)
+{
+ u16 flags = 0;
+ if (!(mode & FMODE_WRITE))
+ flags |= vioblockflags_ro;
+ return flags;
+}
+
+/* This is the actual open code. It gets called from the external
+ * open entry point, as well as from the init code when we're figuring
+ * out what disks we have
+ */
+static int internal_open(int device_no, u16 flags)
+{
+ int i;
+ const int dev_within_major = device_no % DEV_PER_MAJOR;
+ struct gendisk *gendisk =
+ major_to_gendisk(diskno_to_major(device_no));
+ HvLpEvent_Rc hvrc;
+ /* This semaphore is raised in the interrupt handler */
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ struct viodasd_waitevent we = { sem:&Semaphore };
+
+ /* Check that we are dealing with a valid hosting partition */
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ printk(KERN_WARNING_VIO "Invalid hosting partition\n");
+ return -EIO;
+ }
+
+ /* Send the open event to OS/400 */
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_blockio |
+ vioblockopen,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ ((u64) device_no << 48) |
+ ((u64) flags << 32), 0, 0, 0);
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n",
+ (int) hvrc);
+ return -EIO;
+ }
+
+ /* Wait for the interrupt handler to get the response */
+ down(&Semaphore);
+
+ /* Check the return code */
+ if (we.rc != 0) {
+ const struct vio_error_entry *err =
+ vio_lookup_rc(viodasd_err_table, we.data.subRC);
+ printk(KERN_WARNING_VIO
+ "bad rc opening disk: %d:0x%04x (%s)\n",
+ (int) we.rc, we.data.subRC, err->msg);
+ return -err->errno;
+ }
+
+ /* If this is the first open of this device, update the device information */
+ /* If this is NOT the first open, assume that it isn't changing */
+ if (viodasd_devices[device_no].useCount == 0) {
+ if (viodasd_devices[device_no].size > 0) {
+ /* divide by 512 */
+ u64 tmpint = viodasd_devices[device_no].size >> 9;
+ gendisk->part[dev_within_major << PARTITION_SHIFT].nr_sects = tmpint;
+ /* Now the value divided by 1024 */
+ tmpint = tmpint >> 1;
+ gendisk->sizes[dev_within_major << PARTITION_SHIFT] = tmpint;
+
+ for (i = dev_within_major << PARTITION_SHIFT;
+ i < ((dev_within_major + 1) << PARTITION_SHIFT);
+ i++)
+ {
+ hardsect_size[diskno_to_major(device_no)][i] =
+ viodasd_devices[device_no].bytesPerSector;
+ }
+ }
+ } else {
+ /* If the size of the device changed, weird things are happening! */
+ if (gendisk->sizes[dev_within_major << PARTITION_SHIFT] !=
+ viodasd_devices[device_no].size >> 10) {
+ printk(KERN_WARNING_VIO
+ "disk size change (%dK to %dK) for device %d\n",
+ gendisk->sizes[dev_within_major << PARTITION_SHIFT],
+ (int) viodasd_devices[device_no].size >> 10, device_no);
+ }
+ }
+
+ /* Bump the use count */
+ viodasd_devices[device_no].useCount++;
+ return 0;
+}
+
+/* This is the actual release code. It gets called from the external
+ * release entry point, as well as from the init code when we're figuring
+ * out what disks we have
+ */
+static int internal_release(int device_no, u16 flags)
+{
+ /* Send the event to OS/400. We DON'T expect a response */
+ HvLpEvent_Rc hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_blockio
+ | vioblockclose,
+ HvLpEvent_AckInd_NoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ 0,
+ VIOVERSION << 16,
+ ((u64) device_no
+ << 48) | ((u64)
+ flags
+ <<
+ 32),
+ 0, 0, 0);
+
+ viodasd_devices[device_no].useCount--;
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO
+ "bad rc sending event to OS/400 %d\n", (int) hvrc);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+static void internal_register_new_disk(int diskno);
+
+/* External open entry point.
+ */
+static int viodasd_open(struct inode *ino, struct file *fil)
+{
+ int device_no;
+ int old_max_disk = viodasd_max_disk;
+
+ /* Do a bunch of sanity checks */
+ if (!ino) {
+ printk(KERN_WARNING_VIO "no inode provided in open\n");
+ return -ENODEV;
+ }
+
+ if (major_to_index(MAJOR(ino->i_rdev)) < 0) {
+ printk(KERN_WARNING_VIO
+ "Weird error...wrong major number on open\n");
+ return -ENODEV;
+ }
+
+ device_no = DEVICE_NR(ino->i_rdev);
+ if (device_no > MAX_DISKNO || device_no < 0) {
+ printk(KERN_WARNING_VIO
+ "Invalid device number %d in open\n", device_no);
+ return -ENODEV;
+ }
+
+ /* Call the actual open code */
+ if (internal_open(device_no, access_flags(fil ? fil->f_mode : 0)) == 0) {
+ int i;
+ MOD_INC_USE_COUNT;
+ /* For each new disk: */
+ /* update the disk's geometry via internal_open and register it */
+ for (i = old_max_disk + 1; i <= viodasd_max_disk; ++i) {
+ internal_open(i, vioblockflags_ro);
+ internal_register_new_disk(i);
+ internal_release(i, vioblockflags_ro);
+ }
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+/* External release entry point.
+ */
+static int viodasd_release(struct inode *ino, struct file *fil)
+{
+ int device_no;
+
+ /* Do a bunch of sanity checks */
+ if (!ino) {
+ printk(KERN_WARNING_VIO "no inode provided in release\n");
+ return -ENODEV;
+ }
+
+ if (major_to_index(MAJOR(ino->i_rdev)) < 0) {
+ printk(KERN_WARNING_VIO
+ "Weird error...wrong major number on release\n");
+ return -ENODEV;
+ }
+
+ device_no = DEVICE_NR(ino->i_rdev);
+
+ if (device_no > MAX_DISKNO || device_no < 0) {
+ printk("Tried to release invalid disk number %d\n",
+ device_no);
+ return -ENODEV;
+ }
+
+ /* Call the actual release code */
+ internal_release(device_no, access_flags(fil ? fil->f_mode : 0));
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* External ioctl entry point.
+ */
+static int viodasd_ioctl(struct inode *ino, struct file *fil,
+ unsigned int cmd, unsigned long arg)
+{
+ int device_no;
+ int err;
+ HvLpEvent_Rc hvrc;
+ struct hd_struct *partition;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+
+ /* Sanity checks */
+ if (!ino) {
+ printk(KERN_WARNING_VIO "no inode provided in ioctl\n");
+ return -ENODEV;
+ }
+
+ if (major_to_index(MAJOR(ino->i_rdev)) < 0) {
+ printk(KERN_WARNING_VIO
+ "Weird error...wrong major number on ioctl\n");
+ return -ENODEV;
+ }
+
+ partition = devt_to_partition(ino->i_rdev);
+
+ device_no = DEVICE_NR(ino->i_rdev);
+ if (device_no > viodasd_max_disk) {
+ printk(KERN_WARNING_VIO
+ "Invalid device number %d in ioctl\n", device_no);
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case BLKGETSIZE:
+ /* return the device size in sectors */
+ if (!arg)
+ return -EINVAL;
+ err =
+ verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+
+ put_user(partition->nr_sects, (long *) arg);
+ return 0;
+
+ case FDFLUSH:
+ case BLKFLSBUF:
+ if (!suser())
+ return -EACCES;
+ fsync_dev(ino->i_rdev);
+ invalidate_buffers(ino->i_rdev);
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_blockio
+ | vioblockflush,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long)
+ &Semaphore,
+ VIOVERSION << 16,
+ ((u64) device_no <<
+ 48), 0, 0, 0);
+
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO
+ "bad rc on sync signalLpEvent %d\n",
+ (int) hvrc);
+ return -EIO;
+ }
+
+ down(&Semaphore);
+
+ return 0;
+
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ err =
+ verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
+ put_user(read_ahead[MAJOR(ino->i_rdev)], (long *) arg);
+ return 0;
+
+ case BLKRASET:
+ if (!suser())
+ return -EACCES;
+ if (arg > 0x00ff)
+ return -EINVAL;
+ read_ahead[MAJOR(ino->i_rdev)] = arg;
+ return 0;
+
+ case BLKRRPART:
+ viodasd_revalidate(ino->i_rdev);
+ return 0;
+
+ case HDIO_GETGEO:
+ {
+ unsigned char sectors;
+ unsigned char heads;
+ unsigned short cylinders;
+
+ struct hd_geometry *geo =
+ (struct hd_geometry *) arg;
+ if (geo == NULL)
+ return -EINVAL;
+
+ err = verify_area(VERIFY_WRITE, geo, sizeof(*geo));
+ if (err)
+ return err;
+
+ sectors = viodasd_devices[device_no].sectors;
+ if (sectors == 0)
+ sectors = 32;
+
+ heads = viodasd_devices[device_no].tracks;
+ if (heads == 0)
+ heads = 64;
+
+ cylinders = viodasd_devices[device_no].cylinders;
+ if (cylinders == 0)
+ cylinders =
+ partition->nr_sects / (sectors *
+ heads);
+
+ put_user(sectors, &geo->sectors);
+ put_user(heads, &geo->heads);
+ put_user(cylinders, &geo->cylinders);
+
+ put_user(partition->start_sect,
+ (long *) &geo->start);
+
+ return 0;
+ }
+
+#define PRTIOC(x) case x: printk(KERN_WARNING_VIO "got unsupported FD ioctl " #x "\n"); \
+ return -EINVAL;
+
+ PRTIOC(FDCLRPRM);
+ PRTIOC(FDSETPRM);
+ PRTIOC(FDDEFPRM);
+ PRTIOC(FDGETPRM);
+ PRTIOC(FDMSGON);
+ PRTIOC(FDMSGOFF);
+ PRTIOC(FDFMTBEG);
+ PRTIOC(FDFMTTRK);
+ PRTIOC(FDFMTEND);
+ PRTIOC(FDSETEMSGTRESH);
+ PRTIOC(FDSETMAXERRS);
+ PRTIOC(FDGETMAXERRS);
+ PRTIOC(FDGETDRVTYP);
+ PRTIOC(FDSETDRVPRM);
+ PRTIOC(FDGETDRVPRM);
+ PRTIOC(FDGETDRVSTAT);
+ PRTIOC(FDPOLLDRVSTAT);
+ PRTIOC(FDRESET);
+ PRTIOC(FDGETFDCSTAT);
+ PRTIOC(FDWERRORCLR);
+ PRTIOC(FDWERRORGET);
+ PRTIOC(FDRAWCMD);
+ PRTIOC(FDEJECT);
+ PRTIOC(FDTWADDLE);
+
+ }
+
+ return -EINVAL;
+}
+
+/* Send an actual I/O request to OS/400
+ */
+static int send_request(struct request *req)
+{
+ u64 sect_size;
+ u64 start;
+ u64 len;
+ int direction;
+ int nsg;
+ u16 viocmd;
+ HvLpEvent_Rc hvrc;
+ struct vioblocklpevent *bevent;
+ struct scatterlist sg[VIOMAXBLOCKDMA];
+ struct buffer_head *bh;
+ int sgindex;
+ int device_no = DEVICE_NR(req->rq_dev);
+ int dev_within_major = device_no % DEV_PER_MAJOR;
+ int statindex;
+ struct hd_struct *partition = devt_to_partition(req->rq_dev);
+
+ if (device_no > viodasd_max_disk || device_no < 0) {
+ printk
+ ("yikes! sending a request to device %d of %d possible?\n",
+ device_no, viodasd_max_disk + 1);
+ }
+
+ /* Note that this SHOULD always be 512...but lets be architecturally correct */
+ sect_size = hardsect_size[MAJOR(req->rq_dev)][dev_within_major];
+
+ /* Figure out the starting sector and length */
+ start = (req->sector + partition->start_sect) * sect_size;
+ len = req->nr_sectors * sect_size;
+
+ /* More paranoia checks */
+ if ((req->sector + req->nr_sectors) >
+ (partition->start_sect + partition->nr_sects)) {
+ printk(KERN_WARNING_VIO
+ "Invalid request offset & length\n");
+ printk(KERN_WARNING_VIO
+ "req->sector: %ld, req->nr_sectors: %ld\n",
+ req->sector, req->nr_sectors);
+ printk(KERN_WARNING_VIO "major: %d, minor: %d\n",
+ MAJOR(req->rq_dev), MINOR(req->rq_dev));
+ return -1;
+ }
+
+ if (req->cmd == READ) {
+ direction = PCI_DMA_FROMDEVICE;
+ viocmd = viomajorsubtype_blockio | vioblockread;
+ statindex = 0;
+ } else {
+ direction = PCI_DMA_TODEVICE;
+ viocmd = viomajorsubtype_blockio | vioblockwrite;
+ statindex = 1;
+ }
+
+ /* Update totals */
+ viod_stats[device_no][statindex].tot++;
+
+ /* Now build the scatter-gather list */
+ memset(&sg, 0x00, sizeof(sg));
+ sgindex = 0;
+
+ /* See if this is a swap I/O (without a bh pointer) or a regular I/O */
+ if (req->bh) {
+ /* OK...this loop takes buffers from the request and adds them to the SG
+ until we're done, or until we hit a maximum. If we hit a maximum we'll
+ just finish this request later */
+ bh = req->bh;
+ while ((bh) && (sgindex < VIOMAXBLOCKDMA)) {
+ sg[sgindex].address = bh->b_data;
+ sg[sgindex].length = bh->b_size;
+
+ sgindex++;
+ bh = bh->b_reqnext;
+ }
+ nsg = pci_map_sg(iSeries_vio_dev, sg, sgindex, direction);
+ if ((nsg == 0) || (sg[0].dma_length == 0)
+ || (sg[0].dma_address == 0xFFFFFFFF)) {
+ printk(KERN_WARNING_VIO "error getting sg tces\n");
+ return -1;
+ }
+
+ } else {
+ /* Update stats */
+ viod_stats[device_no][statindex].nobh++;
+
+ sg[0].dma_address =
+ pci_map_single(iSeries_vio_dev, req->buffer, len,
+ direction);
+ if (sg[0].dma_address == 0xFFFFFFFF) {
+ printk(KERN_WARNING_VIO
+ "error allocating tce for address %p len %ld\n",
+ req->buffer, (long) len);
+ return -1;
+ }
+ sg[0].dma_length = len;
+ nsg = 1;
+ }
+
+ /* Update stats */
+ viod_stats[device_no][statindex].ntce[sgindex]++;
+
+ /* This optimization handles a single DMA block */
+ if (sgindex == 1) {
+ /* Send the open event to OS/400 */
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_blockio
+ | viocmd,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long)
+ req->buffer,
+ VIOVERSION << 16,
+ ((u64) device_no <<
+ 48), start,
+ ((u64) sg[0].
+ dma_address) << 32,
+ sg[0].dma_length);
+ } else {
+ bevent =
+ (struct vioblocklpevent *)
+ vio_get_event_buffer(viomajorsubtype_blockio);
+ if (bevent == NULL) {
+ printk(KERN_WARNING_VIO
+ "error allocating disk event buffer\n");
+ return -1;
+ }
+
+ /* Now build up the actual request. Note that we store the pointer */
+ /* to the request buffer in the correlation token so we can match */
+ /* this response up later */
+ memset(bevent, 0x00, sizeof(struct vioblocklpevent));
+ bevent->event.xFlags.xValid = 1;
+ bevent->event.xFlags.xFunction = HvLpEvent_Function_Int;
+ bevent->event.xFlags.xAckInd = HvLpEvent_AckInd_DoAck;
+ bevent->event.xFlags.xAckType =
+ HvLpEvent_AckType_ImmediateAck;
+ bevent->event.xType = HvLpEvent_Type_VirtualIo;
+ bevent->event.xSubtype = viocmd;
+ bevent->event.xSourceLp = HvLpConfig_getLpIndex();
+ bevent->event.xTargetLp = viopath_hostLp;
+ bevent->event.xSizeMinus1 =
+ offsetof(struct vioblocklpevent,
+ u.rwData.dmaInfo) +
+ (sizeof(bevent->u.rwData.dmaInfo[0]) * (sgindex)) - 1;
+ bevent->event.xSizeMinus1 =
+ sizeof(struct vioblocklpevent) - 1;
+ bevent->event.xSourceInstanceId =
+ viopath_sourceinst(viopath_hostLp);
+ bevent->event.xTargetInstanceId =
+ viopath_targetinst(viopath_hostLp);
+ bevent->event.xCorrelationToken =
+ (u64) (unsigned long) req->buffer;
+ bevent->mVersion = VIOVERSION;
+ bevent->mDisk = device_no;
+ bevent->u.rwData.mOffset = start;
+
+ /* Copy just the dma information from the sg list into the request */
+ for (sgindex = 0; sgindex < nsg; sgindex++) {
+ bevent->u.rwData.dmaInfo[sgindex].mToken =
+ sg[sgindex].dma_address;
+ bevent->u.rwData.dmaInfo[sgindex].mLen =
+ sg[sgindex].dma_length;
+ }
+
+ /* Send the request */
+ hvrc = HvCallEvent_signalLpEvent(&bevent->event);
+ vio_free_event_buffer(viomajorsubtype_blockio, bevent);
+ }
+
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk(KERN_WARNING_VIO
+ "error sending disk event to OS/400 (rc %d)\n",
+ (int) hvrc);
+ return -1;
+ } else {
+ /* If the request was successful, bump the number of outstanding */
+ num_req_outstanding++;
+ }
+ return 0;
+}
+
+/* This is the external request processing routine
+ */
+static void do_viodasd_request(request_queue_t * q)
+{
+ int device_no;
+ for (;;) {
+ struct request *req;
+ struct gendisk *gendisk;
+
+ /* inlined INIT_REQUEST here because we don't define MAJOR_NR before blk.h */
+ if (list_empty(&q->queue_head))
+ return;
+ req = blkdev_entry_next_request(&q->queue_head);
+ if (major_to_index(MAJOR(req->rq_dev)) < 0)
+ panic(VIOD_DEVICE_NAME ": request list destroyed");
+ if (req->bh) {
+ if (!buffer_locked(req->bh))
+ panic(VIOD_DEVICE_NAME
+ ": block not locked");
+ }
+
+ gendisk = major_to_gendisk(MAJOR(req->rq_dev));
+
+ device_no = DEVICE_NR(req->rq_dev);
+ if (device_no > MAX_DISKNO || device_no < 0) {
+ printk(KERN_WARNING_VIO "Invalid device # %d\n",
+ device_no);
+ viodasd_end_request(req, 0);
+ continue;
+ }
+
+ if (gendisk->sizes == NULL) {
+ printk(KERN_WARNING_VIO
+ "Ouch! gendisk->sizes is NULL\n");
+ viodasd_end_request(req, 0);
+ continue;
+ }
+
+ /* If the queue is plugged, don't dequeue anything right now */
+ if ((q) && (q->plugged)) {
+ return;
+ }
+
+ /* If we already have the maximum number of requests outstanding to OS/400
+ just bail out. We'll come back later */
+ if (num_req_outstanding >= VIOMAXREQ) {
+ return;
+ }
+
+ /* get the current request, then dequeue it from the queue */
+ blkdev_dequeue_request(req);
+
+ /* Try sending the request */
+ if (send_request(req) == 0) {
+ list_add_tail(&req->queue, &reqlist);
+ } else {
+ viodasd_end_request(req, 0);
+ }
+ }
+}
+
+/* Check for changed disks
+ */
+static int viodasd_check_change(kdev_t dev)
+{
+ struct viodasd_waitevent we;
+ HvLpEvent_Rc hvrc;
+ int device_no = DEVICE_NR(dev);
+
+ /* This semaphore is raised in the interrupt handler */
+ DECLARE_MUTEX_LOCKED(Semaphore);
+
+ /* Check that we are dealing with a valid hosting partition */
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ printk(KERN_WARNING_VIO "Invalid hosting partition\n");
+ return -EIO;
+ }
+
+ we.sem = &Semaphore;
+
+ /* Send the open event to OS/400 */
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_blockio |
+ vioblockcheck,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) &we,
+ VIOVERSION << 16,
+ ((u64) device_no << 48), 0, 0,
+ 0);
+
+ if (hvrc != 0) {
+ printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n",
+ (int) hvrc);
+ return -EIO;
+ }
+
+ /* Wait for the interrupt handler to get the response */
+ down(&Semaphore);
+
+ /* Check the return code. If bad, assume no change */
+ if (we.rc != 0) {
+ printk(KERN_WARNING_VIO
+ "bad rc %d on check_change. Assuming no change\n",
+ (int) we.rc);
+ return 0;
+ }
+
+ return we.data.changed;
+}
+
+/* Our file operations table
+ */
+static struct block_device_operations viodasd_fops = {
+ open:viodasd_open,
+ release:viodasd_release,
+ ioctl:viodasd_ioctl,
+ check_media_change:viodasd_check_change,
+ revalidate:viodasd_revalidate
+};
+
+/* returns the total number of scatterlist elements converted */
+static int block_event_to_scatterlist(const struct vioblocklpevent *bevent,
+ struct scatterlist *sg,
+ int *total_len)
+{
+ int i, numsg;
+ const struct rwData *rwData = &bevent->u.rwData;
+ static const int offset =
+ offsetof(struct vioblocklpevent, u.rwData.dmaInfo);
+ static const int element_size = sizeof(rwData->dmaInfo[0]);
+
+ numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size;
+ if (numsg > VIOMAXBLOCKDMA)
+ numsg = VIOMAXBLOCKDMA;
+
+ *total_len = 0;
+ memset(sg, 0x00, sizeof(sg[0]) * VIOMAXBLOCKDMA);
+
+ for (i = 0; (i < numsg) && (rwData->dmaInfo[i].mLen > 0); ++i) {
+ sg[i].dma_address = rwData->dmaInfo[i].mToken;
+ sg[i].dma_length = rwData->dmaInfo[i].mLen;
+ *total_len += rwData->dmaInfo[i].mLen;
+ }
+ return i;
+}
+
+static struct request *find_request_with_token(u64 token)
+{
+ struct request *req = blkdev_entry_to_request(reqlist.next);
+ while ((&req->queue != &reqlist) &&
+ ((u64) (unsigned long) req->buffer != token))
+ req = blkdev_entry_to_request(req->queue.next);
+ if (&req->queue == &reqlist) {
+ return NULL;
+ }
+ return req;
+}
+
+/* Restart all queues, starting with the one _after_ the major given, */
+/* thus reducing the chance of starvation of disks with late majors. */
+static void viodasd_restart_all_queues_starting_from(int first_major)
+{
+ int i, first_index = major_to_index(first_major);
+ for(i = first_index + 1; i < NUM_MAJORS; ++i)
+ do_viodasd_request(BLK_DEFAULT_QUEUE(major_table[i]));
+ for(i = 0; i <= first_index; ++i)
+ do_viodasd_request(BLK_DEFAULT_QUEUE(major_table[i]));
+}
+
+/* For read and write requests, decrement the number of outstanding requests,
+ * Free the DMA buffers we allocated, and find the matching request by
+ * using the buffer pointer we stored in the correlation token.
+ */
+static int viodasd_handleReadWrite(struct vioblocklpevent *bevent)
+{
+ int num_sg, num_sect, pci_direction, total_len, major;
+ struct request *req;
+ struct scatterlist sg[VIOMAXBLOCKDMA];
+ struct HvLpEvent *event = &bevent->event;
+ unsigned long irq_flags;
+
+ num_sg = block_event_to_scatterlist(bevent, sg, &total_len);
+ num_sect = total_len >> 9;
+ if (event->xSubtype == (viomajorsubtype_blockio | vioblockread))
+ pci_direction = PCI_DMA_FROMDEVICE;
+ else
+ pci_direction = PCI_DMA_TODEVICE;
+ pci_unmap_sg(iSeries_vio_dev, sg, num_sg, pci_direction);
+
+
+ /* Since this is running in interrupt mode, we need to make sure we're not
+ * stepping on any global I/O operations
+ */
+ spin_lock_irqsave(&io_request_lock, irq_flags);
+
+ num_req_outstanding--;
+
+ /* Now find the matching request in OUR list (remember we moved the request
+ * from the global list to our list when we got it)
+ */
+ req = find_request_with_token(bevent->event.xCorrelationToken);
+ if (req == NULL) {
+ printk(KERN_WARNING_VIO
+ "Yikes! No request matching 0x%lx found\n",
+ bevent->event.xCorrelationToken);
+ spin_unlock_irqrestore(&io_request_lock, irq_flags);
+ return -1;
+ }
+
+ /* Remove the request from our list */
+ list_del(&req->queue);
+ /* Record this event's major number so we can check that queue again */
+ major = MAJOR(req->rq_dev);
+
+ if (!req->bh) {
+ if (event->xRc != HvLpEvent_Rc_Good) {
+ const struct vio_error_entry *err =
+ vio_lookup_rc(viodasd_err_table,
+ bevent->mSubTypeRc);
+ printk(KERN_WARNING_VIO
+ "read/write error %d:0x%04x (%s)\n",
+ event->xRc, bevent->mSubTypeRc, err->msg);
+ viodasd_end_request(req, 0);
+ } else {
+ if (num_sect != req->current_nr_sectors) {
+ printk(KERN_WARNING_VIO
+ "Yikes...non bh i/o # sect doesn't match!!!\n");
+ }
+ viodasd_end_request(req, 1);
+ }
+ } else {
+ /* record having received the answers we did */
+ while ((num_sect > 0) && (req->bh)) {
+ num_sect -= req->current_nr_sectors;
+ viodasd_end_request(req, 1);
+ }
+ /* if they somehow answered _more_ than we asked for...something weird happened */
+ if (num_sect)
+ printk(KERN_WARNING_VIO
+ "Yikes...sectors left over on a request!!!\n");
+
+ /* if they didn't answer the whole request this time, re-submit the request */
+ if (req->bh) {
+ if (send_request(req) == 0) {
+ list_add_tail(&req->queue, &reqlist);
+ } else {
+ viodasd_end_request(req, 0);
+ }
+ }
+ }
+
+ /* Finally, try to get more requests off of this device's queue */
+ viodasd_restart_all_queues_starting_from(major);
+
+ spin_unlock_irqrestore(&io_request_lock, irq_flags);
+
+ return 0;
+}
+
+/* This routine handles incoming block LP events */
+static void vioHandleBlockEvent(struct HvLpEvent *event)
+{
+ struct vioblocklpevent *bevent = (struct vioblocklpevent *) event;
+ struct viodasd_waitevent *pwe;
+
+ if (event == NULL) {
+ /* Notification that a partition went away! */
+ return;
+ }
+ // First, we should NEVER get an int here...only acks
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ printk(KERN_WARNING_VIO
+ "Yikes! got an int in viodasd event handler!\n");
+ if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ }
+
+ switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
+
+ /* Handle a response to an open request. We get all the disk information
+ * in the response, so update it. The correlation token contains a pointer to
+ * a waitevent structure that has a semaphore in it. update the return code
+ * in the waitevent structure and post the semaphore to wake up the guy who
+ * sent the request */
+ case vioblockopen:
+ pwe =
+ (struct viodasd_waitevent *) (unsigned long) event->
+ xCorrelationToken;
+ pwe->rc = event->xRc;
+ pwe->data.subRC = bevent->mSubTypeRc;
+ if (event->xRc == HvLpEvent_Rc_Good) {
+ const struct openData *data = &bevent->u.openData;
+ struct viodasd_device *device =
+ &viodasd_devices[bevent->mDisk];
+ device->readOnly =
+ bevent->mFlags & vioblockflags_ro;
+ device->size = data->mDiskLen;
+ device->cylinders = data->mCylinders;
+ device->tracks = data->mTracks;
+ device->sectors = data->mSectors;
+ device->bytesPerSector = data->mBytesPerSector;
+ viodasd_max_disk = data->mMaxDisks;
+ }
+ up(pwe->sem);
+ break;
+ case vioblockclose:
+ break;
+ case vioblockcheck:
+ pwe =
+ (struct viodasd_waitevent *) (unsigned long) event->
+ xCorrelationToken;
+ pwe->rc = event->xRc;
+ pwe->data.changed = bevent->u.check.changed;
+ up(pwe->sem);
+ break;
+ case vioblockflush:
+ up((void *) (unsigned long) event->xCorrelationToken);
+ break;
+ case vioblockread:
+ case vioblockwrite:
+ viodasd_handleReadWrite(bevent);
+ break;
+
+ default:
+ printk(KERN_WARNING_VIO "invalid subtype!");
+ if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ }
+}
+
+/* This routine tries to clean up anything we allocated/registered
+ */
+static void viodasd_cleanup_major(int major)
+{
+ const int num_partitions = DEV_PER_MAJOR << PARTITION_SHIFT;
+ int minor;
+
+#define CLEANIT(x) if (x) {kfree(x); x=NULL;}
+
+ for (minor = 0; minor < num_partitions; minor++)
+ fsync_dev(MKDEV(major, minor));
+
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(major));
+
+ read_ahead[major] = 0;
+
+ CLEANIT(blk_size[major]);
+ CLEANIT(blksize_size[major]);
+ CLEANIT(hardsect_size[major]);
+ CLEANIT(max_sectors[major]);
+ CLEANIT(major_to_gendisk(major)->part);
+
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(major));
+
+ devfs_unregister_blkdev(major, VIOD_DEVICE_NAME);
+}
+
+static const char *major_name(int major)
+{
+ static char major_names[NUM_MAJORS][MAX_MAJOR_NAME];
+ int index = major_to_index(major);
+
+ if(index < 0)
+ return NULL;
+ else if(index == 0)
+ strcpy(major_names[index], VIOD_GENHD_NAME);
+ else
+ sprintf(major_names[index], VIOD_GENHD_NAME"%d", index);
+
+ return major_names[index];
+}
+
+/* in case of bad return code, caller must cleanup2() for this major */
+static int viodasd_init_major(int major)
+{
+ int i;
+ const int numpart = DEV_PER_MAJOR << PARTITION_SHIFT;
+ int *sizes, *sectsizes, *blksizes, *maxsectors;
+ struct hd_struct *partitions;
+ struct gendisk *gendisk = major_to_gendisk(major);
+
+ /*
+ * Do the devfs_register. This works even if devfs is not
+ * configured
+ */
+ if (devfs_register_blkdev(major, VIOD_DEVICE_NAME, &viodasd_fops)) {
+ printk(KERN_WARNING_VIO
+ "%s: can't register major number %d\n",
+ VIOD_DEVICE_NAME, major);
+ return -1;
+ }
+
+ blk_init_queue(BLK_DEFAULT_QUEUE(major), do_viodasd_request);
+
+ read_ahead[major] = 8; /* 8 sector (4kB) read ahead */
+
+ /* initialize the struct */
+ gendisk->major = major;
+ gendisk->major_name = major_name(major);
+ gendisk->minor_shift = PARTITION_SHIFT;
+ gendisk->max_p = 1 << PARTITION_SHIFT;
+ gendisk->nr_real = DEV_PER_MAJOR;
+ gendisk->fops = &viodasd_fops;
+
+ /* to be assigned later */
+ gendisk->next = NULL;
+ gendisk->part = NULL;
+ gendisk->sizes = NULL;
+ gendisk->de_arr = NULL;
+ gendisk->flags = NULL;
+
+ /* register us in the global list */
+ add_gendisk(gendisk);
+
+ /*
+ * Now fill in all the device driver info
+ */
+ sizes = kmalloc(numpart * sizeof(int), GFP_KERNEL);
+ if (!sizes)
+ return -ENOMEM;
+ memset(sizes, 0x00, numpart * sizeof(int));
+ blk_size[major] = gendisk->sizes = sizes;
+
+ partitions =
+ kmalloc(numpart * sizeof(struct hd_struct), GFP_KERNEL);
+ if (!partitions)
+ return -ENOMEM;
+ memset(partitions, 0x00, numpart * sizeof(struct hd_struct));
+ gendisk->part = partitions;
+
+ blksizes = kmalloc(numpart * sizeof(int), GFP_KERNEL);
+ if (!blksizes)
+ return -ENOMEM;
+ for (i = 0; i < numpart; i++)
+ blksizes[i] = blksize;
+ blksize_size[major] = blksizes;
+
+ sectsizes = kmalloc(numpart * sizeof(int), GFP_KERNEL);
+ if (!sectsizes)
+ return -ENOMEM;
+ for (i = 0; i < numpart; i++)
+ sectsizes[i] = 0;
+ hardsect_size[major] = sectsizes;
+
+ maxsectors = kmalloc(numpart * sizeof(int), GFP_KERNEL);
+ if (!maxsectors)
+ return -ENOMEM;
+ for (i = 0; i < numpart; i++)
+ maxsectors[i] = VIODASD_MAXSECTORS;
+ max_sectors[major] = maxsectors;
+
+ return 0;
+}
+
+static void internal_register_new_disk(int diskno)
+{
+ int major = diskno_to_major(diskno);
+ int dev_within_major = diskno % DEV_PER_MAJOR;
+ struct gendisk *gendisk = major_to_gendisk(major);
+ int i;
+
+ if (diskno == 0) {
+ printk(KERN_INFO_VIO
+ "%s: Currently %d disks connected\n",
+ VIOD_DEVICE_NAME, (int) viodasd_max_disk + 1);
+ if (viodasd_max_disk > MAX_DISKNO - 1)
+ printk(KERN_INFO_VIO
+ "Only examining the first %d\n",
+ MAX_DISKNO);
+ }
+
+ register_disk(gendisk,
+ MKDEV(major,
+ dev_within_major <<
+ PARTITION_SHIFT),
+ 1 << PARTITION_SHIFT, &viodasd_fops,
+ gendisk->
+ part[dev_within_major << PARTITION_SHIFT].nr_sects);
+
+ printk(KERN_INFO_VIO
+ "%s: Disk %2.2d size %dM, sectors %d, heads %d, cylinders %d, sectsize %d\n",
+ VIOD_DEVICE_NAME,
+ diskno,
+ (int) (viodasd_devices[diskno].size /
+ (1024 * 1024)),
+ (int) viodasd_devices[diskno].sectors,
+ (int) viodasd_devices[diskno].tracks,
+ (int) viodasd_devices[diskno].cylinders,
+ (int) hardsect_size[major][dev_within_major <<
+ PARTITION_SHIFT]);
+
+ for (i = 1; i < (1 << PARTITION_SHIFT); ++i) {
+ int minor = (dev_within_major << PARTITION_SHIFT) + i;
+ struct hd_struct *partition = &gendisk->part[minor];
+ if (partition->nr_sects)
+ printk(KERN_INFO_VIO
+ "%s: Disk %2.2d partition %2.2d start sector %ld, # sector %ld\n",
+ VIOD_DEVICE_NAME, diskno, i,
+ partition->start_sect, partition->nr_sects);
+ }
+}
+
+/* Initialize the whole device driver. Handle module and non-module
+ * versions
+ */
+__init int viodasd_init(void)
+{
+ int i, j;
+ int rc;
+
+ /* Try to open to our host lp
+ */
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ vio_set_hostlp();
+ }
+
+ if (viopath_hostLp == HvLpIndexInvalid) {
+ printk(KERN_WARNING_VIO "%s: invalid hosting partition\n",
+ VIOD_DEVICE_NAME);
+ return -EIO;
+ }
+
+ printk(KERN_INFO_VIO
+ "%s: Disk vers %s, major %d, max disks %d, hosting partition %d\n",
+ VIOD_DEVICE_NAME, VIODASD_VERS, major_table[0], MAX_DISKNO,
+ viopath_hostLp);
+
+ if (ROOT_DEV == NODEV) {
+ /* first disk, first partition */
+ ROOT_DEV = diskno_to_devt(0, 1);
+
+ printk(KERN_INFO_VIO
+ "Claiming root file system as first partition of first virtual disk");
+ }
+
+ /* Actually open the path to the hosting partition */
+ rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio,
+ VIOMAXREQ + 2);
+ if (rc) {
+ printk(KERN_WARNING_VIO
+ "error opening path to host partition %d\n",
+ viopath_hostLp);
+ return -EIO;
+ } else {
+ printk("%s: opened path to hosting partition %d\n",
+ VIOD_DEVICE_NAME, viopath_hostLp);
+ }
+
+ viodasd_devices =
+ kmalloc(MAX_DISKNO * sizeof(struct viodasd_device),
+ GFP_KERNEL);
+ if (!viodasd_devices)
+ return -ENOMEM;
+ memset(viodasd_devices, 0x00,
+ MAX_DISKNO * sizeof(struct viodasd_device));
+
+ /*
+ * Initialize our request handler
+ */
+ vio_setHandler(viomajorsubtype_blockio, vioHandleBlockEvent);
+
+ for (i = 0; i < NUM_MAJORS; ++i) {
+ int init_rc = viodasd_init_major(major_table[i]);
+ if (init_rc < 0) {
+ for (j = 0; j <= i; ++j)
+ viodasd_cleanup_major(major_table[j]);
+ return init_rc;
+ }
+ }
+
+ viodasd_max_disk = MAX_DISKNO - 1;
+ for (i = 0; i <= viodasd_max_disk && i < MAX_DISKNO; i++) {
+ // Note that internal_open has two side effects:
+ // a) it updates the size of the disk
+ // b) it updates viodasd_max_disk
+ if (internal_open(i, vioblockflags_ro) == 0) {
+ internal_register_new_disk(i);
+ internal_release(i, vioblockflags_ro);
+ }
+ }
+
+ /*
+ * Create the proc entry
+ */
+ iSeries_proc_callback(&viodasd_proc_init);
+
+ return 0;
+}
+
+#ifdef MODULE
+void viodasd_exit(void)
+{
+ int i;
+ for(i = 0; i < NUM_MAJORS; ++i)
+ viodasd_cleanup_major(major_table[i]);
+
+ CLEANIT(viodasd_devices);
+
+ viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
+ iSeries_proc_callback(&viodasd_proc_delete);
+
+}
+#endif
+
+#ifdef MODULE
+module_init(viodasd_init);
+module_exit(viodasd_exit);
+#endif
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/viopath.c linuxppc64_2_4/drivers/iseries/viopath.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/viopath.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/viopath.c Tue Apr 16 10:47:52 2002
@@ -0,0 +1,661 @@
+/* -*- linux-c -*-
+ * arch/ppc64/viopath.c
+ *
+ * iSeries Virtual I/O Message Path code
+ *
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * This code is used by the iSeries virtual disk, cd,
+ * tape, and console to communicate with OS/400 in another
+ * partition.
+ *
+ * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "vio.h"
+
+EXPORT_SYMBOL(viopath_hostLp);
+EXPORT_SYMBOL(viopath_ourLp);
+EXPORT_SYMBOL(vio_set_hostlp);
+EXPORT_SYMBOL(vio_lookup_rc);
+EXPORT_SYMBOL(viopath_open);
+EXPORT_SYMBOL(viopath_close);
+EXPORT_SYMBOL(viopath_isactive);
+EXPORT_SYMBOL(viopath_sourceinst);
+EXPORT_SYMBOL(viopath_targetinst);
+EXPORT_SYMBOL(vio_setHandler);
+EXPORT_SYMBOL(vio_clearHandler);
+EXPORT_SYMBOL(vio_get_event_buffer);
+EXPORT_SYMBOL(vio_free_event_buffer);
+
+extern struct pci_dev * iSeries_vio_dev;
+
+/* Status of the path to each other partition in the system.
+ * This is overkill, since we will only ever establish connections
+ * to our hosting partition and the primary partition on the system.
+ * But this allows for other support in the future.
+ */
+static struct viopathStatus {
+ int isOpen:1; /* Did we open the path? */
+ int isActive:1; /* Do we have a mon msg outstanding */
+ int users[VIO_MAX_SUBTYPES];
+ HvLpInstanceId mSourceInst;
+ HvLpInstanceId mTargetInst;
+ int numberAllocated;
+} viopathStatus[HVMAXARCHITECTEDLPS];
+
+static spinlock_t statuslock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * For each kind of event we allocate a buffer that is
+ * guaranteed not to cross a page boundary
+ */
+static void *event_buffer[VIO_MAX_SUBTYPES];
+static atomic_t event_buffer_available[VIO_MAX_SUBTYPES];
+
+static void handleMonitorEvent(struct HvLpEvent *event);
+
+/* We use this structure to handle asynchronous responses. The caller
+ * blocks on the semaphore and the handler posts the semaphore.
+ */
+struct doneAllocParms_t {
+ struct semaphore *sem;
+ int number;
+};
+
+/* Put a sequence number in each mon msg. The value is not
+ * important. Start at something other than 0 just for
+ * readability. wrapping this is ok.
+ */
+static u8 viomonseq = 22;
+
+/* Our hosting logical partition. We get this at startup
+ * time, and different modules access this variable directly.
+ */
+HvLpIndex viopath_hostLp = 0xff; /* HvLpIndexInvalid */
+HvLpIndex viopath_ourLp = 0xff;
+
+/* For each kind of incoming event we set a pointer to a
+ * routine to call.
+ */
+static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES];
+
+/* Handle reads from the proc file system
+ */
+static int proc_read(char *buf, char **start, off_t offset,
+ int blen, int *eof, void *data)
+{
+ HvLpEvent_Rc hvrc;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ dma_addr_t dmaa =
+ pci_map_single(iSeries_vio_dev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ int len = PAGE_SIZE;
+
+ if (len > blen)
+ len = blen;
+
+ memset(buf, 0x00, len);
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_config |
+ vioconfigget,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long)
+ &Semaphore, VIOVERSION << 16,
+ ((u64) dmaa) << 32, len, 0,
+ 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk("viopath hv error on op %d\n", (int) hvrc);
+ }
+
+ down(&Semaphore);
+
+ pci_unmap_single(iSeries_vio_dev, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+
+ *eof = 1;
+ return strlen(buf);
+}
+
+/* Handle writes to our proc file system
+ */
+static int proc_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ /* Doesn't do anything today!!!
+ */
+ return count;
+}
+
+/* setup our proc file system entries
+ */
+static void vio_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ struct proc_dir_entry *ent;
+ ent = create_proc_entry("config", S_IFREG | S_IRUSR, iSeries_proc);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = NULL;
+ ent->read_proc = proc_read;
+ ent->write_proc = proc_write;
+}
+
+/* See if a given LP is active. Allow for invalid lps to be passed in
+ * and just return invalid
+ */
+int viopath_isactive(HvLpIndex lp)
+{
+ if (lp == HvLpIndexInvalid)
+ return 0;
+ if (lp < HVMAXARCHITECTEDLPS)
+ return viopathStatus[lp].isActive;
+ else
+ return 0;
+}
+
+/* We cache the source and target instance ids for each
+ * partition.
+ */
+HvLpInstanceId viopath_sourceinst(HvLpIndex lp)
+{
+ return viopathStatus[lp].mSourceInst;
+}
+
+HvLpInstanceId viopath_targetinst(HvLpIndex lp)
+{
+ return viopathStatus[lp].mTargetInst;
+}
+
+/* Send a monitor message. This is a message with the acknowledge
+ * bit on that the other side will NOT explicitly acknowledge. When
+ * the other side goes down, the hypervisor will acknowledge any
+ * outstanding messages....so we will know when the other side dies.
+ */
+static void sendMonMsg(HvLpIndex remoteLp)
+{
+ HvLpEvent_Rc hvrc;
+
+ viopathStatus[remoteLp].mSourceInst =
+ HvCallEvent_getSourceLpInstanceId(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+ viopathStatus[remoteLp].mTargetInst =
+ HvCallEvent_getTargetLpInstanceId(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+
+ /* Deliberately ignore the return code here. if we call this
+ * more than once, we don't care.
+ */
+ vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent);
+
+ hvrc = HvCallEvent_signalLpEventFast(remoteLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_monitor,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_DeferredAck,
+ viopathStatus[remoteLp].
+ mSourceInst,
+ viopathStatus[remoteLp].
+ mTargetInst, viomonseq++,
+ 0, 0, 0, 0, 0);
+
+ if (hvrc == HvLpEvent_Rc_Good) {
+ viopathStatus[remoteLp].isActive = 1;
+ } else {
+ printk(KERN_WARNING_VIO
+ "could not connect to partition %d\n", remoteLp);
+ viopathStatus[remoteLp].isActive = 0;
+ }
+}
+
+static void handleMonitorEvent(struct HvLpEvent *event)
+{
+ HvLpIndex remoteLp;
+ int i;
+
+ /* This handler is _also_ called as part of the loop
+ * at the end of this routine, so it must be able to
+ * ignore NULL events...
+ */
+ if(!event)
+ return;
+
+ /* First see if this is just a normal monitor message from the
+ * other partition
+ */
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ remoteLp = event->xSourceLp;
+ if (!viopathStatus[remoteLp].isActive)
+ sendMonMsg(remoteLp);
+ return;
+ }
+
+ /* This path is for an acknowledgement; the other partition
+ * died
+ */
+ remoteLp = event->xTargetLp;
+ if ((event->xSourceInstanceId !=
+ viopathStatus[remoteLp].mSourceInst)
+ || (event->xTargetInstanceId !=
+ viopathStatus[remoteLp].mTargetInst)) {
+ printk(KERN_WARNING_VIO
+ "ignoring ack....mismatched instances\n");
+ return;
+ }
+
+ printk(KERN_WARNING_VIO "partition %d ended\n", remoteLp);
+
+ viopathStatus[remoteLp].isActive = 0;
+
+ /* For each active handler, pass them a NULL
+ * message to indicate that the other partition
+ * died
+ */
+ for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
+ if (vio_handler[i] != NULL)
+ (*vio_handler[i]) (NULL);
+ }
+}
+
+int vio_setHandler(int subtype, vio_event_handler_t * beh)
+{
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
+ return -EINVAL;
+
+ if (vio_handler[subtype] != NULL)
+ return -EBUSY;
+
+ vio_handler[subtype] = beh;
+ return 0;
+}
+
+int vio_clearHandler(int subtype)
+{
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
+ return -EINVAL;
+
+ if (vio_handler[subtype] == NULL)
+ return -EAGAIN;
+
+ vio_handler[subtype] = NULL;
+ return 0;
+}
+
+static void handleConfig(struct HvLpEvent *event)
+{
+ if(!event)
+ return;
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ printk(KERN_WARNING_VIO
+ "unexpected config request from partition %d",
+ event->xSourceLp);
+
+ if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
+ (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ return;
+ }
+
+ up((struct semaphore *) event->xCorrelationToken);
+}
+
+/* Initialization of the hosting partition
+ */
+void vio_set_hostlp(void)
+{
+ /* If this has already been set then we DON'T want to either change
+ * it or re-register the proc file system
+ */
+ if (viopath_hostLp != HvLpIndexInvalid)
+ return;
+
+ /* Figure out our hosting partition. This isn't allowed to change
+ * while we're active
+ */
+ viopath_ourLp = HvLpConfig_getLpIndex();
+ viopath_hostLp = HvCallCfg_getHostingLpIndex(viopath_ourLp);
+
+ /* If we have a valid hosting LP, create a proc file system entry
+ * for config information
+ */
+ if (viopath_hostLp != HvLpIndexInvalid) {
+ iSeries_proc_callback(&vio_proc_init);
+ vio_setHandler(viomajorsubtype_config, handleConfig);
+ }
+}
+
+static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs)
+{
+ HvLpIndex remoteLp;
+ int subtype =
+ (event->
+ xSubtype & VIOMAJOR_SUBTYPE_MASK) >> VIOMAJOR_SUBTYPE_SHIFT;
+
+ if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
+ remoteLp = event->xSourceLp;
+ if (event->xSourceInstanceId !=
+ viopathStatus[remoteLp].mTargetInst) {
+ printk(KERN_WARNING_VIO
+ "message from invalid partition. "
+ "int msg rcvd, source inst (%d) doesnt match (%d)\n",
+ viopathStatus[remoteLp].mTargetInst,
+ event->xSourceInstanceId);
+ return;
+ }
+
+ if (event->xTargetInstanceId !=
+ viopathStatus[remoteLp].mSourceInst) {
+ printk(KERN_WARNING_VIO
+ "message from invalid partition. "
+ "int msg rcvd, target inst (%d) doesnt match (%d)\n",
+ viopathStatus[remoteLp].mSourceInst,
+ event->xTargetInstanceId);
+ return;
+ }
+ } else {
+ remoteLp = event->xTargetLp;
+ if (event->xSourceInstanceId !=
+ viopathStatus[remoteLp].mSourceInst) {
+ printk(KERN_WARNING_VIO
+ "message from invalid partition. "
+ "ack msg rcvd, source inst (%d) doesnt match (%d)\n",
+ viopathStatus[remoteLp].mSourceInst,
+ event->xSourceInstanceId);
+ return;
+ }
+
+ if (event->xTargetInstanceId !=
+ viopathStatus[remoteLp].mTargetInst) {
+ printk(KERN_WARNING_VIO
+ "message from invalid partition. "
+ "viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n",
+ viopathStatus[remoteLp].mTargetInst,
+ event->xTargetInstanceId);
+ return;
+ }
+ }
+
+ if (vio_handler[subtype] == NULL) {
+ printk(KERN_WARNING_VIO
+ "unexpected virtual io event subtype %d from partition %d\n",
+ event->xSubtype, remoteLp);
+ /* No handler. Ack if necessary
+ */
+ if ((event->xFlags.xFunction == HvLpEvent_Function_Int) &&
+ (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) {
+ event->xRc = HvLpEvent_Rc_InvalidSubtype;
+ HvCallEvent_ackLpEvent(event);
+ }
+ return;
+ }
+
+ /* This innocuous little line is where all the real work happens
+ */
+ (*vio_handler[subtype]) (event);
+}
+
+static void viopath_donealloc(void *parm, int number)
+{
+ struct doneAllocParms_t *doneAllocParmsp =
+ (struct doneAllocParms_t *) parm;
+ doneAllocParmsp->number = number;
+ up(doneAllocParmsp->sem);
+}
+
+static int allocateEvents(HvLpIndex remoteLp, int numEvents)
+{
+ struct doneAllocParms_t doneAllocParms;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ doneAllocParms.sem = &Semaphore;
+
+ mf_allocateLpEvents(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */
+ numEvents,
+ &viopath_donealloc, &doneAllocParms);
+
+ down(&Semaphore);
+
+ return doneAllocParms.number;
+}
+
+int viopath_open(HvLpIndex remoteLp, int subtype, int numReq)
+{
+ int i;
+ unsigned long flags;
+
+ if ((remoteLp >= HvMaxArchitectedLps)
+ || (remoteLp == HvLpIndexInvalid))
+ return -EINVAL;
+
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
+ return -EINVAL;
+
+ spin_lock_irqsave(&statuslock, flags);
+
+ /* OK...we can fit 4 maximum-sized events (256 bytes) in
+ * each page (4096). Get a new page every 4
+ */
+ if (event_buffer[0] == NULL) {
+ for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
+ if ((i % 4) == 0) {
+ event_buffer[i] =
+ (void *) get_free_page(GFP_KERNEL);
+ if (event_buffer[i] == NULL) {
+ spin_unlock_irqrestore(&statuslock, flags);
+ return -ENOMEM;
+ }
+ } else {
+ event_buffer[i] =
+ event_buffer[i - 1] + 256;
+ }
+ atomic_set(&event_buffer_available[i], 1);
+ }
+ }
+
+ viopathStatus[remoteLp].users[subtype]++;
+
+ if (!viopathStatus[remoteLp].isOpen) {
+ HvCallEvent_openLpEventPath(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+
+ viopathStatus[remoteLp].numberAllocated +=
+ allocateEvents(remoteLp, 1);
+
+ if (viopathStatus[remoteLp].numberAllocated == 0) {
+ HvCallEvent_closeLpEventPath(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+
+ spin_unlock_irqrestore(&statuslock, flags);
+ return -ENOMEM;
+ }
+
+ viopathStatus[remoteLp].mSourceInst =
+ HvCallEvent_getSourceLpInstanceId(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+ viopathStatus[remoteLp].mTargetInst =
+ HvCallEvent_getTargetLpInstanceId(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+
+ HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo,
+ &vio_handleEvent);
+
+ viopathStatus[remoteLp].isOpen = 1;
+
+ sendMonMsg(remoteLp);
+
+ printk(KERN_INFO_VIO
+ "Opening connection to partition %d, setting sinst %d, tinst %d\n",
+ remoteLp,
+ viopathStatus[remoteLp].mSourceInst,
+ viopathStatus[remoteLp].mTargetInst);
+ }
+
+ viopathStatus[remoteLp].numberAllocated +=
+ allocateEvents(remoteLp, numReq);
+ spin_unlock_irqrestore(&statuslock, flags);
+
+ return 0;
+}
+
+int viopath_close(HvLpIndex remoteLp, int subtype, int numReq)
+{
+ unsigned long flags;
+ int i;
+ int numOpen;
+ struct doneAllocParms_t doneAllocParms;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ doneAllocParms.sem = &Semaphore;
+
+ if ((remoteLp >= HvMaxArchitectedLps)
+ || (remoteLp == HvLpIndexInvalid))
+ return -EINVAL;
+
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
+ return -EINVAL;
+
+ spin_lock_irqsave(&statuslock, flags);
+
+ viopathStatus[remoteLp].users[subtype]--;
+
+ mf_deallocateLpEvents( remoteLp,HvLpEvent_Type_VirtualIo,
+ numReq,
+ &viopath_donealloc,
+ &doneAllocParms );
+ down(&Semaphore);
+
+ for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++) {
+ numOpen += viopathStatus[remoteLp].users[i];
+ }
+
+ if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) {
+ printk(KERN_INFO_VIO
+ "Closing connection to partition %d", remoteLp);
+
+ HvCallEvent_closeLpEventPath(remoteLp,
+ HvLpEvent_Type_VirtualIo);
+ viopathStatus[remoteLp].isOpen = 0;
+ viopathStatus[remoteLp].isActive = 0;
+
+ for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
+ atomic_set(&event_buffer_available[i], 0);
+
+ for (i = 0; i < VIO_MAX_SUBTYPES; i += 4) {
+ free_page((unsigned long) event_buffer[i]);
+ }
+ }
+
+ }
+ spin_unlock_irqrestore(&statuslock, flags);
+ return 0;
+}
+
+void *vio_get_event_buffer(int subtype)
+{
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
+ return NULL;
+
+ if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0)
+ return event_buffer[subtype];
+ else
+ return NULL;
+}
+
+void vio_free_event_buffer(int subtype, void *buffer)
+{
+ subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
+ if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) {
+ printk(KERN_WARNING_VIO
+ "unexpected subtype %d freeing event buffer\n",
+ subtype);
+ return;
+ }
+
+ if (atomic_read(&event_buffer_available[subtype]) != 0) {
+ printk(KERN_WARNING_VIO
+ "freeing unallocated event buffer, subtype %d\n",
+ subtype);
+ return;
+ }
+
+ if (buffer != event_buffer[subtype]) {
+ printk(KERN_WARNING_VIO
+ "freeing invalid event buffer, subtype %d\n",
+ subtype);
+ }
+
+ atomic_set(&event_buffer_available[subtype], 1);
+}
+
+static const struct vio_error_entry vio_no_error =
+ { 0, 0, "Non-VIO Error" };
+static const struct vio_error_entry vio_unknown_error =
+ { 0, EIO, "Unknown Error" };
+
+static const struct vio_error_entry vio_default_errors[] = {
+ {0x0001, EIO, "No Connection"},
+ {0x0002, EIO, "No Receiver"},
+ {0x0003, EIO, "No Buffer Available"},
+ {0x0004, EBADRQC, "Invalid Message Type"},
+ {0x0000, 0, NULL},
+};
+
+const struct vio_error_entry *vio_lookup_rc(const struct vio_error_entry
+ *local_table, u16 rc)
+{
+ const struct vio_error_entry *cur;
+ if (!rc)
+ return &vio_no_error;
+ if (local_table)
+ for (cur = local_table; cur->rc; ++cur)
+ if (cur->rc == rc)
+ return cur;
+ for (cur = vio_default_errors; cur->rc; ++cur)
+ if (cur->rc == rc)
+ return cur;
+ return &vio_unknown_error;
+}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/iseries/viotape.c linuxppc64_2_4/drivers/iseries/viotape.c
--- ../kernel.org/linux-2.4.19/drivers/iseries/viotape.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/drivers/iseries/viotape.c Wed Dec 12 13:48:34 2001
@@ -0,0 +1,1185 @@
+/* -*- linux-c -*-
+ * drivers/char/viotape.c
+ *
+ * iSeries Virtual Tape
+ ***************************************************************************
+ *
+ * Authors: Dave Boutcher
+ * Ryan Arnold
+ * Colin Devilbiss
+ *
+ * (C) Copyright 2000 IBM Corporation
+ *
+ * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ***************************************************************************
+ * This routine provides access to tape drives owned and managed by an OS/400
+ * partition running on the same box as this Linux partition.
+ *
+ * All tape operations are performed by sending messages back and forth to
+ * the OS/400 partition. The format of the messages is defined in
+ * iSeries/vio.h
+ *
+ */
+
+
+#undef VIOT_DEBUG
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "vio.h"
+#include
+#include "asm/iSeries/HvCallEvent.h"
+#include "asm/iSeries/HvLpConfig.h"
+#include
+
+extern struct pci_dev * iSeries_vio_dev;
+
+static int viotape_major = 230;
+static int viotape_numdev = 0;
+
+#define VIOTAPE_MAXREQ 1
+
+/* version number for viotape driver */
+static unsigned int version_major = 1;
+static unsigned int version_minor = 0;
+
+static u64 sndMsgSeq;
+static u64 sndMsgAck;
+static u64 rcvMsgSeq;
+static u64 rcvMsgAck;
+
+/***************************************************************************
+ * The minor number follows the conventions of the SCSI tape drives. The
+ * rewind and mode are encoded in the minor #. We use this struct to break
+ * them out
+ ***************************************************************************/
+struct viot_devinfo_struct {
+ int major;
+ int minor;
+ int devno;
+ int mode;
+ int rewind;
+};
+
+#define VIOTAPOP_RESET 0
+#define VIOTAPOP_FSF 1
+#define VIOTAPOP_BSF 2
+#define VIOTAPOP_FSR 3
+#define VIOTAPOP_BSR 4
+#define VIOTAPOP_WEOF 5
+#define VIOTAPOP_REW 6
+#define VIOTAPOP_NOP 7
+#define VIOTAPOP_EOM 8
+#define VIOTAPOP_ERASE 9
+#define VIOTAPOP_SETBLK 10
+#define VIOTAPOP_SETDENSITY 11
+#define VIOTAPOP_SETPOS 12
+#define VIOTAPOP_GETPOS 13
+#define VIOTAPOP_SETPART 14
+
+struct viotapelpevent {
+ struct HvLpEvent event;
+ u32 mReserved1;
+ u16 mVersion;
+ u16 mSubTypeRc;
+ u16 mTape;
+ u16 mFlags;
+ u32 mToken;
+ u64 mLen;
+ union {
+ struct {
+ u32 mTapeOp;
+ u32 mCount;
+ } tapeOp;
+ struct {
+ u32 mType;
+ u32 mResid;
+ u32 mDsreg;
+ u32 mGstat;
+ u32 mErreg;
+ u32 mFileNo;
+ u32 mBlkNo;
+ } getStatus;
+ struct {
+ u32 mBlkNo;
+ } getPos;
+ } u;
+};
+enum viotapesubtype {
+ viotapeopen = 0x0001,
+ viotapeclose = 0x0002,
+ viotaperead = 0x0003,
+ viotapewrite = 0x0004,
+ viotapegetinfo = 0x0005,
+ viotapeop = 0x0006,
+ viotapegetpos = 0x0007,
+ viotapesetpos = 0x0008,
+ viotapegetstatus = 0x0009
+};
+
+enum viotapeRc {
+ viotape_InvalidRange = 0x0601,
+ viotape_InvalidToken = 0x0602,
+ viotape_DMAError = 0x0603,
+ viotape_UseError = 0x0604,
+ viotape_ReleaseError = 0x0605,
+ viotape_InvalidTape = 0x0606,
+ viotape_InvalidOp = 0x0607,
+ viotape_TapeErr = 0x0608,
+
+ viotape_AllocTimedOut = 0x0640,
+ viotape_BOTEnc = 0x0641,
+ viotape_BlankTape = 0x0642,
+ viotape_BufferEmpty = 0x0643,
+ viotape_CleanCartFound = 0x0644,
+ viotape_CmdNotAllowed = 0x0645,
+ viotape_CmdNotSupported = 0x0646,
+ viotape_DataCheck = 0x0647,
+ viotape_DecompressErr = 0x0648,
+ viotape_DeviceTimeout = 0x0649,
+ viotape_DeviceUnavail = 0x064a,
+ viotape_DeviceBusy = 0x064b,
+ viotape_EndOfMedia = 0x064c,
+ viotape_EndOfTape = 0x064d,
+ viotape_EquipCheck = 0x064e,
+ viotape_InsufficientRs = 0x064f,
+ viotape_InvalidLogBlk = 0x0650,
+ viotape_LengthError = 0x0651,
+ viotape_LibDoorOpen = 0x0652,
+ viotape_LoadFailure = 0x0653,
+ viotape_NotCapable = 0x0654,
+ viotape_NotOperational = 0x0655,
+ viotape_NotReady = 0x0656,
+ viotape_OpCancelled = 0x0657,
+ viotape_PhyLinkErr = 0x0658,
+ viotape_RdyNotBOT = 0x0659,
+ viotape_TapeMark = 0x065a,
+ viotape_WriteProt = 0x065b
+};
+
+static const struct vio_error_entry viotape_err_table[] = {
+ {viotape_InvalidRange, EIO, "Internal error"},
+ {viotape_InvalidToken, EIO, "Internal error"},
+ {viotape_DMAError, EIO, "DMA error"},
+ {viotape_UseError, EIO, "Internal error"},
+ {viotape_ReleaseError, EIO, "Internal error"},
+ {viotape_InvalidTape, EIO, "Invalid tape device"},
+ {viotape_InvalidOp, EIO, "Invalid operation"},
+ {viotape_TapeErr, EIO, "Tape error"},
+ {viotape_AllocTimedOut, EBUSY, "Allocate timed out"},
+ {viotape_BOTEnc, EIO, "Beginning of tape encountered"},
+ {viotape_BlankTape, EIO, "Blank tape"},
+ {viotape_BufferEmpty, EIO, "Buffer empty"},
+ {viotape_CleanCartFound, ENOMEDIUM, "Cleaning cartridge found"},
+ {viotape_CmdNotAllowed, EIO, "Command not allowed"},
+ {viotape_CmdNotSupported, EIO, "Command not supported"},
+ {viotape_DataCheck, EIO, "Data check"},
+ {viotape_DecompressErr, EIO, "Decompression error"},
+ {viotape_DeviceTimeout, EBUSY, "Device timeout"},
+ {viotape_DeviceUnavail, EIO, "Device unavailable"},
+ {viotape_DeviceBusy, EBUSY, "Device busy"},
+ {viotape_EndOfMedia, ENOSPC, "End of media"},
+ {viotape_EndOfTape, ENOSPC, "End of tape"},
+ {viotape_EquipCheck, EIO, "Equipment check"},
+ {viotape_InsufficientRs, EOVERFLOW, "Insufficient tape resources"},
+ {viotape_InvalidLogBlk, EIO, "Invalid logical block location"},
+ {viotape_LengthError, EOVERFLOW, "Length error"},
+ {viotape_LibDoorOpen, EBUSY, "Door open"},
+ {viotape_LoadFailure, ENOMEDIUM, "Load failure"},
+ {viotape_NotCapable, EIO, "Not capable"},
+ {viotape_NotOperational, EIO, "Not operational"},
+ {viotape_NotReady, EIO, "Not ready"},
+ {viotape_OpCancelled, EIO, "Operation cancelled"},
+ {viotape_PhyLinkErr, EIO, "Physical link error"},
+ {viotape_RdyNotBOT, EIO, "Ready but not beginning of tape"},
+ {viotape_TapeMark, EIO, "Tape mark"},
+ {viotape_WriteProt, EROFS, "Write protection error"},
+ {0, 0, NULL},
+};
+
+/* Maximum # tapes we support
+ */
+#define VIOTAPE_MAX_TAPE 8
+#define MAX_PARTITIONS 4
+
+/* defines for current tape state */
+#define VIOT_IDLE 0
+#define VIOT_READING 1
+#define VIOT_WRITING 2
+
+/* Our info on the tapes
+ */
+struct tape_descr {
+ char rsrcname[10];
+ char type[4];
+ char model[3];
+};
+
+static struct tape_descr *viotape_unitinfo = NULL;
+
+static const char *lasterr[VIOTAPE_MAX_TAPE];
+
+static struct mtget viomtget[VIOTAPE_MAX_TAPE];
+
+/* maintain the current state of each tape (and partition)
+ so that we know when to write EOF marks.
+*/
+static struct {
+ unsigned char cur_part;
+ devfs_handle_t dev_handle;
+ struct {
+ unsigned char rwi;
+ } part_stat[MAX_PARTITIONS];
+} state[VIOTAPE_MAX_TAPE];
+
+/* We single-thread
+ */
+static struct semaphore reqSem;
+
+/* When we send a request, we use this struct to get the response back
+ * from the interrupt handler
+ */
+struct opStruct {
+ void *buffer;
+ dma_addr_t dmaaddr;
+ size_t count;
+ int rc;
+ struct semaphore *sem;
+ struct opStruct *free;
+};
+
+static spinlock_t opStructListLock;
+static struct opStruct *opStructList;
+
+/* forward declaration to resolve interdependence */
+static int chg_state(int index, unsigned char new_state,
+ struct file *file);
+
+/* Decode the kdev_t into its parts
+ */
+void getDevInfo(kdev_t dev, struct viot_devinfo_struct *devi)
+{
+ devi->major = MAJOR(dev);
+ devi->minor = MINOR(dev);
+ devi->devno = devi->minor & 0x1F;
+ devi->mode = (devi->minor & 0x60) >> 5;
+ /* if bit is set in the minor, do _not_ rewind automatically */
+ devi->rewind = !(devi->minor & 0x80);
+}
+
+
+/* Allocate an op structure from our pool
+ */
+static struct opStruct *getOpStruct(void)
+{
+ struct opStruct *newOpStruct;
+ spin_lock(&opStructListLock);
+
+ if (opStructList == NULL) {
+ newOpStruct = kmalloc(sizeof(struct opStruct), GFP_KERNEL);
+ } else {
+ newOpStruct = opStructList;
+ opStructList = opStructList->free;
+ }
+
+ if (newOpStruct)
+ memset(newOpStruct, 0x00, sizeof(struct opStruct));
+
+ spin_unlock(&opStructListLock);
+
+ return newOpStruct;
+}
+
+/* Return an op structure to our pool
+ */
+static void freeOpStruct(struct opStruct *opStruct)
+{
+ spin_lock(&opStructListLock);
+ opStruct->free = opStructList;
+ opStructList = opStruct;
+ spin_unlock(&opStructListLock);
+}
+
+/* Map our tape return codes to errno values
+ */
+int tapeRcToErrno(int tapeRc, char *operation, int tapeno)
+{
+ const struct vio_error_entry *err;
+ if(tapeRc == 0)
+ return 0;
+ err = vio_lookup_rc(viotape_err_table, tapeRc);
+
+ printk(KERN_WARNING_VIO "tape error 0x%04x on Device %d (%-10s): %s\n",
+ tapeRc, tapeno, viotape_unitinfo[tapeno].rsrcname, err->msg);
+
+ lasterr[tapeno] = err->msg;
+
+ return -err->errno;
+}
+
+/* Handle reads from the proc file system.
+ */
+static int proc_read(char *buf, char **start, off_t offset,
+ int blen, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += sprintf(buf + len, "viotape driver version %d.%d\n",
+ version_major, version_minor);
+
+ for (i = 0; i < viotape_numdev; i++) {
+
+ len +=
+ sprintf(buf + len,
+ "viotape device %d is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ i, viotape_unitinfo[i].rsrcname,
+ viotape_unitinfo[i].type,
+ viotape_unitinfo[i].model);
+ if (lasterr[i])
+ len +=
+ sprintf(buf + len, " last error: %s\n",
+ lasterr[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+/* setup our proc file system entries
+ */
+void viotape_proc_init(struct proc_dir_entry *iSeries_proc)
+{
+ struct proc_dir_entry *ent;
+ ent =
+ create_proc_entry("viotape", S_IFREG | S_IRUSR, iSeries_proc);
+ if (!ent)
+ return;
+ ent->nlink = 1;
+ ent->data = NULL;
+ ent->read_proc = proc_read;
+}
+
+/* clean up our proc file system entries
+ */
+void viotape_proc_delete(struct proc_dir_entry *iSeries_proc)
+{
+ remove_proc_entry("viotape", iSeries_proc);
+}
+
+
+/* Get info on all tapes from OS/400
+ */
+static void get_viotape_info(void)
+{
+ dma_addr_t dmaaddr;
+ HvLpEvent_Rc hvrc;
+ int i;
+ struct opStruct *op = getOpStruct();
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ if (op == NULL)
+ return;
+
+ if (viotape_unitinfo == NULL) {
+ viotape_unitinfo =
+ kmalloc(sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE,
+ GFP_KERNEL);
+ }
+ memset(viotape_unitinfo, 0x00,
+ sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE);
+ memset(lasterr, 0x00, sizeof(lasterr));
+
+ op->sem = &Semaphore;
+
+ dmaaddr = pci_map_single(iSeries_vio_dev, viotape_unitinfo,
+ sizeof(struct tape_descr) *
+ VIOTAPE_MAX_TAPE, PCI_DMA_FROMDEVICE);
+ if (dmaaddr == 0xFFFFFFFF) {
+ printk(KERN_WARNING_VIO "viotape error allocating tce\n");
+ return;
+ }
+
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapegetinfo,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) op,
+ VIOVERSION << 16, dmaaddr,
+ sizeof(struct tape_descr) *
+ VIOTAPE_MAX_TAPE, 0, 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk("viotape hv error on op %d\n", (int) hvrc);
+ }
+
+ down(&Semaphore);
+
+ freeOpStruct(op);
+
+
+ for (i = 0;
+ ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0]));
+ i++) {
+ printk("found a tape %10.10s\n",
+ viotape_unitinfo[i].rsrcname);
+ viotape_numdev++;
+ }
+}
+
+
+/* Write
+ */
+static ssize_t viotap_write(struct file *file, const char *buf,
+ size_t count, loff_t * ppos)
+{
+ HvLpEvent_Rc hvrc;
+ kdev_t dev = file->f_dentry->d_inode->i_rdev;
+ unsigned short flags = file->f_flags;
+ struct opStruct *op = getOpStruct();
+ int noblock = ((flags & O_NONBLOCK) != 0);
+ int err;
+ struct viot_devinfo_struct devi;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+
+ if (op == NULL)
+ return -ENOMEM;
+
+ getDevInfo(dev, &devi);
+
+ /* We need to make sure we can send a request. We use
+ * a semaphore to keep track of # requests in use. If
+ * we are non-blocking, make sure we don't block on the
+ * semaphore
+ */
+ if (noblock) {
+ if (down_trylock(&reqSem)) {
+ freeOpStruct(op);
+ return -EWOULDBLOCK;
+ }
+ } else {
+ down(&reqSem);
+ }
+
+ /* Allocate a DMA buffer */
+ op->buffer = pci_alloc_consistent(iSeries_vio_dev, count, &op->dmaaddr);
+
+ if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) {
+ printk(KERN_WARNING_VIO
+ "tape error allocating dma buffer for len %ld\n",
+ count);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EFAULT;
+ }
+
+ op->count = count;
+
+ /* Copy the data into the buffer */
+ err = copy_from_user(op->buffer, (const void *) buf, count);
+ if (err) {
+ printk(KERN_WARNING_VIO
+ "tape: error on copy from user\n");
+ pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EFAULT;
+ }
+
+ if (noblock) {
+ op->sem = NULL;
+ } else {
+ op->sem = &Semaphore;
+ }
+
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapewrite,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) op,
+ VIOVERSION << 16,
+ ((u64) devi.
+ devno << 48) | op->dmaaddr,
+ count, 0, 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk("viotape hv error on op %d\n", (int) hvrc);
+ pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EIO;
+ }
+
+ if (noblock)
+ return count;
+
+ down(&Semaphore);
+
+ err = op->rc;
+
+ /* Free the buffer */
+ pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr);
+
+ count = op->count;
+
+ freeOpStruct(op);
+ up(&reqSem);
+ if (err)
+ return tapeRcToErrno(err, "write", devi.devno);
+ else {
+ chg_state(devi.devno, VIOT_WRITING, file);
+ return count;
+ }
+}
+
+/* read
+ */
+static ssize_t viotap_read(struct file *file, char *buf, size_t count,
+ loff_t * ptr)
+{
+ HvLpEvent_Rc hvrc;
+ kdev_t dev = file->f_dentry->d_inode->i_rdev;
+ unsigned short flags = file->f_flags;
+ struct opStruct *op = getOpStruct();
+ int noblock = ((flags & O_NONBLOCK) != 0);
+ int err;
+ struct viot_devinfo_struct devi;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+
+ if (op == NULL)
+ return -ENOMEM;
+
+ getDevInfo(dev, &devi);
+
+ /* We need to make sure we can send a request. We use
+ * a semaphore to keep track of # requests in use. If
+ * we are non-blocking, make sure we don't block on the
+ * semaphore
+ */
+ if (noblock) {
+ if (down_trylock(&reqSem)) {
+ freeOpStruct(op);
+ return -EWOULDBLOCK;
+ }
+ } else {
+ down(&reqSem);
+ }
+
+ chg_state(devi.devno, VIOT_READING, file);
+
+ /* Allocate a DMA buffer */
+ op->buffer = pci_alloc_consistent(iSeries_vio_dev, count, &op->dmaaddr);
+
+ if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) {
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EFAULT;
+ }
+
+ op->count = count;
+
+ op->sem = &Semaphore;
+
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotaperead,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) op,
+ VIOVERSION << 16,
+ ((u64) devi.
+ devno << 48) | op->dmaaddr,
+ count, 0, 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk(KERN_WARNING_VIO
+ "tape hv error on op %d\n", (int) hvrc);
+ pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EIO;
+ }
+
+ down(&Semaphore);
+
+ if (op->rc == 0) {
+ /* If we got data back */
+ if (op->count) {
+ /* Copy the data into the buffer */
+ err = copy_to_user(buf, op->buffer, count);
+ if (err) {
+ printk("error on copy_to_user\n");
+ pci_free_consistent(iSeries_vio_dev, count,
+ op->buffer,
+ op->dmaaddr);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EFAULT;
+ }
+ }
+ }
+
+ err = op->rc;
+
+ /* Free the buffer */
+ pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr);
+ count = op->count;
+
+ freeOpStruct(op);
+ up(&reqSem);
+ if (err)
+ return tapeRcToErrno(err, "read", devi.devno);
+ else
+ return count;
+}
+
+/* read
+ */
+static int viotap_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ HvLpEvent_Rc hvrc;
+ int err;
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ kdev_t dev = file->f_dentry->d_inode->i_rdev;
+ struct opStruct *op = getOpStruct();
+ struct viot_devinfo_struct devi;
+ if (op == NULL)
+ return -ENOMEM;
+
+ getDevInfo(dev, &devi);
+
+ down(&reqSem);
+
+ switch (cmd) {
+ case MTIOCTOP:{
+ struct mtop mtc;
+ u32 myOp;
+
+ /* inode is null if and only if we (the kernel) made the request */
+ if (inode == NULL)
+ memcpy(&mtc, (void *) arg,
+ sizeof(struct mtop));
+ else if (copy_from_user
+ ((char *) &mtc, (char *) arg,
+ sizeof(struct mtop))) {
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EFAULT;
+ }
+
+ switch (mtc.mt_op) {
+ case MTRESET:
+ myOp = VIOTAPOP_RESET;
+ break;
+ case MTFSF:
+ myOp = VIOTAPOP_FSF;
+ break;
+ case MTBSF:
+ myOp = VIOTAPOP_BSF;
+ break;
+ case MTFSR:
+ myOp = VIOTAPOP_FSR;
+ break;
+ case MTBSR:
+ myOp = VIOTAPOP_BSR;
+ break;
+ case MTWEOF:
+ myOp = VIOTAPOP_WEOF;
+ break;
+ case MTREW:
+ myOp = VIOTAPOP_REW;
+ break;
+ case MTNOP:
+ myOp = VIOTAPOP_NOP;
+ break;
+ case MTEOM:
+ myOp = VIOTAPOP_EOM;
+ break;
+ case MTERASE:
+ myOp = VIOTAPOP_ERASE;
+ break;
+ case MTSETBLK:
+ myOp = VIOTAPOP_SETBLK;
+ break;
+ case MTSETDENSITY:
+ myOp = VIOTAPOP_SETDENSITY;
+ break;
+ case MTTELL:
+ myOp = VIOTAPOP_GETPOS;
+ break;
+ case MTSEEK:
+ myOp = VIOTAPOP_SETPOS;
+ break;
+ case MTSETPART:
+ myOp = VIOTAPOP_SETPART;
+ break;
+ default:
+ return -EIO;
+ }
+
+/* if we moved the head, we are no longer reading or writing */
+ switch (mtc.mt_op) {
+ case MTFSF:
+ case MTBSF:
+ case MTFSR:
+ case MTBSR:
+ case MTTELL:
+ case MTSEEK:
+ case MTREW:
+ chg_state(devi.devno, VIOT_IDLE, file);
+ }
+
+ op->sem = &Semaphore;
+ hvrc =
+ HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape
+ | viotapeop,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned
+ long) op,
+ VIOVERSION << 16,
+ ((u64) devi.
+ devno << 48), 0,
+ (((u64) myOp) <<
+ 32) | mtc.
+ mt_count, 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk("viotape hv error on op %d\n",
+ (int) hvrc);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EIO;
+ }
+ down(&Semaphore);
+ if (op->rc) {
+ freeOpStruct(op);
+ up(&reqSem);
+ return tapeRcToErrno(op->rc,
+ "tape operation",
+ devi.devno);
+ } else {
+ freeOpStruct(op);
+ up(&reqSem);
+ return 0;
+ }
+ break;
+ }
+
+ case MTIOCGET:
+ op->sem = &Semaphore;
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapegetstatus,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long)
+ op, VIOVERSION << 16,
+ ((u64) devi.
+ devno << 48), 0, 0,
+ 0);
+ if (hvrc != HvLpEvent_Rc_Good) {
+ printk("viotape hv error on op %d\n", (int) hvrc);
+ freeOpStruct(op);
+ up(&reqSem);
+ return -EIO;
+ }
+ down(&Semaphore);
+ up(&reqSem);
+ if (op->rc) {
+ freeOpStruct(op);
+ return tapeRcToErrno(op->rc, "get status",
+ devi.devno);
+ } else {
+ freeOpStruct(op);
+ err =
+ copy_to_user((void *) arg, &viomtget[dev],
+ sizeof(viomtget[0]));
+ if (err) {
+ freeOpStruct(op);
+ return -EFAULT;
+ }
+ return 0;
+ }
+ break;
+ case MTIOCPOS:
+ printk("Got an MTIOCPOS\n");
+ default:
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+/* Open
+ */
+static int viotap_open(struct inode *inode, struct file *file)
+{
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ kdev_t dev = file->f_dentry->d_inode->i_rdev;
+ HvLpEvent_Rc hvrc;
+ struct opStruct *op = getOpStruct();
+ struct viot_devinfo_struct devi;
+ if (op == NULL)
+ return -ENOMEM;
+
+ getDevInfo(dev, &devi);
+
+// Note: We currently only support one mode!
+ if ((devi.devno >= viotape_numdev) || (devi.mode)) {
+ freeOpStruct(op);
+ return -ENODEV;
+ }
+
+ op->sem = &Semaphore;
+
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapeopen,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) op,
+ VIOVERSION << 16,
+ ((u64) devi.devno << 48), 0,
+ 0, 0);
+
+
+ if (hvrc != 0) {
+ printk("viotape bad rc on signalLpEvent %d\n", (int) hvrc);
+ freeOpStruct(op);
+ return -EIO;
+ }
+
+ down(&Semaphore);
+
+ if (op->rc) {
+ freeOpStruct(op);
+ return tapeRcToErrno(op->rc, "open", devi.devno);
+ } else {
+ freeOpStruct(op);
+ MOD_INC_USE_COUNT;
+ return 0;
+ }
+}
+
+
+/* Release
+ */
+static int viotap_release(struct inode *inode, struct file *file)
+{
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ kdev_t dev = file->f_dentry->d_inode->i_rdev;
+ HvLpEvent_Rc hvrc;
+ struct viot_devinfo_struct devi;
+ struct opStruct *op = getOpStruct();
+
+ if (op == NULL)
+ return -ENOMEM;
+ op->sem = &Semaphore;
+
+ getDevInfo(dev, &devi);
+
+ if (devi.devno >= viotape_numdev) {
+ freeOpStruct(op);
+ return -ENODEV;
+ }
+
+ chg_state(devi.devno, VIOT_IDLE, file);
+
+ if (devi.rewind) {
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapeop,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long)
+ op, VIOVERSION << 16,
+ ((u64) devi.
+ devno << 48), 0,
+ ((u64) VIOTAPOP_REW)
+ << 32, 0);
+ down(&Semaphore);
+
+ if (op->rc) {
+ tapeRcToErrno(op->rc, "rewind", devi.devno);
+ }
+ }
+
+ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
+ HvLpEvent_Type_VirtualIo,
+ viomajorsubtype_tape |
+ viotapeclose,
+ HvLpEvent_AckInd_DoAck,
+ HvLpEvent_AckType_ImmediateAck,
+ viopath_sourceinst
+ (viopath_hostLp),
+ viopath_targetinst
+ (viopath_hostLp),
+ (u64) (unsigned long) op,
+ VIOVERSION << 16,
+ ((u64) devi.devno << 48), 0,
+ 0, 0);
+
+
+ if (hvrc != 0) {
+ printk("viotape: bad rc on signalLpEvent %d\n",
+ (int) hvrc);
+ return -EIO;
+ }
+
+ down(&Semaphore);
+
+ if (op->rc) {
+ printk("viotape: close failed\n");
+ }
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+struct file_operations viotap_fops = {
+ owner:THIS_MODULE,
+ read:viotap_read,
+ write:viotap_write,
+ ioctl:viotap_ioctl,
+ open:viotap_open,
+ release:viotap_release,
+};
+
+/* Handle interrupt events for tape
+ */
+static void vioHandleTapeEvent(struct HvLpEvent *event)
+{
+ int tapeminor;
+ struct opStruct *op;
+ struct viotapelpevent *tevent = (struct viotapelpevent *) event;
+
+ if (event == NULL) {
+ /* Notification that a partition went away! */
+ if (!viopath_isactive(viopath_hostLp)) {
+ /* TODO! Clean up */
+ }
+ return;
+ }
+
+ tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK;
+ switch (tapeminor) {
+ case viotapegetinfo:
+ case viotapeopen:
+ case viotapeclose:
+ op = (struct opStruct *) (unsigned long) event->
+ xCorrelationToken;
+ op->rc = tevent->mSubTypeRc;
+ up(op->sem);
+ break;
+ case viotaperead:
+ case viotapewrite:
+ op = (struct opStruct *) (unsigned long) event->
+ xCorrelationToken;
+ op->rc = tevent->mSubTypeRc;
+ op->count = tevent->mLen;
+
+ if (op->sem) {
+ up(op->sem);
+ } else {
+ freeOpStruct(op);
+ up(&reqSem);
+ }
+ break;
+ case viotapeop:
+ case viotapegetpos:
+ case viotapesetpos:
+ case viotapegetstatus:
+ op = (struct opStruct *) (unsigned long) event->
+ xCorrelationToken;
+ if (op) {
+ op->count = tevent->u.tapeOp.mCount;
+ op->rc = tevent->mSubTypeRc;
+
+ if (op->sem) {
+ up(op->sem);
+ }
+ }
+ break;
+ default:
+ printk("viotape: wierd ack\n");
+ }
+}
+
+
+/* Do initialization
+ */
+int __init viotap_init(void)
+{
+ DECLARE_MUTEX_LOCKED(Semaphore);
+ int rc;
+ char tapename[32];
+ int i;
+
+ printk("viotape driver version %d.%d\n", version_major,
+ version_minor);
+
+ sndMsgSeq = sndMsgAck = 0;
+ rcvMsgSeq = rcvMsgAck = 0;
+ opStructList = NULL;
+ spin_lock_init(&opStructListLock);
+
+ sema_init(&reqSem, VIOTAPE_MAXREQ);
+
+ if (viopath_hostLp == HvLpIndexInvalid)
+ vio_set_hostlp();
+
+ /*
+ * Open to our hosting lp
+ */
+ if (viopath_hostLp == HvLpIndexInvalid)
+ return -1;
+
+ printk("viotape: init - open path to hosting (%d)\n",
+ viopath_hostLp);
+
+ rc = viopath_open(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2);
+ if (rc) {
+ printk("viotape: error on viopath_open to hostlp %d\n",
+ rc);
+ }
+
+ vio_setHandler(viomajorsubtype_tape, vioHandleTapeEvent);
+
+ printk("viotape major is %d\n", viotape_major);
+
+ get_viotape_info();
+
+ if (devfs_register_chrdev(viotape_major, "viotape", &viotap_fops)) {
+ printk("Error registering viotape device\n");
+ return -1;
+ }
+
+ for (i = 0; i < viotape_numdev; i++) {
+ int j;
+ state[i].cur_part = 0;
+ for (j = 0; j < MAX_PARTITIONS; ++j)
+ state[i].part_stat[j].rwi = VIOT_IDLE;
+ sprintf(tapename, "viotape%d", i);
+ state[i].dev_handle =
+ devfs_register(NULL, tapename, DEVFS_FL_DEFAULT,
+ viotape_major, i,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP, &viotap_fops, NULL);
+ printk
+ ("viotape device %s is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ tapename, viotape_unitinfo[i].rsrcname,
+ viotape_unitinfo[i].type, viotape_unitinfo[i].model);
+ }
+
+ /*
+ * Create the proc entry
+ */
+ iSeries_proc_callback(&viotape_proc_init);
+
+ return 0;
+}
+
+/* Give a new state to the tape object
+ */
+static int chg_state(int index, unsigned char new_state, struct file *file)
+{
+ unsigned char *cur_state =
+ &state[index].part_stat[state[index].cur_part].rwi;
+ int rc = 0;
+
+ /* if the same state, don't bother */
+ if (*cur_state == new_state)
+ return 0;
+
+ /* write an EOF if changing from writing to some other state */
+ if (*cur_state == VIOT_WRITING) {
+ struct mtop write_eof = { MTWEOF, 1 };
+ rc = viotap_ioctl(NULL, file, MTIOCTOP,
+ (unsigned long) &write_eof);
+ }
+ *cur_state = new_state;
+ return rc;
+}
+
+/* Cleanup
+ */
+static void __exit viotap_exit(void)
+{
+ int i, ret;
+ for (i = 0; i < viotape_numdev; ++i)
+ devfs_unregister(state[i].dev_handle);
+ ret = devfs_unregister_chrdev(viotape_major, "viotape");
+ if (ret < 0)
+ printk("Error unregistering device: %d\n", ret);
+ iSeries_proc_callback(&viotape_proc_delete);
+ if (viotape_unitinfo != NULL) {
+ kfree(viotape_unitinfo);
+ viotape_unitinfo = NULL;
+ }
+ viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2);
+ vio_clearHandler(viomajorsubtype_tape);
+}
+
+MODULE_LICENSE("GPL");
+module_init(viotap_init);
+module_exit(viotap_exit);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/Config.in linuxppc64_2_4/drivers/net/Config.in
--- ../kernel.org/linux-2.4.19/drivers/net/Config.in Mon Apr 22 11:34:26 2002
+++ linuxppc64_2_4/drivers/net/Config.in Tue Apr 23 09:37:28 2002
@@ -250,10 +250,6 @@
endmenu
-if [ "$CONFIG_PPC_ISERIES" = "y" ]; then
- dep_tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH $CONFIG_PPC_ISERIES
-fi
-
bool 'FDDI driver support' CONFIG_FDDI
if [ "$CONFIG_FDDI" = "y" ]; then
if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/Makefile linuxppc64_2_4/drivers/net/Makefile
--- ../kernel.org/linux-2.4.19/drivers/net/Makefile Mon Apr 22 11:34:26 2002
+++ linuxppc64_2_4/drivers/net/Makefile Tue Apr 23 09:37:28 2002
@@ -73,7 +73,6 @@
obj-$(CONFIG_DM9102) += dmfe.o
obj-$(CONFIG_YELLOWFIN) += yellowfin.o
obj-$(CONFIG_ACENIC) += acenic.o
-obj-$(CONFIG_VETH) += veth.o
obj-$(CONFIG_NATSEMI) += natsemi.o
obj-$(CONFIG_NS83820) += ns83820.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/Space.c linuxppc64_2_4/drivers/net/Space.c
--- ../kernel.org/linux-2.4.19/drivers/net/Space.c Wed May 15 08:29:38 2002
+++ linuxppc64_2_4/drivers/net/Space.c Mon May 13 16:39:13 2002
@@ -541,9 +541,10 @@
-#ifdef CONFIG_TR
+#if 0 /* ifdef CONFIG_TR */
/* Token-ring device probe */
extern int ibmtr_probe(struct net_device *);
+extern int olympic_probe(struct net_device *);
extern int smctr_probe(struct net_device *);
static int
@@ -552,6 +553,9 @@
if (1
#ifdef CONFIG_IBMTR
&& ibmtr_probe(dev)
+#endif
+#ifdef CONFIG_IBMOL
+ && olympic_probe(dev)
#endif
#ifdef CONFIG_SMCTR
&& smctr_probe(dev)
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/acenic.c linuxppc64_2_4/drivers/net/acenic.c
--- ../kernel.org/linux-2.4.19/drivers/net/acenic.c Fri Apr 19 11:00:36 2002
+++ linuxppc64_2_4/drivers/net/acenic.c Mon Apr 22 10:32:58 2002
@@ -1917,6 +1917,7 @@
atomic_add(i, &ap->cur_rx_bufs);
ap->rx_std_skbprd = idx;
+ mb(); // DRENG
if (ACE_IS_TIGON_I(ap)) {
struct cmd cmd;
cmd.evt = C_SET_RX_PRD_IDX;
@@ -2289,7 +2290,7 @@
writel(idx, ®s->RxRetCsm);
}
ap->cur_rx = idx;
-
+ mb(); // DRENG
return;
error:
idx = rxretprd;
@@ -2815,6 +2816,7 @@
wmb();
ap->tx_prd = idx;
+ mb(); // DRENG prd must be visible before telling HW to advance
ace_set_txprd(regs, ap, idx);
if (flagsize & BD_FLG_COAL_NOW) {
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/pcnet32.c linuxppc64_2_4/drivers/net/pcnet32.c
--- ../kernel.org/linux-2.4.19/drivers/net/pcnet32.c Fri Apr 19 11:00:36 2002
+++ linuxppc64_2_4/drivers/net/pcnet32.c Mon Apr 22 10:33:07 2002
@@ -55,6 +55,8 @@
#include
#include
+#define DO_DXSUFLO
+
/*
* PCI device identifiers for "new style" Linux PCI Device Drivers
*/
@@ -221,8 +223,8 @@
* That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
*/
#ifndef PCNET32_LOG_TX_BUFFERS
-#define PCNET32_LOG_TX_BUFFERS 4
-#define PCNET32_LOG_RX_BUFFERS 5
+#define PCNET32_LOG_TX_BUFFERS 6
+#define PCNET32_LOG_RX_BUFFERS 7
#endif
#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))
@@ -233,7 +235,7 @@
#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4)
-#define PKT_BUF_SZ 1544
+#define PKT_BUF_SZ 2048
/* Offsets from base I/O address. */
#define PCNET32_WIO_RDP 0x10
@@ -294,34 +296,34 @@
*/
struct pcnet32_private {
/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
- struct pcnet32_rx_head rx_ring[RX_RING_SIZE];
- struct pcnet32_tx_head tx_ring[TX_RING_SIZE];
- struct pcnet32_init_block init_block;
- dma_addr_t dma_addr; /* DMA address of beginning of this object,
- returned by pci_alloc_consistent */
- struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */
- const char *name;
+ struct pcnet32_rx_head rx_ring[RX_RING_SIZE];
+ struct pcnet32_tx_head tx_ring[TX_RING_SIZE];
+ struct pcnet32_init_block init_block;
+ dma_addr_t dma_addr; /* DMA address of beginning of this object,
+ returned by pci_alloc_consistent */
+ struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */
+ const char *name;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- dma_addr_t tx_dma_addr[TX_RING_SIZE];
- dma_addr_t rx_dma_addr[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ dma_addr_t tx_dma_addr[TX_RING_SIZE];
+ dma_addr_t rx_dma_addr[RX_RING_SIZE];
struct pcnet32_access a;
- spinlock_t lock; /* Guard lock */
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ spinlock_t lock; /* Guard lock */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
struct net_device_stats stats;
- char tx_full;
- int options;
- int shared_irq:1, /* shared irq possible */
- ltint:1, /* enable TxDone-intr inhibitor */
- dxsuflo:1, /* disable transmit stop on uflo */
- mii:1; /* mii port available */
- struct net_device *next;
+ char tx_full;
+ int options;
+ int shared_irq:1, /* shared irq possible */
+ ltint:1,
+ dxsuflo:1, /* disable transmit stop on uflo */
+ mii:1; /* mii port available */
+ struct net_device *next;
struct mii_if_info mii_if;
};
-static void pcnet32_probe_vlbus(void);
+static void pcnet32_probe_vlbus(void);
static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
static int pcnet32_probe1(unsigned long, unsigned int, int, struct pci_dev *);
static int pcnet32_open(struct net_device *);
@@ -342,7 +344,6 @@
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
};
-
static u16 pcnet32_wio_read_csr (unsigned long addr, int index)
{
outw (index, addr+PCNET32_WIO_RAP);
@@ -444,15 +445,15 @@
}
static struct pcnet32_access pcnet32_dwio = {
- read_csr: pcnet32_dwio_read_csr,
- write_csr: pcnet32_dwio_write_csr,
- read_bcr: pcnet32_dwio_read_bcr,
- write_bcr: pcnet32_dwio_write_bcr,
- read_rap: pcnet32_dwio_read_rap,
- write_rap: pcnet32_dwio_write_rap,
+ read_csr: pcnet32_dwio_read_csr,
+ write_csr: pcnet32_dwio_write_csr,
+ read_bcr: pcnet32_dwio_read_bcr,
+ write_bcr: pcnet32_dwio_write_bcr,
+ read_rap: pcnet32_dwio_read_rap,
+ write_rap: pcnet32_dwio_write_rap,
reset: pcnet32_dwio_reset
-};
+};
/* only probes for non-PCI devices, the rest are handled by
@@ -461,40 +462,44 @@
static void __devinit
pcnet32_probe_vlbus(void)
{
- unsigned int *port, ioaddr;
+ unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0;
+ int *port;
+
+ printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found);
- /* search for PCnet32 VLB cards at known addresses */
+ /* now look for PCnet32 VLB cards */
for (port = pcnet32_portlist; (ioaddr = *port); port++) {
if (!check_region(ioaddr, PCNET32_TOTAL_SIZE)) {
/* check if there is really a pcnet chip on that ioaddr */
- if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57))
+ if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57))
pcnet32_probe1(ioaddr, 0, 0, NULL);
}
}
}
+
static int __devinit
pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned long ioaddr;
- int err;
+ int err = 0;
err = pci_enable_device(pdev);
if (err < 0) {
- printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err);
- return err;
+ printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err);
+ return err;
}
pci_set_master(pdev);
ioaddr = pci_resource_start (pdev, 0);
if (!ioaddr) {
- printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n");
+ printk (KERN_ERR "card has no PCI IO resources, aborting\n");
return -ENODEV;
}
-
+
if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) {
- printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n");
+ printk(KERN_ERR "pcnet32.c: architecture does not support 32bit PCI busmaster DMA\n");
return -ENODEV;
}
@@ -508,13 +513,13 @@
*/
static int __devinit
pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
- struct pci_dev *pdev)
+ struct pci_dev *pdev)
{
struct pcnet32_private *lp;
struct resource *res;
dma_addr_t lp_dma_addr;
- int i, media;
- int fdx, mii, fset, dxsuflo, ltint;
+ int i,media;
+ int fdx = 0, mii = 0, fset = 0, dxsuflo=0, ltint=0;
int chip_version;
char *chipname;
struct net_device *dev;
@@ -522,25 +527,27 @@
u8 promaddr[6];
/* reset the chip */
+ pcnet32_dwio_reset(ioaddr);
+ udelay (100);
pcnet32_wio_reset(ioaddr);
- /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
- if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check(ioaddr)) {
- a = &pcnet32_wio;
+ /* Important to do the check for dwio mode first. */
+ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
} else {
- pcnet32_dwio_reset(ioaddr);
- if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
- a = &pcnet32_dwio;
+ if (pcnet32_wio_read_csr(ioaddr, 0) == 4 &&
+ pcnet32_wio_check(ioaddr)) {
+ a = &pcnet32_wio;
} else
return -ENODEV;
}
- chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16);
+ chip_version = a->read_csr(ioaddr, 88) | (a->read_csr (ioaddr,89) << 16);
if (pcnet32_debug > 2)
printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version);
if ((chip_version & 0xfff) != 0x003)
return -ENODEV;
-
+
/* initialize variables */
fdx = mii = fset = dxsuflo = ltint = 0;
chip_version = (chip_version >> 12) & 0xffff;
@@ -610,20 +617,27 @@
* one for latency - although on PCI this isnt a big loss. Older chips
* have FIFO's smaller than a packet, so you can't do this.
*/
-
+ /*
+ * UPDATE
+ * Got to make sure that BCR18:MEMCMD, BCR18:BREADE, BCR18:BWRITE are
+ * set on a PCI
+ */
if(fset)
{
- a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
- a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
- dxsuflo = 1;
- ltint = 1;
+ a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0xA60));
+ a->write_csr(ioaddr, 3, 0x2eb7);
+ a->write_csr(ioaddr, 4, 0x32ea);
+ a->write_csr(ioaddr, 80, 0x3f00);
+
+ dxsuflo = 1;
+ ltint = 1;
}
dev = alloc_etherdev(0);
if(!dev)
return -ENOMEM;
- printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr);
+ printk(KERN_INFO "%s at %#3lx,", chipname, ioaddr);
/* In most chips, after a chip reset, the ethernet address is read from the
* station address PROM at the base address and programmed into the
@@ -632,6 +646,7 @@
* they disagree with the CSRs. Either way, we use the CSR values, and
* double check that they are valid.
*/
+#ifndef CONFIG_PPC
for (i = 0; i < 3; i++) {
unsigned int val;
val = a->read_csr(ioaddr, i+12) & 0x0ffff;
@@ -639,28 +654,29 @@
dev->dev_addr[2*i] = val & 0x0ff;
dev->dev_addr[2*i+1] = (val >> 8) & 0x0ff;
}
+#endif
/* read PROM address and compare with CSR address */
- for (i = 0; i < 6; i++)
+ for (i = 0; i < 6; i++) {
promaddr[i] = inb(ioaddr + i);
-
+
if( memcmp( promaddr, dev->dev_addr, 6)
- || !is_valid_ether_addr(dev->dev_addr) ) {
-#ifndef __powerpc__
+ || !is_valid_ether_addr(dev->dev_addr) ) {
+#ifndef __powerpc__
if( is_valid_ether_addr(promaddr) ){
#else
- if( !is_valid_ether_addr(dev->dev_addr)
- && is_valid_ether_addr(promaddr)) {
+ if (!is_valid_ether_addr(dev->dev_addr)
+ && is_valid_ether_addr(promaddr)) {
#endif
- printk(" warning: CSR address invalid,\n");
- printk(KERN_INFO " using instead PROM address of");
- memcpy(dev->dev_addr, promaddr, 6);
+ printk(" warning: CSR address invalid,\n");
+ printk(KERN_INFO " using instead PROM address of");
+ memcpy(dev->dev_addr, promaddr, 6);
}
- }
+ }
/* if the ethernet address is not valid, force to 00:00:00:00:00:00 */
if( !is_valid_ether_addr(dev->dev_addr) )
- memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
+ memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i] );
@@ -800,6 +816,7 @@
cards_found++;
return 0;
}
+}
static int
@@ -893,7 +910,7 @@
lp->init_block.filter[1] = 0x00000000;
if (pcnet32_init_ring(dev))
return -ENOMEM;
-
+
/* Re-initialize the PCNET32, and start it when done. */
lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff);
lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16);
@@ -975,7 +992,10 @@
}
skb_reserve (rx_skbuff, 2);
}
- lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, rx_skbuff->len, PCI_DMA_FROMDEVICE);
+
+ if (lp->rx_dma_addr[i] == NULL)
+ lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
+
lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]);
lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
lp->rx_ring[i].status = le16_to_cpu(0x8000);
@@ -1010,7 +1030,7 @@
/* ReInit Ring */
lp->a.write_csr (ioaddr, 0, 1);
i = 0;
- while (i++ < 100)
+ while (1)
if (lp->a.read_csr (ioaddr, 0) & 0x0100)
break;
@@ -1024,10 +1044,10 @@
struct pcnet32_private *lp = dev->priv;
unsigned long ioaddr = dev->base_addr, flags;
- spin_lock_irqsave(&lp->lock, flags);
+ spin_lock_irqsave(&lp->lock, flags);
/* Transmitter timeout, serious problems. */
printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n",
- dev->name, lp->a.read_csr(ioaddr, 0));
+ dev->name, lp->a.read_csr (ioaddr, 0));
lp->a.write_csr (ioaddr, 0, 0x0004);
lp->stats.tx_errors++;
if (pcnet32_debug > 2) {
@@ -1050,7 +1070,7 @@
dev->trans_start = jiffies;
netif_start_queue(dev);
- spin_unlock_irqrestore(&lp->lock, flags);
+ spin_unlock_irqrestore(&lp->lock, flags);
}
@@ -1065,7 +1085,7 @@
if (pcnet32_debug > 3) {
printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",
- dev->name, lp->a.read_csr(ioaddr, 0));
+ dev->name, lp->a.read_csr (ioaddr, 0));
}
spin_lock_irqsave(&lp->lock, flags);
@@ -1310,12 +1330,12 @@
if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) {
skb_reserve (newskb, 2);
skb = lp->rx_skbuff[entry];
- pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], skb->len, PCI_DMA_FROMDEVICE);
skb_put (skb, pkt_len);
lp->rx_skbuff[entry] = newskb;
newskb->dev = dev;
+ pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
lp->rx_dma_addr[entry] =
- pci_map_single(lp->pci_dev, newskb->tail,
+ pci_map_single(lp->pci_dev, newskb->tail,
newskb->len, PCI_DMA_FROMDEVICE);
lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]);
rx_in_place = 1;
@@ -1369,7 +1389,7 @@
static int
pcnet32_close(struct net_device *dev)
{
- unsigned long ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr, flags;
struct pcnet32_private *lp = dev->priv;
int i;
@@ -1390,13 +1410,23 @@
*/
lp->a.write_bcr (ioaddr, 20, 4);
+ /*
+ * FIXME: What happens if the bcr write is posted, the buffers are
+ * freed and there is still incoming DMA traffic
+ */
+
+#warning "PCI posting bug"
+
free_irq(dev->irq, dev);
-
+
+ /* Lock after free_irq to avoid deadlock with interrupt handler. */
+ spin_lock_irqsave(&lp->lock, flags);
+
/* free all allocated skbuffs */
for (i = 0; i < RX_RING_SIZE; i++) {
lp->rx_ring[i].status = 0;
if (lp->rx_skbuff[i]) {
- pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], lp->rx_skbuff[i]->len, PCI_DMA_FROMDEVICE);
+ pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE);
dev_kfree_skb(lp->rx_skbuff[i]);
}
lp->rx_skbuff[i] = NULL;
@@ -1412,6 +1442,8 @@
lp->tx_dma_addr[i] = 0;
}
+ spin_unlock_irqrestore(&lp->lock, flags);
+
MOD_DEC_USE_COUNT;
return 0;
@@ -1505,13 +1537,13 @@
if (!lp->mii)
return 0;
-
+
phyaddr = lp->a.read_bcr(ioaddr, 33);
- lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
val_out = lp->a.read_bcr(ioaddr, 34);
lp->a.write_bcr(ioaddr, 33, phyaddr);
-
+
return val_out;
}
@@ -1523,7 +1555,7 @@
if (!lp->mii)
return;
-
+
phyaddr = lp->a.read_bcr(ioaddr, 33);
lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
@@ -1549,76 +1581,76 @@
return -EFAULT;
switch (ethcmd) {
- case ETHTOOL_GDRVINFO: {
- struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
- strcpy (info.driver, DRV_NAME);
- strcpy (info.version, DRV_VERSION);
- if (lp->pci_dev)
- strcpy (info.bus_info, lp->pci_dev->slot_name);
- else
- sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr);
- if (copy_to_user (useraddr, &info, sizeof (info)))
- return -EFAULT;
- return 0;
- }
-
- /* get settings */
- case ETHTOOL_GSET: {
- struct ethtool_cmd ecmd = { ETHTOOL_GSET };
- spin_lock_irq(&lp->lock);
- mii_ethtool_gset(&lp->mii_if, &ecmd);
- spin_unlock_irq(&lp->lock);
- if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
- return -EFAULT;
- return 0;
- }
- /* set settings */
- case ETHTOOL_SSET: {
- int r;
- struct ethtool_cmd ecmd;
- if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
- return -EFAULT;
- spin_lock_irq(&lp->lock);
- r = mii_ethtool_sset(&lp->mii_if, &ecmd);
- spin_unlock_irq(&lp->lock);
- return r;
- }
- /* restart autonegotiation */
- case ETHTOOL_NWAY_RST: {
- return mii_nway_restart(&lp->mii_if);
- }
- /* get link status */
- case ETHTOOL_GLINK: {
- struct ethtool_value edata = {ETHTOOL_GLINK};
- edata.data = mii_link_ok(&lp->mii_if);
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ case ETHTOOL_GDRVINFO: {
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strcpy (info.driver, DRV_NAME);
+ strcpy (info.version, DRV_VERSION);
+ if (lp->pci_dev)
+ strcpy (info.bus_info, lp->pci_dev->slot_name);
+ else
+ sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr);
+ if (copy_to_user (useraddr, &info, sizeof (info)))
+ return -EFAULT;
+ return 0;
+ }
+ /* get settings */
+ case ETHTOOL_GSET: {
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ spin_lock_irq(&lp->lock);
+ mii_ethtool_gset(&lp->mii_if, &ecmd);
+ spin_unlock_irq(&lp->lock);
+ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+ return -EFAULT;
+ return 0;
+ }
+ /* set settings */
+ case ETHTOOL_SSET: {
+ int r;
+ struct ethtool_cmd ecmd;
+ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+ return -EFAULT;
+ spin_lock_irq(&lp->lock);
+ r = mii_ethtool_sset(&lp->mii_if, &ecmd);
+ spin_unlock_irq(&lp->lock);
+ return r;
+ }
+ /* restart autonegotiation */
+ case ETHTOOL_NWAY_RST: {
+ return mii_nway_restart(&lp->mii_if);
+ }
+ /* get link status */
+ case ETHTOOL_GLINK: {
+ struct ethtool_value edata = {ETHTOOL_GLINK};
+ edata.data = mii_link_ok(&lp->mii_if);
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
- return 0;
- }
+ return 0;
+ }
- /* get message-level */
- case ETHTOOL_GMSGLVL: {
- struct ethtool_value edata = {ETHTOOL_GMSGLVL};
- edata.data = pcnet32_debug;
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
- }
- /* set message-level */
- case ETHTOOL_SMSGLVL: {
- struct ethtool_value edata;
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
- pcnet32_debug = edata.data;
- return 0;
- }
- default:
- break;
+ /* get message-level */
+ case ETHTOOL_GMSGLVL: {
+ struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+ edata.data = pcnet32_debug;
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ /* set message-level */
+ case ETHTOOL_SMSGLVL: {
+ struct ethtool_value edata;
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+ pcnet32_debug = edata.data;
+ return 0;
+ }
+ default:
+ break;
}
- return -EOPNOTSUPP;
+return -EOPNOTSUPP;
}
+
static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
unsigned long ioaddr = dev->base_addr;
@@ -1626,26 +1658,29 @@
struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
int phyaddr = lp->a.read_bcr (ioaddr, 33);
- if (cmd == SIOCETHTOOL)
- return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data);
+ if (cmd == SIOCETHTOOL)
+ return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data);
if (lp->mii) {
switch(cmd) {
- case SIOCGMIIPHY: /* Get address of MII PHY in use. */
+ case SIOCGMIIPHY: /* Get the address of the PHY in use. */
data->phy_id = (phyaddr >> 5) & 0x1f;
/* Fall Through */
- case SIOCGMIIREG: /* Read MII PHY register. */
+
+ case SIOCGMIIREG: /* Read the specified MII register. */
lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
data->val_out = lp->a.read_bcr (ioaddr, 34);
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
- case SIOCSMIIREG: /* Write MII PHY register. */
+
+ case SIOCSMIIREG: /* Write the specified MII register */
if (!capable(CAP_NET_ADMIN))
return -EPERM;
lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
lp->a.write_bcr (ioaddr, 34, data->val_in);
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
+
default:
return -EOPNOTSUPP;
}
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/net/tokenring/olympic.c linuxppc64_2_4/drivers/net/tokenring/olympic.c
--- ../kernel.org/linux-2.4.19/drivers/net/tokenring/olympic.c Wed May 15 08:29:39 2002
+++ linuxppc64_2_4/drivers/net/tokenring/olympic.c Mon May 13 16:39:14 2002
@@ -311,20 +311,20 @@
writel(readl(olympic_mmio+BCTL)|BCTL_MIMREB,olympic_mmio+BCTL);
if (olympic_priv->olympic_ring_speed == 0) { /* Autosense */
- writel(readl(olympic_mmio+GPR)|GPR_AUTOSENSE,olympic_mmio+GPR);
+ writew(readw(olympic_mmio+GPR)|GPR_AUTOSENSE,olympic_mmio+GPR);
if (olympic_priv->olympic_message_level)
printk(KERN_INFO "%s: Ringspeed autosense mode on\n",olympic_priv->olympic_card_name);
} else if (olympic_priv->olympic_ring_speed == 16) {
if (olympic_priv->olympic_message_level)
printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n", olympic_priv->olympic_card_name);
- writel(GPR_16MBPS, olympic_mmio+GPR);
+ writew(GPR_16MBPS, olympic_mmio+GPR);
} else if (olympic_priv->olympic_ring_speed == 4) {
if (olympic_priv->olympic_message_level)
printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n", olympic_priv->olympic_card_name) ;
- writel(0, olympic_mmio+GPR);
+ writew(0, olympic_mmio+GPR);
}
- writel(readl(olympic_mmio+GPR)|GPR_NEPTUNE_BF,olympic_mmio+GPR);
+ writew(readw(olympic_mmio+GPR)|GPR_NEPTUNE_BF,olympic_mmio+GPR);
#if OLYMPIC_DEBUG
printk("GPR = %x\n",readw(olympic_mmio + GPR) ) ;
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/pci/pci.c linuxppc64_2_4/drivers/pci/pci.c
--- ../kernel.org/linux-2.4.19/drivers/pci/pci.c Fri Apr 19 11:00:45 2002
+++ linuxppc64_2_4/drivers/pci/pci.c Mon Apr 22 10:33:13 2002
@@ -1079,8 +1079,8 @@
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
- base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
- limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;
+ base = (unsigned long)(io_base_lo & PCI_IO_RANGE_MASK) << 8;
+ limit = (unsigned long)(io_limit_lo & PCI_IO_RANGE_MASK) << 8;
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
u16 io_base_hi, io_limit_hi;
@@ -1107,8 +1107,8 @@
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
- base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
- limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
+ base = (unsigned long)(mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
+ limit = (unsigned long)(mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
if (base && base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
@@ -1123,16 +1123,16 @@
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
- base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
- limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+ base = (unsigned long)(mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+ limit = (unsigned long)(mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
#if BITS_PER_LONG == 64
- base |= ((long) mem_base_hi) << 32;
- limit |= ((long) mem_limit_hi) << 32;
+ base |= ((unsigned long) mem_base_hi) << 32;
+ limit |= ((unsigned long) mem_limit_hi) << 32;
#else
if (mem_base_hi || mem_limit_hi) {
printk(KERN_ERR "PCI: Unable to handle 64-bit address space for %s\n", child->name);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/pci/pci.ids linuxppc64_2_4/drivers/pci/pci.ids
--- ../kernel.org/linux-2.4.19/drivers/pci/pci.ids Mon Apr 22 11:34:26 2002
+++ linuxppc64_2_4/drivers/pci/pci.ids Tue Apr 23 09:37:29 2002
@@ -556,6 +556,7 @@
0022 IBM27-82351
002d Python
002e ServeRAID-3x
+ 0031 Serial Adapter
0036 Miami
003a CPU to PCI Bridge
003e 16/4 Token ring UTP/STP controller
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/scsi/sr_ioctl.c linuxppc64_2_4/drivers/scsi/sr_ioctl.c
--- ../kernel.org/linux-2.4.19/drivers/scsi/sr_ioctl.c Fri Apr 19 10:59:57 2002
+++ linuxppc64_2_4/drivers/scsi/sr_ioctl.c Mon Apr 22 10:33:14 2002
@@ -334,7 +334,12 @@
{
u_char sr_cmd[10];
int result, target = MINOR(cdi->dev);
- unsigned char buffer[32];
+ unsigned char *buffer = scsi_malloc(512);
+
+ if (buffer == NULL) {
+ printk("SCSI DMA pool exhausted.");
+ return -ENOMEM;
+ }
memset(sr_cmd, 0, sizeof(sr_cmd));
@@ -407,6 +412,7 @@
return -EINVAL;
}
+ scsi_free(buffer, 512);
#if 0
if (result)
printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/sound/dmabuf.c linuxppc64_2_4/drivers/sound/dmabuf.c
--- ../kernel.org/linux-2.4.19/drivers/sound/dmabuf.c Fri Apr 19 10:30:24 2002
+++ linuxppc64_2_4/drivers/sound/dmabuf.c Mon Feb 25 08:44:33 2002
@@ -113,7 +113,7 @@
}
}
dmap->raw_buf = start_addr;
- dmap->raw_buf_phys = virt_to_bus(start_addr);
+ dmap->raw_buf_phys = pci_map_single(NULL, start_addr, dmap->buffsize, PCI_DMA_BIDIRECTIONAL);
for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
mem_map_reserve(page);
@@ -134,6 +134,8 @@
start_addr = (unsigned long) dmap->raw_buf;
end_addr = start_addr + dmap->buffsize;
+
+ pci_unmap_single(NULL, dmap->raw_buf_phys, dmap->buffsize, PCI_DMA_BIDIRECTIONAL);
for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
mem_map_unreserve(page);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/drivers/video/offb.c linuxppc64_2_4/drivers/video/offb.c
--- ../kernel.org/linux-2.4.19/drivers/video/offb.c Fri Apr 19 10:30:28 2002
+++ linuxppc64_2_4/drivers/video/offb.c Wed Nov 14 21:23:38 2001
@@ -430,7 +430,7 @@
info->cmap_type = cmap_unknown;
if (depth == 8)
{
- /* XXX kludge for ati's */
+ /* XXX kludge for ati */
if (dp && !strncmp(name, "ATY,Rage128", 11)) {
unsigned long regbase = dp->addrs[2].address;
info->cmap_adr = ioremap(regbase, 0x1FFF);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/Config.in linuxppc64_2_4/fs/Config.in
--- ../kernel.org/linux-2.4.19/fs/Config.in Fri Apr 19 11:00:46 2002
+++ linuxppc64_2_4/fs/Config.in Mon Apr 22 10:35:08 2002
@@ -52,6 +52,8 @@
dep_mbool ' Transparent decompression extension' CONFIG_ZISOFS $CONFIG_ISO9660_FS
tristate 'Minix fs support' CONFIG_MINIX_FS
+tristate 'JFS filesystem support' CONFIG_JFS_FS
+dep_mbool ' JFS debugging' CONFIG_JFS_DEBUG $CONFIG_JFS_FS
tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS
tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/Makefile linuxppc64_2_4/fs/Makefile
--- ../kernel.org/linux-2.4.19/fs/Makefile Fri Apr 19 10:29:59 2002
+++ linuxppc64_2_4/fs/Makefile Thu Feb 21 20:57:39 2002
@@ -67,6 +67,7 @@
subdir-$(CONFIG_REISERFS_FS) += reiserfs
subdir-$(CONFIG_DEVPTS_FS) += devpts
subdir-$(CONFIG_SUN_OPENPROMFS) += openpromfs
+subdir-$(CONFIG_JFS_FS) += jfs
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/binfmt_elf.c linuxppc64_2_4/fs/binfmt_elf.c
--- ../kernel.org/linux-2.4.19/fs/binfmt_elf.c Wed May 15 08:29:42 2002
+++ linuxppc64_2_4/fs/binfmt_elf.c Mon May 13 16:41:07 2002
@@ -440,6 +440,7 @@
unsigned int size;
unsigned long elf_entry, interp_load_addr = 0;
unsigned long start_code, end_code, start_data, end_data;
+ unsigned long reloc_func_desc = 0;
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct exec interp_ex;
@@ -536,8 +537,6 @@
interp_ex = *((struct exec *) bprm->buf);
interp_elf_ex = *((struct elfhdr *) bprm->buf);
break;
- } else {
- SET_PERSONALITY(elf_ex, ibcs2_interpreter);
}
elf_ppnt++;
}
@@ -669,6 +668,7 @@
load_bias += error -
ELF_PAGESTART(load_bias + vaddr);
load_addr += load_bias;
+ reloc_func_desc = load_addr;
}
}
k = elf_ppnt->p_vaddr;
@@ -715,6 +715,7 @@
send_sig(SIGSEGV, current, 0);
return 0;
}
+ reloc_func_desc = interp_load_addr;
}
kfree(elf_phdata);
@@ -777,10 +778,14 @@
/*
* The ABI may specify that certain registers be set up in special
* ways (on i386 %edx is the address of a DT_FINI function, for
- * example. This macro performs whatever initialization to
- * the regs structure is required.
+ * example. In addition, it may also specify (eg, PowerPC64 ELF)
+ * that the e_entry field is the address of the function descriptor
+ * for the startup routine, rather than the address of the startup
+ * routine itself. This macro performs whatever initialization to
+ * the regs structure is required as well as any relocations to the
+ * function descriptor entries when executing dynamically links apps.
*/
- ELF_PLAT_INIT(regs);
+ ELF_PLAT_INIT(regs, reloc_func_desc);
#endif
start_thread(regs, elf_entry, bprm->p);
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/buffer.c linuxppc64_2_4/fs/buffer.c
--- ../kernel.org/linux-2.4.19/fs/buffer.c Wed May 15 08:29:42 2002
+++ linuxppc64_2_4/fs/buffer.c Mon May 13 16:41:08 2002
@@ -2761,7 +2761,7 @@
bh_hash_shift++;
hash_table = (struct buffer_head **)
- __get_free_pages(GFP_ATOMIC, order);
+ vmalloc(PAGE_SIZE << order);
} while (hash_table == NULL && --order > 0);
printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n",
nr_hash, order, (PAGE_SIZE << order));
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/dcache.c linuxppc64_2_4/fs/dcache.c
--- ../kernel.org/linux-2.4.19/fs/dcache.c Wed May 15 08:29:42 2002
+++ linuxppc64_2_4/fs/dcache.c Mon May 13 16:41:08 2002
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
@@ -1206,7 +1207,7 @@
d_hash_shift++;
dentry_hashtable = (struct list_head *)
- __get_free_pages(GFP_ATOMIC, order);
+ vmalloc(PAGE_SIZE << order);
} while (dentry_hashtable == NULL && --order >= 0);
printk(KERN_INFO "Dentry cache hash table entries: %d (order: %ld, %ld bytes)\n",
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/exec.c linuxppc64_2_4/fs/exec.c
--- ../kernel.org/linux-2.4.19/fs/exec.c Fri Apr 19 11:00:23 2002
+++ linuxppc64_2_4/fs/exec.c Mon Apr 22 10:35:08 2002
@@ -313,7 +313,7 @@
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = STACK_TOP;
- mpnt->vm_page_prot = PAGE_COPY;
+ mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0xf];
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0;
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/inode.c linuxppc64_2_4/fs/inode.c
--- ../kernel.org/linux-2.4.19/fs/inode.c Wed May 15 08:29:42 2002
+++ linuxppc64_2_4/fs/inode.c Mon May 13 16:41:08 2002
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
/*
* New inode.c implementation.
@@ -1148,7 +1149,7 @@
i_hash_shift++;
inode_hashtable = (struct list_head *)
- __get_free_pages(GFP_ATOMIC, order);
+ vmalloc(PAGE_SIZE << order);
} while (inode_hashtable == NULL && --order >= 0);
printk(KERN_INFO "Inode cache hash table entries: %d (order: %ld, %ld bytes)\n",
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/Makefile linuxppc64_2_4/fs/jfs/Makefile
--- ../kernel.org/linux-2.4.19/fs/jfs/Makefile Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/Makefile Wed Nov 14 10:19:35 2001
@@ -0,0 +1,20 @@
+#
+# Makefile for the Linux JFS filesystem routines.
+#
+# 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 (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := jfs.o
+obj-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \
+ jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \
+ jfs_unicode.o jfs_dtree.o jfs_inode.o \
+ jfs_extent.o symlink.o jfs_metapage.o \
+ jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o
+obj-m := $(O_TARGET)
+
+EXTRA_CFLAGS += -D_JFS_4K
+
+include $(TOPDIR)/Rules.make
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/dir.c linuxppc64_2_4/fs/jfs/dir.c
--- ../kernel.org/linux-2.4.19/fs/jfs/dir.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/dir.c Wed Nov 14 10:19:35 2001
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern int jfs_create(struct inode *, struct dentry *, int);
+extern int jfs_mkdir(struct inode *, struct dentry *, int);
+extern int jfs_unlink(struct inode *, struct dentry *);
+extern int jfs_rmdir(struct inode *, struct dentry *);
+extern int jfs_link(struct dentry *, struct inode *, struct dentry *);
+extern int jfs_symlink(struct inode *, struct dentry *, const char *);
+extern int jfs_rename(struct inode *, struct dentry *, struct inode *,
+ struct dentry *);
+extern int jfs_mknod(struct inode *, struct dentry *, int, int);
+extern int jfs_fsync_file(struct file *, struct dentry *, int);
+
+static ssize_t jfs_dir_read(struct file *filp,
+ char *buf, size_t count, loff_t * ppos)
+{
+ return -EISDIR;
+}
+
+struct file_operations jfs_dir_operations = {
+ fsync: jfs_fsync_file,
+ read: jfs_dir_read,
+ readdir: jfs_readdir,
+};
+
+static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry)
+{
+ btstack_t btstack;
+ ino_t inum;
+ struct inode *ip;
+ component_t key;
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ int rc;
+
+ jFYI(1, ("jfs_lookup: name = %s\n", name));
+
+
+ if ((name[0] == '.') && (len == 1))
+ inum = dip->i_ino;
+ else if (strcmp(name, "..") == 0)
+ inum = PARENT(dip);
+ else {
+ if ((rc =
+ get_UCSname(&key, dentry, JFS_SBI(dip->i_sb)->nls_tab)))
+ return ERR_PTR(-rc);
+ IREAD_LOCK(dip);
+ rc = dtSearch(dip, &key, &inum, &btstack, JFS_LOOKUP);
+ IREAD_UNLOCK(dip);
+ free_UCSname(&key);
+ if (rc == ENOENT) {
+ d_add(dentry, NULL);
+ return ERR_PTR(0);
+ } else if (rc) {
+ jERROR(1,
+ ("jfs_lookup: dtSearch returned %d\n", rc));
+ return ERR_PTR(-rc);
+ }
+ }
+
+ ip = iget(dip->i_sb, inum);
+ if (ip == NULL) {
+ jERROR(1,
+ ("jfs_lookup: iget failed on inum %d\n",
+ (uint) inum));
+ return ERR_PTR(-EACCES);
+ }
+
+ d_add(dentry, ip);
+
+ return ERR_PTR(0);
+}
+
+struct inode_operations jfs_dir_inode_operations = {
+ create: jfs_create,
+ lookup: jfs_lookup,
+ link: jfs_link,
+ unlink: jfs_unlink,
+ symlink: jfs_symlink,
+ mkdir: jfs_mkdir,
+ rmdir: jfs_rmdir,
+ mknod: jfs_mknod,
+ rename: jfs_rename,
+};
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/endian24.h linuxppc64_2_4/fs/jfs/endian24.h
--- ../kernel.org/linux-2.4.19/fs/jfs/endian24.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/endian24.h Tue Apr 23 11:14:25 2002
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _H_ENDIAN24
+#define _H_ENDIAN24
+
+/*
+ * fs/jfs/endian24.h:
+ *
+ * Endian conversion for 24-byte data
+ *
+ */
+#define __swab24(x) \
+({ \
+ __u32 __x = (x); \
+ ((__u32)( \
+ ((__x & (__u32)0x000000ffUL) << 16) | \
+ (__x & (__u32)0x0000ff00UL) | \
+ ((__x & (__u32)0x00ff0000UL) >> 16) )); \
+})
+
+#if (defined(__KERNEL__) && defined(__LITTLE_ENDIAN)) || (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN))
+ #define __cpu_to_le24(x) ((__u32)(x))
+ #define __le24_to_cpu(x) ((__u32)(x))
+#else
+ #define __cpu_to_le24(x) __swab24(x)
+ #define __le24_to_cpu(x) __swab24(x)
+#endif
+
+#ifdef __KERNEL__
+ #define cpu_to_le24 __cpu_to_le24
+ #define le24_to_cpu __le24_to_cpu
+#endif
+
+#endif /* !_H_ENDIAN24 */
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/file.c linuxppc64_2_4/fs/jfs/file.c
--- ../kernel.org/linux-2.4.19/fs/jfs/file.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/file.c Tue Apr 23 11:14:25 2002
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include "jfs_incore.h"
+#include "jfs_txnmgr.h"
+#include "jfs_debug.h"
+
+
+extern int generic_file_open(struct inode *, struct file *) __weak;
+extern loff_t generic_file_llseek(struct file *, loff_t, int origin) __weak;
+
+extern int jfs_commit_inode(struct inode *, int);
+
+int jfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int rc = 0;
+
+ rc = fsync_inode_data_buffers(inode);
+
+ if (!(inode->i_state & I_DIRTY))
+ return rc;
+ if (datasync || !(inode->i_state & I_DIRTY_DATASYNC))
+ return rc;
+
+ IWRITE_LOCK(inode);
+ rc |= jfs_commit_inode(inode, 1);
+ IWRITE_UNLOCK(inode);
+
+ return rc ? -EIO : 0;
+}
+
+struct file_operations jfs_file_operations = {
+ open: generic_file_open,
+ llseek: generic_file_llseek,
+ write: generic_file_write,
+ read: generic_file_read,
+ mmap: generic_file_mmap,
+ fsync: jfs_fsync,
+};
+
+/*
+ * Guts of jfs_truncate. Called with locks already held. Can be called
+ * with directory for truncating directory index table.
+ */
+void jfs_truncate_nolock(struct inode *ip, loff_t length)
+{
+ loff_t newsize;
+ tid_t tid;
+
+ ASSERT(length >= 0);
+
+ if (test_cflag(COMMIT_Nolink, ip)) {
+ xtTruncate(0, ip, length, COMMIT_WMAP);
+ return;
+ }
+
+ do {
+ tid = txBegin(ip->i_sb, 0);
+
+ newsize = xtTruncate(tid, ip, length,
+ COMMIT_TRUNCATE | COMMIT_PWMAP);
+ if (newsize < 0) {
+ txEnd(tid);
+ break;
+ }
+
+ ip->i_mtime = ip->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(ip);
+
+ txCommit(tid, 1, &ip, 0);
+ txEnd(tid);
+ } while (newsize > length); /* Truncate isn't always atomic */
+}
+
+static void jfs_truncate(struct inode *ip)
+{
+ jFYI(1, ("jfs_truncate: size = 0x%lx\n", (ulong) ip->i_size));
+
+ IWRITE_LOCK(ip);
+ jfs_truncate_nolock(ip, ip->i_size);
+ IWRITE_UNLOCK(ip);
+}
+
+struct inode_operations jfs_file_inode_operations = {
+ truncate: jfs_truncate,
+};
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/inode.c linuxppc64_2_4/fs/jfs/inode.c
--- ../kernel.org/linux-2.4.19/fs/jfs/inode.c Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/inode.c Tue Apr 23 11:14:25 2002
@@ -0,0 +1,329 @@
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_imap.h"
+#include "jfs_extent.h"
+#include "jfs_unicode.h"
+#include "jfs_debug.h"
+
+
+extern struct inode_operations jfs_dir_inode_operations;
+extern struct inode_operations jfs_file_inode_operations;
+extern struct inode_operations jfs_symlink_inode_operations;
+extern struct file_operations jfs_dir_operations;
+extern struct file_operations jfs_file_operations;
+struct address_space_operations jfs_aops;
+extern int freeZeroLink(struct inode *);
+
+void jfs_put_inode(struct inode *inode)
+{
+ jFYI(1, ("In jfs_put_inode, inode = 0x%p\n", inode));
+}
+
+void jfs_read_inode(struct inode *inode)
+{
+ int rc;
+
+ rc = alloc_jfs_inode(inode);
+ if (rc) {
+ printk(__FUNCTION__ ": failed.");
+ goto bad_inode;
+ }
+ jFYI(1, ("In jfs_read_inode, inode = 0x%p\n", inode));
+
+ if (diRead(inode))
+ goto bad_inode_free;
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &jfs_file_inode_operations;
+ inode->i_fop = &jfs_file_operations;
+ inode->i_mapping->a_ops = &jfs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &jfs_dir_inode_operations;
+ inode->i_fop = &jfs_dir_operations;
+ inode->i_mapping->a_ops = &jfs_aops;
+ inode->i_mapping->gfp_mask = GFP_NOFS;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (inode->i_size > IDATASIZE) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &jfs_aops;
+ } else
+ inode->i_op = &jfs_symlink_inode_operations;
+ } else {
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
+ }
+
+ return;
+
+ bad_inode_free:
+ free_jfs_inode(inode);
+ bad_inode:
+ make_bad_inode(inode);
+}
+
+/* This define is from fs/open.c */
+#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
+
+/*
+ * Workhorse of both fsync & write_inode
+ */
+int jfs_commit_inode(struct inode *inode, int wait)
+{
+ int rc = 0;
+ tid_t tid;
+ static int noisy = 5;
+
+ jFYI(1, ("In jfs_commit_inode, inode = 0x%p\n", inode));
+
+ /*
+ * Don't commit if inode has been committed since last being
+ * marked dirty, or if it has been deleted.
+ */
+ if (test_cflag(COMMIT_Nolink, inode) ||
+ !test_cflag(COMMIT_Dirty, inode))
+ return 0;
+
+ if (isReadOnly(inode)) {
+ /* kernel allows writes to devices on read-only
+ * partitions and may think inode is dirty
+ */
+ if (!special_file(inode->i_mode) && noisy) {
+ jERROR(1, ("jfs_commit_inode(0x%p) called on "
+ "read-only volume\n", inode));
+ jERROR(1, ("Is remount racy?\n"));
+ noisy--;
+ }
+ return 0;
+ }
+
+ tid = txBegin(inode->i_sb, COMMIT_INODE);
+ rc = txCommit(tid, 1, &inode, wait ? COMMIT_SYNC : 0);
+ txEnd(tid);
+ return -rc;
+}
+
+void jfs_write_inode(struct inode *inode, int wait)
+{
+ /*
+ * If COMMIT_DIRTY is not set, the inode isn't really dirty.
+ * It has been committed since the last change, but was still
+ * on the dirty inode list
+ */
+ if (test_cflag(COMMIT_Nolink, inode) ||
+ !test_cflag(COMMIT_Dirty, inode))
+ return;
+
+ IWRITE_LOCK(inode);
+
+ if (jfs_commit_inode(inode, wait)) {
+ jERROR(1, ("jfs_write_inode: jfs_commit_inode failed!\n"));
+ }
+
+ IWRITE_UNLOCK(inode);
+}
+
+void jfs_delete_inode(struct inode *inode)
+{
+ jFYI(1, ("In jfs_delete_inode, inode = 0x%p\n", inode));
+
+ IWRITE_LOCK(inode);
+ if (test_cflag(COMMIT_Freewmap, inode))
+ freeZeroLink(inode);
+
+ diFree(inode);
+ IWRITE_UNLOCK(inode);
+
+ clear_inode(inode);
+}
+
+void jfs_dirty_inode(struct inode *inode)
+{
+ static int noisy = 5;
+
+ if (isReadOnly(inode)) {
+ if (!special_file(inode->i_mode) && noisy) {
+ /* kernel allows writes to devices on read-only
+ * partitions and may try to mark inode dirty
+ */
+ jERROR(1, ("jfs_dirty_inode called on "
+ "read-only volume\n"));
+ jERROR(1, ("Is remount racy?\n"));
+ noisy--;
+ }
+ return;
+ }
+
+ set_cflag(COMMIT_Dirty, inode);
+}
+
+static int jfs_get_block(struct inode *ip, long lblock,
+ struct buffer_head *bh_result, int create)
+{
+ s64 lblock64 = lblock;
+ int no_size_check = 0;
+ int rc = 0;
+ int take_locks;
+ xad_t xad;
+ s64 xaddr;
+ int xflag;
+ s32 xlen;
+
+ /*
+ * If this is a special inode (imap, dmap) or directory,
+ * the lock should already be taken
+ */
+ take_locks = ((JFS_IP(ip)->fileset != AGGREGATE_I) &&
+ !S_ISDIR(ip->i_mode));
+ /*
+ * Take appropriate lock on inode
+ */
+ if (take_locks) {
+ if (create)
+ IWRITE_LOCK(ip);
+ else
+ IREAD_LOCK(ip);
+ }
+
+ /*
+ * A directory's "data" is the inode index table, but i_size is the
+ * size of the d-tree, so don't check the offset against i_size
+ */
+ if (S_ISDIR(ip->i_mode))
+ no_size_check = 1;
+
+ if ((no_size_check ||
+ ((lblock64 << ip->i_sb->s_blocksize_bits) < ip->i_size)) &&
+ (xtLookup
+ (ip, lblock64, 1, &xflag, &xaddr, &xlen, no_size_check)
+ == 0) && xlen) {
+ if (xflag & XAD_NOTRECORDED) {
+ if (!create)
+ /*
+ * Allocated but not recorded, read treats
+ * this as a hole
+ */
+ goto unlock;
+#ifdef _JFS_4K
+ XADoffset(&xad, lblock64);
+ XADlength(&xad, xlen);
+ XADaddress(&xad, xaddr);
+#else /* _JFS_4K */
+ /*
+ * As long as block size = 4K, this isn't a problem.
+ * We should mark the whole page not ABNR, but how
+ * will we know to mark the other blocks BH_New?
+ */
+ BUG();
+#endif /* _JFS_4K */
+ rc = extRecord(ip, &xad);
+ if (rc)
+ goto unlock;
+ bh_result->b_state |= (1UL << BH_New);
+ }
+
+ bh_result->b_dev = ip->i_dev;
+ bh_result->b_blocknr = xaddr;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ goto unlock;
+ }
+ if (!create)
+ goto unlock;
+
+ /*
+ * Allocate a new block
+ */
+#ifdef _JFS_4K
+ if ((rc =
+ extHint(ip, lblock64 << ip->i_sb->s_blocksize_bits, &xad)))
+ goto unlock;
+ rc = extAlloc(ip, 1, lblock64, &xad, FALSE);
+ if (rc)
+ goto unlock;
+
+ bh_result->b_dev = ip->i_dev;
+ bh_result->b_blocknr = addressXAD(&xad);
+ bh_result->b_state |= ((1UL << BH_Mapped) | (1UL << BH_New));
+
+#else /* _JFS_4K */
+ /*
+ * We need to do whatever it takes to keep all but the last buffers
+ * in 4K pages - see jfs_write.c
+ */
+ BUG();
+#endif /* _JFS_4K */
+
+ unlock:
+ /*
+ * Release lock on inode
+ */
+ if (take_locks) {
+ if (create)
+ IWRITE_UNLOCK(ip);
+ else
+ IREAD_UNLOCK(ip);
+ }
+ return -rc;
+}
+
+static int jfs_writepage(struct page *page)
+{
+ return block_write_full_page(page, jfs_get_block);
+}
+
+static int jfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, jfs_get_block);
+}
+
+static int jfs_prepare_write(struct file *file,
+ struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page, from, to, jfs_get_block);
+}
+
+static int jfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, jfs_get_block);
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,15))
+static int jfs_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf,
+ unsigned long blocknr, int blocksize)
+{
+ return generic_direct_IO(rw, inode, iobuf, blocknr,
+ blocksize, jfs_get_block);
+}
+#endif /* Kernel >= 2.4.15 */
+
+struct address_space_operations jfs_aops = {
+ readpage: jfs_readpage,
+ writepage: jfs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: jfs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: jfs_bmap,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,15))
+ direct_IO: jfs_direct_IO,
+#endif
+};
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/jfs_btree.h linuxppc64_2_4/fs/jfs/jfs_btree.h
--- ../kernel.org/linux-2.4.19/fs/jfs/jfs_btree.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/jfs_btree.h Tue Apr 23 11:14:25 2002
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _H_JFS_BTREE
+#define _H_JFS_BTREE
+/*
+ * jfs_btree.h: B+-tree
+ *
+ * JFS B+-tree (dtree and xtree) common definitions
+ */
+
+/*
+ * basic btree page - btpage_t
+ */
+typedef struct {
+ s64 next; /* 8: right sibling bn */
+ s64 prev; /* 8: left sibling bn */
+
+ u8 flag; /* 1: */
+ u8 rsrvd[7]; /* 7: type specific */
+ s64 self; /* 8: self address */
+
+ u8 entry[4064]; /* 4064: */
+} btpage_t; /* (4096) */
+
+/* btpaget_t flag */
+#define BT_TYPE 0x07 /* B+-tree index */
+#define BT_ROOT 0x01 /* root page */
+#define BT_LEAF 0x02 /* leaf page */
+#define BT_INTERNAL 0x04 /* internal page */
+#define BT_RIGHTMOST 0x10 /* rightmost page */
+#define BT_LEFTMOST 0x20 /* leftmost page */
+#define BT_SWAPPED 0x80 /* used by fsck for endian swapping */
+
+/* btorder (in inode) */
+#define BT_RANDOM 0x0000
+#define BT_SEQUENTIAL 0x0001
+#define BT_LOOKUP 0x0010
+#define BT_INSERT 0x0020
+#define BT_DELETE 0x0040
+
+/*
+ * btree page buffer cache access
+ */
+#define BT_IS_ROOT(MP) (((MP)->xflag & COMMIT_PAGE) == 0)
+
+/* get page from buffer page */
+#define BT_PAGE(IP, MP, TYPE, ROOT)\
+ (BT_IS_ROOT(MP) ? (TYPE *)&JFS_IP(IP)->ROOT : (TYPE *)(MP)->data)
+
+/* get the page buffer and the page for specified block address */
+#define BT_GETPAGE(IP, BN, MP, TYPE, SIZE, P, RC, ROOT)\
+{\
+ if ((BN) == 0)\
+ {\
+ MP = (metapage_t *)&JFS_IP(IP)->bxflag;\
+ P = (TYPE *)&JFS_IP(IP)->ROOT;\
+ RC = 0;\
+ jEVENT(0,("%d BT_GETPAGE returning root\n", __LINE__));\
+ }\
+ else\
+ {\
+ jEVENT(0,("%d BT_GETPAGE reading block %d\n", __LINE__,\
+ (int)BN));\
+ MP = read_metapage((IP), BN, SIZE, 1);\
+ if (MP) {\
+ RC = 0;\
+ P = (MP)->data;\
+ } else {\
+ P = NULL;\
+ jERROR(1,("bread failed!\n"));\
+ RC = EIO;\
+ }\
+ }\
+}
+
+#define BT_MARK_DIRTY(MP, IP)\
+{\
+ if (BT_IS_ROOT(MP))\
+ mark_inode_dirty(IP);\
+ else\
+ mark_metapage_dirty(MP);\
+}
+
+/* put the page buffer */
+#define BT_PUTPAGE(MP)\
+{\
+ if (! BT_IS_ROOT(MP)) \
+ release_metapage(MP); \
+}
+
+
+/*
+ * btree traversal stack
+ *
+ * record the path traversed during the search;
+ * top frame record the leaf page/entry selected.
+ */
+#define MAXTREEHEIGHT 8
+typedef struct btframe { /* stack frame */
+ s64 bn; /* 8: */
+ s16 index; /* 2: */
+ s16 lastindex; /* 2: */
+ struct metapage *mp; /* 4: */
+} btframe_t; /* (16) */
+
+typedef struct btstack {
+ btframe_t *top; /* 4: */
+ int nsplit; /* 4: */
+ btframe_t stack[MAXTREEHEIGHT];
+} btstack_t;
+
+#define BT_CLR(btstack)\
+ (btstack)->top = (btstack)->stack
+
+#define BT_PUSH(BTSTACK, BN, INDEX)\
+{\
+ (BTSTACK)->top->bn = BN;\
+ (BTSTACK)->top->index = INDEX;\
+ ++(BTSTACK)->top;\
+ assert((BTSTACK)->top != &((BTSTACK)->stack[MAXTREEHEIGHT]));\
+}
+
+#define BT_POP(btstack)\
+ ( (btstack)->top == (btstack)->stack ? NULL : --(btstack)->top )
+
+#define BT_STACK(btstack)\
+ ( (btstack)->top == (btstack)->stack ? NULL : (btstack)->top )
+
+/* retrieve search results */
+#define BT_GETSEARCH(IP, LEAF, BN, MP, TYPE, P, INDEX, ROOT)\
+{\
+ BN = (LEAF)->bn;\
+ MP = (LEAF)->mp;\
+ if (BN)\
+ P = (TYPE *)MP->data;\
+ else\
+ P = (TYPE *)&JFS_IP(IP)->ROOT;\
+ INDEX = (LEAF)->index;\
+}
+
+/* put the page buffer of search */
+#define BT_PUTSEARCH(BTSTACK)\
+{\
+ if (! BT_IS_ROOT((BTSTACK)->top->mp))\
+ release_metapage((BTSTACK)->top->mp);\
+}
+#endif /* _H_JFS_BTREE */
diff -uNr --exclude=CVS ../kernel.org/linux-2.4.19/fs/jfs/jfs_compat.h linuxppc64_2_4/fs/jfs/jfs_compat.h
--- ../kernel.org/linux-2.4.19/fs/jfs/jfs_compat.h Wed Dec 31 18:00:00 1969
+++ linuxppc64_2_4/fs/jfs/jfs_compat.h Tue Apr 23 11:14:25 2002
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _H_JFS_COMPAT
+#define _H_JFS_COMPAT
+
+/*
+ * jfs_compat.h:
+ *
+ * Definitions to allow JFS to build on older kernels.
+ *
+ * This file should be removed when JFS is merged with linux kernel
+ *
+ */
+
+#include