aboutsummaryrefslogtreecommitdiffstats
BranchCommit messageAuthorAge
archive/unc-master-3.0P-FP: fix BUG_ON releated to priority inheritanceBjoern Brandenburg13 years
archived-2013.1uncachedev: mmap memory that is not cached by CPUsGlenn Elliott12 years
archived-private-masterMerge branch 'wip-2.6.34' into old-private-masterAndrea Bastoni15 years
archived-semi-partMerge branch 'wip-semi-part' of ssh://cvs/cvs/proj/litmus/repo/litmus2010 int...Andrea Bastoni15 years
demoFurther refinementsJonathan Herman14 years
ecrts-pgm-finalMerge branch 'wip-ecrts14-pgm' of ssh://rtsrv.cs.unc.edu/home/litmus/litmus-r...Glenn Elliott12 years
ecrts14-pgm-finalMerge branch 'wip-ecrts14-pgm' of ssh://rtsrv.cs.unc.edu/home/litmus/litmus-r...Glenn Elliott12 years
gpusync-rtss12Final GPUSync implementation.Glenn Elliott12 years
gpusync/stagingRename IKGLP R2DGLP.Glenn Elliott12 years
linux-tipMerge branch 'slab/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/p...Linus Torvalds15 years
litmus2008-patch-seriesadd i386 feather-trace implementationBjoern B. Brandenburg16 years
masterPSN-EDF: use inferred_sporadic_job_release_atBjoern Brandenburg9 years
pgmmake it compileGlenn Elliott12 years
prop/litmus-signalsInfrastructure for Litmus signals.Glenn Elliott13 years
prop/robust-tie-breakFixed bug in edf_higher_prio().Glenn Elliott13 years
stagingFix tracepoint compilation errorFelipe Cerqueira13 years
test9/23/2016Namhoon Kim9 years
tracing-develTest kernel tracing events capabilitiesAndrea Bastoni16 years
v2.6.34-with-arm-patchessmsc911x: Add spinlocks around registers accessCatalin Marinas15 years
v2015.1Add ARM syscall def for get_current_budgetBjoern Brandenburg10 years
wip-2011.2-bbbLitmus core: simplify np-section protocolBjoern B. Brandenburg14 years
wip-2011.2-bbb-traceRefactor sched_trace_log_message() -> debug_trace_log_message()Andrea Bastoni14 years
wip-2012.3-gpuSOBLIV draining support for C-EDF.Glenn Elliott12 years
wip-2012.3-gpu-preportpick up last C-RM fileGlenn Elliott12 years
wip-2012.3-gpu-rtss13Fix critical bug in GPU tracker.Glenn Elliott12 years
wip-2012.3-gpu-sobliv-budget-w-ksharkProper sobliv draining and many bug fixes.Glenn Elliott12 years
wip-aedzl-finalMake it easier to compile AEDZL interfaces in liblitmus.Glenn Elliott15 years
wip-aedzl-revisedAdd sched_trace data for Apative EDZLGlenn Elliott15 years
wip-arbit-deadlineFix compilation bug.Glenn Elliott13 years
wip-aux-tasksDescription of refined aux task inheritance.Glenn Elliott13 years
wip-bbbGSN-EDF & Core: improve debug TRACE'ing for NP sectionsBjoern B. Brandenburg14 years
wip-bbb-prio-donuse correct timestampBjoern B. Brandenburg14 years
wip-better-breakImplement hash-based EDF tie-breaking.Glenn Elliott13 years
wip-binary-heapMake C-EDF work with simplified binheap_deleteGlenn Elliott13 years
wip-budgetAdded support for choices in budget policy enforcement.Glenn Elliott15 years
wip-colorSummarize schedulability with final recordJonathan Herman13 years
wip-color-jlhsched_color: Fixed two bugs causing crashing on experiment restart and a rare...Jonathan Herman13 years
wip-d10-hz1000Enable HZ=1000 on District 10Bjoern B. Brandenburg15 years
wip-default-clusteringFeature: Make default C-EDF clustering compile-time configurable.Glenn Elliott15 years
wip-dissipation-jericksoUpdate from 2.6.36 to 2.6.36.4Jeremy Erickson11 years
wip-dissipation2-jericksoUpdate 2.6.36 to 2.6.36.4Jeremy Erickson11 years
wip-ecrts14-pgmMerge branch 'wip-ecrts14-pgm' of ssh://rtsrv.cs.unc.edu/home/litmus/litmus-r...Glenn Elliott12 years
wip-edf-hsblast tested versionJonathan Herman14 years
wip-edf-osLookup table EDF-osJeremy Erickson12 years
wip-edf-tie-breakMerge branch 'wip-edf-tie-break' of ssh://rtsrv.cs.unc.edu/home/litmus/litmus...Glenn Elliott13 years
wip-edzl-critiqueUse hr_timer's active checks instead of having own flag.Glenn Elliott15 years
wip-edzl-finalImplementation of the EDZL scheduler.Glenn Elliott15 years
wip-edzl-revisedClean up comments.Glenn Elliott15 years
wip-eventsAdded support for tracing arbitrary actions.Jonathan Herman15 years
wip-extra-debugDBG: add additional tracingBjoern B. Brandenburg15 years
wip-fix-switch-jericksoAttempt to fix race condition with plugin switchingJeremy Erickson15 years
wip-fix3sched: show length of runqueue clock deactivation in /proc/sched_debugBjoern B. Brandenburg15 years
wip-fmlp-dequeueImprove FMLP queue management.Glenn Elliott14 years
wip-ft-irq-flagFeather-Trace: keep track of interrupt-related interference.Bjoern B. Brandenburg14 years
wip-gpu-cleanupEnable sched_trace log injection from userspaceGlenn Elliott13 years
wip-gpu-interruptsRemove option for threading of all softirqs.Glenn Elliott14 years
wip-gpu-rtas12Generalized GPU cost predictors + EWMA. (untested)Glenn Elliott13 years
wip-gpu-rtss12Final GPUSync implementation.Glenn Elliott13 years
wip-gpu-rtss12-srpexperimental changes to support GPUs under SRPGlenn Elliott13 years
wip-gpusync-mergeCleanup priority tracking for budget enforcement.Glenn Elliott12 years
wip-ikglpMove RSM and IKGLP imp. to own .c filesGlenn Elliott13 years
wip-k-fmlpMerge branch 'mpi-master' into wip-k-fmlpGlenn Elliott14 years
wip-kernel-coloringAdded recolor syscallNamhoon Kim7 years
wip-kernthreadsKludge work-queue processing into klitirqd.Glenn Elliott15 years
wip-klmirqd-to-auxAllow klmirqd threads to be given names.Glenn Elliott13 years
wip-ksharkMerge branch 'mpi-staging' into wip-ksharkJonathan Herman13 years
wip-litmus-3.2Merge commit 'v3.2' into litmus-stagingAndrea Bastoni13 years
wip-litmus2011.2Cleanup: Coding conformance for affinity stuff.Glenn Elliott14 years
wip-litmus3.0-2011.2Feather-Trace: keep track of interrupt-related interference.Bjoern B. Brandenburg14 years
wip-master-2.6.33-rtAvoid deadlock when switching task policy to BACKGROUND (ugly)Andrea Bastoni15 years
wip-mcRemoved ARM-specific hacks which disabled less common mixed-criticality featu...Jonathan Herman12 years
wip-mc-bipasaMC-EDF addedbipasa chattopadhyay13 years
wip-mc-jericksoSplit C/D queuesJeremy Erickson15 years
wip-mc2-cache-slackManually patched mc^2 related codeMing Yang10 years
wip-mcrit-maccosmeticMac Mollison15 years
wip-merge-3.0Prevent Linux to send IPI and queue tasks on remote CPUs.Andrea Bastoni14 years
wip-merge-v3.0Prevent Linux to send IPI and queue tasks on remote CPUs.Andrea Bastoni14 years
wip-migration-affinityNULL affinity dereference in C-EDF.Glenn Elliott14 years
wip-mmap-uncacheshare branch with othersGlenn Elliott13 years
wip-modechangeRTSS 2017 submissionNamhoon Kim8 years
wip-nested-lockingAppears to be working.Bryan Ward12 years
wip-omlp-gedfFirst implementation of G-OMLP.Glenn Elliott15 years
wip-paiSome cleanup of PAIGlenn Elliott14 years
wip-percore-lib9/21/2016Namhoon Kim9 years
wip-performanceCONFIG_DONT_PREEMPT_ON_TIE: Don't preeempt a scheduled task on priority tie.Glenn Elliott14 years
wip-pgmAdd PGM support to C-FLGlenn Elliott12 years
wip-pgm-splitFirst draft of C-FL-splitNamhoon Kim12 years
wip-pm-ovdAdd preemption-and-migration overhead tracing supportAndrea Bastoni15 years
wip-prio-inhP-EDF updated to use the generic pi framework.Glenn Elliott15 years
wip-prioq-dglBUG FIX: Support DGLs with PRIOQ_MUTEXGlenn Elliott13 years
wip-refactored-gedfGeneralizd architecture for GEDF-style scheduelrs to reduce code redundancy.Glenn Elliott15 years
wip-release-master-fixbugfix: release master CPU must signal task was pickedBjoern B. Brandenburg14 years
wip-robust-tie-breakEDF priority tie-breaks.Glenn Elliott13 years
wip-rt-ksharkMove task time accounting into the complete_job method.Jonathan Herman13 years
wip-rtas12-pgmScheduling of PGM jobs.Glenn Elliott13 years
wip-semi-partFix compile error with newer GCCJeremy Erickson12 years
wip-semi-part-edfos-jericksoUse initial CPU set by clientJeremy Erickson12 years
wip-shared-libTODO: Fix condition checks in replicate_page_move_mapping()Namhoon Kim9 years
wip-shared-lib2RTAS 2017 Submission ver.Namhoon Kim9 years
wip-shared-memInitial commit for shared libraryNamhoon Kim9 years
wip-splitting-jericksoFix release behaviorJeremy Erickson13 years
wip-splitting-omlp-jericksoBjoern's Dissertation Code with Priority DonationJeremy Erickson13 years
wip-stage-binheapAn efficient binary heap implementation.Glenn Elliott13 years
wip-sun-portDynamic memory allocation and clean exit for FeatherTraceChristopher Kenna15 years
wip-timer-tracebugfix: C-EDF, clear scheduled field of the correct CPU upon task_exitAndrea Bastoni15 years
wip-tracepointsAdd kernel-style events for sched_trace_XXX() functionsAndrea Bastoni14 years
 
TagDownloadAuthorAge
2015.1commit 8e51b37822...Bjoern Brandenburg10 years
2013.1commit bcaacec1ca...Glenn Elliott12 years
2012.3commit c158b5fbe4...Jonathan Herman13 years
2012.2commit b53c479a0f...Glenn Elliott13 years
2012.1commit 83b11ea1c6...Bjoern B. Brandenburg14 years
rtas12-mc-beta-expcommit 8e236ee20f...Christopher Kenna14 years
2011.1commit d11808b5c6...Christopher Kenna15 years
v2.6.37-rc4commit e8a7e48bb2...Linus Torvalds15 years
v2.6.37-rc3commit 3561d43fd2...Linus Torvalds15 years
v2.6.37-rc2commit e53beacd23...Linus Torvalds15 years
v2.6.37-rc1commit c8ddb2713c...Linus Torvalds15 years
v2.6.36commit f6f94e2ab1...Linus Torvalds15 years
2010.2commit 5c5456402d...Bjoern B. Brandenburg15 years
v2.6.36-rc8commit cd07202cc8...Linus Torvalds15 years
v2.6.36-rc7commit cb655d0f3d...Linus Torvalds15 years
v2.6.36-rc6commit 899611ee7d...Linus Torvalds15 years
v2.6.36-rc5commit b30a3f6257...Linus Torvalds15 years
v2.6.36-rc4commit 49553c2ef8...Linus Torvalds15 years
v2.6.36-rc3commit 2bfc96a127...Linus Torvalds15 years
v2.6.36-rc2commit 76be97c1fc...Linus Torvalds15 years
v2.6.36-rc1commit da5cabf80e...Linus Torvalds15 years
v2.6.35commit 9fe6206f40...Linus Torvalds15 years
v2.6.35-rc6commit b37fa16e78...Linus Torvalds15 years
v2.6.35-rc5commit 1c5474a65b...Linus Torvalds15 years
v2.6.35-rc4commit 815c4163b6...Linus Torvalds15 years
v2.6.35-rc3commit 7e27d6e778...Linus Torvalds15 years
v2.6.35-rc2commit e44a21b726...Linus Torvalds15 years
v2.6.35-rc1commit 67a3e12b05...Linus Torvalds15 years
2010.1commit 7c1ff4c544...Andrea Bastoni15 years
v2.6.34commit e40152ee1e...Linus Torvalds15 years
v2.6.33.4commit 4640b4e7d9...Greg Kroah-Hartman15 years
v2.6.34-rc7commit b57f95a382...Linus Torvalds15 years
v2.6.34-rc6commit 66f41d4c5c...Linus Torvalds15 years
v2.6.33.3commit 3e7ad8ed97...Greg Kroah-Hartman15 years
v2.6.34-rc5commit 01bf0b6457...Linus Torvalds15 years
v2.6.34-rc4commit 0d0fb0f9c5...Linus Torvalds15 years
v2.6.33.2commit 19f00f070c...Greg Kroah-Hartman15 years
v2.6.34-rc3commit 2eaa9cfdf3...Linus Torvalds15 years
v2.6.34-rc2commit 220bf991b0...Linus Torvalds16 years
v2.6.33.1commit dbdafe5ccf...Greg Kroah-Hartman16 years
v2.6.34-rc1commit 57d54889cd...Linus Torvalds16 years
v2.6.33commit 60b341b778...Linus Torvalds16 years
v2.6.33-rc8commit 724e6d3fe8...Linus Torvalds16 years
v2.6.33-rc7commit 29275254ca...Linus Torvalds16 years
v2.6.33-rc6commit abe94c756c...Linus Torvalds16 years
v2.6.33-rc5commit 92dcffb916...Linus Torvalds16 years
v2.6.33-rc4commit 7284ce6c9f...Linus Torvalds16 years
v2.6.33-rc3commit 74d2e4f8d7...Linus Torvalds16 years
v2.6.33-rc2commit 6b7b284958...Linus Torvalds16 years
v2.6.33-rc1commit 55639353a0...Linus Torvalds16 years
v2.6.32commit 22763c5cf3...Linus Torvalds16 years
v2.6.32-rc8commit 648f4e3e50...Linus Torvalds16 years
v2.6.32-rc7commit 156171c71a...Linus Torvalds16 years
v2.6.32-rc6commit b419148e56...Linus Torvalds16 years
v2.6.32-rc5commit 012abeea66...Linus Torvalds16 years
v2.6.32-rc4commit 161291396e...Linus Torvalds16 years
v2.6.32-rc3commit 374576a8b6...Linus Torvalds16 years
v2.6.32-rc1commit 17d857be64...Linus Torvalds16 years
v2.6.32-rc2commit 17d857be64...Linus Torvalds16 years
v2.6.31commit 74fca6a428...Linus Torvalds16 years
v2.6.31-rc9commit e07cccf404...Linus Torvalds16 years
v2.6.31-rc8commit 326ba5010a...Linus Torvalds16 years
v2.6.31-rc7commit 422bef879e...Linus Torvalds16 years
v2.6.31-rc6commit 64f1607ffb...Linus Torvalds16 years
v2.6.31-rc5commit ed680c4ad4...Linus Torvalds16 years
v2.6.31-rc4commit 4be3bd7849...Linus Torvalds16 years
v2.6.31-rc3commit 6847e154e3...Linus Torvalds16 years
v2.6.31-rc2commit 8e4a718ff3...Linus Torvalds16 years
v2.6.31-rc1commit 28d0325ce6...Linus Torvalds16 years
v2.6.30commit 07a2039b8e...Linus Torvalds16 years
v2.6.30-rc8commit 9fa7eb283c...Linus Torvalds16 years
v2.6.30-rc7commit 59a3759d0f...Linus Torvalds16 years
v2.6.30-rc6commit 1406de8e11...Linus Torvalds16 years
v2.6.30-rc5commit 091bf7624d...Linus Torvalds16 years
v2.6.30-rc4commit 091438dd56...Linus Torvalds16 years
v2.6.30-rc3commit 0910697403...Linus Torvalds16 years
v2.6.30-rc2commit 0882e8dd3a...Linus Torvalds16 years
v2.6.30-rc1commit 577c9c456f...Linus Torvalds16 years
v2.6.29commit 8e0ee43bc2...Linus Torvalds16 years
v2.6.29-rc8commit 041b62374c...Linus Torvalds17 years
v2.6.29-rc7commit fec6c6fec3...Linus Torvalds17 years
v2.6.29-rc6commit 20f4d6c3a2...Linus Torvalds17 years
v2.6.29-rc5commit d2f8d7ee1a...Linus Torvalds17 years
v2.6.29-rc4commit 8e4921515c...Linus Torvalds17 years
v2.6.29-rc3commit 18e352e4a7...Linus Torvalds17 years
v2.6.29-rc2commit 1de9e8e70f...Linus Torvalds17 years
v2.6.29-rc1commit c59765042f...Linus Torvalds17 years
v2.6.28commit 4a6908a3a0...Linus Torvalds17 years
v2.6.28-rc9commit 929096fe9f...Linus Torvalds17 years
v2.6.28-rc8commit 8b1fae4e42...Linus Torvalds17 years
v2.6.28-rc7commit 061e41fdb5...Linus Torvalds17 years
v2.6.28-rc6commit 13d428afc0...Linus Torvalds17 years
v2.6.28-rc5commit 9bf1a2445f...Linus Torvalds17 years
v2.6.28-rc4commit f7160c7573...Linus Torvalds17 years
v2.6.28-rc3commit 45beca08dd...Linus Torvalds17 years
v2.6.28-rc2commit 0173a3265b...Linus Torvalds17 years
v2.6.28-rc1commit 57f8f7b60d...Linus Torvalds17 years
v2.6.27commit 3fa8749e58...Linus Torvalds17 years
v2.6.27-rc9commit 4330ed8ed4...Linus Torvalds17 years
v2.6.27-rc8commit 94aca1dac6...Linus Torvalds17 years
v2.6.27-rc7commit 72d31053f6...Linus Torvalds17 years
v2.6.27-rc6commit adee14b2e1...Linus Torvalds17 years
v2.6.27-rc5commit 24342c34a0...Linus Torvalds17 years
v2.6.27-rc4commit 6a55617ed5...Linus Torvalds17 years
v2.6.27-rc3commit 30a2f3c60a...Linus Torvalds17 years
v2.6.27-rc2commit 0967d61ea0...Linus Torvalds17 years
v2.6.27-rc1commit 6e86841d05...Linus Torvalds17 years
v2.6.26commit bce7f793da...Linus Torvalds17 years
v2.6.26-rc9commit b7279469d6...Linus Torvalds17 years
v2.6.26-rc8commit 543cf4cb3f...Linus Torvalds17 years
v2.6.26-rc7commit d70ac829b7...Linus Torvalds17 years
v2.6.26-rc6commit 5dd34572ad...Linus Torvalds17 years
v2.6.26-rc5commit 53c8ba9540...Linus Torvalds17 years
v2.6.26-rc4commit e490517a03...Linus Torvalds17 years
v2.6.26-rc3commit b8291ad07a...Linus Torvalds17 years
v2.6.26-rc2commit 492c2e476e...Linus Torvalds17 years
v2.6.26-rc1commit 2ddcca36c8...Linus Torvalds17 years
v2.6.25commit 4b119e21d0...Linus Torvalds17 years
v2.6.25-rc9commit 120dd64cac...Linus Torvalds17 years
v2.6.25-rc8commit 0e81a8ae37...Linus Torvalds17 years
v2.6.25-rc7commit 05dda977f2...Linus Torvalds17 years
v2.6.25-rc6commit a978b30af3...Linus Torvalds18 years
v2.6.25-rc5commit cdeeeae056...Linus Torvalds18 years
v2.6.25-rc4commit 29e8c3c304...Linus Torvalds18 years
v2.6.25-rc3commit bfa274e243...Linus Torvalds18 years
v2.6.25-rc2commit 101142c37b...Linus Torvalds18 years
v2.6.25-rc1commit 19af35546d...Linus Torvalds18 years
v2.6.24commit 49914084e7...Linus Torvalds18 years
v2.6.24-rc8commit cbd9c88369...Linus Torvalds18 years
v2.6.24-rc7commit 3ce5445046...Linus Torvalds18 years
v2.6.24-rc6commit ea67db4cdb...Linus Torvalds18 years
v2.6.24-rc5commit 82d29bf6dc...Linus Torvalds18 years
v2.6.24-rc4commit 09b56adc98...Linus Torvalds18 years
v2.6.24-rc3commit d9f8bcbf67...Linus Torvalds18 years
v2.6.24-rc2commit dbeeb816e8...Linus Torvalds18 years
v2.6.24-rc1commit c9927c2bf4...Linus Torvalds18 years
v2.6.23commit bbf25010f1...Linus Torvalds18 years
v2.6.23-rc9commit 3146b39c18...Linus Torvalds18 years
v2.6.23-rc8commit 4942de4a0e...Linus Torvalds18 years
v2.6.23-rc7commit 81cfe79b9c...Linus Torvalds18 years
v2.6.23-rc6commit 0d4cbb5e7f...Linus Torvalds18 years
v2.6.23-rc5commit 40ffbfad6b...Linus Torvalds18 years
v2.6.23-rc4commit b07d68b5ca...Linus Torvalds18 years
v2.6.23-rc3commit 39d3520c92...Linus Torvalds18 years
v2.6.23-rc2commit d4ac2477fa...Linus Torvalds18 years
v2.6.23-rc1commit f695baf2df...Linus Torvalds18 years
v2.6.22commit 7dcca30a32...Linus Torvalds18 years
v2.6.22-rc7commit a38d6181ff...Linus Torvalds18 years
v2.6.22-rc6commit 189548642c...Linus Torvalds18 years
v2.6.22-rc5commit 188e1f81ba...Linus Torvalds18 years
v2.6.22-rc4commit 5ecd3100e6...Linus Torvalds18 years
v2.6.22-rc3commit c420bc9f09...Linus Torvalds18 years
v2.6.22-rc2commit 55b637c6a0...Linus Torvalds18 years
v2.6.22-rc1commit 39403865d2...Linus Torvalds18 years
v2.6.21commit de46c33745...Linus Torvalds18 years
v2.6.21-rc7commit 94a05509a9...Linus Torvalds18 years
v2.6.21-rc6commit a21bd69e15...Linus Torvalds18 years
v2.6.21-rc5commit e0f2e3a06b...Linus Torvalds18 years
v2.6.21-rc4commit db98e0b434...Linus Torvalds19 years
v2.6.21-rc3commit 08e15e81a4...Linus Torvalds19 years
v2.6.21-rc2commit 606135a308...Linus Torvalds19 years
v2.6.21-rc1commit c8f71b01a5...Linus Torvalds19 years
v2.6.20commit 62d0cfcb27...Linus Torvalds19 years
v2.6.20-rc7commit f56df2f4db...Linus Torvalds19 years
v2.6.20-rc6commit 99abfeafb5...Linus Torvalds19 years
v2.6.20-rc5commit a8b3485287...Linus Torvalds19 years
v2.6.20-rc4commit bf81b46482...Linus Torvalds19 years
v2.6.20-rc3commit 669df1b478...Linus Torvalds19 years
v2.6.20-rc2commit 3bf8ba38f3...Linus Torvalds19 years
v2.6.20-rc1commit cc016448b0...Linus Torvalds19 years
v2.6.19commit 0215ffb08c...Linus Torvalds19 years
v2.6.19-rc6commit 44597f65f6...Linus Torvalds19 years
v2.6.19-rc5commit 80c2188127...Linus Torvalds19 years
v2.6.19-rc4commit ae99a78af3...Linus Torvalds19 years
v2.6.19-rc3commit 7059abedd2...Linus Torvalds19 years
v2.6.19-rc2commit b4bd8c6643...Linus Torvalds19 years
v2.6.19-rc1commit d223a60106...Linus Torvalds19 years
v2.6.18commit e478bec0ba...Linus Torvalds19 years
v2.6.18-rc7commit 95064a75eb...Linus Torvalds19 years
v2.6.18-rc6commit c336923b66...Linus Torvalds19 years
v2.6.18-rc5commit 60d4684068...Linus Torvalds19 years
v2.6.18-rc4commit 9f737633e6...Linus Torvalds19 years
v2.6.18-rc3commit b6ff50833a...Linus Torvalds19 years
v2.6.18-rc2commit 82d6897fef...Linus Torvalds19 years
v2.6.18-rc1commit 120bda20c6...Linus Torvalds19 years
v2.6.17commit 427abfa28a...Linus Torvalds19 years
v2.6.17-rc6commit 1def630a6a...Linus Torvalds19 years
v2.6.17-rc5commit a8bd60705a...Linus Torvalds19 years
v2.6.17-rc4commit d8c3291c73...Linus Torvalds19 years
v2.6.17-rc3commit 2be4d50295...Linus Torvalds19 years
v2.6.17-rc2commit 8bbde0e6d5...Linus Torvalds19 years
v2.6.17-rc1commit 6246b6128b...Linus Torvalds19 years
v2.6.16commit 7705a8792b...Linus Torvalds20 years
v2.6.16-rc6commit 535744878e...Linus Torvalds20 years
v2.6.16-rc5commit b9a33cebac...Linus Torvalds20 years
v2.6.16-rc4commit bd71c2b174...Linus Torvalds20 years
v2.6.16-rc3commit e9bb4c9929...Linus Torvalds20 years
v2.6.16-rc2commit 826eeb53a6...Linus Torvalds20 years
v2.6.16-rc1commit 2664b25051...Linus Torvalds20 years
v2.6.15commit 88026842b0...Linus Torvalds20 years
v2.6.15-rc7commit f89f5948fc...Linus Torvalds20 years
v2.6.15-rc6commit df7addbb45...Linus Torvalds20 years
v2.6.15-rc5commit 436b0f76f2...Linus Torvalds20 years
v2.6.15-rc4commit 5666c0947e...Linus Torvalds20 years
v2.6.15-rc3commit 624f54be20...Linus Torvalds20 years
v2.6.15-rc2commit 3bedff1d73...Linus Torvalds20 years
v2.6.15-rc1commit cd52d1ee9a...Linus Torvalds20 years
v2.6.14commit 741b2252a5...Linus Torvalds20 years
v2.6.14-rc5commit 93918e9afc...Linus Torvalds20 years
v2.6.14-rc4commit 907a426179...Linus Torvalds20 years
v2.6.14-rc3commit 1c9426e8a5...Linus Torvalds20 years
v2.6.14-rc2commit 676d55ae30...Linus Torvalds20 years
v2.6.14-rc1commit 2f4ba45a75...Linus Torvalds20 years
v2.6.13commit 02b3e4e2d7...Linus Torvalds20 years
v2.6.13-rc7commit 0572e3da3f...Linus Torvalds20 years
v2.6.13-rc6commit 6fc32179de...Linus Torvalds20 years
v2.6.13-rc5commit 9a351e30d7...Linus Torvalds20 years
v2.6.13-rc4commit 6395352334...Linus Torvalds20 years
v2.6.11tree c39ae07f39...
v2.6.11-treetree c39ae07f39...
v2.6.12commit 9ee1c939d1...
v2.6.12-rc2commit 1da177e4c3...
v2.6.12-rc3commit a2755a80f4...
v2.6.12-rc4commit 88d7bd8cb9...
v2.6.12-rc5commit 2a24ab628a...
v2.6.12-rc6commit 7cef5677ef...
v2.6.13-rc1commit 4c91aedb75...
v2.6.13-rc2commit a18bcb7450...
v2.6.13-rc3commit c32511e271...
80'>3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243
/*
 *          mxser.c  -- MOXA Smartio/Industio family multiport serial driver.
 *
 *      Copyright (C) 1999-2001  Moxa Technologies (support@moxa.com.tw).
 *
 *      This code is loosely based on the Linux serial driver, written by
 *      Linus Torvalds, Theodore T'so and others.
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *	Original release	10/26/00
 *
 *	02/06/01	Support MOXA Industio family boards.
 *	02/06/01	Support TIOCGICOUNT.
 *	02/06/01	Fix the problem for connecting to serial mouse.
 *	02/06/01	Fix the problem for H/W flow control.
 *	02/06/01	Fix the compling warning when CONFIG_PCI
 *			don't be defined.
 *
 *	Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox
 *	<alan@redhat.com>. The original 1.8 code is available on www.moxa.com.
 *	- Fixed x86_64 cleanness
 *	- Fixed sleep with spinlock held in mxser_send_break
 */


#include <linux/module.h>
#include <linux/autoconf.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/gfp.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/pci.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>

#include "mxser.h"

#define	MXSER_VERSION	"1.8"
#define	MXSERMAJOR	 174
#define	MXSERCUMAJOR	 175

#define	MXSER_EVENT_TXLOW	1
#define	MXSER_EVENT_HANGUP	2

#define MXSER_BOARDS		4	/* Max. boards */
#define MXSER_PORTS		32	/* Max. ports */
#define MXSER_PORTS_PER_BOARD	8	/* Max. ports per board */
#define MXSER_ISR_PASS_LIMIT	256

#define	MXSER_ERR_IOADDR	-1
#define	MXSER_ERR_IRQ		-2
#define	MXSER_ERR_IRQ_CONFLIT	-3
#define	MXSER_ERR_VECTOR	-4

#define SERIAL_TYPE_NORMAL	1
#define SERIAL_TYPE_CALLOUT	2

#define WAKEUP_CHARS		256

#define UART_MCR_AFE		0x20
#define UART_LSR_SPECIAL	0x1E

#define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|\
					  IXON|IXOFF))

#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? IRQF_SHARED : IRQF_DISABLED)

#define C168_ASIC_ID    1
#define C104_ASIC_ID    2
#define C102_ASIC_ID	0xB
#define CI132_ASIC_ID	4
#define CI134_ASIC_ID	3
#define CI104J_ASIC_ID  5

enum {
	MXSER_BOARD_C168_ISA = 1,
	MXSER_BOARD_C104_ISA,
	MXSER_BOARD_CI104J,
	MXSER_BOARD_C168_PCI,
	MXSER_BOARD_C104_PCI,
	MXSER_BOARD_C102_ISA,
	MXSER_BOARD_CI132,
	MXSER_BOARD_CI134,
	MXSER_BOARD_CP132,
	MXSER_BOARD_CP114,
	MXSER_BOARD_CT114,
	MXSER_BOARD_CP102,
	MXSER_BOARD_CP104U,
	MXSER_BOARD_CP168U,
	MXSER_BOARD_CP132U,
	MXSER_BOARD_CP134U,
	MXSER_BOARD_CP104JU,
	MXSER_BOARD_RC7000,
	MXSER_BOARD_CP118U,
	MXSER_BOARD_CP102UL,
	MXSER_BOARD_CP102U,
};

static char *mxser_brdname[] = {
	"C168 series",
	"C104 series",
	"CI-104J series",
	"C168H/PCI series",
	"C104H/PCI series",
	"C102 series",
	"CI-132 series",
	"CI-134 series",
	"CP-132 series",
	"CP-114 series",
	"CT-114 series",
	"CP-102 series",
	"CP-104U series",
	"CP-168U series",
	"CP-132U series",
	"CP-134U series",
	"CP-104JU series",
	"Moxa UC7000 Serial",
	"CP-118U series",
	"CP-102UL series",
	"CP-102U series",
};

static int mxser_numports[] = {
	8,			/* C168-ISA */
	4,			/* C104-ISA */
	4,			/* CI104J */
	8,			/* C168-PCI */
	4,			/* C104-PCI */
	2,			/* C102-ISA */
	2,			/* CI132 */
	4,			/* CI134 */
	2,			/* CP132 */
	4,			/* CP114 */
	4,			/* CT114 */
	2,			/* CP102 */
	4,			/* CP104U */
	8,			/* CP168U */
	2,			/* CP132U */
	4,			/* CP134U */
	4,			/* CP104JU */
	8,			/* RC7000 */
	8,			/* CP118U */
	2,			/* CP102UL */
	2,			/* CP102U */
};

#define UART_TYPE_NUM	2

static const unsigned int Gmoxa_uart_id[UART_TYPE_NUM] = {
	MOXA_MUST_MU150_HWID,
	MOXA_MUST_MU860_HWID
};

/* This is only for PCI */
#define UART_INFO_NUM	3
struct mxpciuart_info {
	int type;
	int tx_fifo;
	int rx_fifo;
	int xmit_fifo_size;
	int rx_high_water;
	int rx_trigger;
	int rx_low_water;
	long max_baud;
};

static const struct mxpciuart_info Gpci_uart_info[UART_INFO_NUM] = {
	{MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L},
	{MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L},
	{MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L}
};


#ifdef CONFIG_PCI

static struct pci_device_id mxser_pcibrds[] = {
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C168_PCI},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C104_PCI},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP114},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CT114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CT114},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104U},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP168U},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132U},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP134U},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104JU, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104JU},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_RC7000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_RC7000},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP118U},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102UL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102UL},
	{PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102U},
	{0}
};

MODULE_DEVICE_TABLE(pci, mxser_pcibrds);


#endif

typedef struct _moxa_pci_info {
	unsigned short busNum;
	unsigned short devNum;
	struct pci_dev *pdev;	/* add by Victor Yu. 06-23-2003 */
} moxa_pci_info;

static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 };
static int ttymajor = MXSERMAJOR;
static int calloutmajor = MXSERCUMAJOR;
static int verbose = 0;

/* Variables for insmod */

MODULE_AUTHOR("Casper Yang");
MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver");
module_param_array(ioaddr, int, NULL, 0);
module_param(ttymajor, int, 0);
module_param(calloutmajor, int, 0);
module_param(verbose, bool, 0);
MODULE_LICENSE("GPL");

struct mxser_log {
	int tick;
	unsigned long rxcnt[MXSER_PORTS];
	unsigned long txcnt[MXSER_PORTS];
};


struct mxser_mon {
	unsigned long rxcnt;
	unsigned long txcnt;
	unsigned long up_rxcnt;
	unsigned long up_txcnt;
	int modem_status;
	unsigned char hold_reason;
};

struct mxser_mon_ext {
	unsigned long rx_cnt[32];
	unsigned long tx_cnt[32];
	unsigned long up_rxcnt[32];
	unsigned long up_txcnt[32];
	int modem_status[32];

	long baudrate[32];
	int databits[32];
	int stopbits[32];
	int parity[32];
	int flowctrl[32];
	int fifo[32];
	int iftype[32];
};

struct mxser_hwconf {
	int board_type;
	int ports;
	int irq;
	int vector;
	int vector_mask;
	int uart_type;
	int ioaddr[MXSER_PORTS_PER_BOARD];
	int baud_base[MXSER_PORTS_PER_BOARD];
	moxa_pci_info pciInfo;
	int IsMoxaMustChipFlag;	/* add by Victor Yu. 08-30-2002 */
	int MaxCanSetBaudRate[MXSER_PORTS_PER_BOARD];	/* add by Victor Yu. 09-04-2002 */
	int opmode_ioaddr[MXSER_PORTS_PER_BOARD];	/* add by Victor Yu. 01-05-2004 */
};

struct mxser_struct {
	int port;
	int base;		/* port base address */
	int irq;		/* port using irq no. */
	int vector;		/* port irq vector */
	int vectormask;		/* port vector mask */
	int rx_high_water;
	int rx_trigger;		/* Rx fifo trigger level */
	int rx_low_water;
	int baud_base;		/* max. speed */
	int flags;		/* defined in tty.h */
	int type;		/* UART type */
	struct tty_struct *tty;
	int read_status_mask;
	int ignore_status_mask;
	int xmit_fifo_size;
	int custom_divisor;
	int x_char;		/* xon/xoff character */
	int close_delay;
	unsigned short closing_wait;
	int IER;		/* Interrupt Enable Register */
	int MCR;		/* Modem control register */
	unsigned long event;
	int count;		/* # of fd on device */
	int blocked_open;	/* # of blocked opens */
	long session;		/* Session of opening process */
	long pgrp;		/* pgrp of opening process */
	unsigned char *xmit_buf;
	int xmit_head;
	int xmit_tail;
	int xmit_cnt;
	struct work_struct tqueue;
	struct termios normal_termios;
	struct termios callout_termios;
	wait_queue_head_t open_wait;
	wait_queue_head_t close_wait;
	wait_queue_head_t delta_msr_wait;
	struct async_icount icount;	/* kernel counters for the 4 input interrupts */
	int timeout;
	int IsMoxaMustChipFlag;	/* add by Victor Yu. 08-30-2002 */
	int MaxCanSetBaudRate;	/* add by Victor Yu. 09-04-2002 */
	int opmode_ioaddr;	/* add by Victor Yu. 01-05-2004 */
	unsigned char stop_rx;
	unsigned char ldisc_stop_rx;
	long realbaud;
	struct mxser_mon mon_data;
	unsigned char err_shadow;
	spinlock_t slock;
};

struct mxser_mstatus {
	tcflag_t cflag;
	int cts;
	int dsr;
	int ri;
	int dcd;
};

static struct mxser_mstatus GMStatus[MXSER_PORTS];

static int mxserBoardCAP[MXSER_BOARDS] = {
	0, 0, 0, 0
	/*  0x180, 0x280, 0x200, 0x320 */
};

static struct tty_driver *mxvar_sdriver;
static struct mxser_struct mxvar_table[MXSER_PORTS];
static struct tty_struct *mxvar_tty[MXSER_PORTS + 1];
static struct termios *mxvar_termios[MXSER_PORTS + 1];
static struct termios *mxvar_termios_locked[MXSER_PORTS + 1];
static struct mxser_log mxvar_log;
static int mxvar_diagflag;
static unsigned char mxser_msr[MXSER_PORTS + 1];
static struct mxser_mon_ext mon_data_ext;
static int mxser_set_baud_method[MXSER_PORTS + 1];
static spinlock_t gm_lock;

/*
 * This is used to figure out the divisor speeds and the timeouts
 */

static struct mxser_hwconf mxsercfg[MXSER_BOARDS];

/*
 * static functions:
 */

static void mxser_getcfg(int board, struct mxser_hwconf *hwconf);
static int mxser_init(void);

/* static void   mxser_poll(unsigned long); */
static int mxser_get_ISA_conf(int, struct mxser_hwconf *);
static int mxser_get_PCI_conf(int, int, int, struct mxser_hwconf *);
static void mxser_do_softint(void *);
static int mxser_open(struct tty_struct *, struct file *);
static void mxser_close(struct tty_struct *, struct file *);
static int mxser_write(struct tty_struct *, const unsigned char *, int);
static int mxser_write_room(struct tty_struct *);
static void mxser_flush_buffer(struct tty_struct *);
static int mxser_chars_in_buffer(struct tty_struct *);
static void mxser_flush_chars(struct tty_struct *);
static void mxser_put_char(struct tty_struct *, unsigned char);
static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong);
static int mxser_ioctl_special(unsigned int, void __user *);
static void mxser_throttle(struct tty_struct *);
static void mxser_unthrottle(struct tty_struct *);
static void mxser_set_termios(struct tty_struct *, struct termios *);
static void mxser_stop(struct tty_struct *);
static void mxser_start(struct tty_struct *);
static void mxser_hangup(struct tty_struct *);
static void mxser_rs_break(struct tty_struct *, int);
static irqreturn_t mxser_interrupt(int, void *, struct pt_regs *);
static void mxser_receive_chars(struct mxser_struct *, int *);
static void mxser_transmit_chars(struct mxser_struct *);
static void mxser_check_modem_status(struct mxser_struct *, int);
static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *);
static int mxser_startup(struct mxser_struct *);
static void mxser_shutdown(struct mxser_struct *);
static int mxser_change_speed(struct mxser_struct *, struct termios *old_termios);
static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct __user *);
static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct __user *);
static int mxser_get_lsr_info(struct mxser_struct *, unsigned int __user *);
static void mxser_send_break(struct mxser_struct *, int);
static int mxser_tiocmget(struct tty_struct *, struct file *);
static int mxser_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int);
static int mxser_set_baud(struct mxser_struct *info, long newspd);
static void mxser_wait_until_sent(struct tty_struct *tty, int timeout);

static void mxser_startrx(struct tty_struct *tty);
static void mxser_stoprx(struct tty_struct *tty);


static int CheckIsMoxaMust(int io)
{
	u8 oldmcr, hwid;
	int i;

	outb(0, io + UART_LCR);
	DISABLE_MOXA_MUST_ENCHANCE_MODE(io);
	oldmcr = inb(io + UART_MCR);
	outb(0, io + UART_MCR);
	SET_MOXA_MUST_XON1_VALUE(io, 0x11);
	if ((hwid = inb(io + UART_MCR)) != 0) {
		outb(oldmcr, io + UART_MCR);
		return MOXA_OTHER_UART;
	}

	GET_MOXA_MUST_HARDWARE_ID(io, &hwid);
	for (i = 0; i < UART_TYPE_NUM; i++) {
		if (hwid == Gmoxa_uart_id[i])
			return (int)hwid;
	}
	return MOXA_OTHER_UART;
}

/* above is modified by Victor Yu. 08-15-2002 */

static struct tty_operations mxser_ops = {
	.open = mxser_open,
	.close = mxser_close,
	.write = mxser_write,
	.put_char = mxser_put_char,
	.flush_chars = mxser_flush_chars,
	.write_room = mxser_write_room,
	.chars_in_buffer = mxser_chars_in_buffer,
	.flush_buffer = mxser_flush_buffer,
	.ioctl = mxser_ioctl,
	.throttle = mxser_throttle,
	.unthrottle = mxser_unthrottle,
	.set_termios = mxser_set_termios,
	.stop = mxser_stop,
	.start = mxser_start,
	.hangup = mxser_hangup,
	.break_ctl = mxser_rs_break,
	.wait_until_sent = mxser_wait_until_sent,
	.tiocmget = mxser_tiocmget,
	.tiocmset = mxser_tiocmset,
};

/*
 * The MOXA Smartio/Industio serial driver boot-time initialization code!
 */

static int __init mxser_module_init(void)
{
	int ret;

	if (verbose)
		printk(KERN_DEBUG "Loading module mxser ...\n");
	ret = mxser_init();
	if (verbose)
		printk(KERN_DEBUG "Done.\n");
	return ret;
}

static void __exit mxser_module_exit(void)
{
	int i, err;

	if (verbose)
		printk(KERN_DEBUG "Unloading module mxser ...\n");

	err = tty_unregister_driver(mxvar_sdriver);
	if (!err)
		put_tty_driver(mxvar_sdriver);
	else
		printk(KERN_ERR "Couldn't unregister MOXA Smartio/Industio family serial driver\n");

	for (i = 0; i < MXSER_BOARDS; i++) {
		struct pci_dev *pdev;

		if (mxsercfg[i].board_type == -1)
			continue;
		else {
			pdev = mxsercfg[i].pciInfo.pdev;
			free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]);
			if (pdev != NULL) {	/* PCI */
				release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2));
				release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
			} else {
				release_region(mxsercfg[i].ioaddr[0], 8 * mxsercfg[i].ports);
				release_region(mxsercfg[i].vector, 1);
			}
		}
	}
	if (verbose)
		printk(KERN_DEBUG "Done.\n");
}

static void process_txrx_fifo(struct mxser_struct *info)
{
	int i;

	if ((info->type == PORT_16450) || (info->type == PORT_8250)) {
		info->rx_trigger = 1;
		info->rx_high_water = 1;
		info->rx_low_water = 1;
		info->xmit_fifo_size = 1;
	} else {
		for (i = 0; i < UART_INFO_NUM; i++) {
			if (info->IsMoxaMustChipFlag == Gpci_uart_info[i].type) {
				info->rx_trigger = Gpci_uart_info[i].rx_trigger;
				info->rx_low_water = Gpci_uart_info[i].rx_low_water;
				info->rx_high_water = Gpci_uart_info[i].rx_high_water;
				info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size;
				break;
			}
		}
	}
}

static int mxser_initbrd(int board, struct mxser_hwconf *hwconf)
{
	struct mxser_struct *info;
	int retval;
	int i, n;

	n = board * MXSER_PORTS_PER_BOARD;
	info = &mxvar_table[n];
	/*if (verbose) */  {
		printk(KERN_DEBUG "        ttyM%d - ttyM%d ",
			n, n + hwconf->ports - 1);
		printk(" max. baud rate = %d bps.\n",
			hwconf->MaxCanSetBaudRate[0]);
	}

	for (i = 0; i < hwconf->ports; i++, n++, info++) {
		info->port = n;
		info->base = hwconf->ioaddr[i];
		info->irq = hwconf->irq;
		info->vector = hwconf->vector;
		info->vectormask = hwconf->vector_mask;
		info->opmode_ioaddr = hwconf->opmode_ioaddr[i];	/* add by Victor Yu. 01-05-2004 */
		info->stop_rx = 0;
		info->ldisc_stop_rx = 0;

		info->IsMoxaMustChipFlag = hwconf->IsMoxaMustChipFlag;
		/* Enhance mode enabled here */
		if (info->IsMoxaMustChipFlag != MOXA_OTHER_UART) {
			ENABLE_MOXA_MUST_ENCHANCE_MODE(info->base);
		}

		info->flags = ASYNC_SHARE_IRQ;
		info->type = hwconf->uart_type;
		info->baud_base = hwconf->baud_base[i];

		info->MaxCanSetBaudRate = hwconf->MaxCanSetBaudRate[i];

		process_txrx_fifo(info);


		info->custom_divisor = hwconf->baud_base[i] * 16;
		info->close_delay = 5 * HZ / 10;
		info->closing_wait = 30 * HZ;
		INIT_WORK(&info->tqueue, mxser_do_softint, info);
		info->normal_termios = mxvar_sdriver->init_termios;
		init_waitqueue_head(&info->open_wait);
		init_waitqueue_head(&info->close_wait);
		init_waitqueue_head(&info->delta_msr_wait);
		memset(&info->mon_data, 0, sizeof(struct mxser_mon));
		info->err_shadow = 0;
		spin_lock_init(&info->slock);
	}
	/*
	 * Allocate the IRQ if necessary
	 */


	/* before set INT ISR, disable all int */
	for (i = 0; i < hwconf->ports; i++) {
		outb(inb(hwconf->ioaddr[i] + UART_IER) & 0xf0,
			hwconf->ioaddr[i] + UART_IER);
	}

	n = board * MXSER_PORTS_PER_BOARD;
	info = &mxvar_table[n];

	retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info),
				"mxser", info);
	if (retval) {
		printk(KERN_ERR "Board %d: %s",
			board, mxser_brdname[hwconf->board_type - 1]);
		printk("  Request irq failed, IRQ (%d) may conflict with"
			" another device.\n", info->irq);
		return retval;
	}
	return 0;
}

static void mxser_getcfg(int board, struct mxser_hwconf *hwconf)
{
	mxsercfg[board] = *hwconf;
}

#ifdef CONFIG_PCI
static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf)
{
	int i, j;
	/* unsigned int val; */
	unsigned int ioaddress;
	struct pci_dev *pdev = hwconf->pciInfo.pdev;

	/* io address */
	hwconf->board_type = board_type;
	hwconf->ports = mxser_numports[board_type - 1];
	ioaddress = pci_resource_start(pdev, 2);
	request_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2),
			"mxser(IO)");

	for (i = 0; i < hwconf->ports; i++)
		hwconf->ioaddr[i] = ioaddress + 8 * i;

	/* vector */
	ioaddress = pci_resource_start(pdev, 3);
	request_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3),
			"mxser(vector)");
	hwconf->vector = ioaddress;

	/* irq */
	hwconf->irq = hwconf->pciInfo.pdev->irq;

	hwconf->IsMoxaMustChipFlag = CheckIsMoxaMust(hwconf->ioaddr[0]);
	hwconf->uart_type = PORT_16550A;
	hwconf->vector_mask = 0;


	for (i = 0; i < hwconf->ports; i++) {
		for (j = 0; j < UART_INFO_NUM; j++) {
			if (Gpci_uart_info[j].type == hwconf->IsMoxaMustChipFlag) {
				hwconf->MaxCanSetBaudRate[i] = Gpci_uart_info[j].max_baud;

				/* exception....CP-102 */
				if (board_type == MXSER_BOARD_CP102)
					hwconf->MaxCanSetBaudRate[i] = 921600;
				break;
			}
		}
	}

	if (hwconf->IsMoxaMustChipFlag == MOXA_MUST_MU860_HWID) {
		for (i = 0; i < hwconf->ports; i++) {
			if (i < 4)
				hwconf->opmode_ioaddr[i] = ioaddress + 4;
			else
				hwconf->opmode_ioaddr[i] = ioaddress + 0x0c;
		}
		outb(0, ioaddress + 4);	/* default set to RS232 mode */
		outb(0, ioaddress + 0x0c);	/* default set to RS232 mode */
	}

	for (i = 0; i < hwconf->ports; i++) {
		hwconf->vector_mask |= (1 << i);
		hwconf->baud_base[i] = 921600;
	}
	return 0;
}
#endif

static int mxser_init(void)
{
	int i, m, retval, b, n;
	struct pci_dev *pdev = NULL;
	int index;
	unsigned char busnum, devnum;
	struct mxser_hwconf hwconf;

	mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1);
	if (!mxvar_sdriver)
		return -ENOMEM;
	spin_lock_init(&gm_lock);

	for (i = 0; i < MXSER_BOARDS; i++) {
		mxsercfg[i].board_type = -1;
	}

	printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n",
		MXSER_VERSION);

	/* Initialize the tty_driver structure */
	memset(mxvar_sdriver, 0, sizeof(struct tty_driver));
	mxvar_sdriver->magic = TTY_DRIVER_MAGIC;
	mxvar_sdriver->name = "ttyM";
	mxvar_sdriver->major = ttymajor;
	mxvar_sdriver->minor_start = 0;
	mxvar_sdriver->num = MXSER_PORTS + 1;
	mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL;
	mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL;
	mxvar_sdriver->init_termios = tty_std_termios;
	mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
	mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW;
	tty_set_operations(mxvar_sdriver, &mxser_ops);
	mxvar_sdriver->ttys = mxvar_tty;
	mxvar_sdriver->termios = mxvar_termios;
	mxvar_sdriver->termios_locked = mxvar_termios_locked;

	mxvar_diagflag = 0;
	memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct));
	memset(&mxvar_log, 0, sizeof(struct mxser_log));

	memset(&mxser_msr, 0, sizeof(unsigned char) * (MXSER_PORTS + 1));
	memset(&mon_data_ext, 0, sizeof(struct mxser_mon_ext));
	memset(&mxser_set_baud_method, 0, sizeof(int) * (MXSER_PORTS + 1));
	memset(&hwconf, 0, sizeof(struct mxser_hwconf));

	m = 0;
	/* Start finding ISA boards here */
	for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) {
		int cap;

		if (!(cap = mxserBoardCAP[b]))
			continue;

		retval = mxser_get_ISA_conf(cap, &hwconf);

		if (retval != 0)
			printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n",
				mxser_brdname[hwconf.board_type - 1], ioaddr[b]);

		if (retval <= 0) {
			if (retval == MXSER_ERR_IRQ)
				printk(KERN_ERR "Invalid interrupt number, "
					"board not configured\n");
			else if (retval == MXSER_ERR_IRQ_CONFLIT)
				printk(KERN_ERR "Invalid interrupt number, "
					"board not configured\n");
			else if (retval == MXSER_ERR_VECTOR)
				printk(KERN_ERR "Invalid interrupt vector, "
					"board not configured\n");
			else if (retval == MXSER_ERR_IOADDR)
				printk(KERN_ERR "Invalid I/O address, "
					"board not configured\n");

			continue;
		}

		hwconf.pciInfo.busNum = 0;
		hwconf.pciInfo.devNum = 0;
		hwconf.pciInfo.pdev = NULL;

		mxser_getcfg(m, &hwconf);
		/*
		 * init mxsercfg first,
		 * or mxsercfg data is not correct on ISR.
		 */
		/* mxser_initbrd will hook ISR. */
		if (mxser_initbrd(m, &hwconf) < 0)
			continue;

		m++;
	}

	/* Start finding ISA boards from module arg */
	for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) {
		int cap;

		if (!(cap = ioaddr[b]))
			continue;

		retval = mxser_get_ISA_conf(cap, &hwconf);

		if (retval != 0)
			printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n",
				mxser_brdname[hwconf.board_type - 1], ioaddr[b]);

		if (retval <= 0) {
			if (retval == MXSER_ERR_IRQ)
				printk(KERN_ERR "Invalid interrupt number, "
					"board not configured\n");
			else if (retval == MXSER_ERR_IRQ_CONFLIT)
				printk(KERN_ERR "Invalid interrupt number, "
					"board not configured\n");
			else if (retval == MXSER_ERR_VECTOR)
				printk(KERN_ERR "Invalid interrupt vector, "
					"board not configured\n");
			else if (retval == MXSER_ERR_IOADDR)
				printk(KERN_ERR "Invalid I/O address, "
					"board not configured\n");

			continue;
		}

		hwconf.pciInfo.busNum = 0;
		hwconf.pciInfo.devNum = 0;
		hwconf.pciInfo.pdev = NULL;

		mxser_getcfg(m, &hwconf);
		/*
		 * init mxsercfg first,
		 * or mxsercfg data is not correct on ISR.
		 */
		/* mxser_initbrd will hook ISR. */
		if (mxser_initbrd(m, &hwconf) < 0)
			continue;

		m++;
	}

	/* start finding PCI board here */
#ifdef CONFIG_PCI
	n = ARRAY_SIZE(mxser_pcibrds) - 1;
	index = 0;
	b = 0;
	while (b < n) {
		pdev = pci_find_device(mxser_pcibrds[b].vendor,
				mxser_pcibrds[b].device, pdev);
			if (pdev == NULL) {
			b++;
			continue;
		}
		hwconf.pciInfo.busNum = busnum = pdev->bus->number;
		hwconf.pciInfo.devNum = devnum = PCI_SLOT(pdev->devfn) << 3;
		hwconf.pciInfo.pdev = pdev;
		printk(KERN_INFO "Found MOXA %s board(BusNo=%d,DevNo=%d)\n",
			mxser_brdname[(int) (mxser_pcibrds[b].driver_data) - 1],
			busnum, devnum >> 3);
		index++;
		if (m >= MXSER_BOARDS)
			printk(KERN_ERR
				"Too many Smartio/Industio family boards find "
				"(maximum %d), board not configured\n",
				MXSER_BOARDS);
		else {
			if (pci_enable_device(pdev)) {
				printk(KERN_ERR "Moxa SmartI/O PCI enable "
					"fail !\n");
				continue;
			}
			retval = mxser_get_PCI_conf(busnum, devnum,
					(int)mxser_pcibrds[b].driver_data,
					&hwconf);
			if (retval < 0) {
				if (retval == MXSER_ERR_IRQ)
					printk(KERN_ERR
						"Invalid interrupt number, "
						"board not configured\n");
				else if (retval == MXSER_ERR_IRQ_CONFLIT)
					printk(KERN_ERR
						"Invalid interrupt number, "
						"board not configured\n");
				else if (retval == MXSER_ERR_VECTOR)
					printk(KERN_ERR
						"Invalid interrupt vector, "
						"board not configured\n");
				else if (retval == MXSER_ERR_IOADDR)
					printk(KERN_ERR
						"Invalid I/O address, "
						"board not configured\n");
				continue;
			}
			mxser_getcfg(m, &hwconf);
			/* init mxsercfg first,
			 * or mxsercfg data is not correct on ISR.
			 */
			/* mxser_initbrd will hook ISR. */
			if (mxser_initbrd(m, &hwconf) < 0)
				continue;
			m++;
		}
	}
#endif

	retval = tty_register_driver(mxvar_sdriver);
	if (retval) {
		printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family"
				" driver !\n");
		put_tty_driver(mxvar_sdriver);

		for (i = 0; i < MXSER_BOARDS; i++) {
			if (mxsercfg[i].board_type == -1)
				continue;
			else {
				free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]);
				/* todo: release io, vector */
			}
		}
		return retval;
	}

	return 0;
}

static void mxser_do_softint(void *private_)
{
	struct mxser_struct *info = private_;
	struct tty_struct *tty;

	tty = info->tty;

	if (tty) {
		if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event))
			tty_wakeup(tty);
		if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event))
			tty_hangup(tty);
	}
}

static unsigned char mxser_get_msr(int baseaddr, int mode, int port, struct mxser_struct *info)
{
	unsigned char status = 0;

	status = inb(baseaddr + UART_MSR);

	mxser_msr[port] &= 0x0F;
	mxser_msr[port] |= status;
	status = mxser_msr[port];
	if (mode)
		mxser_msr[port] = 0;

	return status;
}

/*
 * This routine is called whenever a serial port is opened.  It
 * enables interrupts for a serial port, linking in its async structure into
 * the IRQ chain.   It also performs the serial-specific
 * initialization for the tty structure.
 */
static int mxser_open(struct tty_struct *tty, struct file *filp)
{
	struct mxser_struct *info;
	int retval, line;

	/* initialize driver_data in case something fails */
	tty->driver_data = NULL;

	line = tty->index;
	if (line == MXSER_PORTS)
		return 0;
	if (line < 0 || line > MXSER_PORTS)
		return -ENODEV;
	info = mxvar_table + line;
	if (!info->base)
		return -ENODEV;

	tty->driver_data = info;
	info->tty = tty;
	/*
	 * Start up serial port
	 */
	retval = mxser_startup(info);
	if (retval)
		return retval;

	retval = mxser_block_til_ready(tty, filp, info);
	if (retval)
		return retval;

	info->count++;

	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
		if (tty->driver->subtype == SERIAL_TYPE_NORMAL)
			*tty->termios = info->normal_termios;
		else
			*tty->termios = info->callout_termios;
		mxser_change_speed(info, NULL);
	}

	info->session = current->signal->session;
	info->pgrp = process_group(current);

	/*
	status = mxser_get_msr(info->base, 0, info->port);
	mxser_check_modem_status(info, status);
	*/

/* unmark here for very high baud rate (ex. 921600 bps) used */
	tty->low_latency = 1;
	return 0;
}

/*
 * 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, and we free
 * that IRQ if nothing is left in the chain.
 */
static void mxser_close(struct tty_struct *tty, struct file *filp)
{
	struct mxser_struct *info = tty->driver_data;

	unsigned long timeout;
	unsigned long flags;
	struct tty_ldisc *ld;

	if (tty->index == MXSER_PORTS)
		return;
	if (!info)
		return;

	spin_lock_irqsave(&info->slock, flags);

	if (tty_hung_up_p(filp)) {
		spin_unlock_irqrestore(&info->slock, flags);
		return;
	}
	if ((tty->count == 1) && (info->count != 1)) {
		/*
		 * Uh, oh.  tty->count is 1, which means that the tty
		 * structure will be freed.  Info->count should always
		 * be one in these conditions.  If it's greater than
		 * one, we've got real problems, since it means the
		 * serial port won't be shutdown.
		 */
		printk(KERN_ERR "mxser_close: bad serial port count; "
			"tty->count is 1, info->count is %d\n", info->count);
		info->count = 1;
	}
	if (--info->count < 0) {
		printk(KERN_ERR "mxser_close: bad serial port count for "
			"ttys%d: %d\n", info->port, info->count);
		info->count = 0;
	}
	if (info->count) {
		spin_unlock_irqrestore(&info->slock, flags);
		return;
	}
	info->flags |= ASYNC_CLOSING;
	spin_unlock_irqrestore(&info->slock, flags);
	/*
	 * Save the termios structure, since this port may have
	 * separate termios for callout and dialin.
	 */
	if (info->flags & ASYNC_NORMAL_ACTIVE)
		info->normal_termios = *tty->termios;
	/*
	 * Now we wait for the transmit buffer to clear; and we notify
	 * the line discipline to only process XON/XOFF characters.
	 */
	tty->closing = 1;
	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
		tty_wait_until_sent(tty, info->closing_wait);
	/*
	 * At this point we stop accepting input.  To do this, we
	 * disable the receive line status interrupts, and tell the
	 * interrupt driver to stop checking the data ready bit in the
	 * line status register.
	 */
	info->IER &= ~UART_IER_RLSI;
	if (info->IsMoxaMustChipFlag)
		info->IER &= ~MOXA_MUST_RECV_ISR;
/* by William
	info->read_status_mask &= ~UART_LSR_DR;
*/
	if (info->flags & ASYNC_INITIALIZED) {
		outb(info->IER, info->base + UART_IER);
		/*
		 * Before we drop DTR, make sure the UART transmitter
		 * has completely drained; this is especially
		 * important if there is a transmit FIFO!
		 */
		timeout = jiffies + HZ;
		while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) {
			schedule_timeout_interruptible(5);
			if (time_after(jiffies, timeout))
				break;
		}
	}
	mxser_shutdown(info);

	if (tty->driver->flush_buffer)
		tty->driver->flush_buffer(tty);
		
	ld = tty_ldisc_ref(tty);
	if (ld) {
		if (ld->flush_buffer)
			ld->flush_buffer(tty);
		tty_ldisc_deref(ld);
	}
		
	tty->closing = 0;
	info->event = 0;
	info->tty = NULL;
	if (info->blocked_open) {
		if (info->close_delay)
			schedule_timeout_interruptible(info->close_delay);
		wake_up_interruptible(&info->open_wait);
	}

	info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
	wake_up_interruptible(&info->close_wait);

}

static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
	int c, total = 0;
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	if (!info->xmit_buf)
		return 0;

	while (1) {
		c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
					  SERIAL_XMIT_SIZE - info->xmit_head));
		if (c <= 0)
			break;

		memcpy(info->xmit_buf + info->xmit_head, buf, c);
		spin_lock_irqsave(&info->slock, flags);
		info->xmit_head = (info->xmit_head + c) &
				  (SERIAL_XMIT_SIZE - 1);
		info->xmit_cnt += c;
		spin_unlock_irqrestore(&info->slock, flags);

		buf += c;
		count -= c;
		total += c;
	}

	if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) {
		if (!tty->hw_stopped ||
				(info->type == PORT_16550A) ||
				(info->IsMoxaMustChipFlag)) {
			spin_lock_irqsave(&info->slock, flags);
			info->IER |= UART_IER_THRI;
			outb(info->IER, info->base + UART_IER);
			spin_unlock_irqrestore(&info->slock, flags);
		}
	}
	return total;
}

static void mxser_put_char(struct tty_struct *tty, unsigned char ch)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	if (!info->xmit_buf)
		return;

	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
		return;

	spin_lock_irqsave(&info->slock, flags);
	info->xmit_buf[info->xmit_head++] = ch;
	info->xmit_head &= SERIAL_XMIT_SIZE - 1;
	info->xmit_cnt++;
	spin_unlock_irqrestore(&info->slock, flags);
	if (!tty->stopped && !(info->IER & UART_IER_THRI)) {
		if (!tty->hw_stopped ||
				(info->type == PORT_16550A) ||
				info->IsMoxaMustChipFlag) {
			spin_lock_irqsave(&info->slock, flags);
			info->IER |= UART_IER_THRI;
			outb(info->IER, info->base + UART_IER);
			spin_unlock_irqrestore(&info->slock, flags);
		}
	}
}


static void mxser_flush_chars(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	if (info->xmit_cnt <= 0 ||
			tty->stopped ||
			!info->xmit_buf ||
			(tty->hw_stopped &&
			 (info->type != PORT_16550A) &&
			 (!info->IsMoxaMustChipFlag)
			))
		return;

	spin_lock_irqsave(&info->slock, flags);

	info->IER |= UART_IER_THRI;
	outb(info->IER, info->base + UART_IER);

	spin_unlock_irqrestore(&info->slock, flags);
}

static int mxser_write_room(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	int ret;

	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
	if (ret < 0)
		ret = 0;
	return ret;
}

static int mxser_chars_in_buffer(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	return info->xmit_cnt;
}

static void mxser_flush_buffer(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	char fcr;
	unsigned long flags;


	spin_lock_irqsave(&info->slock, flags);
	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;

	/* below added by shinhay */
	fcr = inb(info->base + UART_FCR);
	outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
		info->base + UART_FCR);
	outb(fcr, info->base + UART_FCR);

	spin_unlock_irqrestore(&info->slock, flags);
	/* above added by shinhay */

	wake_up_interruptible(&tty->write_wait);
	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
		(tty->ldisc.write_wakeup) (tty);
}

static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct mxser_struct *info = tty->driver_data;
	int retval;
	struct async_icount cprev, cnow;	/* kernel counter temps */
	struct serial_icounter_struct __user *p_cuser;
	unsigned long templ;
	unsigned long flags;
	void __user *argp = (void __user *)arg;

	if (tty->index == MXSER_PORTS)
		return mxser_ioctl_special(cmd, argp);

	/* following add by Victor Yu. 01-05-2004 */
	if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) {
		int opmode, p;
		static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f };
		int shiftbit;
		unsigned char val, mask;

		p = info->port % 4;
		if (cmd == MOXA_SET_OP_MODE) {
			if (get_user(opmode, (int __user *) argp))
				return -EFAULT;
			if (opmode != RS232_MODE &&
					opmode != RS485_2WIRE_MODE &&
					opmode != RS422_MODE &&
					opmode != RS485_4WIRE_MODE)
				return -EFAULT;
			mask = ModeMask[p];
			shiftbit = p * 2;
			val = inb(info->opmode_ioaddr);
			val &= mask;
			val |= (opmode << shiftbit);
			outb(val, info->opmode_ioaddr);
		} else {
			shiftbit = p * 2;
			opmode = inb(info->opmode_ioaddr) >> shiftbit;
			opmode &= OP_MODE_MASK;
			if (copy_to_user(argp, &opmode, sizeof(int)))
				return -EFAULT;
		}
		return 0;
	}
	/* above add by Victor Yu. 01-05-2004 */

	if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
		if (tty->flags & (1 << TTY_IO_ERROR))
			return -EIO;
	}
	switch (cmd) {
	case TCSBRK:		/* SVID version: non-zero arg --> no break */
		retval = tty_check_change(tty);
		if (retval)
			return retval;
		tty_wait_until_sent(tty, 0);
		if (!arg)
			mxser_send_break(info, HZ / 4);	/* 1/4 second */
		return 0;
	case TCSBRKP:		/* support for POSIX tcsendbreak() */
		retval = tty_check_change(tty);
		if (retval)
			return retval;
		tty_wait_until_sent(tty, 0);
		mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4);
		return 0;
	case TIOCGSOFTCAR:
		return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);
	case TIOCSSOFTCAR:
		if (get_user(templ, (unsigned long __user *) argp))
			return -EFAULT;
		arg = templ;
		tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
		return 0;
	case TIOCGSERIAL:
		return mxser_get_serial_info(info, argp);
	case TIOCSSERIAL:
		return mxser_set_serial_info(info, argp);
	case TIOCSERGETLSR:	/* Get line status register */
		return mxser_get_lsr_info(info, argp);
		/*
		 * 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: {
			DECLARE_WAITQUEUE(wait, current);
			int ret;
			spin_lock_irqsave(&info->slock, flags);
			cprev = info->icount;	/* note the counters on entry */
			spin_unlock_irqrestore(&info->slock, flags);

			add_wait_queue(&info->delta_msr_wait, &wait);
			while (1) {
				spin_lock_irqsave(&info->slock, flags);
				cnow = info->icount;	/* atomic copy */
				spin_unlock_irqrestore(&info->slock, flags);

				set_current_state(TASK_INTERRUPTIBLE);
				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))) {
					ret = 0;
					break;
				}
				/* see if a signal did it */
				if (signal_pending(current)) {
					ret = -ERESTARTSYS;
					break;
				}
				cprev = cnow;
			}
			current->state = TASK_RUNNING;
			remove_wait_queue(&info->delta_msr_wait, &wait);
			break;
		}
		/* 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(&info->slock, flags);
		cnow = info->icount;
		spin_unlock_irqrestore(&info->slock, flags);
		p_cuser = argp;
		/* modified by casper 1/11/2000 */
		if (put_user(cnow.frame, &p_cuser->frame))
			return -EFAULT;
		if (put_user(cnow.brk, &p_cuser->brk))
			return -EFAULT;
		if (put_user(cnow.overrun, &p_cuser->overrun))
			return -EFAULT;
		if (put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
			return -EFAULT;
		if (put_user(cnow.parity, &p_cuser->parity))
			return -EFAULT;
		if (put_user(cnow.rx, &p_cuser->rx))
			return -EFAULT;
		if (put_user(cnow.tx, &p_cuser->tx))
			return -EFAULT;
		put_user(cnow.cts, &p_cuser->cts);
		put_user(cnow.dsr, &p_cuser->dsr);
		put_user(cnow.rng, &p_cuser->rng);
		put_user(cnow.dcd, &p_cuser->dcd);
		return 0;
	case MOXA_HighSpeedOn:
		return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
	case MOXA_SDS_RSTICOUNTER: {
			info->mon_data.rxcnt = 0;
			info->mon_data.txcnt = 0;
			return 0;
		}
/* (above) added by James. */
	case MOXA_ASPP_SETBAUD:{
			long baud;
			if (get_user(baud, (long __user *)argp))
				return -EFAULT;
			mxser_set_baud(info, baud);
			return 0;
		}
	case MOXA_ASPP_GETBAUD:
		if (copy_to_user(argp, &info->realbaud, sizeof(long)))
			return -EFAULT;

		return 0;

	case MOXA_ASPP_OQUEUE:{
			int len, lsr;

			len = mxser_chars_in_buffer(tty);

			lsr = inb(info->base + UART_LSR) & UART_LSR_TEMT;

			len += (lsr ? 0 : 1);

			if (copy_to_user(argp, &len, sizeof(int)))
				return -EFAULT;

			return 0;
		}
	case MOXA_ASPP_MON: {
			int mcr, status;

			/* info->mon_data.ser_param = tty->termios->c_cflag; */

			status = mxser_get_msr(info->base, 1, info->port, info);
			mxser_check_modem_status(info, status);

			mcr = inb(info->base + UART_MCR);
			if (mcr & MOXA_MUST_MCR_XON_FLAG)
				info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
			else
				info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD;

			if (mcr & MOXA_MUST_MCR_TX_XON)
				info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT;
			else
				info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT;

			if (info->tty->hw_stopped)
				info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
			else
				info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;

			if (copy_to_user(argp, &info->mon_data,
					sizeof(struct mxser_mon)))
				return -EFAULT;

			return 0;
		}

	case MOXA_ASPP_LSTATUS: {
			if (copy_to_user(argp, &info->err_shadow,
					sizeof(unsigned char)))
				return -EFAULT;

			info->err_shadow = 0;
			return 0;
		}
	case MOXA_SET_BAUD_METHOD: {
			int method;

			if (get_user(method, (int __user *)argp))
				return -EFAULT;
			mxser_set_baud_method[info->port] = method;
			if (copy_to_user(argp, &method, sizeof(int)))
				return -EFAULT;

			return 0;
		}
	default:
		return -ENOIOCTLCMD;
	}
	return 0;
}

#ifndef CMSPAR
#define	CMSPAR 010000000000
#endif

static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
{
	int i, result, status;

	switch (cmd) {
	case MOXA_GET_CONF:
		if (copy_to_user(argp, mxsercfg,
				sizeof(struct mxser_hwconf) * 4))
			return -EFAULT;
		return 0;
	case MOXA_GET_MAJOR:
		if (copy_to_user(argp, &ttymajor, sizeof(int)))
			return -EFAULT;
		return 0;

	case MOXA_GET_CUMAJOR:
		if (copy_to_user(argp, &calloutmajor, sizeof(int)))
			return -EFAULT;
		return 0;

	case MOXA_CHKPORTENABLE:
		result = 0;
		for (i = 0; i < MXSER_PORTS; i++) {
			if (mxvar_table[i].base)
				result |= (1 << i);
		}
		return put_user(result, (unsigned long __user *)argp);
	case MOXA_GETDATACOUNT:
		if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
			return -EFAULT;
		return 0;
	case MOXA_GETMSTATUS:
		for (i = 0; i < MXSER_PORTS; i++) {
			GMStatus[i].ri = 0;
			if (!mxvar_table[i].base) {
				GMStatus[i].dcd = 0;
				GMStatus[i].dsr = 0;
				GMStatus[i].cts = 0;
				continue;
			}

			if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios)
				GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag;
			else
				GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag;

			status = inb(mxvar_table[i].base + UART_MSR);
			if (status & 0x80 /*UART_MSR_DCD */ )
				GMStatus[i].dcd = 1;
			else
				GMStatus[i].dcd = 0;

			if (status & 0x20 /*UART_MSR_DSR */ )
				GMStatus[i].dsr = 1;
			else
				GMStatus[i].dsr = 0;


			if (status & 0x10 /*UART_MSR_CTS */ )
				GMStatus[i].cts = 1;
			else
				GMStatus[i].cts = 0;
		}
		if (copy_to_user(argp, GMStatus,
				sizeof(struct mxser_mstatus) * MXSER_PORTS))
			return -EFAULT;
		return 0;
	case MOXA_ASPP_MON_EXT: {
			int status;
			int opmode, p;
			int shiftbit;
			unsigned cflag, iflag;

			for (i = 0; i < MXSER_PORTS; i++) {
				if (!mxvar_table[i].base)
					continue;

				status = mxser_get_msr(mxvar_table[i].base, 0,
							i, &(mxvar_table[i]));
				/*
				mxser_check_modem_status(&mxvar_table[i],
								status);
				*/
				if (status & UART_MSR_TERI)
					mxvar_table[i].icount.rng++;
				if (status & UART_MSR_DDSR)
					mxvar_table[i].icount.dsr++;
				if (status & UART_MSR_DDCD)
					mxvar_table[i].icount.dcd++;
				if (status & UART_MSR_DCTS)
					mxvar_table[i].icount.cts++;

				mxvar_table[i].mon_data.modem_status = status;
				mon_data_ext.rx_cnt[i] = mxvar_table[i].mon_data.rxcnt;
				mon_data_ext.tx_cnt[i] = mxvar_table[i].mon_data.txcnt;
				mon_data_ext.up_rxcnt[i] = mxvar_table[i].mon_data.up_rxcnt;
				mon_data_ext.up_txcnt[i] = mxvar_table[i].mon_data.up_txcnt;
				mon_data_ext.modem_status[i] = mxvar_table[i].mon_data.modem_status;
				mon_data_ext.baudrate[i] = mxvar_table[i].realbaud;

				if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) {
					cflag = mxvar_table[i].normal_termios.c_cflag;
					iflag = mxvar_table[i].normal_termios.c_iflag;
				} else {
					cflag = mxvar_table[i].tty->termios->c_cflag;
					iflag = mxvar_table[i].tty->termios->c_iflag;
				}

				mon_data_ext.databits[i] = cflag & CSIZE;

				mon_data_ext.stopbits[i] = cflag & CSTOPB;

				mon_data_ext.parity[i] = cflag & (PARENB | PARODD | CMSPAR);

				mon_data_ext.flowctrl[i] = 0x00;

				if (cflag & CRTSCTS)
					mon_data_ext.flowctrl[i] |= 0x03;

				if (iflag & (IXON | IXOFF))
					mon_data_ext.flowctrl[i] |= 0x0C;

				if (mxvar_table[i].type == PORT_16550A)
					mon_data_ext.fifo[i] = 1;
				else
					mon_data_ext.fifo[i] = 0;

				p = i % 4;
				shiftbit = p * 2;
				opmode = inb(mxvar_table[i].opmode_ioaddr) >> shiftbit;
				opmode &= OP_MODE_MASK;

				mon_data_ext.iftype[i] = opmode;

			}
			if (copy_to_user(argp, &mon_data_ext, sizeof(struct mxser_mon_ext)))
				return -EFAULT;

			return 0;

		}
	default:
		return -ENOIOCTLCMD;
	}
	return 0;
}

static void mxser_stoprx(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	/* unsigned long flags; */

	info->ldisc_stop_rx = 1;
	if (I_IXOFF(tty)) {
		/* MX_LOCK(&info->slock); */
		/* following add by Victor Yu. 09-02-2002 */
		if (info->IsMoxaMustChipFlag) {
			info->IER &= ~MOXA_MUST_RECV_ISR;
			outb(info->IER, info->base + UART_IER);
		} else {
			/* above add by Victor Yu. 09-02-2002 */
			info->x_char = STOP_CHAR(tty);
			/* mask by Victor Yu. 09-02-2002 */
			/* outb(info->IER, 0); */
			outb(0, info->base + UART_IER);
			info->IER |= UART_IER_THRI;
			/* force Tx interrupt */
			outb(info->IER, info->base + UART_IER);
		}		/* add by Victor Yu. 09-02-2002 */
		/* MX_UNLOCK(&info->slock); */
	}

	if (info->tty->termios->c_cflag & CRTSCTS) {
		/* MX_LOCK(&info->slock); */
		info->MCR &= ~UART_MCR_RTS;
		outb(info->MCR, info->base + UART_MCR);
		/* MX_UNLOCK(&info->slock); */
	}
}

static void mxser_startrx(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	/* unsigned long flags; */

	info->ldisc_stop_rx = 0;
	if (I_IXOFF(tty)) {
		if (info->x_char)
			info->x_char = 0;
		else {
			/* MX_LOCK(&info->slock); */

			/* following add by Victor Yu. 09-02-2002 */
			if (info->IsMoxaMustChipFlag) {
				info->IER |= MOXA_MUST_RECV_ISR;
				outb(info->IER, info->base + UART_IER);
			} else {
				/* above add by Victor Yu. 09-02-2002 */

				info->x_char = START_CHAR(tty);
				/* mask by Victor Yu. 09-02-2002 */
				/* outb(info->IER, 0); */
				/* add by Victor Yu. 09-02-2002 */
				outb(0, info->base + UART_IER);
				/* force Tx interrupt */
				info->IER |= UART_IER_THRI;
				outb(info->IER, info->base + UART_IER);
			}	/* add by Victor Yu. 09-02-2002 */
			/* MX_UNLOCK(&info->slock); */
		}
	}

	if (info->tty->termios->c_cflag & CRTSCTS) {
		/* MX_LOCK(&info->slock); */
		info->MCR |= UART_MCR_RTS;
		outb(info->MCR, info->base + UART_MCR);
		/* MX_UNLOCK(&info->slock); */
	}
}

/*
 * This routine is called by the upper-layer tty layer to signal that
 * incoming characters should be throttled.
 */
static void mxser_throttle(struct tty_struct *tty)
{
	/* struct mxser_struct *info = tty->driver_data; */
	/* unsigned long flags; */

	/* MX_LOCK(&info->slock); */
	mxser_stoprx(tty);
	/* MX_UNLOCK(&info->slock); */
}

static void mxser_unthrottle(struct tty_struct *tty)
{
	/* struct mxser_struct *info = tty->driver_data; */
	/* unsigned long flags; */

	/* MX_LOCK(&info->slock); */
	mxser_startrx(tty);
	/* MX_UNLOCK(&info->slock); */
}

static void mxser_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	if ((tty->termios->c_cflag != old_termios->c_cflag) ||
			(RELEVANT_IFLAG(tty->termios->c_iflag) != RELEVANT_IFLAG(old_termios->c_iflag))) {

		mxser_change_speed(info, old_termios);

		if ((old_termios->c_cflag & CRTSCTS) &&
				!(tty->termios->c_cflag & CRTSCTS)) {
			tty->hw_stopped = 0;
			mxser_start(tty);
		}
	}

/* Handle sw stopped */
	if ((old_termios->c_iflag & IXON) &&
			!(tty->termios->c_iflag & IXON)) {
		tty->stopped = 0;

		/* following add by Victor Yu. 09-02-2002 */
		if (info->IsMoxaMustChipFlag) {
			spin_lock_irqsave(&info->slock, flags);
			DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base);
			spin_unlock_irqrestore(&info->slock, flags);
		}
		/* above add by Victor Yu. 09-02-2002 */

		mxser_start(tty);
	}
}

/*
 * mxser_stop() and mxser_start()
 *
 * This routines are called before setting or resetting tty->stopped.
 * They enable or disable transmitter interrupts, as necessary.
 */
static void mxser_stop(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);
	if (info->IER & UART_IER_THRI) {
		info->IER &= ~UART_IER_THRI;
		outb(info->IER, info->base + UART_IER);
	}
	spin_unlock_irqrestore(&info->slock, flags);
}

static void mxser_start(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);
	if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
		info->IER |= UART_IER_THRI;
		outb(info->IER, info->base + UART_IER);
	}
	spin_unlock_irqrestore(&info->slock, flags);
}

/*
 * mxser_wait_until_sent() --- wait until the transmitter is empty
 */
static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long orig_jiffies, char_time;
	int lsr;

	if (info->type == PORT_UNKNOWN)
		return;

	if (info->xmit_fifo_size == 0)
		return;		/* Just in case.... */

	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 = (info->timeout - HZ / 50) / info->xmit_fifo_size;
	char_time = char_time / 5;
	if (char_time == 0)
		char_time = 1;
	if (timeout && 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 info->timeout, this is probably due to a
	 * UART bug of some kind.  So, we clamp the timeout parameter at
	 * 2*info->timeout.
	 */
	if (!timeout || timeout > 2 * info->timeout)
		timeout = 2 * info->timeout;
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
	printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...",
		timeout, char_time);
	printk("jiff=%lu...", jiffies);
#endif
	while (!((lsr = inb(info->base + UART_LSR)) & UART_LSR_TEMT)) {
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
		printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
		schedule_timeout_interruptible(char_time);
		if (signal_pending(current))
			break;
		if (timeout && time_after(jiffies, orig_jiffies + timeout))
			break;
	}
	set_current_state(TASK_RUNNING);

#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
#endif
}


/*
 * This routine is called by tty_hangup() when a hangup is signaled.
 */
void mxser_hangup(struct tty_struct *tty)
{
	struct mxser_struct *info = tty->driver_data;

	mxser_flush_buffer(tty);
	mxser_shutdown(info);
	info->event = 0;
	info->count = 0;
	info->flags &= ~ASYNC_NORMAL_ACTIVE;
	info->tty = NULL;
	wake_up_interruptible(&info->open_wait);
}


/* added by James 03-12-2004. */
/*
 * mxser_rs_break() --- routine which turns the break handling on or off
 */
static void mxser_rs_break(struct tty_struct *tty, int break_state)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);
	if (break_state == -1)
		outb(inb(info->base + UART_LCR) | UART_LCR_SBC,
			info->base + UART_LCR);
	else
		outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC,
			info->base + UART_LCR);
	spin_unlock_irqrestore(&info->slock, flags);
}

/* (above) added by James. */


/*
 * This is the serial driver's generic interrupt routine
 */
static irqreturn_t mxser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	int status, iir, i;
	struct mxser_struct *info;
	struct mxser_struct *port;
	int max, irqbits, bits, msr;
	int pass_counter = 0;
	int handled = IRQ_NONE;

	port = NULL;
	/* spin_lock(&gm_lock); */

	for (i = 0; i < MXSER_BOARDS; i++) {
		if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) {
			port = dev_id;
			break;
		}
	}

	if (i == MXSER_BOARDS)
		goto irq_stop;
	if (port == 0)
		goto irq_stop;
	max = mxser_numports[mxsercfg[i].board_type - 1];
	while (1) {
		irqbits = inb(port->vector) & port->vectormask;
		if (irqbits == port->vectormask)
			break;

		handled = IRQ_HANDLED;
		for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) {
			if (irqbits == port->vectormask)
				break;
			if (bits & irqbits)
				continue;
			info = port + i;

			/* following add by Victor Yu. 09-13-2002 */
			iir = inb(info->base + UART_IIR);
			if (iir & UART_IIR_NO_INT)
				continue;
			iir &= MOXA_MUST_IIR_MASK;
			if (!info->tty) {
				status = inb(info->base + UART_LSR);
				outb(0x27, info->base + UART_FCR);
				inb(info->base + UART_MSR);
				continue;
			}
			/* above add by Victor Yu. 09-13-2002 */
			/*
			   if (info->tty->flip.count < TTY_FLIPBUF_SIZE / 4) {
			   info->IER |= MOXA_MUST_RECV_ISR;
			   outb(info->IER, info->base + UART_IER);
			   }
			 */


			/* mask by Victor Yu. 09-13-2002
			   if ( !info->tty ||
			   (inb(info->base + UART_IIR) & UART_IIR_NO_INT) )
			   continue;
			 */
			/* mask by Victor Yu. 09-02-2002
			   status = inb(info->base + UART_LSR) & info->read_status_mask;
			 */

			/* following add by Victor Yu. 09-02-2002 */
			status = inb(info->base + UART_LSR);

			if (status & UART_LSR_PE)
				info->err_shadow |= NPPI_NOTIFY_PARITY;
			if (status & UART_LSR_FE)
				info->err_shadow |= NPPI_NOTIFY_FRAMING;
			if (status & UART_LSR_OE)
				info->err_shadow |= NPPI_NOTIFY_HW_OVERRUN;
			if (status & UART_LSR_BI)
				info->err_shadow |= NPPI_NOTIFY_BREAK;

			if (info->IsMoxaMustChipFlag) {
				/*
				   if ( (status & 0x02) && !(status & 0x01) ) {
				   outb(info->base+UART_FCR,  0x23);
				   continue;
				   }
				 */
				if (iir == MOXA_MUST_IIR_GDA ||
						iir == MOXA_MUST_IIR_RDA ||
						iir == MOXA_MUST_IIR_RTO ||
						iir == MOXA_MUST_IIR_LSR)
					mxser_receive_chars(info, &status);

			} else {
				/* above add by Victor Yu. 09-02-2002 */

				status &= info->read_status_mask;
				if (status & UART_LSR_DR)
					mxser_receive_chars(info, &status);
			}
			msr = inb(info->base + UART_MSR);
			if (msr & UART_MSR_ANY_DELTA) {
				mxser_check_modem_status(info, msr);
			}
			/* following add by Victor Yu. 09-13-2002 */
			if (info->IsMoxaMustChipFlag) {
				if ((iir == 0x02) && (status & UART_LSR_THRE)) {
					mxser_transmit_chars(info);
				}
			} else {
				/* above add by Victor Yu. 09-13-2002 */

				if (status & UART_LSR_THRE) {
/* 8-2-99 by William
			    if ( info->x_char || (info->xmit_cnt > 0) )
*/
					mxser_transmit_chars(info);
				}
			}
		}
		if (pass_counter++ > MXSER_ISR_PASS_LIMIT) {
			break;	/* Prevent infinite loops */
		}
	}

      irq_stop:
	/* spin_unlock(&gm_lock); */
	return handled;
}

static void mxser_receive_chars(struct mxser_struct *info, int *status)
{
	struct tty_struct *tty = info->tty;
	unsigned char ch, gdl;
	int ignored = 0;
	int cnt = 0;
	int recv_room;
	int max = 256;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);

	recv_room = tty->receive_room;
	if ((recv_room == 0) && (!info->ldisc_stop_rx)) {
		/* mxser_throttle(tty); */
		mxser_stoprx(tty);
		/* return; */
	}

	/* following add by Victor Yu. 09-02-2002 */
	if (info->IsMoxaMustChipFlag != MOXA_OTHER_UART) {

		if (*status & UART_LSR_SPECIAL) {
			goto intr_old;
		}
		/* following add by Victor Yu. 02-11-2004 */
		if (info->IsMoxaMustChipFlag == MOXA_MUST_MU860_HWID &&
				(*status & MOXA_MUST_LSR_RERR))
			goto intr_old;
		/* above add by Victor Yu. 02-14-2004 */
		if (*status & MOXA_MUST_LSR_RERR)
			goto intr_old;

		gdl = inb(info->base + MOXA_MUST_GDL_REGISTER);

		/* add by Victor Yu. 02-11-2004 */
		if (info->IsMoxaMustChipFlag == MOXA_MUST_MU150_HWID)
			gdl &= MOXA_MUST_GDL_MASK;
		if (gdl >= recv_room) {
			if (!info->ldisc_stop_rx) {
				/* mxser_throttle(tty); */
				mxser_stoprx(tty);
			}
			/* return; */
		}
		while (gdl--) {
			ch = inb(info->base + UART_RX);
			tty_insert_flip_char(tty, ch, 0);
			cnt++;
			/*
			   if ((cnt >= HI_WATER) && (info->stop_rx == 0)) {
			   mxser_stoprx(tty);
			   info->stop_rx = 1;
			   break;
			   } */
		}
		goto end_intr;
	}
 intr_old:
	/* above add by Victor Yu. 09-02-2002 */

	do {
		if (max-- < 0)
			break;
		/*
		   if ((cnt >= HI_WATER) && (info->stop_rx == 0)) {
		   mxser_stoprx(tty);
		   info->stop_rx=1;
		   break;
		   }
		 */

		ch = inb(info->base + UART_RX);
		/* following add by Victor Yu. 09-02-2002 */
		if (info->IsMoxaMustChipFlag && (*status & UART_LSR_OE) /*&& !(*status&UART_LSR_DR) */ )
			outb(0x23, info->base + UART_FCR);
		*status &= info->read_status_mask;
		/* above add by Victor Yu. 09-02-2002 */
		if (*status & info->ignore_status_mask) {
			if (++ignored > 100)
				break;
		} else {
			char flag = 0;
			if (*status & UART_LSR_SPECIAL) {
				if (*status & UART_LSR_BI) {
					flag = TTY_BREAK;
/* added by casper 1/11/2000 */
					info->icount.brk++;
/* */
					if (info->flags & ASYNC_SAK)
						do_SAK(tty);
				} else if (*status & UART_LSR_PE) {
					flag = TTY_PARITY;
/* added by casper 1/11/2000 */
					info->icount.parity++;
/* */
				} else if (*status & UART_LSR_FE) {
					flag = TTY_FRAME;
/* added by casper 1/11/2000 */
					info->icount.frame++;
/* */
				} else if (*status & UART_LSR_OE) {
					flag = TTY_OVERRUN;
/* added by casper 1/11/2000 */
					info->icount.overrun++;
/* */
				}
			}
			tty_insert_flip_char(tty, ch, flag);
			cnt++;
			if (cnt >= recv_room) {
				if (!info->ldisc_stop_rx) {
					/* mxser_throttle(tty); */
					mxser_stoprx(tty);
				}
				break;
			}

		}

		/* following add by Victor Yu. 09-02-2002 */
		if (info->IsMoxaMustChipFlag)
			break;
		/* above add by Victor Yu. 09-02-2002 */

		/* mask by Victor Yu. 09-02-2002
		 *status = inb(info->base + UART_LSR) & info->read_status_mask;
		 */
		/* following add by Victor Yu. 09-02-2002 */
		*status = inb(info->base + UART_LSR);
		/* above add by Victor Yu. 09-02-2002 */
	} while (*status & UART_LSR_DR);

end_intr:		/* add by Victor Yu. 09-02-2002 */
	mxvar_log.rxcnt[info->port] += cnt;
	info->mon_data.rxcnt += cnt;
	info->mon_data.up_rxcnt += cnt;
	spin_unlock_irqrestore(&info->slock, flags);

	tty_flip_buffer_push(tty);
}

static void mxser_transmit_chars(struct mxser_struct *info)
{
	int count, cnt;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);

	if (info->x_char) {
		outb(info->x_char, info->base + UART_TX);
		info->x_char = 0;
		mxvar_log.txcnt[info->port]++;
		info->mon_data.txcnt++;
		info->mon_data.up_txcnt++;

/* added by casper 1/11/2000 */
		info->icount.tx++;
/* */
		spin_unlock_irqrestore(&info->slock, flags);
		return;
	}

	if (info->xmit_buf == 0) {
		spin_unlock_irqrestore(&info->slock, flags);
		return;
	}

	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
			(info->tty->hw_stopped &&
			(info->type != PORT_16550A) &&
			(!info->IsMoxaMustChipFlag))) {
		info->IER &= ~UART_IER_THRI;
		outb(info->IER, info->base + UART_IER);
		spin_unlock_irqrestore(&info->slock, flags);
		return;
	}

	cnt = info->xmit_cnt;
	count = info->xmit_fifo_size;
	do {
		outb(info->xmit_buf[info->xmit_tail++],
			info->base + UART_TX);
		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);
		if (--info->xmit_cnt <= 0)
			break;
	} while (--count > 0);
	mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt);

/* added by James 03-12-2004. */
	info->mon_data.txcnt += (cnt - info->xmit_cnt);
	info->mon_data.up_txcnt += (cnt - info->xmit_cnt);
/* (above) added by James. */

/* added by casper 1/11/2000 */
	info->icount.tx += (cnt - info->xmit_cnt);
/* */

	if (info->xmit_cnt < WAKEUP_CHARS) {
		set_bit(MXSER_EVENT_TXLOW, &info->event);
		schedule_work(&info->tqueue);
	}
	if (info->xmit_cnt <= 0) {
		info->IER &= ~UART_IER_THRI;
		outb(info->IER, info->base + UART_IER);
	}
	spin_unlock_irqrestore(&info->slock, flags);
}

static void mxser_check_modem_status(struct mxser_struct *info, int status)
{
	/* update input line counters */
	if (status & UART_MSR_TERI)
		info->icount.rng++;
	if (status & UART_MSR_DDSR)
		info->icount.dsr++;
	if (status & UART_MSR_DDCD)
		info->icount.dcd++;
	if (status & UART_MSR_DCTS)
		info->icount.cts++;
	info->mon_data.modem_status = status;
	wake_up_interruptible(&info->delta_msr_wait);

	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
		if (status & UART_MSR_DCD)
			wake_up_interruptible(&info->open_wait);
		schedule_work(&info->tqueue);
	}

	if (info->flags & ASYNC_CTS_FLOW) {
		if (info->tty->hw_stopped) {
			if (status & UART_MSR_CTS) {
				info->tty->hw_stopped = 0;

				if ((info->type != PORT_16550A) &&
						(!info->IsMoxaMustChipFlag)) {
					info->IER |= UART_IER_THRI;
					outb(info->IER, info->base + UART_IER);
				}
				set_bit(MXSER_EVENT_TXLOW, &info->event);
				schedule_work(&info->tqueue);			}
		} else {
			if (!(status & UART_MSR_CTS)) {
				info->tty->hw_stopped = 1;
				if ((info->type != PORT_16550A) &&
						(!info->IsMoxaMustChipFlag)) {
					info->IER &= ~UART_IER_THRI;
					outb(info->IER, info->base + UART_IER);
				}
			}
		}
	}
}

static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, struct mxser_struct *info)
{
	DECLARE_WAITQUEUE(wait, current);
	int retval;
	int do_clocal = 0;
	unsigned long flags;

	/*
	 * 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))) {
		info->flags |= ASYNC_NORMAL_ACTIVE;
		return 0;
	}

	if (tty->termios->c_cflag & CLOCAL)
		do_clocal = 1;

	/*
	 * Block waiting for the carrier detect and the line to become
	 * free (i.e., not in use by the callout).  While we are in
	 * this loop, info->count is dropped by one, so that
	 * mxser_close() knows when to free things.  We restore it upon
	 * exit, either normal or abnormal.
	 */
	retval = 0;
	add_wait_queue(&info->open_wait, &wait);

	spin_lock_irqsave(&info->slock, flags);
	if (!tty_hung_up_p(filp))
		info->count--;
	spin_unlock_irqrestore(&info->slock, flags);
	info->blocked_open++;
	while (1) {
		spin_lock_irqsave(&info->slock, flags);
		outb(inb(info->base + UART_MCR) |
			UART_MCR_DTR | UART_MCR_RTS, info->base + UART_MCR);
		spin_unlock_irqrestore(&info->slock, flags);
		set_current_state(TASK_INTERRUPTIBLE);
		if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) {
			if (info->flags & ASYNC_HUP_NOTIFY)
				retval = -EAGAIN;
			else
				retval = -ERESTARTSYS;
			break;
		}
		if (!(info->flags & ASYNC_CLOSING) &&
				(do_clocal ||
				(inb(info->base + UART_MSR) & UART_MSR_DCD)))
			break;
		if (signal_pending(current)) {
			retval = -ERESTARTSYS;
			break;
		}
		schedule();
	}
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&info->open_wait, &wait);
	if (!tty_hung_up_p(filp))
		info->count++;
	info->blocked_open--;
	if (retval)
		return retval;
	info->flags |= ASYNC_NORMAL_ACTIVE;
	return 0;
}

static int mxser_startup(struct mxser_struct *info)
{
	unsigned long page;
	unsigned long flags;

	page = __get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	spin_lock_irqsave(&info->slock, flags);

	if (info->flags & ASYNC_INITIALIZED) {
		free_page(page);
		spin_unlock_irqrestore(&info->slock, flags);
		return 0;
	}

	if (!info->base || !info->type) {
		if (info->tty)
			set_bit(TTY_IO_ERROR, &info->tty->flags);
		free_page(page);
		spin_unlock_irqrestore(&info->slock, flags);
		return 0;
	}
	if (info->xmit_buf)
		free_page(page);
	else
		info->xmit_buf = (unsigned char *) page;

	/*
	 * Clear the FIFO buffers and disable them
	 * (they will be reenabled in mxser_change_speed())
	 */
	if (info->IsMoxaMustChipFlag)
		outb((UART_FCR_CLEAR_RCVR |
			UART_FCR_CLEAR_XMIT |
			MOXA_MUST_FCR_GDA_MODE_ENABLE), info->base + UART_FCR);
	else
		outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
			info->base + UART_FCR);

	/*
	 * At this point there's no way the LSR could still be 0xFF;
	 * if it is, then bail out, because there's likely no UART
	 * here.
	 */
	if (inb(info->base + UART_LSR) == 0xff) {
		spin_unlock_irqrestore(&info->slock, flags);
		if (capable(CAP_SYS_ADMIN)) {
			if (info->tty)
				set_bit(TTY_IO_ERROR, &info->tty->flags);
			return 0;
		} else
			return -ENODEV;
	}

	/*
	 * Clear the interrupt registers.
	 */
	(void) inb(info->base + UART_LSR);
	(void) inb(info->base + UART_RX);
	(void) inb(info->base + UART_IIR);
	(void) inb(info->base + UART_MSR);

	/*
	 * Now, initialize the UART
	 */
	outb(UART_LCR_WLEN8, info->base + UART_LCR);	/* reset DLAB */
	info->MCR = UART_MCR_DTR | UART_MCR_RTS;
	outb(info->MCR, info->base + UART_MCR);

	/*
	 * Finally, enable interrupts
	 */
	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
	/* info->IER = UART_IER_RLSI | UART_IER_RDI; */

	/* following add by Victor Yu. 08-30-2002 */
	if (info->IsMoxaMustChipFlag)
		info->IER |= MOXA_MUST_IER_EGDAI;
	/* above add by Victor Yu. 08-30-2002 */
	outb(info->IER, info->base + UART_IER);	/* enable interrupts */

	/*
	 * And clear the interrupt registers again for luck.
	 */
	(void) inb(info->base + UART_LSR);
	(void) inb(info->base + UART_RX);
	(void) inb(info->base + UART_IIR);
	(void) inb(info->base + UART_MSR);

	if (info->tty)
		clear_bit(TTY_IO_ERROR, &info->tty->flags);
	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;

	/*
	 * and set the speed of the serial port
	 */
	spin_unlock_irqrestore(&info->slock, flags);
	mxser_change_speed(info, NULL);

	info->flags |= ASYNC_INITIALIZED;
	return 0;
}

/*
 * This routine will shutdown a serial port; interrupts maybe disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 */
static void mxser_shutdown(struct mxser_struct *info)
{
	unsigned long flags;

	if (!(info->flags & ASYNC_INITIALIZED))
		return;

	spin_lock_irqsave(&info->slock, flags);

	/*
	 * 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(&info->delta_msr_wait);

	/*
	 * Free the IRQ, if necessary
	 */
	if (info->xmit_buf) {
		free_page((unsigned long) info->xmit_buf);
		info->xmit_buf = NULL;
	}

	info->IER = 0;
	outb(0x00, info->base + UART_IER);

	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
		info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
	outb(info->MCR, info->base + UART_MCR);

	/* clear Rx/Tx FIFO's */
	/* following add by Victor Yu. 08-30-2002 */
	if (info->IsMoxaMustChipFlag)
		outb((UART_FCR_CLEAR_RCVR |
			UART_FCR_CLEAR_XMIT |
			MOXA_MUST_FCR_GDA_MODE_ENABLE), info->base + UART_FCR);
	else
		/* above add by Victor Yu. 08-30-2002 */
		outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
			info->base + UART_FCR);

	/* read data port to reset things */
	(void) inb(info->base + UART_RX);

	if (info->tty)
		set_bit(TTY_IO_ERROR, &info->tty->flags);

	info->flags &= ~ASYNC_INITIALIZED;

	/* following add by Victor Yu. 09-23-2002 */
	if (info->IsMoxaMustChipFlag)
		SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->base);
	/* above add by Victor Yu. 09-23-2002 */

	spin_unlock_irqrestore(&info->slock, flags);
}

/*
 * This routine is called to set the UART divisor registers to match
 * the specified baud rate for a serial port.
 */
static int mxser_change_speed(struct mxser_struct *info, struct termios *old_termios)
{
	unsigned cflag, cval, fcr;
	int ret = 0;
	unsigned char status;
	long baud;
	unsigned long flags;

	if (!info->tty || !info->tty->termios)
		return ret;
	cflag = info->tty->termios->c_cflag;
	if (!(info->base))
		return ret;

#ifndef B921600
#define B921600 (B460800 +1)
#endif
	if (mxser_set_baud_method[info->port] == 0) {
		switch (cflag & (CBAUD | CBAUDEX)) {
		case B921600:
			baud = 921600;
			break;
		case B460800:
			baud = 460800;
			break;
		case B230400:
			baud = 230400;
			break;
		case B115200:
			baud = 115200;
			break;
		case B57600:
			baud = 57600;
			break;
		case B38400:
			baud = 38400;
			break;
		case B19200:
			baud = 19200;
			break;
		case B9600:
			baud = 9600;
			break;
		case B4800:
			baud = 4800;
			break;
		case B2400:
			baud = 2400;
			break;
		case B1800:
			baud = 1800;
			break;
		case B1200:
			baud = 1200;
			break;
		case B600:
			baud = 600;
			break;
		case B300:
			baud = 300;
			break;
		case B200:
			baud = 200;
			break;
		case B150:
			baud = 150;
			break;
		case B134:
			baud = 134;
			break;
		case B110:
			baud = 110;
			break;
		case B75:
			baud = 75;
			break;
		case B50:
			baud = 50;
			break;
		default:
			baud = 0;
			break;
		}
		mxser_set_baud(info, baud);
	}

	/* byte size and parity */
	switch (cflag & CSIZE) {
	case CS5:
		cval = 0x00;
		break;
	case CS6:
		cval = 0x01;
		break;
	case CS7:
		cval = 0x02;
		break;
	case CS8:
		cval = 0x03;
		break;
	default:
		cval = 0x00;
		break;		/* too keep GCC shut... */
	}
	if (cflag & CSTOPB)
		cval |= 0x04;
	if (cflag & PARENB)
		cval |= UART_LCR_PARITY;
	if (!(cflag & PARODD))
		cval |= UART_LCR_EPAR;
	if (cflag & CMSPAR)
		cval |= UART_LCR_SPAR;

	if ((info->type == PORT_8250) || (info->type == PORT_16450)) {
		if (info->IsMoxaMustChipFlag) {
			fcr = UART_FCR_ENABLE_FIFO;
			fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
			SET_MOXA_MUST_FIFO_VALUE(info);
		} else
			fcr = 0;
	} else {
		fcr = UART_FCR_ENABLE_FIFO;
		/* following add by Victor Yu. 08-30-2002 */
		if (info->IsMoxaMustChipFlag) {
			fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
			SET_MOXA_MUST_FIFO_VALUE(info);
		} else {
			/* above add by Victor Yu. 08-30-2002 */
			switch (info->rx_trigger) {
			case 1:
				fcr |= UART_FCR_TRIGGER_1;
				break;
			case 4:
				fcr |= UART_FCR_TRIGGER_4;
				break;
			case 8:
				fcr |= UART_FCR_TRIGGER_8;
				break;
			default:
				fcr |= UART_FCR_TRIGGER_14;
				break;
			}
		}
	}

	/* CTS flow control flag and modem status interrupts */
	info->IER &= ~UART_IER_MSI;
	info->MCR &= ~UART_MCR_AFE;
	if (cflag & CRTSCTS) {
		info->flags |= ASYNC_CTS_FLOW;
		info->IER |= UART_IER_MSI;
		if ((info->type == PORT_16550A) || (info->IsMoxaMustChipFlag)) {
			info->MCR |= UART_MCR_AFE;
			/* status = mxser_get_msr(info->base, 0, info->port); */
/*
	save_flags(flags);
	cli();
	status = inb(baseaddr + UART_MSR);
	restore_flags(flags);
*/
			/* mxser_check_modem_status(info, status); */
		} else {
			/* status = mxser_get_msr(info->base, 0, info->port); */
			/* MX_LOCK(&info->slock); */
			status = inb(info->base + UART_MSR);
			/* MX_UNLOCK(&info->slock); */
			if (info->tty->hw_stopped) {
				if (status & UART_MSR_CTS) {
					info->tty->hw_stopped = 0;
					if ((info->type != PORT_16550A) &&
							(!info->IsMoxaMustChipFlag)) {
						info->IER |= UART_IER_THRI;
						outb(info->IER, info->base + UART_IER);
					}
					set_bit(MXSER_EVENT_TXLOW, &info->event);
					schedule_work(&info->tqueue);				}
			} else {
				if (!(status & UART_MSR_CTS)) {
					info->tty->hw_stopped = 1;
					if ((info->type != PORT_16550A) &&
							(!info->IsMoxaMustChipFlag)) {
						info->IER &= ~UART_IER_THRI;
						outb(info->IER, info->base + UART_IER);
					}
				}
			}
		}
	} else {
		info->flags &= ~ASYNC_CTS_FLOW;
	}
	outb(info->MCR, info->base + UART_MCR);
	if (cflag & CLOCAL) {
		info->flags &= ~ASYNC_CHECK_CD;
	} else {
		info->flags |= ASYNC_CHECK_CD;
		info->IER |= UART_IER_MSI;
	}
	outb(info->IER, info->base + UART_IER);

	/*
	 * Set up parity check flag
	 */
	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
	if (I_INPCK(info->tty))
		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
		info->read_status_mask |= UART_LSR_BI;

	info->ignore_status_mask = 0;

	if (I_IGNBRK(info->tty)) {
		info->ignore_status_mask |= UART_LSR_BI;
		info->read_status_mask |= UART_LSR_BI;
		/*
		 * If we're ignore parity and break indicators, ignore
		 * overruns too.  (For real raw support).
		 */
		if (I_IGNPAR(info->tty)) {
			info->ignore_status_mask |=
						UART_LSR_OE |
						UART_LSR_PE |
						UART_LSR_FE;
			info->read_status_mask |=
						UART_LSR_OE |
						UART_LSR_PE |
						UART_LSR_FE;
		}
	}
	/* following add by Victor Yu. 09-02-2002 */
	if (info->IsMoxaMustChipFlag) {
		spin_lock_irqsave(&info->slock, flags);
		SET_MOXA_MUST_XON1_VALUE(info->base, START_CHAR(info->tty));
		SET_MOXA_MUST_XOFF1_VALUE(info->base, STOP_CHAR(info->tty));
		if (I_IXON(info->tty)) {
			ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base);
		} else {
			DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base);
		}
		if (I_IXOFF(info->tty)) {
			ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->base);
		} else {
			DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->base);
		}
		/*
		   if ( I_IXANY(info->tty) ) {
		   info->MCR |= MOXA_MUST_MCR_XON_ANY;
		   ENABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(info->base);
		   } else {
		   info->MCR &= ~MOXA_MUST_MCR_XON_ANY;
		   DISABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(info->base);
		   }
		 */
		spin_unlock_irqrestore(&info->slock, flags);
	}
	/* above add by Victor Yu. 09-02-2002 */


	outb(fcr, info->base + UART_FCR);	/* set fcr */
	outb(cval, info->base + UART_LCR);

	return ret;
}


static int mxser_set_baud(struct mxser_struct *info, long newspd)
{
	int quot = 0;
	unsigned char cval;
	int ret = 0;
	unsigned long flags;

	if (!info->tty || !info->tty->termios)
		return ret;

	if (!(info->base))
		return ret;

	if (newspd > info->MaxCanSetBaudRate)
		return 0;

	info->realbaud = newspd;
	if (newspd == 134) {
		quot = (2 * info->baud_base / 269);
	} else if (newspd) {
		quot = info->baud_base / newspd;
		if (quot == 0)
			quot = 1;
	} else {
		quot = 0;
	}

	info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base);
	info->timeout += HZ / 50;	/* Add .02 seconds of slop */

	if (quot) {
		spin_lock_irqsave(&info->slock, flags);
		info->MCR |= UART_MCR_DTR;
		outb(info->MCR, info->base + UART_MCR);
		spin_unlock_irqrestore(&info->slock, flags);
	} else {
		spin_lock_irqsave(&info->slock, flags);
		info->MCR &= ~UART_MCR_DTR;
		outb(info->MCR, info->base + UART_MCR);
		spin_unlock_irqrestore(&info->slock, flags);
		return ret;
	}

	cval = inb(info->base + UART_LCR);

	outb(cval | UART_LCR_DLAB, info->base + UART_LCR);	/* set DLAB */

	outb(quot & 0xff, info->base + UART_DLL);	/* LS of divisor */
	outb(quot >> 8, info->base + UART_DLM);	/* MS of divisor */
	outb(cval, info->base + UART_LCR);	/* reset DLAB */


	return ret;
}

/*
 * ------------------------------------------------------------
 * friends of mxser_ioctl()
 * ------------------------------------------------------------
 */
static int mxser_get_serial_info(struct mxser_struct *info, struct serial_struct __user *retinfo)
{
	struct serial_struct tmp;

	if (!retinfo)
		return -EFAULT;
	memset(&tmp, 0, sizeof(tmp));
	tmp.type = info->type;
	tmp.line = info->port;
	tmp.port = info->base;
	tmp.irq = info->irq;
	tmp.flags = info->flags;
	tmp.baud_base = info->baud_base;
	tmp.close_delay = info->close_delay;
	tmp.closing_wait = info->closing_wait;
	tmp.custom_divisor = info->custom_divisor;
	tmp.hub6 = 0;
	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
		return -EFAULT;
	return 0;
}

static int mxser_set_serial_info(struct mxser_struct *info, struct serial_struct __user *new_info)
{
	struct serial_struct new_serial;
	unsigned int flags;
	int retval = 0;

	if (!new_info || !info->base)
		return -EFAULT;
	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
		return -EFAULT;

	if ((new_serial.irq != info->irq) ||
			(new_serial.port != info->base) ||
			(new_serial.custom_divisor != info->custom_divisor) ||
			(new_serial.baud_base != info->baud_base))
		return -EPERM;

	flags = info->flags & ASYNC_SPD_MASK;

	if (!capable(CAP_SYS_ADMIN)) {
		if ((new_serial.baud_base != info->baud_base) ||
				(new_serial.close_delay != info->close_delay) ||
				((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK)))
			return -EPERM;
		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
				(new_serial.flags & ASYNC_USR_MASK));
	} else {
		/*
		 * OK, past this point, all the error checking has been done.
		 * At this point, we start making changes.....
		 */
		info->flags = ((info->flags & ~ASYNC_FLAGS) |
				(new_serial.flags & ASYNC_FLAGS));
		info->close_delay = new_serial.close_delay * HZ / 100;
		info->closing_wait = new_serial.closing_wait * HZ / 100;
		info->tty->low_latency =
				(info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
		info->tty->low_latency = 0;	/* (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; */
	}

	/* added by casper, 3/17/2000, for mouse */
	info->type = new_serial.type;

	process_txrx_fifo(info);

	if (info->flags & ASYNC_INITIALIZED) {
		if (flags != (info->flags & ASYNC_SPD_MASK)) {
			mxser_change_speed(info, NULL);
		}
	} else {
		retval = mxser_startup(info);
	}
	return retval;
}

/*
 * mxser_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 mxser_get_lsr_info(struct mxser_struct *info, unsigned int __user *value)
{
	unsigned char status;
	unsigned int result;
	unsigned long flags;

	spin_lock_irqsave(&info->slock, flags);
	status = inb(info->base + UART_LSR);
	spin_unlock_irqrestore(&info->slock, flags);
	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
	return put_user(result, value);
}

/*
 * This routine sends a break character out the serial port.
 */
static void mxser_send_break(struct mxser_struct *info, int duration)
{
	unsigned long flags;

	if (!info->base)
		return;
	set_current_state(TASK_INTERRUPTIBLE);
	spin_lock_irqsave(&info->slock, flags);
	outb(inb(info->base + UART_LCR) | UART_LCR_SBC,
		info->base + UART_LCR);
	spin_unlock_irqrestore(&info->slock, flags);
	schedule_timeout(duration);
	spin_lock_irqsave(&info->slock, flags);
	outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC,
		info->base + UART_LCR);
	spin_unlock_irqrestore(&info->slock, flags);
}

static int mxser_tiocmget(struct tty_struct *tty, struct file *file)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned char control, status;
	unsigned long flags;


	if (tty->index == MXSER_PORTS)
		return -ENOIOCTLCMD;
	if (tty->flags & (1 << TTY_IO_ERROR))
		return -EIO;

	control = info->MCR;

	spin_lock_irqsave(&info->slock, flags);
	status = inb(info->base + UART_MSR);
	if (status & UART_MSR_ANY_DELTA)
		mxser_check_modem_status(info, status);
	spin_unlock_irqrestore(&info->slock, flags);
	return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
		    ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
		    ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) |
		    ((status & UART_MSR_RI) ? TIOCM_RNG : 0) |
		    ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) |
		    ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
}

static int mxser_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear)
{
	struct mxser_struct *info = tty->driver_data;
	unsigned long flags;


	if (tty->index == MXSER_PORTS)
		return -ENOIOCTLCMD;
	if (tty->flags & (1 << TTY_IO_ERROR))
		return -EIO;

	spin_lock_irqsave(&info->slock, flags);

	if (set & TIOCM_RTS)
		info->MCR |= UART_MCR_RTS;
	if (set & TIOCM_DTR)
		info->MCR |= UART_MCR_DTR;

	if (clear & TIOCM_RTS)
		info->MCR &= ~UART_MCR_RTS;
	if (clear & TIOCM_DTR)
		info->MCR &= ~UART_MCR_DTR;

	outb(info->MCR, info->base + UART_MCR);
	spin_unlock_irqrestore(&info->slock, flags);
	return 0;
}


static int mxser_read_register(int, unsigned short *);
static int mxser_program_mode(int);
static void mxser_normal_mode(int);

static int mxser_get_ISA_conf(int cap, struct mxser_hwconf *hwconf)
{
	int id, i, bits;
	unsigned short regs[16], irq;
	unsigned char scratch, scratch2;

	hwconf->IsMoxaMustChipFlag = MOXA_OTHER_UART;

	id = mxser_read_register(cap, regs);
	if (id == C168_ASIC_ID) {
		hwconf->board_type = MXSER_BOARD_C168_ISA;
		hwconf->ports = 8;
	} else if (id == C104_ASIC_ID) {
		hwconf->board_type = MXSER_BOARD_C104_ISA;
		hwconf->ports = 4;
	} else if (id == C102_ASIC_ID) {
		hwconf->board_type = MXSER_BOARD_C102_ISA;
		hwconf->ports = 2;
	} else if (id == CI132_ASIC_ID) {
		hwconf->board_type = MXSER_BOARD_CI132;