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...
pan> rxwinon(struct channel *ch); static void txwinon(struct channel *ch); static void memoff(struct channel *ch); static void assertgwinon(struct channel *ch); static void assertmemoff(struct channel *ch); /* ---- Begin more 'specific' memory functions for cx_like products --- */ static void pcxem_memwinon(struct board_info *b, unsigned int win); static void pcxem_memwinoff(struct board_info *b, unsigned int win); static void pcxem_globalwinon(struct channel *ch); static void pcxem_rxwinon(struct channel *ch); static void pcxem_txwinon(struct channel *ch); static void pcxem_memoff(struct channel *ch); /* ------ Begin more 'specific' memory functions for the pcxe ------- */ static void pcxe_memwinon(struct board_info *b, unsigned int win); static void pcxe_memwinoff(struct board_info *b, unsigned int win); static void pcxe_globalwinon(struct channel *ch); static void pcxe_rxwinon(struct channel *ch); static void pcxe_txwinon(struct channel *ch); static void pcxe_memoff(struct channel *ch); /* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */ /* Note : pc64xe and pcxi share the same windowing routines */ static void pcxi_memwinon(struct board_info *b, unsigned int win); static void pcxi_memwinoff(struct board_info *b, unsigned int win); static void pcxi_globalwinon(struct channel *ch); static void pcxi_rxwinon(struct channel *ch); static void pcxi_txwinon(struct channel *ch); static void pcxi_memoff(struct channel *ch); /* - Begin 'specific' do nothing memory functions needed for some cards - */ static void dummy_memwinon(struct board_info *b, unsigned int win); static void dummy_memwinoff(struct board_info *b, unsigned int win); static void dummy_globalwinon(struct channel *ch); static void dummy_rxwinon(struct channel *ch); static void dummy_txwinon(struct channel *ch); static void dummy_memoff(struct channel *ch); static void dummy_assertgwinon(struct channel *ch); static void dummy_assertmemoff(struct channel *ch); /* ------------------- Begin declare functions ----------------------- */ static struct channel *verifyChannel(struct tty_struct *); static void pc_sched_event(struct channel *, int); static void epca_error(int, char *); static void pc_close(struct tty_struct *, struct file *); static void shutdown(struct channel *); static void pc_hangup(struct tty_struct *); static void pc_put_char(struct tty_struct *, unsigned char); static int pc_write_room(struct tty_struct *); static int pc_chars_in_buffer(struct tty_struct *); static void pc_flush_buffer(struct tty_struct *); static void pc_flush_chars(struct tty_struct *); static int block_til_ready(struct tty_struct *, struct file *, struct channel *); static int pc_open(struct tty_struct *, struct file *); static void post_fep_init(unsigned int crd); static void epcapoll(unsigned long); static void doevent(int); static void fepcmd(struct channel *, int, int, int, int, int); static unsigned termios2digi_h(struct channel *ch, unsigned); static unsigned termios2digi_i(struct channel *ch, unsigned); static unsigned termios2digi_c(struct channel *ch, unsigned); static void epcaparam(struct tty_struct *, struct channel *); static void receive_data(struct channel *); static int pc_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); static int info_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); static void pc_set_termios(struct tty_struct *, struct termios *); static void do_softint(void *); static void pc_stop(struct tty_struct *); static void pc_start(struct tty_struct *); static void pc_throttle(struct tty_struct * tty); static void pc_unthrottle(struct tty_struct *tty); static void digi_send_break(struct channel *ch, int msec); static void setup_empty_event(struct tty_struct *tty, struct channel *ch); void epca_setup(char *, int *); static int get_termio(struct tty_struct *, struct termio __user *); static int pc_write(struct tty_struct *, const unsigned char *, int); static int pc_init(void); static int init_PCI(void); /* ------------------------------------------------------------------ Table of functions for each board to handle memory. Mantaining parallelism is a *very* good idea here. The idea is for the runtime code to blindly call these functions, not knowing/caring about the underlying hardware. This stuff should contain no conditionals; if more functionality is needed a different entry should be established. These calls are the interface calls and are the only functions that should be accessed. Anyone caught making direct calls deserves what they get. -------------------------------------------------------------------- */ static void memwinon(struct board_info *b, unsigned int win) { (b->memwinon)(b, win); } static void memwinoff(struct board_info *b, unsigned int win) { (b->memwinoff)(b, win); } static void globalwinon(struct channel *ch) { (ch->board->globalwinon)(ch); } static void rxwinon(struct channel *ch) { (ch->board->rxwinon)(ch); } static void txwinon(struct channel *ch) { (ch->board->txwinon)(ch); } static void memoff(struct channel *ch) { (ch->board->memoff)(ch); } static void assertgwinon(struct channel *ch) { (ch->board->assertgwinon)(ch); } static void assertmemoff(struct channel *ch) { (ch->board->assertmemoff)(ch); } /* --------------------------------------------------------- PCXEM windowing is the same as that used in the PCXR and CX series cards. ------------------------------------------------------------ */ static void pcxem_memwinon(struct board_info *b, unsigned int win) { outb_p(FEPWIN|win, b->port + 1); } static void pcxem_memwinoff(struct board_info *b, unsigned int win) { outb_p(0, b->port + 1); } static void pcxem_globalwinon(struct channel *ch) { outb_p( FEPWIN, (int)ch->board->port + 1); } static void pcxem_rxwinon(struct channel *ch) { outb_p(ch->rxwin, (int)ch->board->port + 1); } static void pcxem_txwinon(struct channel *ch) { outb_p(ch->txwin, (int)ch->board->port + 1); } static void pcxem_memoff(struct channel *ch) { outb_p(0, (int)ch->board->port + 1); } /* ----------------- Begin pcxe memory window stuff ------------------ */ static void pcxe_memwinon(struct board_info *b, unsigned int win) { outb_p(FEPWIN | win, b->port + 1); } static void pcxe_memwinoff(struct board_info *b, unsigned int win) { outb_p(inb(b->port) & ~FEPMEM, b->port + 1); outb_p(0, b->port + 1); } static void pcxe_globalwinon(struct channel *ch) { outb_p( FEPWIN, (int)ch->board->port + 1); } static void pcxe_rxwinon(struct channel *ch) { outb_p(ch->rxwin, (int)ch->board->port + 1); } static void pcxe_txwinon(struct channel *ch) { outb_p(ch->txwin, (int)ch->board->port + 1); } static void pcxe_memoff(struct channel *ch) { outb_p(0, (int)ch->board->port); outb_p(0, (int)ch->board->port + 1); } /* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ static void pcxi_memwinon(struct board_info *b, unsigned int win) { outb_p(inb(b->port) | FEPMEM, b->port); } static void pcxi_memwinoff(struct board_info *b, unsigned int win) { outb_p(inb(b->port) & ~FEPMEM, b->port); } static void pcxi_globalwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_rxwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_txwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_memoff(struct channel *ch) { outb_p(0, ch->board->port); } static void pcxi_assertgwinon(struct channel *ch) { epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off"); } static void pcxi_assertmemoff(struct channel *ch) { epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on"); } /* ---------------------------------------------------------------------- Not all of the cards need specific memory windowing routines. Some cards (Such as PCI) needs no windowing routines at all. We provide these do nothing routines so that the same code base can be used. The driver will ALWAYS call a windowing routine if it thinks it needs to; regardless of the card. However, dependent on the card the routine may or may not do anything. ---------------------------------------------------------------------------*/ static void dummy_memwinon(struct board_info *b, unsigned int win) { } static void dummy_memwinoff(struct board_info *b, unsigned int win) { } static void dummy_globalwinon(struct channel *ch) { } static void dummy_rxwinon(struct channel *ch) { } static void dummy_txwinon(struct channel *ch) { } static void dummy_memoff(struct channel *ch) { } static void dummy_assertgwinon(struct channel *ch) { } static void dummy_assertmemoff(struct channel *ch) { } /* ----------------- Begin verifyChannel function ----------------------- */ static struct channel *verifyChannel(struct tty_struct *tty) { /* Begin verifyChannel */ /* -------------------------------------------------------------------- This routine basically provides a sanity check. It insures that the channel returned is within the proper range of addresses as well as properly initialized. If some bogus info gets passed in through tty->driver_data this should catch it. --------------------------------------------------------------------- */ if (tty) { struct channel *ch = (struct channel *)tty->driver_data; if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) { if (ch->magic == EPCA_MAGIC) return ch; } } return NULL; } /* End verifyChannel */ /* ------------------ Begin pc_sched_event ------------------------- */ static void pc_sched_event(struct channel *ch, int event) { /* ---------------------------------------------------------------------- We call this to schedule interrupt processing on some event. The kernel sees our request and calls the related routine in OUR driver. -------------------------------------------------------------------------*/ ch->event |= 1 << event; schedule_work(&ch->tqueue); } /* End pc_sched_event */ /* ------------------ Begin epca_error ------------------------- */ static void epca_error(int line, char *msg) { printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); } /* ------------------ Begin pc_close ------------------------- */ static void pc_close(struct tty_struct * tty, struct file * filp) { struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if ch != NULL */ spin_lock_irqsave(&epca_lock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&epca_lock, flags); return; } /* Check to see if the channel is open more than once */ if (ch->count-- > 1) { /* Begin channel is open more than once */ /* ------------------------------------------------------------- Return without doing anything. Someone might still be using the channel. ---------------------------------------------------------------- */ spin_unlock_irqrestore(&epca_lock, flags); return; } /* End channel is open more than once */ /* Port open only once go ahead with shutdown & reset */ BUG_ON(ch->count < 0); /* --------------------------------------------------------------- Let the rest of the driver know the channel is being closed. This becomes important if an open is attempted before close is finished. ------------------------------------------------------------------ */ ch->asyncflags |= ASYNC_CLOSING; tty->closing = 1; spin_unlock_irqrestore(&epca_lock, flags); if (ch->asyncflags & ASYNC_INITIALIZED) { /* Setup an event to indicate when the transmit buffer empties */ setup_empty_event(tty, ch); tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ } if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); spin_lock_irqsave(&epca_lock, flags); tty->closing = 0; ch->event = 0; ch->tty = NULL; spin_unlock_irqrestore(&epca_lock, flags); if (ch->blocked_open) { /* Begin if blocked_open */ if (ch->close_delay) msleep_interruptible(jiffies_to_msecs(ch->close_delay)); wake_up_interruptible(&ch->open_wait); } /* End if blocked_open */ ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait); } /* End if ch != NULL */ } /* End pc_close */ /* ------------------ Begin shutdown ------------------------- */ static void shutdown(struct channel *ch) { /* Begin shutdown */ unsigned long flags; struct tty_struct *tty; struct board_chan __iomem *bc; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; /* ------------------------------------------------------------------ In order for an event to be generated on the receipt of data the idata flag must be set. Since we are shutting down, this is not necessary clear this flag. --------------------------------------------------------------------- */ if (bc) writeb(0, &bc->idata); tty = ch->tty; /* ---------------------------------------------------------------- If we're a modem control device and HUPCL is on, drop RTS & DTR. ------------------------------------------------------------------ */ if (tty->termios->c_cflag & HUPCL) { ch->omodem &= ~(ch->m_rts | ch->m_dtr); fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); } memoff(ch); /* ------------------------------------------------------------------ The channel has officialy been closed. The next time it is opened it will have to reinitialized. Set a flag to indicate this. ---------------------------------------------------------------------- */ /* Prevent future Digi programmed interrupts from coming active */ ch->asyncflags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&epca_lock, flags); } /* End shutdown */ /* ------------------ Begin pc_hangup ------------------------- */ static void pc_hangup(struct tty_struct *tty) { /* Begin pc_hangup */ struct channel *ch; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if ch != NULL */ unsigned long flags; if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); spin_lock_irqsave(&epca_lock, flags); ch->tty = NULL; ch->event = 0; ch->count = 0; ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); spin_unlock_irqrestore(&epca_lock, flags); wake_up_interruptible(&ch->open_wait); } /* End if ch != NULL */ } /* End pc_hangup */ /* ------------------ Begin pc_write ------------------------- */ static int pc_write(struct tty_struct * tty, const unsigned char *buf, int bytesAvailable) { /* Begin pc_write */ unsigned int head, tail; int dataLen; int size; int amountCopied; struct channel *ch; unsigned long flags; int remain; struct board_chan __iomem *bc; /* ---------------------------------------------------------------- pc_write is primarily called directly by the kernel routine tty_write (Though it can also be called by put_char) found in tty_io.c. pc_write is passed a line discipline buffer where the data to be written out is stored. The line discipline implementation itself is done at the kernel level and is not brought into the driver. ------------------------------------------------------------------- */ /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return 0; /* Make a pointer to the channel data structure found on the board. */ bc = ch->brdchan; size = ch->txbufsize; amountCopied = 0; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); head = readw(&bc->tin) & (size - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); tail &= (size - 1); /* If head >= tail, head has not wrapped around. */ if (head >= tail) { /* Begin head has not wrapped */ /* --------------------------------------------------------------- remain (much like dataLen above) represents the total amount of space available on the card for data. Here dataLen represents the space existing between the head pointer and the end of buffer. This is important because a memcpy cannot be told to automatically wrap around when it hits the buffer end. ------------------------------------------------------------------ */ dataLen = size - head; remain = size - (head - tail) - 1; } else { /* Begin head has wrapped around */ remain = tail - head - 1; dataLen = remain; } /* End head has wrapped around */ /* ------------------------------------------------------------------- Check the space on the card. If we have more data than space; reduce the amount of data to fit the space. ---------------------------------------------------------------------- */ bytesAvailable = min(remain, bytesAvailable); txwinon(ch); while (bytesAvailable > 0) { /* Begin while there is data to copy onto card */ /* ----------------------------------------------------------------- If head is not wrapped, the below will make sure the first data copy fills to the end of card buffer. ------------------------------------------------------------------- */ dataLen = min(bytesAvailable, dataLen); memcpy_toio(ch->txptr + head, buf, dataLen); buf += dataLen; head += dataLen; amountCopied += dataLen; bytesAvailable -= dataLen; if (head >= size) { head = 0; dataLen = tail; } } /* End while there is data to copy onto card */ ch->statusflags |= TXBUSY; globalwinon(ch); writew(head, &bc->tin); if ((ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return(amountCopied); } /* End pc_write */ /* ------------------ Begin pc_put_char ------------------------- */ static void pc_put_char(struct tty_struct *tty, unsigned char c) { /* Begin pc_put_char */ pc_write(tty, &c, 1); } /* End pc_put_char */ /* ------------------ Begin pc_write_room ------------------------- */ static int pc_write_room(struct tty_struct *tty) { /* Begin pc_write_room */ int remain; struct channel *ch; unsigned long flags; unsigned int head, tail; struct board_chan __iomem *bc; remain = 0; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; head = readw(&bc->tin) & (ch->txbufsize - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); /* Wrap tail if necessary */ tail &= (ch->txbufsize - 1); if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; if (remain && (ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* Return how much room is left on card */ return remain; } /* End pc_write_room */ /* ------------------ Begin pc_chars_in_buffer ---------------------- */ static int pc_chars_in_buffer(struct tty_struct *tty) { /* Begin pc_chars_in_buffer */ int chars; unsigned int ctail, head, tail; int remain; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return(0); spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); head = readw(&bc->tin); ctail = readw(&ch->mailbox->cout); if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0) chars = 0; else { /* Begin if some space on the card has been used */ head = readw(&bc->tin) & (ch->txbufsize - 1); tail &= (ch->txbufsize - 1); /* -------------------------------------------------------------- The logic here is basically opposite of the above pc_write_room here we are finding the amount of bytes in the buffer filled. Not the amount of bytes empty. ------------------------------------------------------------------- */ if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; chars = (int)(ch->txbufsize - remain); /* ------------------------------------------------------------- Make it possible to wakeup anything waiting for output in tty_ioctl.c, etc. If not already set. Setup an event to indicate when the transmit buffer empties ----------------------------------------------------------------- */ if (!(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); } /* End if some space on the card has been used */ memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); /* Return number of characters residing on card. */ return(chars); } /* End pc_chars_in_buffer */ /* ------------------ Begin pc_flush_buffer ---------------------- */ static void pc_flush_buffer(struct tty_struct *tty) { /* Begin pc_flush_buffer */ unsigned int tail; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); /* Have FEP move tout pointer; effectively flushing transmit buffer */ fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); wake_up_interruptible(&tty->write_wait); tty_wakeup(tty); } /* End pc_flush_buffer */ /* ------------------ Begin pc_flush_chars ---------------------- */ static void pc_flush_chars(struct tty_struct *tty) { /* Begin pc_flush_chars */ struct channel * ch; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { unsigned long flags; spin_lock_irqsave(&epca_lock, flags); /* ---------------------------------------------------------------- If not already set and the transmitter is busy setup an event to indicate when the transmit empties. ------------------------------------------------------------------- */ if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); } } /* End pc_flush_chars */ /* ------------------ Begin block_til_ready ---------------------- */ static int block_til_ready(struct tty_struct *tty, struct file *filp, struct channel *ch) { /* Begin block_til_ready */ DECLARE_WAITQUEUE(wait,current); int retval, do_clocal = 0; unsigned long flags; if (tty_hung_up_p(filp)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; return(retval); } /* ----------------------------------------------------------------- If the device is in the middle of being closed, then block until it's done, and then try again. -------------------------------------------------------------------- */ if (ch->asyncflags & ASYNC_CLOSING) { interruptible_sleep_on(&ch->close_wait); if (ch->asyncflags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } if (filp->f_flags & O_NONBLOCK) { /* ----------------------------------------------------------------- If non-blocking mode is set, then make the check up front and then exit. -------------------------------------------------------------------- */ ch->asyncflags |= 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 */ retval = 0; add_wait_queue(&ch->open_wait, &wait); spin_lock_irqsave(&epca_lock, flags); /* We dec count so that pc_close will know when to free things */ if (!tty_hung_up_p(filp)) ch->count--; ch->blocked_open++; while(1) { /* Begin forever while */ set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(ch->asyncflags & ASYNC_INITIALIZED)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || (ch->imodem & ch->dcd))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } spin_unlock_irqrestore(&epca_lock, flags); /* --------------------------------------------------------------- Allow someone else to be scheduled. We will occasionally go through this loop until one of the above conditions change. The below schedule call will allow other processes to enter and prevent this loop from hogging the cpu. ------------------------------------------------------------------ */ schedule(); spin_lock_irqsave(&epca_lock, flags); } /* End forever while */ current->state = TASK_RUNNING; remove_wait_queue(&ch->open_wait, &wait); if (!tty_hung_up_p(filp)) ch->count++; ch->blocked_open--; spin_unlock_irqrestore(&epca_lock, flags); if (retval) return retval; ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return 0; } /* End block_til_ready */ /* ------------------ Begin pc_open ---------------------- */ static int pc_open(struct tty_struct *tty, struct file * filp) { /* Begin pc_open */ struct channel *ch; unsigned long flags; int line, retval, boardnum; struct board_chan __iomem *bc; unsigned int head; line = tty->index; if (line < 0 || line >= nbdevs) return -ENODEV; ch = &digi_channels[line]; boardnum = ch->boardnum; /* Check status of board configured in system. */ /* ----------------------------------------------------------------- I check to see if the epca_setup routine detected an user error. It might be better to put this in pc_init, but for the moment it goes here. ---------------------------------------------------------------------- */ if (invalid_lilo_config) { if (setup_error_code & INVALID_BOARD_TYPE) printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n"); if (setup_error_code & INVALID_NUM_PORTS) printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n"); if (setup_error_code & INVALID_MEM_BASE) printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n"); if (setup_error_code & INVALID_PORT_BASE) printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n"); if (setup_error_code & INVALID_BOARD_STATUS) printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n"); if (setup_error_code & INVALID_ALTPIN) printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n"); tty->driver_data = NULL; /* Mark this device as 'down' */ return -ENODEV; } if (boardnum >= num_cards || boards[boardnum].status == DISABLED) { tty->driver_data = NULL; /* Mark this device as 'down' */ return(-ENODEV); } if ((bc = ch->brdchan) == 0) { tty->driver_data = NULL; return -ENODEV; } spin_lock_irqsave(&epca_lock, flags); /* ------------------------------------------------------------------ Every time a channel is opened, increment a counter. This is necessary because we do not wish to flush and shutdown the channel until the last app holding the channel open, closes it. --------------------------------------------------------------------- */ ch->count++; /* ---------------------------------------------------------------- Set a kernel structures pointer to our local channel structure. This way we can get to it when passed only a tty struct. ------------------------------------------------------------------ */ tty->driver_data = ch; /* ---------------------------------------------------------------- If this is the first time the channel has been opened, initialize the tty->termios struct otherwise let pc_close handle it. -------------------------------------------------------------------- */ globalwinon(ch); ch->statusflags = 0; /* Save boards current modem status */ ch->imodem = readb(&bc->mstat); /* ---------------------------------------------------------------- Set receive head and tail ptrs to each other. This indicates no data available to read. ----------------------------------------------------------------- */ head = readw(&bc->rin); writew(head, &bc->rout); /* Set the channels associated tty structure */ ch->tty = tty; /* ----------------------------------------------------------------- The below routine generally sets up parity, baud, flow control issues, etc.... It effect both control flags and input flags. -------------------------------------------------------------------- */ epcaparam(tty,ch); ch->asyncflags |= ASYNC_INITIALIZED; memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); retval = block_til_ready(tty, filp, ch); if (retval) return retval; /* ------------------------------------------------------------- Set this again in case a hangup set it to zero while this open() was waiting for the line... --------------------------------------------------------------- */ spin_lock_irqsave(&epca_lock, flags); ch->tty = tty; globalwinon(ch); /* Enable Digi Data events */ writeb(1, &bc->idata); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return 0; } /* End pc_open */ static int __init epca_module_init(void) { /* Begin init_module */ return pc_init(); } module_init(epca_module_init); static struct pci_driver epca_driver; static void __exit epca_module_exit(void) { int count, crd; struct board_info *bd; struct channel *ch; del_timer_sync(&epca_timer); if ((tty_unregister_driver(pc_driver)) || (tty_unregister_driver(pc_info))) { printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n"); return; } put_tty_driver(pc_driver); put_tty_driver(pc_info); for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ bd = &boards[crd]; if (!bd) { /* Begin sanity check */ printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n"); return; } /* End sanity check */ ch = card_ptr[crd]; for (count = 0; count < bd->numports; count++, ch++) { /* Begin for each port */ if (ch) { if (ch->tty) tty_hangup(ch->tty); kfree(ch->tmp_buf); } } /* End for each port */ } /* End for each card */ pci_unregister_driver (&epca_driver); } module_exit(epca_module_exit); static struct tty_operations pc_ops = { .open = pc_open, .close = pc_close, .write = pc_write, .write_room = pc_write_room, .flush_buffer = pc_flush_buffer, .chars_in_buffer = pc_chars_in_buffer, .flush_chars = pc_flush_chars, .put_char = pc_put_char, .ioctl = pc_ioctl, .set_termios = pc_set_termios, .stop = pc_stop, .start = pc_start, .throttle = pc_throttle, .unthrottle = pc_unthrottle, .hangup = pc_hangup, }; static int info_open(struct tty_struct *tty, struct file * filp) { return 0; } static struct tty_operations info_ops = { .open = info_open, .ioctl = info_ioctl, }; /* ------------------ Begin pc_init ---------------------- */ static int __init pc_init(void) { /* Begin pc_init */ int crd; struct board_info *bd; unsigned char board_id = 0; int pci_boards_found, pci_count; pci_count = 0; pc_driver = alloc_tty_driver(MAX_ALLOC); if (!pc_driver) return -ENOMEM; pc_info = alloc_tty_driver(MAX_ALLOC); if (!pc_info) { put_tty_driver(pc_driver); return -ENOMEM; } /* ----------------------------------------------------------------------- If epca_setup has not been ran by LILO set num_cards to defaults; copy board structure defined by digiConfig into drivers board structure. Note : If LILO has ran epca_setup then epca_setup will handle defining num_cards as well as copying the data into the board structure. -------------------------------------------------------------------------- */ if (!liloconfig) { /* Begin driver has been configured via. epcaconfig */ nbdevs = NBDEVS; num_cards = NUMCARDS; memcpy((void *)&boards, (void *)&static_boards, (sizeof(struct board_info) * NUMCARDS)); } /* End driver has been configured via. epcaconfig */ /* ----------------------------------------------------------------- Note : If lilo was used to configure the driver and the ignore epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards will equal 0 at this point. This is okay; PCI cards will still be picked up if detected. --------------------------------------------------------------------- */ /* ----------------------------------------------------------- Set up interrupt, we will worry about memory allocation in post_fep_init. --------------------------------------------------------------- */ printk(KERN_INFO "DIGI epca driver version %s loaded.\n",VERSION); /* ------------------------------------------------------------------ NOTE : This code assumes that the number of ports found in the boards array is correct. This could be wrong if the card in question is PCI (And therefore has no ports entry in the boards structure.) The rest of the information will be valid for PCI because the beginning of pc_init scans for PCI and determines i/o and base memory addresses. I am not sure if it is possible to read the number of ports supported by the card prior to it being booted (Since that is the state it is in when pc_init is run). Because it is not possible to query the number of supported ports until after the card has booted; we are required to calculate the card_ptrs as the card is is initialized (Inside post_fep_init). The negative thing about this approach is that digiDload's call to GET_INFO will have a bad port value. (Since this is called prior to post_fep_init.) --------------------------------------------------------------------- */ pci_boards_found = 0; if(num_cards < MAXBOARDS) pci_boards_found += init_PCI(); num_cards += pci_boards_found; pc_driver->owner = THIS_MODULE; pc_driver->name = "ttyD"; pc_driver->devfs_name = "tts/D"; pc_driver->major = DIGI_MAJOR; pc_driver->minor_start = 0; pc_driver->type = TTY_DRIVER_TYPE_SERIAL; pc_driver->subtype = SERIAL_TYPE_NORMAL; pc_driver->init_termios = tty_std_termios; pc_driver->init_termios.c_iflag = 0; pc_driver->init_termios.c_oflag = 0; pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; pc_driver->init_termios.c_lflag = 0; pc_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(pc_driver, &pc_ops); pc_info->owner = THIS_MODULE; pc_info->name = "digi_ctl"; pc_info->major = DIGIINFOMAJOR; pc_info->minor_start = 0; pc_info->type = TTY_DRIVER_TYPE_SERIAL; pc_info->subtype = SERIAL_TYPE_INFO; pc_info->init_termios = tty_std_termios; pc_info->init_termios.c_iflag = 0; pc_info->init_termios.c_oflag = 0; pc_info->init_termios.c_lflag = 0; pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; pc_info->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(pc_info, &info_ops); for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ /* ------------------------------------------------------------------ This is where the appropriate memory handlers for the hardware is set. Everything at runtime blindly jumps through these vectors. ---------------------------------------------------------------------- */ /* defined in epcaconfig.h */ bd = &boards[crd]; switch (bd->type) { /* Begin switch on bd->type {board type} */ case PCXEM: case EISAXEM: bd->memwinon = pcxem_memwinon ; bd->memwinoff = pcxem_memwinoff ; bd->globalwinon = pcxem_globalwinon ; bd->txwinon = pcxem_txwinon ; bd->rxwinon = pcxem_rxwinon ; bd->memoff = pcxem_memoff ; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCIXEM: case PCIXRJ: case PCIXR: bd->memwinon = dummy_memwinon; bd->memwinoff = dummy_memwinoff; bd->globalwinon = dummy_globalwinon; bd->txwinon = dummy_txwinon; bd->rxwinon = dummy_rxwinon; bd->memoff = dummy_memoff; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCXE: case PCXEVE: bd->memwinon = pcxe_memwinon; bd->memwinoff = pcxe_memwinoff; bd->globalwinon = pcxe_globalwinon; bd->txwinon = pcxe_txwinon; bd->rxwinon = pcxe_rxwinon; bd->memoff = pcxe_memoff; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCXI: case PC64XE: bd->memwinon = pcxi_memwinon; bd->memwinoff = pcxi_memwinoff; bd->globalwinon = pcxi_globalwinon; bd->txwinon = pcxi_txwinon; bd->rxwinon = pcxi_rxwinon; bd->memoff = pcxi_memoff; bd->assertgwinon = pcxi_assertgwinon; bd->assertmemoff = pcxi_assertmemoff; break; default: break; } /* End switch on bd->type */ /* --------------------------------------------------------------- Some cards need a memory segment to be defined for use in transmit and receive windowing operations. These boards are listed in the below switch. In the case of the XI the amount of memory on the board is variable so the memory_seg is also variable. This code determines what they segment should be. ----------------------------------------------------------------- */ switch (bd->type) { /* Begin switch on bd->type {board type} */ case PCXE: case PCXEVE: case PC64XE: bd->memory_seg = 0xf000; break; case PCXI: board_id = inb((int)bd->port); if ((board_id & 0x1) == 0x1) { /* Begin it's an XI card */ /* Is it a 64K board */ if ((board_id & 0x30) == 0) bd->memory_seg = 0xf000; /* Is it a 128K board */ if ((board_id & 0x30) == 0x10) bd->memory_seg = 0xe000; /* Is is a 256K board */ if ((board_id & 0x30) == 0x20) bd->memory_seg = 0xc000; /* Is it a 512K board */ if ((board_id & 0x30) == 0x30) bd->memory_seg = 0x8000; } else printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); break; } /* End switch on bd->type */ } /* End for each card */ if (tty_register_driver(pc_driver)) panic("Couldn't register Digi PC/ driver"); if (tty_register_driver(pc_info)) panic("Couldn't register Digi PC/ info "); /* ------------------------------------------------------------------- Start up the poller to check for events on all enabled boards ---------------------------------------------------------------------- */ init_timer(&epca_timer); epca_timer.function = epcapoll; mod_timer(&epca_timer, jiffies + HZ/25); return 0; } /* End pc_init */ /* ------------------ Begin post_fep_init ---------------------- */ static void post_fep_init(unsigned int crd) { /* Begin post_fep_init */ int i; void __iomem *memaddr; struct global_data __iomem *gd; struct board_info *bd; struct board_chan __iomem *bc; struct channel *ch; int shrinkmem = 0, lowwater ; /* ------------------------------------------------------------- This call is made by the user via. the ioctl call DIGI_INIT. It is responsible for setting up all the card specific stuff. ---------------------------------------------------------------- */ bd = &boards[crd]; /* ----------------------------------------------------------------- If this is a PCI board, get the port info. Remember PCI cards do not have entries into the epcaconfig.h file, so we can't get the number of ports from it. Unfortunetly, this means that anyone doing a DIGI_GETINFO before the board has booted will get an invalid number of ports returned (It should return 0). Calls to DIGI_GETINFO after DIGI_INIT has been called will return the proper values. ------------------------------------------------------------------- */ if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */ /* -------------------------------------------------------------------- Below we use XEMPORTS as a memory offset regardless of which PCI card it is. This is because all of the supported PCI cards have the same memory offset for the channel data. This will have to be changed if we ever develop a PCI/XE card. NOTE : The FEP manual states that the port offset is 0xC22 as opposed to 0xC02. This is only true for PC/XE, and PC/XI cards; not for the XEM, or CX series. On the PCI cards the number of ports is determined by reading a ID PROM located in the box attached to the card. The card can then determine the index the id to determine the number of ports available. (FYI - The id should be located at 0x1ac (And may use up to 4 bytes if the box in question is a XEM or CX)). ------------------------------------------------------------------------ */ /* PCI cards are already remapped at this point ISA are not */ bd->numports = readw(bd->re_map_membase + XEMPORTS); epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); nbdevs += (bd->numports); } else { /* Fix up the mappings for ISA/EISA etc */ /* FIXME: 64K - can we be smarter ? */ bd->re_map_membase = ioremap(bd->membase, 0x10000); } if (crd != 0) card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; else card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ ch = card_ptr[crd]; epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); memaddr = bd->re_map_membase; /* ----------------------------------------------------------------- The below assignment will set bc to point at the BEGINING of the cards channel structures. For 1 card there will be between 8 and 64 of these structures. -------------------------------------------------------------------- */ bc = memaddr + CHANSTRUCT; /* ------------------------------------------------------------------- The below assignment will set gd to point at the BEGINING of global memory address 0xc00. The first data in that global memory actually starts at address 0xc1a. The command in pointer begins at 0xd10. ---------------------------------------------------------------------- */ gd = memaddr + GLOBAL; /* -------------------------------------------------------------------- XEPORTS (address 0xc22) points at the number of channels the card supports. (For 64XE, XI, XEM, and XR use 0xc02) ----------------------------------------------------------------------- */ if ((bd->type == PCXEVE || bd->type == PCXE) && (readw(memaddr + XEPORTS) < 3)) shrinkmem = 1; if (bd->type < PCIXEM) if (!request_region((int)bd->port, 4, board_desc[bd->type])) return; memwinon(bd, 0); /* -------------------------------------------------------------------- Remember ch is the main drivers channels structure, while bc is the cards channel structure. ------------------------------------------------------------------------ */ /* For every port on the card do ..... */ for (i = 0; i < bd->numports; i++, ch++, bc++) { /* Begin for each port */ unsigned long flags; u16 tseg, rseg; ch->brdchan = bc; ch->mailbox = gd; INIT_WORK(&ch->tqueue, do_softint, ch); ch->board = &boards[crd]; spin_lock_irqsave(&epca_lock, flags); switch (bd->type) { /* ---------------------------------------------------------------- Since some of the boards use different bitmaps for their control signals we cannot hard code these values and retain portability. We virtualize this data here. ------------------------------------------------------------------- */ case EISAXEM: case PCXEM: case PCIXEM: case PCIXRJ: case PCIXR: ch->m_rts = 0x02 ; ch->m_dcd = 0x80 ; ch->m_dsr = 0x20 ; ch->m_cts = 0x10 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x01 ; break; case PCXE: case PCXEVE: case PCXI: case PC64XE: ch->m_rts = 0x02 ; ch->m_dcd = 0x08 ; ch->m_dsr = 0x10 ; ch->m_cts = 0x20 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x80 ; break; } /* End switch bd->type */ if (boards[crd].altpin) { ch->dsr = ch->m_dcd; ch->dcd = ch->m_dsr; ch->digiext.digi_flags |= DIGI_ALTPIN; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } ch->boardnum = crd; ch->channelnum = i; ch->magic = EPCA_MAGIC; ch->tty = NULL; if (shrinkmem) { fepcmd(ch, SETBUFFER, 32, 0, 0, 0); shrinkmem = 0; } tseg = readw(&bc->tseg); rseg = readw(&bc->rseg); switch (bd->type) { case PCIXEM: case PCIXRJ: case PCIXR: /* Cover all the 2MEG cards */ ch->txptr = memaddr + ((tseg << 4) & 0x1fffff); ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEM: case EISAXEM: /* Cover all the 32K windowed cards */ /* Mask equal to window size - 1 */ ch->txptr = memaddr + ((tseg << 4) & 0x7fff); ch->rxptr = memaddr + ((rseg << 4) & 0x7fff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEVE: case PCXE: ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) & 0x1fff); ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) & 0x1fff); ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >>9 ); break; case PCXI: case PC64XE: ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4); ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4); ch->txwin = ch->rxwin = 0; break; } /* End switch bd->type */ ch->txbufhead = 0; ch->txbufsize = readw(&bc->tmax) + 1; ch->rxbufhead = 0; ch->rxbufsize = readw(&bc->rmax) + 1; lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); /* Set transmitter low water mark */ fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); /* Set receiver low water mark */ fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); /* Set receiver high water mark */ fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); writew(100, &bc->edelay); writeb(1, &bc->idata); ch->startc = readb(&bc->startc); ch->stopc = readb(&bc->stopc); ch->startca = readb(&bc->startca); ch->stopca = readb(&bc->stopca); ch->fepcflag = 0; ch->fepiflag = 0; ch->fepoflag = 0; ch->fepstartc = 0; ch->fepstopc = 0; ch->fepstartca = 0; ch->fepstopca = 0; ch->close_delay = 50; ch->count = 0; ch->blocked_open = 0; init_waitqueue_head(&ch->open_wait); init_waitqueue_head(&ch->close_wait); spin_unlock_irqrestore(&epca_lock, flags); ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); if (!ch->tmp_buf) { printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); release_region((int)bd->port, 4); while(i-- > 0) kfree((ch--)->tmp_buf); return; } else memset((void *)ch->tmp_buf,0,ch->txbufsize); } /* End for each port */ printk(KERN_INFO "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); memwinoff(bd, 0); } /* End post_fep_init */ /* --------------------- Begin epcapoll ------------------------ */ static void epcapoll(unsigned long ignored) { /* Begin epcapoll */ unsigned long flags; int crd; volatile unsigned int head, tail; struct channel *ch; struct board_info *bd; /* ------------------------------------------------------------------- This routine is called upon every timer interrupt. Even though the Digi series cards are capable of generating interrupts this method of non-looping polling is more efficient. This routine checks for card generated events (Such as receive data, are transmit buffer empty) and acts on those events. ----------------------------------------------------------------------- */ for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ bd = &boards[crd]; ch = card_ptr[crd]; if ((bd->status == DISABLED) || digi_poller_inhibited) continue; /* Begin loop next interation */ /* ----------------------------------------------------------- assertmemoff is not needed here; indeed it is an empty subroutine. It is being kept because future boards may need this as well as some legacy boards. ---------------------------------------------------------------- */ spin_lock_irqsave(&epca_lock, flags); assertmemoff(ch); globalwinon(ch); /* --------------------------------------------------------------- In this case head and tail actually refer to the event queue not the transmit or receive queue. ------------------------------------------------------------------- */ head = readw(&ch->mailbox->ein); tail = readw(&ch->mailbox->eout); /* If head isn't equal to tail we have an event */ if (head != tail) doevent(crd); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* End for each card */ mod_timer(&epca_timer, jiffies + (HZ / 25)); } /* End epcapoll */ /* --------------------- Begin doevent ------------------------ */ static void doevent(int crd) { /* Begin doevent */ void __iomem *eventbuf; struct channel *ch, *chan0; static struct tty_struct *tty; struct board_info *bd; struct board_chan __iomem *bc; unsigned int tail, head; int event, channel; int mstat, lstat; /* ------------------------------------------------------------------- This subroutine is called by epcapoll when an event is detected in the event queue. This routine responds to those events. --------------------------------------------------------------------- */ bd = &boards[crd]; chan0 = card_ptr[crd]; epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); assertgwinon(chan0); while ((tail = readw(&chan0->mailbox->eout)) != (head = readw(&chan0->mailbox->ein))) { /* Begin while something in event queue */ assertgwinon(chan0); eventbuf = bd->re_map_membase + tail + ISTART; /* Get the channel the event occurred on */ channel = readb(eventbuf); /* Get the actual event code that occurred */ event = readb(eventbuf + 1); /* ---------------------------------------------------------------- The two assignments below get the current modem status (mstat) and the previous modem status (lstat). These are useful becuase an event could signal a change in modem signals itself. ------------------------------------------------------------------- */ mstat = readb(eventbuf + 2); lstat = readb(eventbuf + 3); ch = chan0 + channel; if ((unsigned)channel >= bd->numports || !ch) { if (channel >= bd->numports) ch = chan0; bc = ch->brdchan; goto next; } if ((bc = ch->brdchan) == NULL) goto next; if (event & DATA_IND) { /* Begin DATA_IND */ receive_data(ch); assertgwinon(ch); } /* End DATA_IND */ /* else *//* Fix for DCD transition missed bug */ if (event & MODEMCHG_IND) { /* Begin MODEMCHG_IND */ /* A modem signal change has been indicated */ ch->imodem = mstat; if (ch->asyncflags & ASYNC_CHECK_CD) { if (mstat & ch->dcd) /* We are now receiving dcd */ wake_up_interruptible(&ch->open_wait); else pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ } } /* End MODEMCHG_IND */ tty = ch->tty; if (tty) { /* Begin if valid tty */ if (event & BREAK_IND) { /* Begin if BREAK_IND */ /* A break has been indicated */ tty_insert_flip_char(tty, 0, TTY_BREAK); tty_schedule_flip(tty); } else if (event & LOWTX_IND) { /* Begin LOWTX_IND */ if (ch->statusflags & LOWWAIT) { /* Begin if LOWWAIT */ ch->statusflags &= ~LOWWAIT; tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } /* End if LOWWAIT */ } else if (event & EMPTYTX_IND) { /* Begin EMPTYTX_IND */ /* This event is generated by setup_empty_event */ ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { /* Begin if EMPTYWAIT */ ch->statusflags &= ~EMPTYWAIT; tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } /* End if EMPTYWAIT */ } /* End EMPTYTX_IND */ } /* End if valid tty */ next: globalwinon(ch); BUG_ON(!bc); writew(1, &bc->idata); writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout); globalwinon(chan0); } /* End while something in event queue */ } /* End doevent */ /* --------------------- Begin fepcmd ------------------------ */ static void fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds, int bytecmd) { /* Begin fepcmd */ unchar __iomem *memaddr; unsigned int head, cmdTail, cmdStart, cmdMax; long count; int n; /* This is the routine in which commands may be passed to the card. */ if (ch->board->status == DISABLED) return; assertgwinon(ch); /* Remember head (As well as max) is just an offset not a base addr */ head = readw(&ch->mailbox->cin); /* cmdStart is a base address */ cmdStart = readw(&ch->mailbox->cstart); /* ------------------------------------------------------------------ We do the addition below because we do not want a max pointer relative to cmdStart. We want a max pointer that points at the physical end of the command queue. -------------------------------------------------------------------- */ cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax)); memaddr = ch->board->re_map_membase; if (head >= (cmdMax - cmdStart) || (head & 03)) { printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", __LINE__, cmd, head); printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", __LINE__, cmdMax, cmdStart); return; } if (bytecmd) { writeb(cmd, memaddr + head + cmdStart + 0); writeb(ch->channelnum, memaddr + head + cmdStart + 1); /* Below word_or_byte is bits to set */ writeb(word_or_byte, memaddr + head + cmdStart + 2); /* Below byte2 is bits to reset */ writeb(byte2, memaddr + head + cmdStart + 3); } else { writeb(cmd, memaddr + head + cmdStart + 0); writeb(ch->channelnum, memaddr + head + cmdStart + 1); writeb(word_or_byte, memaddr + head + cmdStart + 2); } head = (head + 4) & (cmdMax - cmdStart - 4); writew(head, &ch->mailbox->cin); count = FEPTIMEOUT; for (;;) { /* Begin forever loop */ count--; if (count == 0) { printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n"); return; } head = readw(&ch->mailbox->cin); cmdTail = readw(&ch->mailbox->cout); n = (head - cmdTail) & (cmdMax - cmdStart - 4); /* ---------------------------------------------------------- Basically this will break when the FEP acknowledges the command by incrementing cmdTail (Making it equal to head). ------------------------------------------------------------- */ if (n <= ncmds * (sizeof(short) * 4)) break; /* Well nearly forever :-) */ } /* End forever loop */ } /* End fepcmd */ /* --------------------------------------------------------------------- Digi products use fields in their channels structures that are very similar to the c_cflag and c_iflag fields typically found in UNIX termios structures. The below three routines allow mappings between these hardware "flags" and their respective Linux flags. ------------------------------------------------------------------------- */ /* --------------------- Begin termios2digi_h -------------------- */ static unsigned termios2digi_h(struct channel *ch, unsigned cflag) { /* Begin termios2digi_h */ unsigned res = 0; if (cflag & CRTSCTS) { ch->digiext.digi_flags |= (RTSPACE | CTSPACE); res |= ((ch->m_cts) | (ch->m_rts)); } if (ch->digiext.digi_flags & RTSPACE) res |= ch->m_rts; if (ch->digiext.digi_flags & DTRPACE) res |= ch->m_dtr; if (ch->digiext.digi_flags & CTSPACE) res |= ch->m_cts; if (ch->digiext.digi_flags & DSRPACE) res |= ch->dsr; if (ch->digiext.digi_flags & DCDPACE) res |= ch->dcd; if (res & (ch->m_rts)) ch->digiext.digi_flags |= RTSPACE; if (res & (ch->m_cts)) ch->digiext.digi_flags |= CTSPACE; return res; } /* End termios2digi_h */ /* --------------------- Begin termios2digi_i -------------------- */ static unsigned termios2digi_i(struct channel *ch, unsigned iflag) { /* Begin termios2digi_i */ unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP|IXON|IXANY|IXOFF); if (ch->digiext.digi_flags & DIGI_AIXON) res |= IAIXON; return res; } /* End termios2digi_i */ /* --------------------- Begin termios2digi_c -------------------- */ static unsigned termios2digi_c(struct channel *ch, unsigned cflag) { /* Begin termios2digi_c */ unsigned res = 0; if (cflag & CBAUDEX) { /* Begin detected CBAUDEX */ ch->digiext.digi_flags |= DIGI_FAST; /* ------------------------------------------------------------- HUPCL bit is used by FEP to indicate fast baud table is to be used. ----------------------------------------------------------------- */ res |= FEP_HUPCL; } /* End detected CBAUDEX */ else ch->digiext.digi_flags &= ~DIGI_FAST; /* ------------------------------------------------------------------- CBAUD has bit position 0x1000 set these days to indicate Linux baud rate remap. Digi hardware can't handle the bit assignment. (We use a different bit assignment for high speed.). Clear this bit out. ---------------------------------------------------------------------- */ res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); /* ------------------------------------------------------------- This gets a little confusing. The Digi cards have their own representation of c_cflags controling baud rate. For the most part this is identical to the Linux implementation. However; Digi supports one rate (76800) that Linux doesn't. This means that the c_cflag entry that would normally mean 76800 for Digi actually means 115200 under Linux. Without the below mapping, a stty 115200 would only drive the board at 76800. Since the rate 230400 is also found after 76800, the same problem afflicts us when we choose a rate of 230400. Without the below modificiation stty 230400 would actually give us 115200. There are two additional differences. The Linux value for CLOCAL (0x800; 0004000) has no meaning to the Digi hardware. Also in later releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000) ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be checked for a screened out prior to termios2digi_c returning. Since CLOCAL isn't used by the board this can be ignored as long as the returned value is used only by Digi hardware. ----------------------------------------------------------------- */ if (cflag & CBAUDEX) { /* ------------------------------------------------------------- The below code is trying to guarantee that only baud rates 115200 and 230400 are remapped. We use exclusive or because the various baud rates share common bit positions and therefore can't be tested for easily. ----------------------------------------------------------------- */ if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) res += 1; } return res; } /* End termios2digi_c */ /* --------------------- Begin epcaparam ----------------------- */ /* Caller must hold the locks */ static void epcaparam(struct tty_struct *tty, struct channel *ch) { /* Begin epcaparam */ unsigned int cmdHead; struct termios *ts; struct board_chan __iomem *bc; unsigned mval, hflow, cflag, iflag; bc = ch->brdchan; epcaassert(bc !=0, "bc out of range"); assertgwinon(ch); ts = tty->termios; if ((ts->c_cflag & CBAUD) == 0) { /* Begin CBAUD detected */ cmdHead = readw(&bc->rin); writew(cmdHead, &bc->rout); cmdHead = readw(&bc->tin); /* Changing baud in mid-stream transmission can be wonderful */ /* --------------------------------------------------------------- Flush current transmit buffer by setting cmdTail pointer (tout) to cmdHead pointer (tin). Hopefully the transmit buffer is empty. ----------------------------------------------------------------- */ fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); mval = 0; } else { /* Begin CBAUD not detected */ /* ------------------------------------------------------------------- c_cflags have changed but that change had nothing to do with BAUD. Propagate the change to the card. ---------------------------------------------------------------------- */ cflag = termios2digi_c(ch, ts->c_cflag); if (cflag != ch->fepcflag) { ch->fepcflag = cflag; /* Set baud rate, char size, stop bits, parity */ fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); } /* ---------------------------------------------------------------- If the user has not forced CLOCAL and if the device is not a CALLOUT device (Which is always CLOCAL) we set flags such that the driver will wait on carrier detect. ------------------------------------------------------------------- */ if (ts->c_cflag & CLOCAL) ch->asyncflags &= ~ASYNC_CHECK_CD; else ch->asyncflags |= ASYNC_CHECK_CD; mval = ch->m_dtr | ch->m_rts; } /* End CBAUD not detected */ iflag = termios2digi_i(ch, ts->c_iflag); /* Check input mode flags */ if (iflag != ch->fepiflag) { ch->fepiflag = iflag; /* --------------------------------------------------------------- Command sets channels iflag structure on the board. Such things as input soft flow control, handling of parity errors, and break handling are all set here. ------------------------------------------------------------------- */ /* break handling, parity handling, input stripping, flow control chars */ fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); } /* --------------------------------------------------------------- Set the board mint value for this channel. This will cause hardware events to be generated each time the DCD signal (Described in mint) changes. ------------------------------------------------------------------- */ writeb(ch->dcd, &bc->mint); if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) if (ch->digiext.digi_flags & DIGI_FORCEDCD) writeb(0, &bc->mint); ch->imodem = readb(&bc->mstat); hflow = termios2digi_h(ch, ts->c_cflag); if (hflow != ch->hflow) { ch->hflow = hflow; /* -------------------------------------------------------------- Hard flow control has been selected but the board is not using it. Activate hard flow control now. ----------------------------------------------------------------- */ fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); } mval ^= ch->modemfake & (mval ^ ch->modem); if (ch->omodem ^ mval) { ch->omodem = mval; /* -------------------------------------------------------------- The below command sets the DTR and RTS mstat structure. If hard flow control is NOT active these changes will drive the output of the actual DTR and RTS lines. If hard flow control is active, the changes will be saved in the mstat structure and only asserted when hard flow control is turned off. ----------------------------------------------------------------- */ /* First reset DTR & RTS; then set them */ fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); fepcmd(ch, SETMODEM, mval, 0, 0, 1); } if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { ch->fepstartc = ch->startc; ch->fepstopc = ch->stopc; /* ------------------------------------------------------------ The XON / XOFF characters have changed; propagate these changes to the card. --------------------------------------------------------------- */ fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); } if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { ch->fepstartca = ch->startca; ch->fepstopca = ch->stopca; /* --------------------------------------------------------------- Similar to the above, this time the auxilarly XON / XOFF characters have changed; propagate these changes to the card. ------------------------------------------------------------------ */ fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); } } /* End epcaparam */ /* --------------------- Begin receive_data ----------------------- */ /* Caller holds lock */ static void receive_data(struct channel *ch) { /* Begin receive_data */ unchar *rptr; struct termios *ts = NULL; struct tty_struct *tty; struct board_chan __iomem *bc; int dataToRead, wrapgap, bytesAvailable; unsigned int tail, head; unsigned int wrapmask; /* --------------------------------------------------------------- This routine is called by doint when a receive data event has taken place. ------------------------------------------------------------------- */ globalwinon(ch); if (ch->statusflags & RXSTOPPED) return; tty = ch->tty; if (tty) ts = tty->termios; bc = ch->brdchan; BUG_ON(!bc); wrapmask = ch->rxbufsize - 1; /* --------------------------------------------------------------------- Get the head and tail pointers to the receiver queue. Wrap the head pointer if it has reached the end of the buffer. ------------------------------------------------------------------------ */ head = readw(&bc->rin); head &= wrapmask; tail = readw(&bc->rout) & wrapmask; bytesAvailable = (head - tail) & wrapmask; if (bytesAvailable == 0) return; /* ------------------------------------------------------------------ If CREAD bit is off or device not open, set TX tail to head --------------------------------------------------------------------- */ if (!tty || !ts || !(ts->c_cflag & CREAD)) { writew(head, &bc->rout); return; } if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0) return; if (readb(&bc->orun)) { writeb(0, &bc->orun); printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",tty->name); tty_insert_flip_char(tty, 0, TTY_OVERRUN); } rxwinon(ch); while (bytesAvailable > 0) { /* Begin while there is data on the card */ wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; /* --------------------------------------------------------------- Even if head has wrapped around only report the amount of data to be equal to the size - tail. Remember memcpy can't automaticly wrap around the receive buffer. ----------------------------------------------------------------- */ dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable; /* -------------------------------------------------------------- Make sure we don't overflow the buffer ----------------------------------------------------------------- */ dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead); if (dataToRead == 0) break; /* --------------------------------------------------------------- Move data read from our card into the line disciplines buffer for translation if necessary. ------------------------------------------------------------------ */ memcpy_fromio(rptr, ch->rxptr + tail, dataToRead); tail = (tail + dataToRead) & wrapmask; bytesAvailable -= dataToRead; } /* End while there is data on the card */ globalwinon(ch); writew(tail, &bc->rout); /* Must be called with global data */ tty_schedule_flip(ch->tty); return; } /* End receive_data */ static int info_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { switch (cmd) { /* Begin switch cmd */ case DIGI_GETINFO: { /* Begin case DIGI_GETINFO */ struct digi_info di ; int brd; if(get_user(brd, (unsigned int __user *)arg)) return -EFAULT; if (brd < 0 || brd >= num_cards || num_cards == 0) return -ENODEV; memset(&di, 0, sizeof(di)); di.board = brd ; di.status = boards[brd].status; di.type = boards[brd].type ; di.numports = boards[brd].numports ; /* Legacy fixups - just move along nothing to see */ di.port = (unsigned char *)boards[brd].port ; di.membase = (unsigned char *)boards[brd].membase ; if (copy_to_user((void __user *)arg, &di, sizeof (di))) return -EFAULT; break; } /* End case DIGI_GETINFO */ case DIGI_POLLER: { /* Begin case DIGI_POLLER */ int brd = arg & 0xff000000 >> 16 ; unsigned char state = arg & 0xff ; if (brd < 0 || brd >= num_cards) { printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n"); return (-ENODEV); } digi_poller_inhibited = state ; break ; } /* End case DIGI_POLLER */ case DIGI_INIT: { /* Begin case DIGI_INIT */ /* ------------------------------------------------------------ This call is made by the apps to complete the initilization of the board(s). This routine is responsible for setting the card to its initial state and setting the drivers control fields to the sutianle settings for the card in question. ---------------------------------------------------------------- */ int crd ; for (crd = 0; crd < num_cards; crd++) post_fep_init (crd); break ; } /* End case DIGI_INIT */ default: return -ENOTTY; } /* End switch cmd */ return (0) ; } /* --------------------- Begin pc_ioctl ----------------------- */ static int pc_tiocmget(struct tty_struct *tty, struct file *file) { struct channel *ch = (struct channel *) tty->driver_data; struct board_chan __iomem *bc; unsigned int mstat, mflag = 0; unsigned long flags; if (ch) bc = ch->brdchan; else return -EINVAL; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); mstat = readb(&bc->mstat); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); if (mstat & ch->m_dtr) mflag |= TIOCM_DTR; if (mstat & ch->m_rts) mflag |= TIOCM_RTS; if (mstat & ch->m_cts) mflag |= TIOCM_CTS; if (mstat & ch->dsr) mflag |= TIOCM_DSR; if (mstat & ch->m_ri) mflag |= TIOCM_RI; if (mstat & ch->dcd) mflag |= TIOCM_CD; return mflag; } static int pc_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct channel *ch = (struct channel *) tty->driver_data; unsigned long flags; if (!ch) return -EINVAL; spin_lock_irqsave(&epca_lock, flags); /* * I think this modemfake stuff is broken. It doesn't * correctly reflect the behaviour desired by the TIOCM* * ioctls. Therefore this is probably broken. */ if (set & TIOCM_RTS) { ch->modemfake |= ch->m_rts; ch->modem |= ch->m_rts; } if (set & TIOCM_DTR) { ch->modemfake |= ch->m_dtr; ch->modem |= ch->m_dtr; } if (clear & TIOCM_RTS) { ch->modemfake |= ch->m_rts; ch->modem &= ~ch->m_rts; } if (clear & TIOCM_DTR) { ch->modemfake |= ch->m_dtr; ch->modem &= ~ch->m_dtr; } globalwinon(ch); /* -------------------------------------------------------------- The below routine generally sets up parity, baud, flow control issues, etc.... It effect both control flags and input flags. ------------------------------------------------------------------ */ epcaparam(tty,ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return 0; } static int pc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { /* Begin pc_ioctl */ digiflow_t dflow; int retval; unsigned long flags; unsigned int mflag, mstat; unsigned char startc, stopc; struct board_chan __iomem *bc; struct channel *ch = (struct channel *) tty->driver_data; void __user *argp = (void __user *)arg; if (ch) bc = ch->brdchan; else return -EINVAL; /* ------------------------------------------------------------------- For POSIX compliance we need to add more ioctls. See tty_ioctl.c in /usr/src/linux/drivers/char for a good example. In particular think about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS. ---------------------------------------------------------------------- */ switch (cmd) { /* Begin switch cmd */ case TCGETS: if (copy_to_user(argp, tty->termios, sizeof(struct termios))) return -EFAULT; return 0; case TCGETA: return get_termio(tty, argp); case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); if (!arg) digi_send_break(ch, HZ/4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)arg)) return -EFAULT; return 0; case TIOCSSOFTCAR: { unsigned int value; if (get_user(value, (unsigned __user *)argp)) return -EFAULT; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (value ? CLOCAL : 0)); return 0; } case TIOCMODG: mflag = pc_tiocmget(tty, file); if (put_user(mflag, (unsigned long __user *)argp)) return -EFAULT; break; case TIOCMODS: if (get_user(mstat, (unsigned __user *)argp)) return -EFAULT; return pc_tiocmset(tty, file, mstat, ~mstat); case TIOCSDTR: spin_lock_irqsave(&epca_lock, flags); ch->omodem |= ch->m_dtr; globalwinon(ch); fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case TIOCCDTR: spin_lock_irqsave(&epca_lock, flags); ch->omodem &= ~ch->m_dtr; globalwinon(ch); fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case DIGI_GETA: if (copy_to_user(argp, &ch->digiext, sizeof(digi_t))) return -EFAULT; break; case DIGI_SETAW: case DIGI_SETAF: if (cmd == DIGI_SETAW) { /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); } else { /* ldisc lock already held in ioctl */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } /* Fall Thru */ case DIGI_SETA: if (copy_from_user(&ch->digiext, argp, sizeof(digi_t))) return -EFAULT; if (ch->digiext.digi_flags & DIGI_ALTPIN) { ch->dcd = ch->m_dsr; ch->dsr = ch->m_dcd; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); /* ----------------------------------------------------------------- The below routine generally sets up parity, baud, flow control issues, etc.... It effect both control flags and input flags. ------------------------------------------------------------------- */ epcaparam(tty,ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case DIGI_GETFLOW: case DIGI_GETAFLOW: spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); if (cmd == DIGI_GETFLOW) { dflow.startc = readb(&bc->startc); dflow.stopc = readb(&bc->stopc); } else { dflow.startc = readb(&bc->startca); dflow.stopc = readb(&bc->stopca); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); if (copy_to_user(argp, &dflow, sizeof(dflow))) return -EFAULT; break; case DIGI_SETAFLOW: case DIGI_SETFLOW: if (cmd == DIGI_SETFLOW) { startc = ch->startc; stopc = ch->stopc; } else { startc = ch->startca; stopc = ch->stopca; } if (copy_from_user(&dflow, argp, sizeof(dflow))) return -EFAULT; if (dflow.startc != startc || dflow.stopc != stopc) { /* Begin if setflow toggled */ spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); if (cmd == DIGI_SETFLOW) { ch->fepstartc = ch->startc = dflow.startc; ch->fepstopc = ch->stopc = dflow.stopc; fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); } else { ch->fepstartca = ch->startca = dflow.startc; ch->fepstopca = ch->stopca = dflow.stopc; fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); } if (ch->statusflags & TXSTOPPED) pc_start(tty); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* End if setflow toggled */ break; default: return -ENOIOCTLCMD; } /* End switch cmd */ return 0; } /* End pc_ioctl */ /* --------------------- Begin pc_set_termios ----------------------- */ static void pc_set_termios(struct tty_struct *tty, struct termios *old_termios) { /* Begin pc_set_termios */ struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if channel valid */ spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); epcaparam(tty, ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && ((tty->termios->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&ch->open_wait); } /* End if channel valid */ } /* End pc_set_termios */ /* --------------------- Begin do_softint ----------------------- */ static void do_softint(void *private_) { /* Begin do_softint */ struct channel *ch = (struct channel *) private_; /* Called in response to a modem change event */ if (ch && ch->magic == EPCA_MAGIC) { /* Begin EPCA_MAGIC */ struct tty_struct *tty = ch->tty; if (tty && tty->driver_data) { if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { /* Begin if clear_bit */ tty_hangup(tty); /* FIXME: module removal race here - AKPM */ wake_up_interruptible(&ch->open_wait); ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; } /* End if clear_bit */ } } /* End EPCA_MAGIC */ } /* End do_softint */ /* ------------------------------------------------------------ pc_stop and pc_start provide software flow control to the routine and the pc_ioctl routine. ---------------------------------------------------------------- */ /* --------------------- Begin pc_stop ----------------------- */ static void pc_stop(struct tty_struct *tty) { /* Begin pc_stop */ struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if valid channel */ spin_lock_irqsave(&epca_lock, flags); if ((ch->statusflags & TXSTOPPED) == 0) { /* Begin if transmit stop requested */ globalwinon(ch); /* STOP transmitting now !! */ fepcmd(ch, PAUSETX, 0, 0, 0, 0); ch->statusflags |= TXSTOPPED; memoff(ch); } /* End if transmit stop requested */ spin_unlock_irqrestore(&epca_lock, flags); } /* End if valid channel */ } /* End pc_stop */ /* --------------------- Begin pc_start ----------------------- */ static void pc_start(struct tty_struct *tty) { /* Begin pc_start */ struct channel *ch; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if channel valid */ unsigned long flags; spin_lock_irqsave(&epca_lock, flags); /* Just in case output was resumed because of a change in Digi-flow */ if (ch->statusflags & TXSTOPPED) { /* Begin transmit resume requested */ struct board_chan __iomem *bc; globalwinon(ch); bc = ch->brdchan; if (ch->statusflags & LOWWAIT) writeb(1, &bc->ilow); /* Okay, you can start transmitting again... */ fepcmd(ch, RESUMETX, 0, 0, 0, 0); ch->statusflags &= ~TXSTOPPED; memoff(ch); } /* End transmit resume requested */ spin_unlock_irqrestore(&epca_lock, flags); } /* End if channel valid */ } /* End pc_start */ /* ------------------------------------------------------------------ The below routines pc_throttle and pc_unthrottle are used to slow (And resume) the receipt of data into the kernels receive buffers. The exact occurrence of this depends on the size of the kernels receive buffer and what the 'watermarks' are set to for that buffer. See the n_ttys.c file for more details. ______________________________________________________________________ */ /* --------------------- Begin throttle ----------------------- */ static void pc_throttle(struct tty_struct * tty) { /* Begin pc_throttle */ struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if channel valid */ spin_lock_irqsave(&epca_lock, flags); if ((ch->statusflags & RXSTOPPED) == 0) { globalwinon(ch); fepcmd(ch, PAUSERX, 0, 0, 0, 0); ch->statusflags |= RXSTOPPED; memoff(ch); } spin_unlock_irqrestore(&epca_lock, flags); } /* End if channel valid */ } /* End pc_throttle */ /* --------------------- Begin unthrottle ----------------------- */ static void pc_unthrottle(struct tty_struct *tty) { /* Begin pc_unthrottle */ struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if channel valid */ /* Just in case output was resumed because of a change in Digi-flow */ spin_lock_irqsave(&epca_lock, flags); if (ch->statusflags & RXSTOPPED) { globalwinon(ch); fepcmd(ch, RESUMERX, 0, 0, 0, 0); ch->statusflags &= ~RXSTOPPED; memoff(ch); } spin_unlock_irqrestore(&epca_lock, flags); } /* End if channel valid */ } /* End pc_unthrottle */ /* --------------------- Begin digi_send_break ----------------------- */ void digi_send_break(struct channel *ch, int msec) { /* Begin digi_send_break */ unsigned long flags; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); /* -------------------------------------------------------------------- Maybe I should send an infinite break here, schedule() for msec amount of time, and then stop the break. This way, the user can't screw up the FEP by causing digi_send_break() to be called (i.e. via an ioctl()) more than once in msec amount of time. Try this for now... ------------------------------------------------------------------------ */ fepcmd(ch, SENDBREAK, msec, 0, 10, 0); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* End digi_send_break */ /* --------------------- Begin setup_empty_event ----------------------- */ /* Caller MUST hold the lock */ static void setup_empty_event(struct tty_struct *tty, struct channel *ch) { /* Begin setup_empty_event */ struct board_chan __iomem *bc = ch->brdchan; globalwinon(ch); ch->statusflags |= EMPTYWAIT; /* ------------------------------------------------------------------ When set the iempty flag request a event to be generated when the transmit buffer is empty (If there is no BREAK in progress). --------------------------------------------------------------------- */ writeb(1, &bc->iempty); memoff(ch); } /* End setup_empty_event */ /* --------------------- Begin get_termio ----------------------- */ static int get_termio(struct tty_struct * tty, struct termio __user * termio) { /* Begin get_termio */ return kernel_termios_to_user_termio(termio, tty->termios); } /* End get_termio */ /* ---------------------- Begin epca_setup -------------------------- */ void epca_setup(char *str, int *ints) { /* Begin epca_setup */ struct board_info board; int index, loop, last; char *temp, *t2; unsigned len; /* ---------------------------------------------------------------------- If this routine looks a little strange it is because it is only called if a LILO append command is given to boot the kernel with parameters. In this way, we can provide the user a method of changing his board configuration without rebuilding the kernel. ----------------------------------------------------------------------- */ if (!liloconfig) liloconfig = 1; memset(&board, 0, sizeof(board)); /* Assume the data is int first, later we can change it */ /* I think that array position 0 of ints holds the number of args */ for (last = 0, index = 1; index <= ints[0]; index++) switch(index) { /* Begin parse switch */ case 1: board.status = ints[index]; /* --------------------------------------------------------- We check for 2 (As opposed to 1; because 2 is a flag instructing the driver to ignore epcaconfig.) For this reason we check for 2. ------------------------------------------------------------ */ if (board.status == 2) { /* Begin ignore epcaconfig as well as lilo cmd line */ nbdevs = 0; num_cards = 0; return; } /* End ignore epcaconfig as well as lilo cmd line */ if (board.status > 2) { printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n", board.status); invalid_lilo_config = 1; setup_error_code |= INVALID_BOARD_STATUS; return; } last = index; break; case 2: board.type = ints[index]; if (board.type >= PCIXEM) { printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type); invalid_lilo_config = 1; setup_error_code |= INVALID_BOARD_TYPE; return; } last = index; break; case 3: board.altpin = ints[index]; if (board.altpin > 1) { printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin); invalid_lilo_config = 1; setup_error_code |= INVALID_ALTPIN; return; } last = index; break; case 4: board.numports = ints[index]; if (board.numports < 2 || board.numports > 256) { printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports); invalid_lilo_config = 1; setup_error_code |= INVALID_NUM_PORTS; return; } nbdevs += board.numports; last = index; break; case 5: board.port = ints[index]; if (ints[index] <= 0) { printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port); invalid_lilo_config = 1; setup_error_code |= INVALID_PORT_BASE; return; } last = index; break; case 6: board.membase = ints[index]; if (ints[index] <= 0) { printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n",(unsigned int)board.membase); invalid_lilo_config = 1; setup_error_code |= INVALID_MEM_BASE; return; } last = index; break; default: printk(KERN_ERR "<Error> - epca_setup: Too many integer parms\n"); return; } /* End parse switch */ while (str && *str) { /* Begin while there is a string arg */ /* find the next comma or terminator */ temp = str; /* While string is not null, and a comma hasn't been found */ while (*temp && (*temp != ',')) temp++; if (!*temp) temp = NULL; else *temp++ = 0; /* Set index to the number of args + 1 */ index = last + 1; switch(index) { case 1: len = strlen(str); if (strncmp("Disable", str, len) == 0) board.status = 0; else if (strncmp("Enable", str, len) == 0) board.status = 1; else { printk(KERN_ERR "epca_setup: Invalid status %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_BOARD_STATUS; return; } last = index; break; case 2: for(loop = 0; loop < EPCA_NUM_TYPES; loop++) if (strcmp(board_desc[loop], str) == 0) break; /* --------------------------------------------------------------- If the index incremented above refers to a legitamate board type set it here. ------------------------------------------------------------------*/ if (index < EPCA_NUM_TYPES) board.type = loop; else { printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_BOARD_TYPE; return; } last = index; break; case 3: len = strlen(str); if (strncmp("Disable", str, len) == 0) board.altpin = 0; else if (strncmp("Enable", str, len) == 0) board.altpin = 1; else { printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_ALTPIN; return; } last = index; break; case 4: t2 = str; while (isdigit(*t2)) t2++; if (*t2) { printk(KERN_ERR "epca_setup: Invalid port count %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_NUM_PORTS; return; } /* ------------------------------------------------------------ There is not a man page for simple_strtoul but the code can be found in vsprintf.c. The first argument is the string to translate (To an unsigned long obviously), the second argument can be the address of any character variable or a NULL. If a variable is given, the end pointer of the string will be stored in that variable; if a NULL is given the end pointer will not be returned. The last argument is the base to use. If a 0 is indicated, the routine will attempt to determine the proper base by looking at the values prefix (A '0' for octal, a 'x' for hex, etc ... If a value is given it will use that value as the base. ---------------------------------------------------------------- */ board.numports = simple_strtoul(str, NULL, 0); nbdevs += board.numports; last = index; break; case 5: t2 = str; while (isxdigit(*t2)) t2++; if (*t2) { printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str); invalid_lilo_config = 1; setup_error_code |= INVALID_PORT_BASE; return; } board.port = simple_strtoul(str, NULL, 16); last = index; break; case 6: t2 = str; while (isxdigit(*t2)) t2++; if (*t2) { printk(KERN_ERR "epca_setup: Invalid memory base %s\n",str); invalid_lilo_config = 1; setup_error_code |= INVALID_MEM_BASE; return; } board.membase = simple_strtoul(str, NULL, 16); last = index; break; default: printk(KERN_ERR "epca: Too many string parms\n"); return; } str = temp; } /* End while there is a string arg */ if (last < 6) { printk(KERN_ERR "epca: Insufficient parms specified\n"); return; } /* I should REALLY validate the stuff here */ /* Copies our local copy of board into boards */ memcpy((void *)&boards[num_cards],(void *)&board, sizeof(board)); /* Does this get called once per lilo arg are what ? */ printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", num_cards, board_desc[board.type], board.numports, (int)board.port, (unsigned int) board.membase); num_cards++; } /* End epca_setup */ /* ------------------------ Begin init_PCI --------------------------- */ enum epic_board_types { brd_xr = 0, brd_xem, brd_cx, brd_xrj, }; /* indexed directly by epic_board_types enum */ static struct { unsigned char board_type; unsigned bar_idx; /* PCI base address region */ } epca_info_tbl[] = { { PCIXR, 0, }, { PCIXEM, 0, }, { PCICX, 0, }, { PCIXRJ, 2, }, }; static int __devinit epca_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int board_num = -1; int board_idx, info_idx = ent->driver_data; unsigned long addr; if (pci_enable_device(pdev)) return -EIO; board_num++; board_idx = board_num + num_cards; if (board_idx >= MAXBOARDS) goto err_out; addr = pci_resource_start (pdev, epca_info_tbl[info_idx].bar_idx); if (!addr) { printk (KERN_ERR PFX "PCI region #%d not available (size 0)\n", epca_info_tbl[info_idx].bar_idx); goto err_out; } boards[board_idx].status = ENABLED; boards[board_idx].type = epca_info_tbl[info_idx].board_type; boards[board_idx].numports = 0x0; boards[board_idx].port = addr + PCI_IO_OFFSET; boards[board_idx].membase = addr; if (!request_mem_region (addr + PCI_IO_OFFSET, 0x200000, "epca")) { printk (KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out; } boards[board_idx].re_map_port = ioremap(addr + PCI_IO_OFFSET, 0x200000); if (!boards[board_idx].re_map_port) { printk (KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out_free_pciio; } if (!request_mem_region (addr, 0x200000, "epca")) { printk (KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n", 0x200000, addr); goto err_out_free_iounmap; } boards[board_idx].re_map_membase = ioremap(addr, 0x200000); if (!boards[board_idx].re_map_membase) { printk (KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n", 0x200000, addr + PCI_IO_OFFSET); goto err_out_free_memregion; } /* -------------------------------------------------------------- I don't know what the below does, but the hardware guys say its required on everything except PLX (In this case XRJ). ---------------------------------------------------------------- */ if (info_idx != brd_xrj) { pci_write_config_byte(pdev, 0x40, 0); pci_write_config_byte(pdev, 0x46, 0); } return 0; err_out_free_memregion: release_mem_region (addr, 0x200000); err_out_free_iounmap: iounmap (boards[board_idx].re_map_port); err_out_free_pciio: release_mem_region (addr + PCI_IO_OFFSET, 0x200000); err_out: return -ENODEV; } static struct pci_device_id epca_pci_tbl[] = { { PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr }, { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem }, { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx }, { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj }, { 0, } }; MODULE_DEVICE_TABLE(pci, epca_pci_tbl); int __init init_PCI (void) { /* Begin init_PCI */ memset (&epca_driver, 0, sizeof (epca_driver)); epca_driver.name = "epca"; epca_driver.id_table = epca_pci_tbl; epca_driver.probe = epca_init_one; return pci_register_driver(&epca_driver); } MODULE_LICENSE("GPL");