ࡱ; }  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry  !"#$%&'()*+,-./12346789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst ®`VTextStarWriter 5.0p<{:\ SfxDocumentInfo Mike {1bMike {1<1Mike {1u Info 0 Info 1 Info 2 Info 3 {1bЊ 5<44Standard LIBIMBEDDED LIBIMBEDDED TASK,0,1,H32,0,100,1,3808;478805;110;0;474224;14971;482528;0;0SW5HDR.0{1<1!Outline0 #ZSBX sb Z Standard StarBASICSBX ARSBX AR SBX AR2c%bqqSWG, A<  #$%&'()*./0123456789:;<=>?@ABCDGHK  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFQRSTUd0'&@ TimesNewRomanX'@d X' @X'@X+'$@X'(!@4. !. XA'2*@fdxdxdxdXXJJ@X@d%P' TimesNewRoman$' '(. . p. @ . . . . . P. . . !. $. `'. 0*. -. /. 2. p5. @8. ;. =. @. 6')(0N2@ StandardStandard@ Text bodyStandard Text body@2A'List Text bodyList'CaptionStandardCaption'' '2A'JJIndexStandardIndex'JJ FooterStandardFooter@('JJTable Contents Text bodyTable Contents@JJ Table HeadingTable Contents Table Heading@ ''$+'JJ<|?}Oh+'0 h t 53@n2a@V-@@:dMike Mike SW5HDR.0{1<1! Frameformat ZeichenformatTextformatvorlageStandard Text bodyListCaption IndexFooterTable Contents Table HeadingRoot 12 Table1 13 Table2 14Standard  Illustration Table TextDrawingY .Y .Y .Y .q= GeneralGeneraldNC#,###.00#,###.00SystemNC #,##0.00 CCC#,##0.00 CCCNC$#,##0.--;[RED]-$#,##0.-- $#,##0.---$#,##0.--REDNC$ MM/DD/YYYYMM/DD/YYYY def/SystemNC%MM/DD/YYMM/DD/YY def/SystemNC&NNNNMMMM DD, YYYYNNNNMMMM  DD, YYYYSystemNC' MMM D, YYMMM D, YY def/SystemNC. [HH]:MM:SS.00 [HH ]:MM:SS .00NC3MM/DD/YYYY HH:MM:SS MM/DD/YYYY HH :MM:SS  NCK MMM D, YYYYMMM D, YYYY def/SystemNCL MMMM D, YYYYMMMM  D, YYYY def/SystemNCM NN, MMM D, YYNN, MMM D, YY def/SystemNCNNN, MMMM D, YYYYNN, MMMM  D, YYYY def/SystemNCONNNNMMMM D, YYYYNNNNMMMM  D, YYYY def/SystemNCP D. MMM. YYYYD. MMM. YYYYDIN 5008 (EN 28601)NCQ D. MMMM YYYYD. MMMM  YYYYDIN 5008 (EN 28601)NCRMM-DDMM-DDDIN 5008 (EN 28601)NCSYY-MM-DDYY-MM-DDDIN 5008 (EN 28601)NCT YYYY-MM-DDYYYY-MM-DDDIN 5008 (EN 28601)NCUWWWWNCBXoeP<p5 2$99 SAP/=APd#ddAPddA PAu Prk FooterSYAPddADPN;T2SA @A0SAAP/=APd#ddAPddZSW5HDR.0{1<1C(569a(Build:5169)(SV569)]D!Address Book Fileaddress! Frameformat ZeichenformatTextformatvorlageStandard Text bodyListCaption IndexFooterTable Contents Table HeadingRoot 12 Table1 13 Table2 14Standard  Illustration Table TextDrawingd-1 4 5*jK standard.dic soffice.dicZsun.dic@ IgnoreAllListY .Y .Y .Y .6NXTJDevice DriversS1APdddA A @T<S1APdddA A @Td(O/S Components responsible for managing:S1APdddA A @T<S1APdddA A @Ti-Peripheral devices attached to the system busS1APdddA A @T<S1APdddA A @TLdisk controllersS1APdddA A @TOnetwork controllersS1APdddA A @TG sound cardsS1APdddA A @TMgraphics adaptersS1APdddA A @T<S1APdddA A @TOVirtual devicesS'APdddA @A8T<S1APdddA A @TWperformance measuring toolsS1APdddA A @TE firewallsS1APdddA A @TBpipes S1APdddA A @TPshared memory spacesS1APdddA A @T<S1APdddA A @TQExporting of servicesS1APdddA A @T<S1APdddA A @TPTo kernel componentsS1APdddA A @TTDirectly to applicationsS1APdddA A @T<S1APdddA A @Tg+Exporting services directly to applicationsS1APdddA A @T<S1APdddA A @T]!Requires a node in the /dev spaceS1APdddA A @Tkmknod /dev/ia5515 c 17 0SHAPdddACourierA A @TYSNAPdddA TimesNewRomanA A @TiDriver uses the SNAPdddA TimesNewRomanA A @Teregister_chrdev() SHAPdddACourierA A @T>kernel function to bind itself the associated node /dev space SFAPdddATimesA A @A!8= TimesNewRomanTQSFAPdddATimesA A @ToApps use system calls SNAPdddA TimesNewRomanA A @Ts open, close, read, write, ioctl SHAPdddACourierA A @TSSHAPdddACourierA A @TDNetwork devices may not export any services directly to applicationsSNAPdddA TimesNewRomanA A @TQSFAPdddATimesA A @T[SPAPdddA PATimesA A @T'Exporting services to kernel componentsSNAPdddA TimesNewRomanA A @T2S'APdddA @TX&Other register type calls may be used:S'APdddA @T2S'APdddA @T{2atm_dev_register("ia5515", &atm_ops, intf, flags);S>APdddACourierA @Tm$rc = sock_register(&xtp_family_ops);S>APdddACourierA @Tv-register_8022_client(xtp_8022_type, xtp_rcv);S>APdddACourierA @Tt+register_snap_client(xtp_snap_id, xtp_rcv);S>APdddACourierA @Tx/register_netdevice_notifier(&xtp_dev_notifier);S>APdddACourierA @T2S'APdddA @TRegister calls always pass either function pointers or tables of function pointers up to the enitity with which the driver is registering. S8APdd dA @A@. TCS8APdd dA @A@. TdThis mode of operation is a low level version of the O-O inheritance idea (or the COBOL alter verb).S8APdd dA @A@. A8 X]A8 ]dT2S'APdddA @Tk/Responsibilities of drivers of physical devicesS1APdddA A @T<S1APdddA A @TYInitialization timeS;APdddA  A A @TFS;APdddA  A A @T^"Identify the device to be managed S1APdddA A @Tn.Map its register space into memory if requiredS'APdddA @A8.TsAInitialize (enable) the device... may be over 50% of the driver!!S'APdddA @Tk/Insert self as an interrupt handler if requiredS1APdddA A @Tv:Register own internal device service functions with kernelS1APdddA A @Tg+Register services in /dev space if exportedS1APdddA A @T_#Acquire any necessary buffer pools S1APdddA A @T2S'APpdddpA @TDRun timeS1APdddA A @T<S1APdddA A @Tn2Service requests to initiate I/O device operationsS1APdddA A @T\ Start them if the device is idleS1APpdddpA A @T\ Queue them if the device is busyS1APpdddpA A @Ty=Initiate DMA transfers between main memory and device buffersS1APpdddpA A @T2S'APdddA @TUHandle device interrupts S1APdddA A @Tr6Query final status and retry or report failed requestsS1APpdddpA A @Ta%Start next request if queue not emptyS1APpdddpA A @T<S1APdddA A @TLTermination timeS1APdddA A @T<S1APdddA A @TXUnregister service functionsS1APdddA A @TMFree buffer poolsS1APdddA A @TNDisable the deviceS1APdddA A @T<S1APdddA A @TFS;APdddA PA A @TUImplementation strategiesS1APdddA A @T<S1APdddA A @T]!Compile and link with the kernel S1APdddA A @TZBuild as an installable moduleS1APdddA A @T<S1APdddA A @TRModular implementationS1APdddA A @T<S1APdddA A @Ts7Always the best approach during development and testingS1APdddA A @Tz>Module compilation is fast and full kernel compile unnecessaryS1APdddA A @T<Modules can be uninstalled and reinstalled without rebootingS1APdddA A @A8 +<T>Seriously buggy modules can crash system or render it unstableS;APdddA  A A @A8 TFS;APdddA  A A @TXCompiling a moduleS;APdddA  A A @TFS;APdddA  A A @T@Makefiles are a critical component of building kernel componentsS;APdddA  A A @T3The O'Reilly book on make is an excellent referenceS;APdddA  A A @A8 TFS;APdddA  A A @TuKERNELDIR=/usr/src/linuxSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Tyinclude $(KERNELDIR)/.configSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T7CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include \SRAPdddACourierA  A A @Tq -Wall -c -gSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Tmifdef CONFIG_SMPSRAPdddACourierA  A A @T"CFLAGS = $(CFLAGS) -D__SMP__ -DSMPSRAPdddACourierA  A A @TbendifSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Ti sem0: sem0.oSRAPdddACourierA  A A @T& ld -Map sem0.map -r -o $@ $@.oSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Tksem0.o: sem0.cSRAPdddACourierA  A A @T& gcc -o sem0.o $(CFLAGS) sem0.cSRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Te.c.o: $ $(@:.o=.err)SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @Tcclean:SRAPdddACourierA  A A @Tt rm sem0.o *.errSRAPdddACourierA  A A @T]SRAPpdddpACourierA  A A @T}Managing modulesSbAPdddA PA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T1The following commands are used to manage modulesSXAPdddA TimesNewRomanA  A A @T2S'APdddA @T7insmod module-name "modparm=val" load specified moduleSXAPdddA TimesNewRomanA  A A @A8!d A8!CourierA8 "7T,rmmod module-name remove specified moduleSXAPdddA TimesNewRomanA  A A @A8d A8CourierA8 ,T-lsmod show currently loaded module namesSXAPdddA TimesNewRomanA  A A @A8CourierA8d A8  -TcSXAPdddA TimesNewRomanA  A A @T>These commands normally require root access (for good reason).SgAPdddA TimesNewRomanAd A  A A @TcSXAPdddA TimesNewRomanA  A A @TXDoing software development (as opposed to system administration) as root is a bad idea SiAPdd-dA TimesNewRomanA  A A @A@. A8 NWTCS8APdddA @A@. TCOn a restricted access systems users can be given permission via: SiAPdddA TimesNewRomanA  A A @A@. A8 TtSiAPdddA TimesNewRomanA  A A @A@. Tchmod a+s /sbin/insmodScAPdddpACourierA  A A @A@. TtSiAPdddpA TimesNewRomanA  A A @A@. TExample Module StructureS8APdddA @A@. A8A8 A!8 TimesNewRomanTtSiAPdddA TimesNewRomanA  A A @A@. T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include SaAPdddACourierAd A  A A @T#include S6APdddAd A @A8A8 A8CourierTSxAPdddA TimesNewRomanAd A  A A @A@. Tstatic int semcount = 8;SrAPdddACourierAd A  A A @A@. TMODULE_PARM(semcount, "i");SrAPdddACourierAd A  A A @A@. T;MODULE_PARM_DESC(semcount, "Number of sems.. default = 8");SrAPdddACourierAd A  A A @A@. T}SrAPdddACourierAd A  A A @A@. Tint init_module(void)SrAPdddACourierAd A  A A @A@. T~{SrAPdddACourierAd A  A A @A@. T int rc = 0;SrAPdddACourierAd A  A A @A@. T}SrAPdddACourierAd A  A A @A@. T' printk(D_LEVEL "SEM0: Starting \n");SrAPdddACourierAd A  A A @A@. T8 rc = register_chrdev(SEM0_MAJOR, "sem0", &sem0_fops);SrAPdddACourierAd A  A A @A@. T if (rc)SrAPdddACourierAd A  A A @A@. T {SrAPdddACourierAd A  A A @A@. T: printk(D_LEVEL "SEM0: Register returned %d \n", rc);SrAPdddACourierAd A  A A @A@. T return(-1);SrAPdddACourierAd A  A A @A@. T }SrAPdddACourierAd A  A A @A@. T0 printk(D_LEVEL "SEM0: Install completed \n");SrAPdddACourierAd A  A A @A@. T return(0);SrAPdddACourierAd A  A A @A@. T~}SrAPdddACourierAd A  A A @A@. T}SrAPdddACourierAd A  A A @A@. Tint cleanup_module(void)SrAPdddACourierAd A  A A @A@. T~{SrAPdddACourierAd A  A A @A@. T int rc;SrAPdddACourierAd A  A A @A@. T SrAPdddACourierAd A  A A @A@. T. rc = unregister_chrdev(SEM0_MAJOR, "sem0");SrAPdddACourierAd A  A A @A@. T6 printk(D_LEVEL "SEM0: Finished with rc %d \n", rc);SrAPdddACourierAd A  A A @A@. T return(0);SrAPdddACourierAd A  A A @A@. T~}SrAPdddACourierAd A  A A @A@. TCS8APdddpA @A@. TCS8APdddA @A@. TtSiAPdddA TimesNewRomanA  A A @A@. TcSXAPdddA TimesNewRomanA  A A @TsModule debuggingSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TjWhen all else fails, traditional hex dump screens equivalent to the infamous MS Blue Screen of Death existS8APdd;dA @A@. A8*A8 *A!8* TimesNewRomanA8 PdTtSiAPdd;dA TimesNewRomanA  A A @A@. T%The front line of defense is printk()SXAPdddA TimesNewRomanA  A A @A8 %T2S'APdddA @Td Use of printkSLAP dd;dA  A A @A@. TMSBAPdd;dA  A @A@. T#define D_LEVEL KERN_CRITShAPdd;dACourierAd A  A @A@. Ty :ShAPdd;dACourierAd A  A @A@. Ty :ShAPdd;dACourierAd A  A @A@. TBprintk(D_LEVEL "IA 5515: NULL skb on vci %d in rdv w/ %d \n", vci,ShAPdd;dACourierAd A  A @A@. T9 softc->tx_backlog);ShAPdd;dACourierAd A  A @A@. TsShAPdd;dACourierAd A  A @A@. T3#define KERN_EMERG "<0>" /* system is unusable */ShAPdd;dACourierAd A  A @A@. T@#define KERN_ALERT "<1>" /* action must be taken immediately */ShAPdd;dACourierAd A  A @A@. T2#define KERN_CRIT "<2>" /* critical conditions */ShAPdd;dACourierAd A  A @A@. T/#define KERN_ERR "<3>" /* error conditions */ShAPdd;dACourierAd A  A @A@. T5#define KERN_WARNING "<4>" /* warning conditions */ShAPdd;dACourierAd A  A @A@. T@#define KERN_NOTICE "<5>" /* normal but significant condition */ShAPdd;dACourierAd A  A @A@. T-#define KERN_INFO "<6>" /* informational */ShAPdd;dACourierAd A  A @A@. T4#define KERN_DEBUG "<7>" /* debug-level messages */ShAPdd;dACourierAd A  A @A@. TsShAPdd;dACourierAd A  A @A@. T4The debug level determines which messages appear on SBAPdd;dA  A @A@. T_the system consoleSBAPdd;dpA  A @A@. Toin /var/log/messagesSBAPdd;dpA  A @A@. A8 TMSBAPdd;dpA  A @A@. T3If the level is omitted the default is (normally) 6SBAPdd;dA  A @A@. TMSBAPdd;dA  A @A@. Tz-See man syslog, syslogd, klogd for more info.SBAPdd;dA  A @A@. TMSBAPdd;dA  A @A@. TmAn alternate approach SLAP dd;dA  A A @A@. TWSLAP dd;dA  A A @A@. T /*SrAPdd;dACourierAd A  A A @A@. T4 * Check to see if our controller in interrupting.SrAPdd;dACourierAd A  A A @A@. T */SrAPdd;dACourierAd A  A A @A@. TF flip_intr_status = ia_get32(&softc->brd_regs.flipper->fl_status);SrAPdd;dACourierAd A  A A @A@. T+ flip_intr_status &= ~FL_STAT_RESERVED;SrAPdd;dACourierAd A  A A @A@. T SrAPdd;dACourierAd A  A A @A@. T #ifdef DBG_INTRSrAPdd;dACourierAd A  A A @A@. T: printk("IA 5515: I stat is %x \n", flip_intr_status);SrAPdd;dACourierAd A  A A @A@. T #endifSrAPdd;dACourierAd A  A A @A@. TlSaAPdddACourierAd A  A A @TMDebugging device drivers without adequate printk() support may be IMPOSSIBLE!SgAPdddA TimesNewRomanAd A  A A @A8 *2TrSgAPdddA TimesNewRomanAd A  A A @T;/local/mac2/atm/ia5515-0.78e ==> grep printk ia_*.c | wc -lSgAPdddA TimesNewRomanAd A  A A @A8 ;A8;CourierTs 234SaAPdddACourierAd A  A A @T;/local/mac2/atm/ia5515-0.78e ==> grep ";" ia_*.c | wc -l SaAPdddACourierAd A  A A @Ts 1836SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T?Note that 1 of every 8 lines in the entire system is a printk()S'APdddA @A8?A8 ?A!8? TimesNewRomanT*Kernel Services Exported to Device DriversSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T-Registering as a driver of a character deviceSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T%Create a table of exported functions:SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T1 59 static struct file_operations sem0_fops =SXAPdddA TimesNewRomanA  A A @A81CourierTe 60 {SRAPdddACourierA  A A @T; 61 ioctl: sem0_ioctl, /* ioctl */SRAPdddACourierA  A A @T; 62 open: sem0_open, /* open */SRAPdddACourierA  A A @T; 63 release: sem0_release /* release */SRAPdddACourierA  A A @Tf 64 };SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @T5Bind your table to your major device number and name:SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TA 99 rc = register_chrdev(SEM0_MAJOR, "sem0", &sem0_fops);SXAPdddA TimesNewRomanA  A A @A8ACourierTcSXAPdddA TimesNewRomanA  A A @T# Unregistering yourselfSXAPdddA TimesNewRomanA  A A @A8 #T2S'APdddA @T0101 rc = unregister_chrdev(SEM0_MAJOR, "sem0");SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @TAllocating and freeing memorySXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T7402 softc = kmalloc(sizeof(ia_softc_t), GFP_KERNEL);SRAPdddACourierA  A A @T^:SRAPdddACourierA  A A @T^:SRAPdddACourierA  A A @Tp905 kfree(softc);SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T#The GFP_ flags are defined in mm.hSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TTIf GFP_ATOMIC is not specified, the caller will be blocked until memory is availableSXAPdddA TimesNewRomanA  A A @A8 TFGFP_ATOMIC must be used when allocating memory in an interrupt handlerSXAPdddA TimesNewRomanA  A A @A8  TcSXAPdddA TimesNewRomanA  A A @TmSbAPdddA PA TimesNewRomanA  A A @T!Block and unblocking of processesSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T$Declaring a process wait queue: SXAPdddA TimesNewRomanA  A A @A8TcSXAPdddA TimesNewRomanA  A A @Tt#include SRAPdddACourierA  A A @T{wait_queue_head_t tfd_waitq; SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @T|Initializing a wait queueSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T%init_waitqueue_head(&ape->tfd_waitq);SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @T~Sleeping the active processSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T(interruptible_sleep_on(&ape->tfd_waitq);SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @T2Waking up all processes sleeping on the wait queueSXAPdddA TimesNewRomanA  A A @T2S'APdddA @T.wake_up_interruptible(&ape->rlc_waitq[lcndx]);SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @TWUninterruptible versions exist.. but may lead to permanent sleeps during development!!SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T1Low level mutual exclusion (needed only for SMP )SXAPdddA TimesNewRomanA  A A @A8/TcSXAPdddA TimesNewRomanA  A A @TyDeclaring a spin lock:SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @Tv#include SRAPdddACourierA  A A @Tm :S'APdddA @A8A8 A8CourierTwspinlock_t xmitlock; SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T{Initializing a spin lockSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T} spin_lock_init(&ape->xmitlock);SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @TxAcquiring a spin lockSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @Tuunsigned long lockflags;SRAPdddACourierA  A A @Tf:S>APdddACourierA @A8A8 T-spin_lock_irqsave(&ape->recvlock, lockflags);SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @TxReleasing a spin lockSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T3 spin_unlock_irqrestore(&ape->xmitlock, lockflags);SXAPdddA TimesNewRomanA  A A @A83CourierT]SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T)Moving data between user and kernel spaceSbAPdddA PA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T1The 4th argument of ioctl is a user space pointerSXAPdddA TimesNewRomanA  A A @A8:eTcSXAPdddA TimesNewRomanA  A A @Tlint sem0_ioctl(SRAPdddACourierA  A A @Tqstruct inode *inode,SRAPdddACourierA  A A @Tnstruct file *fp,SRAPdddACourierA  A A @Tuunsigned int func_code,SRAPdddACourierA  A A @T1unsigned long arg) /* ptr to user data */SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T_ :SRAPdddACourierA  A A @T_ :SRAPdddACourierA  A A @T2S'APdddA @TPIt is imperative that kernel code verify access before dereferencing the pointerSXAPdddA TimesNewRomanA  A A @A8 A8 06T]SRAPdddACourierA  A A @T6rc = access_ok(VERIFY_WRITE, (unsigned char *)arg, 4);SRAPdddACourierA  A A @Ti if (rc == 0)SRAPdddACourierA  A A @To return(-EFAULT);SRAPdddACourierA  A A @T2S'APdddA @T_: SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T*copy_from_user(kernbuf, (char *)arg, len);S'APdddA @A8*A8 *A8*CourierTk :S'APdddA @A8A8 A8CourierT(copy_to_user((char *)arg, kernbuf, len);SRAPdddACourierA  A A @T_ :SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @T]SRAPdddACourierA  A A @TcSXAPdddA TimesNewRomanA  A A @TDrivers of Physical DevicesSbAPdddA PA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TjExampleSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TrInterphase 5515SXAPdddA TimesNewRomanA  A A @T 155 Mbps MMF/SMF/UTP ATM adapterSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T{Source code organizationSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T$ia_eeprom.c - set / get MAC address SXAPdddA TimesNewRomanA  A A @T ia_ffred.c - transmit side logicSXAPdddA TimesNewRomanA  A A @TAia_flip.c - initialization / termination / interrupt handling SXAPdddA TimesNewRomanA  A A @T'ia_ioctl.c - interface for diagnosticsSXAPdddA TimesNewRomanA  A A @T,ia_lnxatm.c - interface to linux ATM stack SXAPdddA TimesNewRomanA  A A @T-ia_pci.c - low level hardware initializationSXAPdddA TimesNewRomanA  A A @T ia_rfred.c - receive side logicSXAPdddA TimesNewRomanA  A A @T+ia_suni.c - front end (interface) control SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TqInitializationSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @Tm Find boardSXAPdddA TimesNewRomanA  A A @TrAllocate memorySXAPdddA TimesNewRomanA  A A @TsEnable the boardSXAPdddA TimesNewRomanA  A A @TuRegister functionsSXAPdddA TimesNewRomanA  A A @Tscharacter deviceSXAPdddA TimesNewRomanA  A A @Tuinterrupt handlersSXAPdddA TimesNewRomanA  A A @Ttbind to ATM stackSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T!See if the system contains a 5515SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T struct pci_dev *pci_dev;SaAPdddACourierAd A  A A @T struct pci_dev *ia_dev;SaAPdddACourierAd A  A A @Tn :SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T~ pci_dev = NULL;SaAPdddACourierAd A  A A @T9 while (pci_dev = pci_find_device(PCI_VENDOR_ID_IPHASE,SaAPdddACourierAd A  A A @T= PCI_DEVICE_ID_IPHASE_5515, pci_dev))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T ia_dev = pci_dev;SaAPdddACourierAd A  A A @T} found += 1;SaAPdddACourierAd A  A A @T }SXAPdddA TimesNewRomanA  A A @A8d A8CourierTcSXAPdddA TimesNewRomanA  A A @T,Allocate memory for device control structureSbAPdddA PA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T3 softc = kmalloc(sizeof(ia_softc_t), GFP_KERNEL);SaAPdddACourierAd A  A A @A83T~ if (softc == 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T< printk("IA 5515: softc memory allocation failed..\n");SaAPdddACourierAd A  A A @T} return(-1);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @T( memset(softc, 0, sizeof(ia_softc_t));SaAPdddACourierAd A  A A @T softc->ia_dev = ia_dev;SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T Enable DeviceSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T(/* enable the adapter as a bus master */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TK error = pci_read_config_word(softc->ia_dev, PCI_COMMAND, &command);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T8 command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);SaAPdddACourierAd A  A A @T4 if ((error = pci_write_config_word(softc->ia_dev,SaAPdddACourierAd A  A A @TA PCI_COMMAND, command)))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T3 printk("IA 5515: Activate board failed \n");SaAPdddACourierAd A  A A @T return error;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TcSXAPdddA TimesNewRomanA  A A @T)Map Adapter Registers into Virtual MemorySXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T vaddr = softc->pci_base_v =SaAPdddACourierAd A  A A @T@ (caddr_t)ioremap((unsigned long)softc->real_base,SaAPdddACourierAd A  A A @T/ softc->pci_map_size);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tz if (!vaddr)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T6 printk("IA 5515: ioremap failed... goodbye \n");SaAPdddACourierAd A  A A @T} return(-1);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{#ifdef DBG_INITSaAPdddACourierAd A  A A @T; printk("IA 5515: Board mapped at virtual %x \n", vaddr);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @T"Determine hardware characteristicsSbAPdddA PA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T| Onboard RAMSXAPdddA TimesNewRomanA  A A @A8 TInterface type (MMF / UTP)SXAPdddA TimesNewRomanA  A A @A8Td SXAPdddA TimesNewRomanA  A A @T if (softc->atlantic)SXAPdddA TimesNewRomanA  A A @A8d A8CourierTW hw_conf = ia_eeprom_get(softc, HW_CONF_OFFSET_ATLANTIC/sizeof(u_short));SaAPdddACourierAd A  A A @Ts elseSaAPdddACourierAd A  A A @TE hw_conf = ia_eeprom_get(softc, HW_CONF_OFFSET/sizeof(u_short));SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TD printk("IA 5515: Hardware configuration word is %x \n", hw_conf);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T0 if ((hw_conf & MEM_SIZE_MASK) == MEM_SIZE_1M)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T' softc->num_tx_desc = NUM_TX_DESC;SaAPdddACourierAd A  A A @T' softc->num_rx_desc = NUM_RX_DESC;SaAPdddACourierAd A  A A @T' softc->num_tx_bufs = NUM_TX_BUFS;SaAPdddACourierAd A  A A @T! softc->ram_size = RAM_SIZE;SaAPdddACourierAd A  A A @T, softc->rx_packet_ram = TX_PACKET_RAM +SaAPdddACourierAd A  A A @T+ (TX_BUF_SIZE * NUM_TX_DESC);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @T7 else if ((hw_conf & MEM_SIZE_MASK) == MEM_SIZE_512K)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T+ softc->num_tx_desc = NUM_TX_DESC_512;SaAPdddACourierAd A  A A @T+ softc->num_rx_desc = NUM_RX_DESC_512;SaAPdddACourierAd A  A A @T+ softc->num_tx_bufs = NUM_TX_BUFS_512;SaAPdddACourierAd A  A A @T% softc->ram_size = RAM_SIZE_512;SaAPdddACourierAd A  A A @T- softc->rx_packet_ram = TX_PACKET_RAM +SaAPdddACourierAd A  A A @T/ (TX_BUF_SIZE * NUM_TX_DESC_512);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T- if ((hw_conf & FE_MASK) == FE_SINGLE_MODE)SaAPdddACourierAd A  A A @T' softc->phy_type = FE_SINGLE_MODE;SaAPdddACourierAd A  A A @T1 else if ((hw_conf & FE_MASK) == FE_UTP_OPTION)SaAPdddACourierAd A  A A @T& softc->phy_type = FE_UTP_OPTION;SaAPdddACourierAd A  A A @Ts elseSaAPdddACourierAd A  A A @T& softc->phy_type = FE_MULTI_MODE;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T0Initialize control, transmit and receive enginesSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T|This is code mountain andSXAPdddA TimesNewRomanA  A A @T.Any error can render the whole system useless!SXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TmSbAPdddA PA TimesNewRomanA  A A @T4Hook into interrupt handler chain for the device I      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|~RQSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T1 rc = request_irq(ia_dev->irq, (void *)ia_intr,SaAPdddACourierAd A  A A @TA SA_INTERRUPT | SA_SHIRQ, "ia5515", softc);SaAPdddACourierAd A  A A @Tv if (rc)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T> printk("IA 5515: Can't allocate IRQ %d. Code was %d \n",SaAPdddACourierAd A  A A @T* ia_dev->irq, rc);SaAPdddACourierAd A  A A @T~ return -EIO;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TcSXAPdddA TimesNewRomanA  A A @T2Initialize spin locks used for mutex on MP systemsSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T% spin_lock_init(&softc->xmitlock);SXAPdddA TimesNewRomanA  A A @A8%d A8%CourierT$ spin_lock_init(&softc->recvlock);SaAPdddACourierAd A  A A @T$ spin_lock_init(&softc->xramlock);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T#Register character device functionsSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @TB if (rc = register_chrdev(IA5515_MAJOR, "ia5515", &ia5515_fops))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T@ printk( "IA_5515: register/dev/ia5515 returned %d\n", rc);SaAPdddACourierAd A  A A @T} return(-1);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T'Initialize skb queues for outgoing dataSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T softc->last_tx_vcc = 32;SaAPdddACourierAd A  A A @T! for (i = 0; i < NUM_VCCS; i++)SaAPdddACourierAd A  A A @T8 skb_queue_head_init(&softc->vcctab[i].tx_backlog);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TmSbAPdddA PA TimesNewRomanA  A A @TRegister with Linux ATM stackSXAPdddA TimesNewRomanA  A A @TcSXAPdddA TimesNewRomanA  A A @T"static struct atmdev_ops atm_ops =SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T* NULL, /* dev_close */SaAPdddACourierAd A  A A @T* ia5515_open, /* open VCC */SaAPdddACourierAd A  A A @T* ia5515_close, /* close VCC */SaAPdddACourierAd A  A A @T* 0, /* ioctl */SaAPdddACourierAd A  A A @T* 0, /* getsockopt */SaAPdddACourierAd A  A A @T* 0, /* setsockopt */SaAPdddACourierAd A  A A @T* ia5515_send, /* send AAL5 */SaAPdddACourierAd A  A A @Tp :SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tp/**/SaAPdddACourierAd A  A A @T,/* Attempt to register the IA 5515 device */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tint ia5515_register(SaAPdddACourierAd A  A A @TBia_softc_t *softc, /* Pointer to device dependent descriptor */SaAPdddACourierAd A  A A @TBint intf) /* Physical interface (NIC) 0, 1, 2,.. */SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @Tu int i;SaAPdddACourierAd A  A A @T} int flags = 0;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TE softc->atmdev = atm_dev_register("ia5515", &atm_ops, intf, flags);SaAPdddACourierAd A  A A @T if (softc->atmdev == NULL)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T2 printk("ia5515: Can't register device \n");SaAPdddACourierAd A  A A @T return -EIO;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TvSkAPdddA PACourierAd A  A A @TMemory ManagementSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T$Three separate memories are in play:SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T+System memory (the main memory of the host)SgAPdddA TimesNewRomanAd A  A A @T*Control ram (Adapter local control memory)SgAPdddA TimesNewRomanAd A  A A @T)Buffer ram (Adapter local buffer memory)SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TSystem memory containsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T*The tables of 256 DMA list elements (DLEs)SgAPdddA TimesNewRomanAd A  A A @THost buffers (skbs)SgAPdddA TimesNewRomanAd A  A A @THost buffer descriptors listsSgAPdddA TimesNewRomanAd A  A A @Txtx_bufSgAPdddA TimesNewRomanAd A  A A @Txrx_bufSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T'Buffer ram (128K bytes on these boards)S'APdddA @A8 A8  A8 d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TATransmit packet buffers (Currently configured 16 bufs @ 2KB each)SgAPdddA TimesNewRomanAd A  A A @T@Receive packet buffers (Currently configured 32 bufs @ 2KB each)SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TControl ram containsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T>Board registers, segment descriptor tables, and packet queuesSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tztypedef structSaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @TE ffred_t *ffred; /* F FRED (Pre-Atlantic) regs */SaAPdddACourierAd A  A A @TE rfred_t *rfred; /* R FRED (Pre-Atlantic) regs */SaAPdddACourierAd A  A A @TE U32 *ctrl; /* Control Register (pre-Atlantic) */SaAPdddACourierAd A  A A @TE ia_suni_t *suni; /* SUNI (Front end regs ) */SaAPdddACourierAd A  A A @TE suni_pm7345_t *suni_pm7345; /* DS3 PHY */SaAPdddACourierAd A  A A @TE ia_flip_t *flipper; /* Flipper */SaAPdddACourierAd A  A A @TE fl_config_t *config; /* Configuration Registers */SaAPdddACourierAd A  A A @TE U32 *tx_tc; /* Tx transection count */SaAPdddACourierAd A  A A @TE U32 *rx_tc; /* Tx transection count */SaAPdddACourierAd A  A A @TE caddr_t ffred_mem; /* Seg descriptors and pkt queues */SaAPdddACourierAd A  A A @TE caddr_t rfred_mem; /* Seg descriptors and pkt queues */SaAPdddACourierAd A  A A @T|} ia_brd_regs_t;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TvSkAPdddA PACourierAd A  A A @T+These entities are set accessed as follows:S1APdddA A @A8 A8d A!8 TimesNewRomanTXSMAPdddACourierAd A @Tw vaddr = softc->pci_base_v =SMAPdddACourierAd A @T@ (caddr_t)ioremap((unsigned long)softc->real_base,SMAPdddACourierAd A @T/ softc->pci_map_size);SMAPdddACourierAd A @TXSMAPdddACourierAd A @T- softc->ffred_map.f_num_vc = F_NUM_VC_128K;SMAPdddACourierAd A @TA softc->brd_regs.ffred = (ffred_t *)(vaddr + FFRED_OFFSET);SMAPdddACourierAd A @TA softc->brd_regs.rfred = (rfred_t *)(vaddr + RFRED_OFFSET);SMAPdddACourierAd A @T? softc->brd_regs.ctrl = (uint_t *)(vaddr + FECR_OFFSET);SMAPdddACourierAd A @TB softc->brd_regs.suni = (ia_suni_t *)(vaddr + SUNI_OFFSET);SMAPdddACourierAd A @TB softc->brd_regs.flipper = (ia_flip_t *)(vaddr + FLIP_OFFSET);SMAPdddACourierAd A @TF softc->brd_regs.config = (fl_config_t *)(vaddr + CONFIG_OFFSET);SMAPdddACourierAd A @T@ softc->brd_regs.tx_tc = (uint_t *)(vaddr + TX_TC_OFFSET);SMAPdddACourierAd A @T@ softc->brd_regs.rx_tc = (uint_t *)(vaddr + RX_TC_OFFSET);SMAPdddACourierAd A @TC softc->brd_regs.ffred_mem = (caddr_t)(vaddr + FFRED_MEM_OFFSET);SMAPdddACourierAd A @TC softc->brd_regs.rfred_mem = (caddr_t)(vaddr + RFRED_MEM_OFFSET);SMAPdddACourierAd A @TXSMAPdddACourierAd A @TNWhere the offset within register space of the individual register groups are: S'APdddA @A8Md A!8M TimesNewRomanA8MNd A8MNCourierTXSMAPdddACourierAd A @TZ/*SMAPdddACourierAd A @Ti * Device offsetsSMAPdddACourierAd A @T[ */SMAPdddACourierAd A @Tj/* Pre-Atlantic */SMAPdddACourierAd A @T<#define FFRED_OFFSET 0x00000 /* FFRED registers */SMAPdddACourierAd A @T<#define RFRED_OFFSET 0x01000 /* RFRED registers */SMAPdddACourierAd A @TG#define FECR_OFFSET 0x02000 /* Front end control register */SMAPdddACourierAd A @T;#define SUNI_OFFSET 0x02800 /* SUNI registers */SMAPdddACourierAd A @TG#define FLIP_OFFSET 0x03000 /* Flipper Internal registers */SMAPdddACourierAd A @TD#define CONFIG_OFFSET 0x04000 /* Configuration registers */SMAPdddACourierAd A @TC#define TX_TC_OFFSET 0x05000 /* Tx transaction counter */SMAPdddACourierAd A @TC#define RX_TC_OFFSET 0x05004 /* Rx transaction counter */SMAPdddACourierAd A @T>#define FFRED_MEM_OFFSET 0x10000 /* FFRED control RAM */SMAPdddACourierAd A @T>#define RFRED_MEM_OFFSET 0x20000 /* RFRED control RAM */SMAPdddACourierAd A @T2S'APdddA @T&Transmit side control memory contains:S'APdddA @A8&A8 &A8&d A!8& TimesNewRomanT2S'APdddA @TDSegmentation buffer descriptor tables (on entry per transmit buffer)SgAPdddA TimesNewRomanAd A  A A @TThe packet ready queue (prq)SgAPdddA TimesNewRomanAd A  A A @A8 T The transmit complete queue(tcq)SgAPdddA TimesNewRomanAd A  A A @A8 TVC table entriesSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T%Receive side control memory contains:SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TBReassembly buffer descriptor tables (on entry per transmit buffer)SgAPdddA TimesNewRomanAd A  A A @T%The large free descriptor queue (lrg)SgAPdddA TimesNewRomanAd A  A A @A8 !%TThe packet complete queue(pcq)SgAPdddA TimesNewRomanAd A  A A @A8 TrSgAPdddA TimesNewRomanAd A  A A @T|SqAPdddA PA TimesNewRomanAd A  A A @T!Segmentation Descriptor StructureSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Toffset functionSgAPdddA TimesNewRomanAd A  A A @T;0x0 Descriptor mode bits (includes CMPL_INTR and EOM_EN)SgAPdddA TimesNewRomanAd A  A A @T| 0x2 VCISgAPdddA TimesNewRomanAd A  A A @T0x4 ReservedSgAPdddA TimesNewRomanAd A  A A @T 0x6 LengthSgAPdddA TimesNewRomanAd A  A A @T/0x8 High part of address in local buffer RAMSgAPdddA TimesNewRomanAd A  A A @T.0xA Low part of address in local buffer RAMSgAPdddA TimesNewRomanAd A  A A @T0xC Reserved...SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tn/*SaAPdddACourierAd A  A A @T" * ffred / rfred buffer descriptorSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T|typedef struct {SaAPdddACourierAd A  A A @T5 ushort_t desc_stat; /* descriptor status bits */SaAPdddACourierAd A  A A @T5 ushort_t vci; /* destination vci */SaAPdddACourierAd A  A A @T5 ushort_t mid; /* mid (AAL 3, 4 junk */SaAPdddACourierAd A  A A @T; ushort_t length; /* Buf size - packet byte count */SaAPdddACourierAd A  A A @T; ushort_t buf_high; /* fixed buffer address high */SaAPdddACourierAd A  A A @T; ushort_t buf_low; /* fixed buffer address low */SaAPdddACourierAd A  A A @T; ushort_t dma_high; /* dma address high */SaAPdddACourierAd A  A A @T; ushort_t dma_low; /* dma low address */SaAPdddACourierAd A  A A @T /* Pre-Atlantic only */SaAPdddACourierAd A  A A @T; ushort_t crc_high; /* crc residual high */SaAPdddACourierAd A  A A @T; ushort_t crc_low; /* crc residual high */SaAPdddACourierAd A  A A @T; ushort_t nrq; /* NRQ */SaAPdddACourierAd A  A A @T; ushort_t nvc; /* NVC */SaAPdddACourierAd A  A A @T> ushort_t que_check; /* 18, used for desc. reconciliation*/SaAPdddACourierAd A  A A @T= ushort_t buf_index; /* 1a (software) */SaAPdddACourierAd A  A A @T= ushort_t order_q; /* 1c, used for single threaded vci*/SaAPdddACourierAd A  A A @T8 ushort_t flags; /* 1e, used to idenitify aal */SaAPdddACourierAd A  A A @Ty } f_buf_desc;SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T|SqAPdddA PA TimesNewRomanAd A  A A @TBuffer managementSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TCThe key to NIC buffer management is the 4 buffer descriptor queues:SgAPdddA TimesNewRomanAd A  A A @T2S'APdddA @TTransmit side queuesSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TThe packet ready queue (prq) SgAPdddA TimesNewRomanAd A  A A @TCBuffers that have been DMA'ed to NIC buffers and are available for SgAPpdddpA TimesNewRomanAd A  A A @Tsegmentation and transmissionSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @T"The transmit complete queue (tcq) SgAPdddA TimesNewRomanAd A  A A @T=Buffers for which segmentation and transmission has completedSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T/Initially all buffer id's are placed in the tcqSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TReceive side queues SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TThe large free queue (lrg)SgAPdddA TimesNewRomanAd A  A A @T0Buffers that are available for use in reassemblySgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @TThe packet ready queue (prq)SgAPdddA TimesNewRomanAd A  A A @TMBuffers that contain packets now fully reassembled and ready for DMA transferSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @T:Initially all buffer id's are placed in the lrg free queueS'APdddA @A8/A8 /A8/d A!8/ TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T|SqAPdddA PA TimesNewRomanAd A  A A @TQueue managment structureSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T4Each of the 4 queues is managed by a circular queue.SgAPdddA TimesNewRomanAd A  A A @T* A table of up to 8192 descriptor numbers.SgAPdddA TimesNewRomanAd A  A A @T Each element holdsSgAPdddA TimesNewRomanAd A  A A @T 3 status bitsSgAPpdddpA TimesNewRomanAd A  A A @T#13 bit index of a buffer descriptorSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T7 The queues are each managed by 4 FFRED/RFRED registersSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T * start (st_addr)SgAPdddA TimesNewRomanAd A  A A @T * end (ed_addr)SgAPdddA TimesNewRomanAd A  A A @T * read (rd_ptr)SgAPdddA TimesNewRomanAd A  A A @T * write (wr_ptr)SgAPdddA TimesNewRomanAd A  A A @Ts SgAPdddA TimesNewRomanAd A  A A @T!Initialization of an empty queueSgAPdddA TimesNewRomanAd A  A A @Trd_ptr = wr_ptr = st_addrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TInitialization of a full queueSgAPdddA TimesNewRomanAd A  A A @Trd_ptr = st_addrSgAPdddA TimesNewRomanAd A  A A @Twr_ptr = end_addrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T Queue ptr Desc IDS'APdddA @A8A8 A8d A!8 TimesNewRomanE #fK S@APU APdPddAPL)BfSA PtfZSQAPA9P7NT st_addr->S'APdddA @A8A8 A8d A!8 TimesNewRomantfSAPiAHP7APA`A`?N|Ts1SgAPdddA TimesNewRomanAd A  A A @Ldt@fKSBAPA*P7N{TrSgAPdddA TimesNewRomanAd A  A A @t fSAPiA9P7APA`A`@N|Ts2SgAPdddA TimesNewRomanAd A  A A @LdtbNTrd_ptr->S'APdddA @A8A8 A8d A!8 TimesNewRomant fSAPiA9P7APA`A`@N|Ts3SgAPdddA TimesNewRomanAd A  A A @LdtbN{TrSgAPdddA TimesNewRomanAd A  A A @t fSAPiA9P7APA`A`@N|Ts4SgAPdddA TimesNewRomanAd A  A A @LdtbNTzwr_ptr->SgAPdddA TimesNewRomanAd A  A A @t fSAPiA9P7APA`A`@N|Ts5SgAPdddA TimesNewRomanAd A  A A @LdtbN{TrSgAPdddA TimesNewRomanAd A  A A @t fSAPiA9P7APA`A`@N|Ts6SgAPdddA TimesNewRomanAd A  A A @LUdtbNT ed_addr->S'APdddA @A8 A8  A8 d A!8 TimesNewRomantfSAPiA9P7APA`A`@N$T7SA @TrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T.Inserting a buffer descriptor id into a queue:SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TDWrite the descriptor number at the location pointed to by the wr_ptrSgAPdddA TimesNewRomanAd A  A A @TIncrement the wr_ptrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TSia_putw ((ushort_t *)(softc->brd_regs.rfred_mem + rfL->lrg_wr_ptr), desc_num);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TrfL->lrg_wr_ptr += 2;SaAPdddACourierAd A  A A @T&if (rfL->lrg_wr_ptr > rfL->lrg_ed_adr)SaAPdddACourierAd A  A A @T"rfL->lrg_wr_ptr = rfL->lrg_st_adr;SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T*Removing a buffer descriptor id from queueSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T6Consume the descriptor number pointed to by the rd_ptrSgAPdddA TimesNewRomanAd A  A A @TIncrement the rd_ptrSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T=/* In the present version sk_bufs get bound to descriptors */SaAPdddACourierAd A  A A @T=/* and they are unbound in the tx_complete interrupt */SaAPdddACourierAd A  A A @T=/* handling logic.. This leads to a potentially nasty race */SaAPdddACourierAd A  A A @T=/* condition in which buffers appear on the hardware tcq */SaAPdddACourierAd A  A A @T=/* but haven't yet been consumed by the interrupt handler */SaAPdddACourierAd A  A A @T=/* If we were to try to consume one of those here, it would*/SaAPdddACourierAd A  A A @T=/* cause a huge mess... so we dont! */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T=/* See if the read pointer has caught up with buffers that */SaAPdddACourierAd A  A A @T=/* haven't been scavenged yet. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T1 if (softc->last_tcq_wr_ptr == ffL->tcq_rd_ptr)SaAPdddACourierAd A  A A @T| return -1;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T./* Get the next available descriptor number */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TV desc_num = ia_getw ((ushort_t*)(softc->brd_regs.ffred_mem + ffL->tcq_rd_ptr));SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T comp_code = desc_num >> 13;SgAPdddA TimesNewRomanAd A  A A @A8A8d A8CourierTrSgAPdddA TimesNewRomanAd A  A A @T#Buffer queue producers and consumerSgAPdddA TimesNewRomanAd A  A A @A8#TrSgAPdddA TimesNewRomanAd A  A A @E#fK S@APC APdddAPLBfSA PtxfZSQAPA9P7NT Queuet{fZSQAPh A9P7NT ProducertfiS`AP AHP7NT ConsumerLZdtg@fKSBAPA*P7NT tcqtj@fKSBAPh A*P7NT IA5515t@fZSQAP A9P7N!T Device driverLzdtbNT prqt2bN+T" Device driver (via DLE)t!bNT IA5515LpdtbNT lrgt(bN!T Device drivert!bNT IA5515LpdtbNT pcqt!bNT IA5515t(bN!T Device driverTt SgAPdddA TimesNewRomanAd A  A A @Tw SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T+The DMA Control Engine (a.k.a. the flipper)SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TDMA control structuresSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TTwo circular lists (Rx and Tx)SgAPdddA TimesNewRomanAd A  A A @T"256 DMA list elements each (DLE's)SgAPdddA TimesNewRomanAd A  A A @TAEach element describes a single block of memory to be transferredSgAPdddA TimesNewRomanAd A  A A @THHowever, a single scatter/gather (SG) transfer may involve multiple DLEsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TIn this driver SgAPdddA TimesNewRomanAd A  A A @T'All Rx transfers employ exactly one DLESgAPdddA TimesNewRomanAd A  A A @T All Tx transfers employ two DLEsSgAPdddA TimesNewRomanAd A  A A @TOne for the data in the skbufSgAPpdddpA TimesNewRomanAd A  A A @T$One for the 8 byte AAL5 CPCS trailerSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @TDMA List Elements (DLEs) SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TKControl the DMA transfer of data between system RAM and Adapter buffer RAMSgAPdddA TimesNewRomanAd A  A A @TStructure is as follows:SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Toffset functionSgAPdddA TimesNewRomanAd A  A A @T, 0x00 - 32 bit host physical memory addressSgAPdddA TimesNewRomanAd A  A A @T, 0x04 - 24 bit adapter local buffer addressSgAPdddA TimesNewRomanAd A  A A @T# 0x08 - 17 bit transfer byte countSgAPdddA TimesNewRomanAd A  A A @T8 0x0c - Mode word (contains PRQ disable and INT enable)SgAPdddA TimesNewRomanAd A  A A @T0 0x0e - New desc value for PRQ table[wr_ptr++] SgAPdddA TimesNewRomanAd A  A A @T2S'APdddA @Ttypedef struct _dle SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T< uint_t dle_sys_addr; /* phys addr in system mem */SaAPdddACourierAd A  A A @T< uint_t dle_local_addr; /* Address in buffer ram */S'APdddA @A8<A8 <A8<d A8<CourierT< uint_t dle_count; /* Number of bytes to xfer */SaAPdddACourierAd A  A A @T< ushort_t dle_prq_wr_ptr; /* set PRQ[wr_ptr++] = this */S'APdddA @A8<A8 <A8<d A8<CourierT< ushort_t dle_mode; /* Flags.. interrupt, etc. */SaAPdddACourierAd A  A A @Ts}dle_t;SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T|SqAPdddA PA TimesNewRomanAd A  A A @T5The flipper accesses the DLE lists via two registers:SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Ttypedef struct _fl_internalSaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @TC uint_t fl_ctrl; /* general purpose flipper control */SaAPdddACourierAd A  A A @T4 uint_t fl_status; /* interrupt status */SaAPdddACourierAd A  A A @T8 uint_t fl_mac1; /* lower 32 bits of mac */SaAPdddACourierAd A  A A @TA uint_t fl_mac2; /* ID etc & upper 16 bits of mac */SaAPdddACourierAd A  A A @T : S'APdddA @A8A8 A8d A8CourierTn :SaAPdddACourierAd A  A A @T5 uint_t fl_transmit_list; /* Pre-Atlantic only */SaAPdddACourierAd A  A A @T5 uint_t fl_receive_list; /* Pre-Atlantic only */SaAPdddACourierAd A  A A @T uint_t fl_eeprom_access;SaAPdddACourierAd A  A A @To :SaAPdddACourierAd A  A A @T|} fl_internal_t;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TDriver management of DLE listsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T/Process is analogous to packet queue managementSgAPdddA TimesNewRomanAd A  A A @T6Thus "list pointers" are something of a misnomer here SgAPdddA TimesNewRomanAd A  A A @T=Tx DLE list initialization is done in ia_pci.c as shown here:SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @T dma = &softc->tx_list_dma;SaAPdddACourierAd A  A A @T! dma->len = SIZE_DLE_SPACE * 2;SaAPdddACourierAd A  A A @T* dma->addr = kmalloc(dma->len, GFP_DMA);SaAPdddACourierAd A  A A @T dma->real_len = dma->len;SaAPdddACourierAd A  A A @T dma->taddr = dma->addr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T dma->alloc_handle_done = 1;SaAPdddACourierAd A  A A @T dma->mem_alloc_done = 1;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TA offset = ROUND_UP(dma->addr, SIZE_DLE_SPACE) - (int)dma->addr;SaAPdddACourierAd A  A A @T dma->addr += offset;SaAPdddACourierAd A  A A @T dma->real_len -= offset;SaAPdddACourierAd A  A A @T) dmac_address = virt_to_bus(dma->addr);SaAPdddACourierAd A  A A @T6 ia_put32(&flipper->fl_transmit_list, dmac_address);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T list = &softc->list[TX_LIST];SaAPdddACourierAd A  A A @T' list->list_start = (DLE *)dma->addr;SaAPdddACourierAd A  A A @T: list->list_end = (DLE *)(dma->addr + SIZE_DLE_SPACE);SaAPdddACourierAd A  A A @T' list->list_read = (DLE *)dma->addr;SaAPdddACourierAd A  A A @T' list->list_write = (DLE *)dma->addr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TWrite pointers SgAPdddA TimesNewRomanAd A  A A @T7always identifies the next DLE to be used by the driverSgAPdddA TimesNewRomanAd A  A A @T5 is incremented by the driver each time a DLE is usedSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TRead pointers SgAPdddA TimesNewRomanAd A  A A @THmay be used to identify the next DLE for which DMA completion will occurSgAPdddA TimesNewRomanAd A  A A @T0Tx logic doesn't currently use the read pointer.SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TNOTE: The flipper doesn't touch the list values and the driver never touches the fl_transmit_list and fl_receive_list registers after initialization.SgAPdddA TimesNewRomanAd A  A A @A8 $(TInitiating a DMA transferSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TIThe transaction count registers are mapped in the adapter register space.SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TTo initiate a DMA transfer:SgAPdddA TimesNewRomanAd A  A A @T$Fill in the required number of DLE'sSgAPdddA TimesNewRomanAd A  A A @T:Write the required number of DLE's into the tx_tc or rx_tcSgAPdddA TimesNewRomanAd A  A A @TXSMAPdddACourierAd A @TB softc->brd_regs.flipper = (ia_flip_t *)(vaddr + FLIP_OFFSET);SMAPdddACourierAd A @TF softc->brd_regs.config = (fl_config_t *)(vaddr + CONFIG_OFFSET);SMAPdddACourierAd A @T@ softc->brd_regs.tx_tc = (uint_t *)(vaddr + TX_TC_OFFSET);SMAPdddACourierAd A @T@ softc->brd_regs.rx_tc = (uint_t *)(vaddr + RX_TC_OFFSET);SMAPdddACourierAd A @TlSaAPdddACourierAd A  A A @TPossible problemsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TNWhat if the list_write pointer and the fl_transmit_list reg got out of sync??SgAPdddA TimesNewRomanAd A  A A @A8  A8 A8 '7T S'APdddA @A8A8 A8d A!8 TimesNewRomanTAns: It would be fatal!SgAPpdddpA TimesNewRomanAd A  A A @T2S'APdddA @TNWhat if the tx_tc reg were to be overwritten before a DMA transfer completes??SgAPdddA TimesNewRomanAd A  A A @A8  TrSgAPpdddpA TimesNewRomanAd A  A A @T|Ans: Have to depend on the hardware designers to immediately add whatever is written to a hidden pending tx count register!!SgAPpdddpA TimesNewRomanAd A  A A @A8 1<TrSgAPpdddpA TimesNewRomanAd A  A A @T3What if the list_write pointer should wrap around??SgAPdddA TimesNewRomanAd A  A A @A8  TrSgAPdddA TimesNewRomanAd A  A A @THAns: That would also be fatal. The driver doesn't check for that though because the number of buffers (and thus the number of possible pending DMA's is << 256). This is a possible exposure that should be fixed. SgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPpdddpA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TThe Complete Transmission PathSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T ia_lnxatm.cS'APdddA @A8 A8  A8 d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TNThe softc data structure contains or points to all info required by the driverSgAPdddA TimesNewRomanAd A  A A @A8  TIA pointer to it was stored in the atm_dev structure at registration time.SgAPdddA TimesNewRomanAd A  A A @Ts SgAPdddA TimesNewRomanAd A  A A @T|int ia5515_send(SaAPdddACourierAd A  A A @Tstruct atm_vcc *vcc,SaAPdddACourierAd A  A A @Tstruct sk_buff *skb)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T ia_softc_t *softc;SaAPdddACourierAd A  A A @T~ int rc;SaAPdddACourierAd A  A A @T int len;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T0/* Recover softc ptr from where we stashed it */SaAPdddACourierAd A  A A @T0/* Then call send5skb to do real work. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T! softc = vcc->dev->dev_data; SaAPdddACourierAd A  A A @T~ S'APdddA @A8A8 A8d A8CourierT% rc = ia_send5skb(softc, vcc, skb);SaAPdddACourierAd A  A A @Tz return(rc);SaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tia_ffred.c: ia_send5skb()SaAPdddACourierAd A  A A @A8A8d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TVThe objective here is to see if the request can be started now or if it must be queuedSgAPdddA TimesNewRomanAd A  A A @T.If it can be started the driver does start it.SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T}int ia_send5skb(SaAPdddACourierAd A  A A @Tia_softc_t *softc,SaAPdddACourierAd A  A A @TDstruct atm_vcc *vcc, /* Pointer to VCC struct. */SaAPdddACourierAd A  A A @TDstruct sk_buff *skb) /* Pointer to socket buffer */SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T unsigned long lockflags;SaAPdddACourierAd A  A A @T aal_parms_t aal;SaAPdddACourierAd A  A A @T ushort_t prqwp;SaAPdddACourierAd A  A A @T int desc_num;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T2 spin_lock_irqsave(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @T2 softc->driver_stat.tx_skb_consumed += skb->len;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T aal.ap_vpci = vcc->vci;SaAPdddACourierAd A  A A @T aal.ap_mid = 0;SaAPdddACourierAd A  A A @T aal.ap_orderq = 0;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T?/* If something is already queued for this vcc we MUST queue */SaAPdddACourierAd A  A A @T?/* this request to retain FIFO scheduling. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T. if (softc->vcctab[vcc->vci].tx_pending > 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T ATM_SKB(skb)->vcc = vcc;SaAPdddACourierAd A  A A @T? skb_queue_tail(&softc->vcctab[vcc->vci].tx_backlog, skb);SaAPdddACourierAd A  A A @T. softc->vcctab[vcc->vci].tx_pending += 1;SaAPdddACourierAd A  A A @T softc->tx_backlog += 1;SaAPdddACourierAd A  A A @T: spin_unlock_irqrestore(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @T2S'APdddA @TLNow check for excessive resource utilization unless this is a privileged VCCSqAPdddA PA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T8 * Find a free descriptor in the transmit complete queueSaAPdddACourierAd A  A A @T4 * If none available or quota exceeded have to queueSaAPdddACourierAd A  A A @T */S'APdddA @A8A8 A8d A8CourierT2S'APdddA @T desc_num = -1;S'APdddA @A8A8 A8d A8CourierTlSaAPdddACourierAd A  A A @TB if ((vcc->vci < 16) || (softc->vcctab[vcc->vci].tx_active < 4))SaAPdddACourierAd A  A A @T6 desc_num = ia_find_free_tx_desc(softc, &prqwp);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T>/* If desc_num is still -1 then we must queue the reqest */ SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (desc_num == -1)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T ATM_SKB(skb)->vcc = vcc;SaAPdddACourierAd A  A A @T? skb_queue_tail(&softc->vcctab[vcc->vci].tx_backlog, skb);SaAPdddACourierAd A  A A @T. softc->vcctab[vcc->vci].tx_pending += 1;SaAPdddACourierAd A  A A @T softc->tx_backlog += 1;SaAPdddACourierAd A  A A @T: spin_unlock_irqrestore(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T+/* Obviously this should NEVER happen !! */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T9 if ((desc_num < 1) || (desc_num > softc->num_tx_desc))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TI printk("IA 5515: transmit descriptor %X out of range\n", desc_num);SaAPdddACourierAd A  A A @T: spin_unlock_irqrestore(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @T return IA_FAIL;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T3/* Finally we call drive tx_to start the request */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T2 ia_drive_tx(softc, desc_num, prqwp, vcc, skb);SaAPdddACourierAd A  A A @T7 spin_unlock_irqrestore(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T"ia_ffred.c: ia_find_free_tx_desc()SkAPdddA PACourierAd A  A A @A8"A8"d A!8" TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TThings to note:SgAPdddA TimesNewRomanAd A  A A @TCuse of local copy of board registers where possible for performanceSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tpotential race contention SxAPdd,dA TimesNewRomanAd A  A A @A@. TVinvolves this routine, ffred, and the interrupt handler regarding recycling of buffersSxAPdd,dpA TimesNewRomanAd A  A A @A@. T8could be resolved by freeing skb's here.. disadvantage??SxAPdd,dpA TimesNewRomanAd A  A A @A@. TSxAPdd,dpA TimesNewRomanAd A  A A @A@. T(ia_find_free_tx_desc (ia_softc_t *softc,SaAPdddACourierAd A  A A @T;ushort_t *prqwp) /* new value of prq write ptr here */SaAPdddACourierAd A  A A @T}{S'APdddA @A8A8 A8d A8CourierT+ ffred_t *ff = softc->brd_regs.ffred;SaAPdddACourierAd A  A A @T0 ffred_t *ffL = &softc->brd_regs_ll.ffred;SaAPdddACourierAd A  A A @T ushort_t desc_num;SaAPdddACourierAd A  A A @T u_int comp_code;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T=/* In the present version sk_bufs get bound to descriptors */SaAPdddACourierAd A  A A @T=/* and they are unbound in the tx_complete interrupt */SaAPdddACourierAd A  A A @T=/* handling logic.. This leads to a potentially nasty race */SaAPdddACourierAd A  A A @T=/* condition in which buffers appear on the hardware tcq */SaAPdddACourierAd A  A A @T=/* but haven't yet been consumed by the interrupt handler */SaAPdddACourierAd A  A A @T=/* If we were to try to consume one of those here, it would*/SaAPdddACourierAd A  A A @T=/* cause a huge mess... so we dont! */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T=/* See if the read pointer has caught up with buffers that */SaAPdddACourierAd A  A A @T=/* haven't been scavenged yet. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T1 if (softc->last_tcq_wr_ptr == ffL->tcq_rd_ptr)SaAPdddACourierAd A  A A @T| return -1;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T./* Get the next available descriptor number */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TV desc_num = ia_getw((ushort_t*)(softc->brd_regs.ffred_mem + ffL->tcq_rd_ptr));SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T comp_code = desc_num >> 13;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T desc_num &= 0x1fff;SaAPdddACourierAd A  A A @T ffL->tcq_rd_ptr += 2;SaAPdddACourierAd A  A A @T) if (ffL->tcq_rd_ptr > ffL->tcq_ed_adr)SaAPdddACourierAd A  A A @T( ffL->tcq_rd_ptr = ffL->tcq_st_adr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T*/* Update the "real" copy of the rd_ptr */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T. ia_put32(&ff->tcq_rd_ptr, ffL->tcq_rd_ptr);S'APdddA @A8.A8 .A8.d A8.CourierT2S'APdddA @Tn/*SaAPdddACourierAd A  A A @T8 * Put the descriptor index in the PRQ and increment theSaAPdddACourierAd A  A A @T7 * local PRQ write pointer. Real PRQ write pointer willSaAPdddACourierAd A  A A @T * be updated by the DLESaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TT ia_putw ((ushort_t *)(softc->brd_regs.ffred_mem + ffL->prq_wr_ptr), desc_num);SaAPdddACourierAd A  A A @T ffL->prq_wr_ptr += 2;SaAPdddACourierAd A  A A @T) if (ffL->prq_wr_ptr > ffL->prq_ed_adr)SaAPdddACourierAd A  A A @T( ffL->prq_wr_ptr = ffL->prq_st_adr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T *prqwp = ffL->prq_wr_ptr;SaAPdddACourierAd A  A A @T return(desc_num);SaAPdddACourierAd A  A A @Tia_ffred.c: ia_drive_tx()SkAPdddA PACourierAd A  A A @A8A8d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @T=This function initiate DMA transfer of a packet to buffer RAMSgAPdddA TimesNewRomanAd A  A A @TJBy setting the DLE_PRQWD flag it, it indirectly triggers the FFRED engine SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @T}void ia_drive_tx(SaAPdddACourierAd A  A A @Tia_softc_t *softc,SaAPdddACourierAd A  A A @Tushort_t desc_num,SaAPdddACourierAd A  A A @Tushort_t prqwp,SaAPdddACourierAd A  A A @Tstruct atm_vcc *vcc,SaAPdddACourierAd A  A A @Tstruct sk_buff *skb)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T aal_parms_t aal;SaAPdddACourierAd A  A A @T ia_tx_buf_t *tx_buf;SaAPdddACourierAd A  A A @T DLE *dle;SaAPdddACourierAd A  A A @T ulong_t baddr;SaAPdddACourierAd A  A A @T int len;SaAPdddACourierAd A  A A @T int total_len;SaAPdddACourierAd A  A A @T/ ia_dle_list_t *list = &softc->list[TX_LIST];SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T:/* Host buffer control structure holds packet data info */SaAPdddACourierAd A  A A @To/* SaAPdddACourierAd A  A A @T' tx_buf = &softc->tx_buf[desc_num-1];SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T~ len = skb->len;SaAPdddACourierAd A  A A @T# softc->driver_stat.tx_packets++;SaAPdddACourierAd A  A A @T& softc->driver_stat.tx_bytes += len;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T aal.ap_vpci = vcc->vci;SaAPdddACourierAd A  A A @T aal.ap_mid = 0;SaAPdddACourierAd A  A A @T aal.ap_orderq = 0;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T* softc->vcctab[vcc->vci].tx_active += 1;SaAPdddACourierAd A  A A @T2S'APdddA @TH/* Setup buffer descriptor and CS trailer (pointed to by tx_buf->addr */SaAPdddACourierAd A  A A @T S'APdddA @A8A8 A8d A8CourierTQ total_len = ia_setup_ffred (softc, &aal, tx_buf->desc, tx_buf->addr, len);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @To SaAPdddACourierAd A  A A @TD/* Be sure that skb and vcc ptrs are in our host buffer structure */SaAPdddACourierAd A  A A @TD/* so that they may be recovered at interrupt time. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T tx_buf->skb = skb;SaAPdddACourierAd A  A A @T tx_buf->vcc = vcc;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TvSkAPdddA PACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T/ * Build DLEs to transfer the data to the boardSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T dle = list->list_write;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T;/* The first one here copies the data from the skb.. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T" baddr = (ulong_t)tx_buf->baddr;SaAPdddACourierAd A  A A @T dle->dle_local_addr = baddr;SaAPdddACourierAd A  A A @T0 dle->dle_sys_addr = virt_to_bus(skb->data);SaAPdddACourierAd A  A A @T dle->dle_count = len;SaAPdddACourierAd A  A A @T dle->dle_mode = DLE_PRQWD;SaAPdddACourierAd A  A A @T dle->dle_prq_wr_ptr = prqwp;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (++dle == list->list_end)SaAPdddACourierAd A  A A @T dle = list->list_start;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T list->list_write = dle;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T7/* This one puts the CS trailer where it needs to be */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T/ dle->dle_local_addr = baddr + total_len - 8;SaAPdddACourierAd A  A A @T3 dle->dle_sys_addr = (ulong_t)tx_buf->daddr[0];SaAPdddACourierAd A  A A @T dle->dle_count = 8;SaAPdddACourierAd A  A A @T" dle->dle_mode = DLE_INT_ENABLE;SaAPdddACourierAd A  A A @T dle->dle_prq_wr_ptr = prqwp;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (++dle == list->list_end)SaAPdddACourierAd A  A A @T dle = list->list_start;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T list->list_write = dle;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T~/*S'APdddA @A8A8 A8d A8CourierT; * Incrementing the transaction counter starts the DMA xferSaAPdddACourierAd A  A A @T5 * The value 2 is the number of DLE's to be processedSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T' ia_put32 (softc->brd_regs.tx_tc, 2);SaAPdddACourierAd A  A A @Tv return;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tia_ffred.c: ia_setup_ffred()SkAPdddA PACourierAd A  A A @A8A8d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TFThis function initializes the CPSC trailer and fills in the descriptorSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Ty static size_tSaAPdddACourierAd A  A A @T|ia_setup_ffred (SaAPdddACourierAd A  A A @Tia_softc_t *softc,SaAPdddACourierAd A  A A @T+struct aal_parms *al, /* AAL parms */SaAPdddACourierAd A  A A @T6f_buf_desc *desc, /* address of desc in ffred */SaAPdddACourierAd A  A A @T8caddr_t addr, /* address of cs trailer buffer */SaAPdddACourierAd A  A A @T1size_t len) /* Length of data part of pkt */S'APdddA @A81A8 1A81d A81CourierTm{SaAPdddACourierAd A  A A @T CS_HEADER *hdr;SaAPdddACourierAd A  A A @T CS_TRAILER *trl;SaAPdddACourierAd A  A A @T CS5_TRAILER *trl5;SaAPdddACourierAd A  A A @T int total_len;SaAPdddACourierAd A  A A @T u_int pad_loc;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T. * Build CPCS header/trailer for AAL 5 packetsSaAPdddACourierAd A  A A @Tn*/SaAPdddACourierAd A  A A @T) total_len = len + sizeof(CS5_TRAILER);SaAPdddACourierAd A  A A @T& total_len = ((total_len+47)/48)*48;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T trl5 = (CS5_TRAILER *)(addr);SaAPdddACourierAd A  A A @T trl5->control = 0;SaAPdddACourierAd A  A A @T trl5->length = swap(len);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T * Set up descriptorSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TY ia_putw (&desc->desc_stat, F_AAL5 | F_EOM_EN | F_XD_INTT_EN | F_D_APP_CRC32);SaAPdddACourierAd A  A A @T+ ia_putw (&desc->vci, al->ap_vpci);SaAPdddACourierAd A  A A @T* ia_putw (&desc->mid, al->ap_mid);SaAPdddACourierAd A  A A @T7 ia_putw (&desc->length, (total_len * 52)/48 - 4);SaAPdddACourierAd A  A A @T- ia_putw (&desc->order_q, al->ap_orderq);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T return total_len;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TInterrupt ManagementSkAPdddA PACourierAd A  A A @A8A8d A!8 TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @TInterrupt sources:SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tn/*SaAPdddACourierAd A  A A @T * Status Register BitsSaAPdddACourierAd A  A A @T( * These are interupt status indicators.SaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T4#define FL_STAT_ERRINT 0x00000040 /* Trouble */SaAPdddACourierAd A  A A @T(#define FL_STAT_MARKINT 0x00000020 SMAPdddACourierAd A @A8(A8 (TA#define FL_STAT_DLETINT 0x00000010 /* transmit DMA done */ SMAPdddACourierAd A @A8@A8 @T@#define FL_STAT_DLERINT 0x00000008 /* receive DMA done */SaAPdddACourierAd A  A A @TF#define FL_STAT_FEINT 0x00000004 /* Front end trouble */ SaAPdddACourierAd A  A A @T@#define FL_STAT_FFREDINT 0x00000002 /* Transmit complete */SaAPdddACourierAd A  A A @T@#define FL_STAT_RFREDINT 0x00000001 /* New packet on board */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T<Interrupts are individually maskable in the fl_ctrl registerSgAPdddA TimesNewRomanAd A  A A @A8 ,4TDDLE and FE ints are cleared by writing a 1 to the corresponding bit SgAPdddA TimesNewRomanAd A  A A @TLFFRED and RFRED ints are cleared by reading the FFRED/RFRED intr_status_regsSgAPdddA TimesNewRomanAd A  A A @A8 $+A8 ;KTrSgAPdddA TimesNewRomanAd A  A A @T4FFred interrupt status bits: ffred->intr_status_reg;SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T! * Interrupt Status/Mask registerSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T~/* Pre-Atlantic */SaAPdddACourierAd A  A A @TM#define F_CM_PARERR 0x8000 /* Control memory parity error */SaAPdddACourierAd A  A A @TM#define F_NORM_PM_PARERR 0x4000 /* Normal (non-CBR) packet mem error */SaAPdddACourierAd A  A A @TM#define F_CBR_PM_PARERR 0x2000 /* CBR packet memory error */SaAPdddACourierAd A  A A @TM#define F_TCQ_NOT_EMPTY 0x1000 /* TCQ went non-empty */SaAPdddACourierAd A  A A @TM#define F_TCQ_FULL 0x0800 /* TCQ full */SaAPdddACourierAd A  A A @TM#define F_TCC_FULL 0x0400 /* Cell counter overflowed */SaAPdddACourierAd A  A A @TM#define F_TRANSMIT_DONE 0x0200 /* Xmit done (if XD_INTT set in desc) */SaAPdddACourierAd A  A A @TM#define F_CBR_DONE 0x0100 /* CBR done */SaAPdddACourierAd A  A A @T#define F_RQ_A_MIS 0x0080 /* Rate queue in bank A missed service */ #define F_RQ_B_MIS 0x0040 /* Rate queue in bank B missed service*/SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T4RFred interrupt status bits: rfred->intr_status_reg;SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T! * Interrupt Status/Mask RegisterSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T~/* Pre-Atlantic */SaAPdddACourierAd A  A A @T>#define R_CBR_CTR_OF 0x2000 /* Sync counter overflow */SaAPdddACourierAd A  A A @TD#define R_CM_PARERR 0x0400 /* Control memory parity error */SaAPdddACourierAd A  A A @TA#define R_LRG_FREEQ_MT 0x0200 /* No Large free descriptor */SaAPdddACourierAd A  A A @TA#define R_SML_FREEQ_MT 0x0100 /* No Small free descriptor */SaAPdddACourierAd A  A A @T>#define R_PCQ_FL_I 0x0040 /* Complete queue full */SaAPdddACourierAd A  A A @T8#define R_CBRQ_FL_I 0x0020 /* Sync fifo full */SaAPdddACourierAd A  A A @T>#define R_CBR_RCVD 0x0002 /* Sync cell received */SaAPdddACourierAd A  A A @TD#define R_PKT_CTR_OF 0x8000 /* Drop packet counter overflow */SaAPdddACourierAd A  A A @TD#define R_ERR_CTR_OF 0x4000 /* Error cell counter overflow */SaAPdddACourierAd A  A A @TD#define R_CELL_CTR_OF 0x1000 /* Received cell counter o-flow */SaAPdddACourierAd A  A A @T>#define R_FREEQ_MT 0x0200 /* No free descriptor */SaAPdddACourierAd A  A A @T>#define R_EXCPQ_FL_I 0x0080 /* Exception queue full */SaAPdddACourierAd A  A A @T>#define R_RAWQ_FL_I 0x0010 /* Flow control fifo full */SaAPdddACourierAd A  A A @T>#define R_EXCP_RCVD 0x0008 /* Excepton error occurred */SaAPdddACourierAd A  A A @T;#define R_PKT_RCVD 0x0004 /* Packet received */SaAPdddACourierAd A  A A @TA#define R_RAW_RCVD 0x0001 /* Flow control cell received */SaAPdddACourierAd A  A A @TTransmit Interrupt HandlingSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tia_flip.c:in_intr()SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T( The common front end for all interruptsSgAPdddA TimesNewRomanAd A  A A @TKFor both transmit and receive ops there are two possible interrupt sources:SgAPdddA TimesNewRomanAd A  A A @TDMA transfer completeSgAPdddA TimesNewRomanAd A  A A @T&Packet transmission/reception completeSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TpvoidSaAPdddACourierAd A  A A @Ttia_intr(SaAPdddACourierAd A  A A @Tx int irq,SaAPdddACourierAd A  A A @Ty void *arg)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T* ia_softc_t *softc = (ia_softc_t *)arg;SaAPdddACourierAd A  A A @T. ffred_t *ffred = softc->brd_regs.ffred;SaAPdddACourierAd A  A A @T. rfred_t *rfred = softc->brd_regs.rfred;SaAPdddACourierAd A  A A @T- ia_suni_t *suni = softc->brd_regs.suni;SaAPdddACourierAd A  A A @T uint_t flip_intr_status;SaAPdddACourierAd A  A A @T! uint_t ffred_intr_status;SaAPdddACourierAd A  A A @T! uint_t rfred_intr_status;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T2 * Check to see if our controller in interrupting.SaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TD flip_intr_status = ia_get32(&softc->brd_regs.flipper->fl_status);SaAPdddACourierAd A  A A @T# softc->driver_stat.interrupts++;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tq doSaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T~ S'APdddA @A8A8 A8d A8CourierT. if (flip_intr_status & FL_STAT_RFREDINT)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T) softc->driver_stat.rfred_intr++;SaAPdddACourierAd A  A A @T! ia_rfred_handler(softc);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T[ In the buggy version initially distributed we read the rfred intr_status_reg right here!!S'APdddA @A8[A8 [A8[d A!8[ TimesNewRomanTrSgAPdddA TimesNewRomanAd A  A A @T.If a new packet had arrived while the one that caused the original interrupt was being processed we destroyed the interrupt for it! Since rfred hangs interrupts only on empty to non-empty and not-full to full transmissions we had to wait for the pcq to fill completely before seeing another packet :-(SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Ts }SaAPdddACourierAd A  A A @T2S'APdddA @T. if (flip_intr_status & FL_STAT_FFREDINT)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T" uint_t ffred_intr_status;SaAPdddACourierAd A  A A @T) softc->driver_stat.tfred_intr++;SaAPdddACourierAd A  A A @T> ffred_intr_status = ia_getw(&ffred->intr_status_reg);SaAPdddACourierAd A  A A @T% ffred_intr_status &= 0xffff;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T ia_tx_tcq_intr(softc);S'APdddA @A8A8 A8d A8CourierTs }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TDMA complete interruptsSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T9Receive DMA complete means we have an skbuf ready to pushSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TETransmit DMA means data copied to board we could free the skbuf here,SgAPdddA TimesNewRomanAd A  A A @TDbut we can't recycle the descriptor.. so we just wait for TxCompleteSgAPdddA TimesNewRomanAd A  A A @Tm SaAPdddACourierAd A  A A @T- if (flip_intr_status & FL_STAT_DLERINT)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T* softc->driver_stat.addgen_intr++;SaAPdddACourierAd A  A A @TI ia_put32 (&softc->brd_regs.flipper->fl_status, FL_STAT_DLERINT);SaAPdddACourierAd A  A A @T ia_host_rx_intr(softc);SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T- if (flip_intr_status & FL_STAT_DLETINT)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @TH ia_put32(&softc->brd_regs.flipper->fl_status, FL_STAT_DLETINT);SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T, if (flip_intr_status & FL_STAT_ERRINT)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T@ printk("IA 5515: FATAL PCI Error cmd/stat is is %x \n",SaAPdddACourierAd A  A A @T? softc->brd_regs.config->fl_cfg_cmd_status);SaAPdddACourierAd A  A A @T ia_card_reset(softc);SaAPdddACourierAd A  A A @T{ break;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TzH Read the status register to see if another interrupt has become pendingS'APdddA @T2S'APdddA @T< flip_intr_status = softc->brd_regs.flipper->fl_status;SaAPdddACourierAd A  A A @T, flip_intr_status &= ~FL_STAT_RESERVED;SaAPdddACourierAd A  A A @T } while (flip_intr_status);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tv return;SaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @T2S'APdddA @T2S'APdddA @T2S'APdddA @Tia_ffred.c: ia_tx_tcq_intrSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T;Multiple buffers may be recovered during a single interruptSgAPdddA TimesNewRomanAd A  A A @TEHost buffer control structure is used to recover vcc and skb pointersSgAPdddA TimesNewRomanAd A  A A @A8 14A8 9<TrSgAPdddA TimesNewRomanAd A  A A @Tvoid ia_tx_tcq_intr(SaAPdddACourierAd A  A A @T~ia_softc_t *softc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T) ffred_t *ff = softc->brd_regs.ffred;SaAPdddACourierAd A  A A @T. ffred_t *ffL = &softc->brd_regs_ll.ffred;SaAPdddACourierAd A  A A @T ia_tx_buf_t *tx_buf;SaAPdddACourierAd A  A A @T ulong_t lockflags;SaAPdddACourierAd A  A A @T} ulong_t ptr;SaAPdddACourierAd A  A A @T int desc_num;SaAPdddACourierAd A  A A @T! struct atm_vcc *vcc = NULL;SaAPdddACourierAd A  A A @T struct sk_buff *skb;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T2 spin_lock_irqsave(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TB/* The write pointer is where the Fred will post the descriptor */SaAPdddACourierAd A  A A @TB/* number of the text buffer that it frees.. Our software */SaAPdddACourierAd A  A A @TB/* last_tcq_wr_ptr chases the actual tcq_wr_ptr around the */SaAPdddACourierAd A  A A @TB/* the pointer list.. Descriptor numbers lying between the */SaAPdddACourierAd A  A A @TB/* two represent buffers that are complete but which have skbs */SaAPdddACourierAd A  A A @TB/* attached that need to be returned. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T, ptr = ia_get32(&ff->tcq_wr_ptr) & 0xffff;SaAPdddACourierAd A  A A @T( while (softc->last_tcq_wr_ptr != ptr)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TA desc_num = ia_getw((ushort_t *)(softc->brd_regs.ffred_mem +SaAPdddACourierAd A  A A @TB softc->last_tcq_wr_ptr));SaAPdddACourierAd A  A A @T* tx_buf = &softc->tx_buf[desc_num-1];SaAPdddACourierAd A  A A @T if (tx_buf->skb != NULL)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T skb = tx_buf->skb;SaAPdddACourierAd A  A A @T vcc = tx_buf->vcc;SaAPdddACourierAd A  A A @T0 softc->vcctab[vcc->vci].tx_active -= 1;SaAPdddACourierAd A  A A @T8 softc->driver_stat.tx_skb_returned += skb->len;SaAPdddACourierAd A  A A @T if (vcc != NULL)SaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @T! if (vcc->pop != NULL)SaAPdddACourierAd A  A A @T" vcc->pop(vcc, skb);SaAPdddACourierAd A  A A @T| elseSaAPdddACourierAd A  A A @T" dev_kfree_skb(skb);SaAPdddACourierAd A  A A @Tv }SaAPdddACourierAd A  A A @Ty elseSaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @TE printk("IA_5515: Null vcc address in free desc ???? \n");SaAPdddACourierAd A  A A @T dev_kfree_skb(skb);SaAPdddACourierAd A  A A @Tv }SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tv elseSaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @TC printk("IA_5515: No skb adr in free desc %d\n", desc_num);SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T$Update buffer consumed queue pointerSqAPdddA PA TimesNewRomanAd A  A A @T0If queued backlog exists try to redrive it now..SgAPdddA TimesNewRomanAd A  A A @T9Why not do redrive at end and outside of the while loop??SgAPdddA TimesNewRomanAd A  A A @A8 A8 %TrSgAPdddA TimesNewRomanAd A  A A @T tx_buf->skb = NULL;SaAPdddACourierAd A  A A @T tx_buf->vcc = NULL;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T" softc->last_tcq_wr_ptr += 2;SaAPdddACourierAd A  A A @T3 if (softc->last_tcq_wr_ptr > ffL->tcq_ed_adr)SaAPdddACourierAd A  A A @T2 softc->last_tcq_wr_ptr = ffL->tcq_st_adr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (softc->tx_backlog > 0)SaAPdddACourierAd A  A A @T ia_redrive_tx(softc);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @T7 spin_unlock_irqrestore(&softc->xmitlock, lockflags);SaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tia_ffred.c: ia_redrive_tx()SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T8When packets are queued in software, we start them here.SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T~int ia_redrive_tx(SaAPdddACourierAd A  A A @T~ia_softc_t *softc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T int i;SaAPdddACourierAd A  A A @T int ndx;SaAPdddACourierAd A  A A @T int vci = 0;SaAPdddACourierAd A  A A @T ushort_t desc_num;SaAPdddACourierAd A  A A @T struct sk_buff *skb = 0;SaAPdddACourierAd A  A A @T struct atm_vcc *vcc = 0;SaAPdddACourierAd A  A A @T ushort_t prqwp;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T8/* Queuing discpline is strict priority for vcis < 32 */SaAPdddACourierAd A  A A @T8/* and round robin for vcis >= 32 */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T for (i = 1; i < 32; i++)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T* if (softc->vcctab[i].tx_pending > 0)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T: skb = skb_dequeue(&softc->vcctab[i].tx_backlog);SaAPdddACourierAd A  A A @T~ vci = i;SaAPdddACourierAd A  A A @T| break;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TvSkAPdddA PACourierAd A  A A @T./* Nothing hot to go... sched users vccs RR */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T| if (skb == 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T ndx = softc->last_tx_vcc;SaAPdddACourierAd A  A A @T{ ndx += 1;SaAPdddACourierAd A  A A @T if (ndx == NUM_VCCS)SaAPdddACourierAd A  A A @T ndx = 32;S'APdddA @A8A8 A8d A8CourierT2S'APdddA @T% for (i = 32; i < NUM_VCCS; i++)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T/ if (softc->vcctab[ndx].tx_pending > 0)SaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @T? skb = skb_dequeue(&softc->vcctab[ndx].tx_backlog);SaAPdddACourierAd A  A A @T& softc->last_tx_vcc = ndx;SaAPdddACourierAd A  A A @T vci = ndx;SaAPdddACourierAd A  A A @T break;SaAPdddACourierAd A  A A @Tv }SaAPdddACourierAd A  A A @T~ ndx += 1;SaAPdddACourierAd A  A A @T if (ndx == NUM_VCCS)SaAPdddACourierAd A  A A @T ndx = 32;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T9/* Since a backlog is counted... this shouldn't happen */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T| if (skb == 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T@ printk("IA 5515: NULL skb on vci %d in rdv w/ %d \n", vci,SaAPdddACourierAd A  A A @TC softc->tx_backlog);SaAPdddACourierAd A  A A @T- printk("Ndx = %d and lastvcc is %d \n",SaAPdddACourierAd A  A A @T? ndx, softc->last_tx_vcc);SaAPdddACourierAd A  A A @T softc->tx_backlog = 0;SaAPdddACourierAd A  A A @T~ if (vci > 0)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T, softc->vcctab[vci].tx_pending = 0;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @T return(IA_FAIL);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TWRecall that we made sure the skbuff contained the vcc pointer before queuing the packetSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T vcc = ATM_SKB(skb)->vcc;SaAPdddACourierAd A  A A @T| if (vcc == 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T2 printk("IA 5515: NULL vcc in redrive!! \n");SaAPdddACourierAd A  A A @T return(IA_FAIL);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T2 desc_num = ia_find_free_tx_desc(softc, &prqwp);SaAPdddACourierAd A  A A @T9 if ((desc_num < 1) || (desc_num > softc->num_tx_desc))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TV printk("IA 5515: Redrive: transmit descriptor %X out of range\n", desc_num);SaAPdddACourierAd A  A A @T return IA_FAIL;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T softc->tx_backlog--;SaAPdddACourierAd A  A A @T# softc->vcctab[vci].tx_pending--;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{#ifdef DBG_XMITSaAPdddACourierAd A  A A @TC printk("IA_5515: Redriving vci %d with %d and %d \n ", vcc->vci,SaAPdddACourierAd A  A A @T@ softc->tx_backlog, softc->vcctab[vci].tx_pending);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T1 ia_drive_tx(softc, desc_num, prqwp, vcc, skb);SaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TThe Receive PathSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T2Driven by two different interrupt service routinesSgAPdddA TimesNewRomanAd A  A A @T#Packet received in local buffer RAMSgAPdddA TimesNewRomanAd A  A A @T0Alloc skbuf and schedule transfer to system RAM.SgAPdddA TimesNewRomanAd A  A A @TDMA to system ram completeSgAPdddA TimesNewRomanAd A  A A @TPush skbuf up to the atm layerSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tia_rfred:ia_rfred_handler():S'APdddA @A8A8 A8d A!8 TimesNewRomanT2S'APdddA @TRNote that rfreds state register appears to have a bit (R_PCQ_EMPTY) that reflects SgAPdddA TimesNewRomanAd A  A A @A8 Tprq_rd_ptr == prq_wr_ptrSgAPdddA TimesNewRomanAd A  A A @T0Can the board really do all of this atomically??SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TpvoidSaAPdddACourierAd A  A A @T}ia_rfred_handler(SaAPdddACourierAd A  A A @T~ia_softc_t *softc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T' rfred_t *rf = softc->brd_regs.rfred;SaAPdddACourierAd A  A A @T, rfred_t *rfL = &softc->brd_regs_ll.rfred;SaAPdddACourierAd A  A A @T ushort_t state;SaAPdddACourierAd A  A A @Tu int n;SaAPdddACourierAd A  A A @T ulong_t ptr;SaAPdddACourierAd A  A A @T ulong_t ptr1;SaAPdddACourierAd A  A A @T ulong_t ptr2;SaAPdddACourierAd A  A A @T ulong_t fdc;SaAPdddACourierAd A  A A @T ulong_t status;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TB/* Begin by getting the interrupt status and clearing the int */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T4 status = ia_get32(&rf->intr_status_reg) & 0xffff;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T~ * Packet receivedSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T if (status & R_PKT_RCVD)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T0 state = ia_get32(&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @T{#ifdef DBG_RECVSaAPdddACourierAd A  A A @T@ printk("IA 5515: RFred reception. State is %x \n", state);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @T$ while (!(state & R_PCQ_EMPTY))SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T0 if (ia_proc_rcv_pkt (softc) == IA_FAIL)SaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @TC printk("IA 5515: proc rcvd packet returned errror \n");SaAPdddACourierAd A  A A @T~ break;SaAPdddACourierAd A  A A @Tv }SaAPdddACourierAd A  A A @T4 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T(Is this code really needed any longer???SqAPdddA PA TimesNewRomanAd A  A A @T^Or was it a failed attempt to correct the problem of reading the rfred_intr_status reg twice??SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T;/* If the descriptor queue is empty then the packet receiveSaAPdddACourierAd A  A A @T9 * queue is probably full!!! Adding this stuff here seemsSaAPdddACourierAd A  A A @T5 * to have removed a nasty hang on full PRC empty LFQSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (status & R_LRG_FREEQ_MT)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T1 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{#ifdef DBG_RECVSaAPdddACourierAd A  A A @T9 printk("IA 5515: RFred MT. State is %x \n", state);SaAPdddACourierAd A  A A @T ia_put_regs(softc);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T$ while (!(state & R_PCQ_EMPTY))SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T0 if (ia_proc_rcv_pkt (softc) == IA_FAIL)SaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @TE printk("IA 5515: procMT rcvd packet returned errror \n");SaAPdddACourierAd A  A A @T~ break;SaAPdddACourierAd A  A A @Tv }SaAPdddACourierAd A  A A @T4 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T/Much of exception handling omitted for brevity.SgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T8 * Quick check to see if any other status needs handlingSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T# if ((status & ~R_PKT_RCVD) == 0)SaAPdddACourierAd A  A A @Ty return;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T/* Exception received */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (status & R_EXCP_RCVD)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T1 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @TO printk("IA 5515: RFred exception at %d.%06d. State is %x Status is %x\n",SaAPdddACourierAd A  A A @TO softc->tod->tv_sec, softc->tod->tv_usec, state, status);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (softc->logrx == 0)SaAPdddACourierAd A  A A @T softc->logrx = 1;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{#ifdef DBG_RECVSaAPdddACourierAd A  A A @T ia_put_regs(softc);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @T& while (!(state & R_EXCPQ_EMPTY))SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T$ ia_proc_rcv_excpt (softc);SaAPdddACourierAd A  A A @T5 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T!ia_rfred.c:ia_proc_recvd_packet()SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @Tv static intSaAPdddACourierAd A  A A @T#ia_proc_rcv_pkt (ia_softc_t *softc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T* rfred_t *rf = softc->brd_regs.rfred;SaAPdddACourierAd A  A A @T. rfred_t *rfL = &softc->brd_regs_ll.rfred;SaAPdddACourierAd A  A A @T$ ushort_t pcqrp = rfL->pcq_rd_ptr;SaAPdddACourierAd A  A A @T ushort_t desc_num;SaAPdddACourierAd A  A A @T r_buf_desc *desc;SaAPdddACourierAd A  A A @T ushort_t status;SaAPdddACourierAd A  A A @T int drop_it = 0;SaAPdddACourierAd A  A A @T int ret;SaAPdddACourierAd A  A A @T uint_t val;SaAPdddACourierAd A  A A @T int n;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TB/* If the read pointer and the write pointer are the same here, */SaAPdddACourierAd A  A A @TB/* we have a SERIOUS problem. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T$ val = ia_get32 (&rf->pcq_wr_ptr);SaAPdddACourierAd A  A A @T if (pcqrp == (val & 0xffff))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T. printk("IA 5515: Fatal pcqrp error \n");SaAPdddACourierAd A  A A @Ty return;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T6/* Get the next descriptor in Packet Complete Queue */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T< desc_num = ia_getw((ushort_t *)(softc->brd_regs.rfred_memSaAPdddACourierAd A  A A @TC + rfL->pcq_rd_ptr));SaAPdddACourierAd A  A A @T desc_num &= 0x1fff;SaAPdddACourierAd A  A A @TC desc = (r_buf_desc *)(softc->brd_regs.rfred_mem + R_DESC_START);SaAPdddACourierAd A  A A @T desc += desc_num;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T) * Update local and real PCQ read pointerSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T rfL->pcq_rd_ptr += 2;SaAPdddACourierAd A  A A @T) if (rfL->pcq_rd_ptr > rfL->pcq_ed_adr)SaAPdddACourierAd A  A A @T( rfL->pcq_rd_ptr = rfL->pcq_st_adr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T/ ia_put32 (&rf->pcq_rd_ptr, rfL->pcq_rd_ptr);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T8 * If no host receive buffer available, drop the packet.SaAPdddACourierAd A  A A @T< * This should NEVER occur in practice since there should beSaAPdddACourierAd A  A A @T= * a one to one correspondence between host buffer structuresSaAPdddACourierAd A  A A @T$ * and receive buffers on the board.SaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (!softc->rx_buf_free)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TA printk("IA 5515: rx_buf_free is null.. packet dropped \n");SaAPdddACourierAd A  A A @T) softc->rfred_stat.rx_no_host_buf++;SaAPdddACourierAd A  A A @T2 ia_free_desc(softc, desc, (ia_rx_buf_t *)0);SaAPdddACourierAd A  A A @T return IA_FAIL;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @Ty SkAPdddA PACourierAd A  A A @T0/* Ensure that we didn't get a mangled packet */SaAPdddACourierAd A  A A @T2S'APdddA @T& status = ia_getw(&desc->desc_stat);SaAPdddACourierAd A  A A @T- status = (ushort_t)(status & R_PKT_ERROR);SaAPdddACourierAd A  A A @Tz if (status)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T> printk("IA 5515: Packet error status is %x \n", status);SaAPdddACourierAd A  A A @T| drop_it++;SaAPdddACourierAd A  A A @Tm:SaAPdddACourierAd A  A A @Tm:SaAPdddACourierAd A  A A @T!much error recording omitted hereSaAPdddACourierAd A  A A @Tm:SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{ if (drop_it)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T# printk("Dropping packet \n");SaAPdddACourierAd A  A A @T3 ia_free_desc (softc, desc, (ia_rx_buf_t *)0);SaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T# softc->rx_buf_free->desc = desc;SaAPdddACourierAd A  A A @T% ret = ia_set_up_dma (softc, desc);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (ret == IA_FAIL)SaAPdddACourierAd A  A A @T3 ia_free_desc (softc, desc, (ia_rx_buf_t *)0);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tia_rfred.c:ia_set_up_dma()SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T\The length of the data packet is conveyed via the oddly named dma_high / dma_low desc fieldsSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T3ia_set_up_dma (ia_softc_t *softc, r_buf_desc *desc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T0 ia_dle_list_t *list = &softc->list[RX_LIST];SaAPdddACourierAd A  A A @T struct sk_buff *skb;SaAPdddACourierAd A  A A @T ia_rx_buf_t *rx_buf;SaAPdddACourierAd A  A A @T DLE *dle;SaAPdddACourierAd A  A A @T mblk_t *mp;SaAPdddACourierAd A  A A @T ulong_t buf_addr;SaAPdddACourierAd A  A A @T ulong_t dma_addr;SaAPdddACourierAd A  A A @T size_t len;SaAPdddACourierAd A  A A @T int status;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TH buf_addr = ia_getw(&desc->buf_high) << 16 | ia_getw (&desc->buf_low);SaAPdddACourierAd A  A A @TH dma_addr = ia_getw(&desc->dma_high) << 16 | ia_getw (&desc->dma_low);SaAPdddACourierAd A  A A @T len = dma_addr - buf_addr;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T!/* As usual check for insanity */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (len > RX_BUF_SIZE)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T printk("IA 5515: \SaAPdddACourierAd A  A A @T= ia_set_up_dma_n: bad len %d (desc len %d) (vci %d)",SaAPdddACourierAd A  A A @T= len, ia_getw (&desc->length), ia_getw (&desc->vci));SaAPdddACourierAd A  A A @T return IA_FAIL;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TC status = ia_get_rx_skb(softc, desc->vci & R_VC_MASK, len, &skb);SaAPdddACourierAd A  A A @T if (status == IA_FAIL)SaAPdddACourierAd A  A A @T return(IA_FAIL);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @TH * Get a host free receive buffer control structure from the free queue.SaAPdddACourierAd A  A A @T; * Use it to remember packet length and where the skbuff isSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T rx_buf = softc->rx_buf_free;SaAPdddACourierAd A  A A @T% softc->rx_buf_free = rx_buf->next;SaAPdddACourierAd A  A A @T rx_buf->len = len;SaAPdddACourierAd A  A A @T rx_buf->skb = skb;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T./* printk("RXB is %x SKB is %x Len is %d \n",SaAPdddACourierAd A  A A @T' rx_buf, skb, len); */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TInitiate the DMA transferSqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @TAs with Tx side DMASgAPdddA TimesNewRomanAd A  A A @T4Multiple transfer requests may be queued in hardwareSgAPdddA TimesNewRomanAd A  A A @T(Only one transfer will proceed at a timeSgAPdddA TimesNewRomanAd A  A A @THardware ensures FIFO queuingSgAPdddA TimesNewRomanAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T/ * Build DLEs to transfer the data to the boardSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T dle = list->list_write;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T" dle->dle_local_addr = buf_addr;SaAPdddACourierAd A  A A @T. dle->dle_sys_addr = virt_to_bus(skb->data);SaAPdddACourierAd A  A A @T dle->dle_count = len;SaAPdddACourierAd A  A A @T# dle->dle_mode = DLE_INT_ENABLE;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (++dle == list->list_end)SaAPdddACourierAd A  A A @T dle = list->list_start;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T list->list_write = dle;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T; * The state of pending receive DMA's is tracked via a FIFOSaAPdddACourierAd A  A A @T* * queue of host buffer control structuresSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @T rx_buf->next = NULL;SaAPdddACourierAd A  A A @T if (!softc->rx_buf_dmahead)SaAPdddACourierAd A  A A @T% softc->rx_buf_dmahead = rx_buf;SaAPdddACourierAd A  A A @Ts elseSaAPdddACourierAd A  A A @T+ softc->rx_buf_dmatail->next = rx_buf;SaAPdddACourierAd A  A A @T" softc->rx_buf_dmatail = rx_buf;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T{#ifdef DBG_RECVSaAPdddACourierAd A  A A @T7 printk("IA 5515: DMA buf head is now %x desc %x \n",SaAPdddACourierAd A  A A @T: softc->rx_buf_dmahead->addr, rx_buf->desc);SaAPdddACourierAd A  A A @Tr#endifSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T( softc->driver_stat.rx_buf_on_dma_q++;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T; * Kick off the DMA by incrementing the transection counterSaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T& ia_put32(softc->brd_regs.rx_tc, 1);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T return IA_SUCCESS;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tia_rfred.c:ia_get_rx_skb()SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T!Allocate a network stack skbuff()SgAPdddA TimesNewRomanAd A  A A @TMPackets are passed up/down network stack by pointer passing, not data copyingSgAPdddA TimesNewRomanAd A  A A @A8 =ATrSgAPdddA TimesNewRomanAd A  A A @Tint ia_get_rx_skb(SgAPdddA TimesNewRomanAd A  A A @A8d A8CourierTia_softc_t *softc,SaAPdddACourierAd A  A A @Tint vci,SaAPdddACourierAd A  A A @Tint data_len,SaAPdddACourierAd A  A A @Tstruct sk_buff **skbadloc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T struct atm_vcc *vcc;SaAPdddACourierAd A  A A @T struct sk_buff *skb;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T</* Recover the linux VCC stucture associated with the vci */SaAPdddACourierAd A  A A @T</* If the vcc is zero then the vci is not open and we */SaAPdddACourierAd A  A A @T</* just want to dump the packet. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T ia5515_getvcc(vci, &vcc);SaAPdddACourierAd A  A A @T| if (vcc == 0)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T return(IA_FAIL);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T"/* Attempt to allocate a buffer */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T< if (!(skb = atm_alloc_charge(vcc, data_len, GFP_ATOMIC)))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (vcc->vci < 32)SaAPdddACourierAd A  A A @T? printk("IA 5515: Dropping packets on %d\n", vcc->vci);SaAPdddACourierAd A  A A @T return(IA_FAIL);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T *skbadloc = skb;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T return(IA_SUCCESS);SaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @Tia_rfred.c:ia_host_rx_intr()SqAPdddA PA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T"Handle Rx DMA transfer completion SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T|ia_host_rx_intr(SaAPdddACourierAd A  A A @T~ia_softc_t *softc)SaAPdddACourierAd A  A A @Tm{SaAPdddACourierAd A  A A @T2 ia_flip_t *flip = softc->brd_regs.flipper;SaAPdddACourierAd A  A A @T0 ia_dle_list_t *list = &softc->list[RX_LIST];SaAPdddACourierAd A  A A @T. rfred_t *rf = softc->brd_regs.rfred;SaAPdddACourierAd A  A A @T ushort_t state;SaAPdddACourierAd A  A A @T ia_rx_buf_t *rx_buf;SaAPdddACourierAd A  A A @T DLE *current_dle;SaAPdddACourierAd A  A A @T DLE *dle;SaAPdddACourierAd A  A A @T size_t len;SaAPdddACourierAd A  A A @T uint_t dle_offset;SaAPdddACourierAd A  A A @T uint_t dle_offset1;SaAPdddACourierAd A  A A @T CS5_TRAILER *trl5;SaAPdddACourierAd A  A A @T int data_len;SaAPdddACourierAd A  A A @T int i;SaAPdddACourierAd A  A A @T struct atm_vcc *vcc;SaAPdddACourierAd A  A A @T struct sk_buff *skb;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tn/*SaAPdddACourierAd A  A A @T% * Read the offset into the DLE list.SaAPdddACourierAd A  A A @T= * DLE receive list register is not de-bounced. Read it untilSaAPdddACourierAd A  A A @T@ * two consecutive values are the same to be sure that the rightSaAPdddACourierAd A  A A @T} * value is read.SaAPdddACourierAd A  A A @T> * NOTE: In almost all the cases only two reads of the receiveSaAPdddACourierAd A  A A @T( * list register will be required.SaAPdddACourierAd A  A A @To */SaAPdddACourierAd A  A A @TI dle_offset = ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);SaAPdddACourierAd A  A A @TJ dle_offset1 = ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);SaAPdddACourierAd A  A A @T$ while (dle_offset != dle_offset1)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T dle_offset = dle_offset1;SaAPdddACourierAd A  A A @T dle_offset1 =SaAPdddACourierAd A  A A @TB ia_get32 (&flip->fl_receive_list) & (SIZE_DLE_SPACE - 1);SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T@ current_dle = (DLE *)((u_long)list->list_start + dle_offset);SaAPdddACourierAd A  A A @T dle = list->list_read;SaAPdddACourierAd A  A A @To SaAPdddACourierAd A  A A @T7AAL5 frames are always a multiple of 48 bytes in lengthSqAPdddA PA TimesNewRomanAd A  A A @T4Actual data length must be recovered from CS trailerSgAPdddA TimesNewRomanAd A  A A @T.Why not also recover vcc from rx_buf structureSgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T while (dle != current_dle)SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T< /* The dmahead pointer is the head of a list of rx bufferSaAPdddACourierAd A  A A @T: * structures that have been scheduled for DMA transferSaAPdddACourierAd A  A A @Tr */SaAPdddACourierAd A  A A @T% rx_buf = softc->rx_buf_dmahead;SaAPdddACourierAd A  A A @T if (rx_buf == 0)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T> printk("IA 5515: Yeow... dma head rx_buf was 0! \n");SaAPdddACourierAd A  A A @T| return;SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tq /*SaAPdddACourierAd A  A A @T * Get the received packetSaAPdddACourierAd A  A A @Tr */SaAPdddACourierAd A  A A @T: softc->rx_buf_dmahead = softc->rx_buf_dmahead->next;SaAPdddACourierAd A  A A @T rx_buf->next = NULL;SaAPdddACourierAd A  A A @T len = rx_buf->len;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TL trl5 = (CS5_TRAILER *)(rx_buf->skb->data + len - sizeof(CS5_TRAILER));SaAPdddACourierAd A  A A @T$ data_len = swap(trl5->length);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T> /* Recover skb address and length from buffer descriptor */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T skb = rx_buf->skb;SaAPdddACourierAd A  A A @T skb->len = data_len;SaAPdddACourierAd A  A A @T& softc->driver_stat.rx_packets++;SaAPdddACourierAd A  A A @T) softc->driver_stat.rx_bytes += len;SaAPdddACourierAd A  A A @T2S'APdddA @TvSkAPdddA PACourierAd A  A A @T7Why not also store/recover vcc from rx_buf structure???SgAPdddA TimesNewRomanAd A  A A @T=Yet another race condition.... vcc could be closed during DMASgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T*This looks like a potential memory leak! SgAPdddA TimesNewRomanAd A  A A @T*SKB should be freed if it can't be pushed.SgAPdddA TimesNewRomanAd A  A A @TrSgAPdddA TimesNewRomanAd A  A A @T9 ia5515_getvcc(rx_buf->desc->vci & R_VC_MASK, &vcc);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T if (vcc != NULL)SaAPdddACourierAd A  A A @Ts {SaAPdddACourierAd A  A A @T if (vcc->push != NULL)SaAPdddACourierAd A  A A @Tv {SaAPdddACourierAd A  A A @TA ATM_SKB(skb)->vcc = vcc; /* Heikki Vatianinen */SaAPdddACourierAd A  A A @T% ATM_SKB(skb)->iovcnt = 0;SaAPdddACourierAd A  A A @T skb->len = data_len;SaAPdddACourierAd A  A A @T vcc->push(vcc, skb);SaAPdddACourierAd A  A A @Tw }SaAPdddACourierAd A  A A @Ts }SaAPdddACourierAd A  A A @T0 ia_free_desc(softc, rx_buf->desc, rx_buf);SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @Tqdone:SaAPdddACourierAd A  A A @T" if (++dle == list->list_end)SaAPdddACourierAd A  A A @T dle = list->list_start;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T/ } /* end while(more DLE's left to process */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T list->list_read = dle;SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T?/* This code is included to reduce latency in handling ready */SaAPdddACourierAd A  A A @T?/* packets and to defend against the possiblity of a lost */SaAPdddACourierAd A  A A @T?/* interrupt for a ready packet. */SaAPdddACourierAd A  A A @TlSaAPdddACourierAd A  A A @T. state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @T! while (!(state & R_PCQ_EMPTY))SaAPdddACourierAd A  A A @Tp {SaAPdddACourierAd A  A A @T. if (ia_proc_rcv_pkt (softc) == IA_FAIL)SaAPdddACourierAd A  A A @T| break;SaAPdddACourierAd A  A A @T2 state = ia_get32 (&rf->state_reg) & 0xffff;SaAPdddACourierAd A  A A @Tp }SaAPdddACourierAd A  A A @Tm}SaAPdddACourierAd A  A A @JLexmark Optra T614 Laser PrinterLOPT614 PostScript`Tl`Tld,,lp -d lwnlwnLOPT614PageSize:LetterDuplex:NoneDuplexer:FalseInputSlot:AutoSelectMediaType:NoneTray2:FalseTray3:FalseTray4:FalseTray5:FalseMPFeeder:TrueFeeder:FalseImageEnhance:TrueResolution:600dpiSmoothing:False7 UA:V88/0 ?D--/=U%2P~U%x0?%B%B% B#%B9%E"BO% E" B\% E" Bi% E Bv% E B% E  B % E  B % E  B % E"  B % E"  B % E B% E B% E B% E B% E" B%B%B1% E" B>% E" BK% E" BX%Bn%B% E" B%E Bu% E" B% E" B%E Bs% E B % E  B!% E" !B"%E "B~#%E #Bb$% E" $Bo%% E" %,U6%(% F@% Щ>/=U%2p]U%x0?,B% E" &B %'B#% E" (B0% E" )B=% E" *BJ%E"+B.%E",B%E"-B%E".B%E"/B % E" 0B %E"1B % E" 2B %E"3B % E" 4B%5B/%6BE% E" 7BR% E" 8B_% E 9Bl% E :By% E ;B% E <B% E =B% E >@B% E ?AB% Ep5 @BB% E" ACB% E BDB% E CEB % Ep5 DFB!% Ep5 EGB#% Ep5 FHB$% E GIB"%% E HJB/&% Ep5 IKB<'% Ep5 JLBI(% E KMBV)% E" LNBc*% E MOBp+% E NPB},% E OQB-% E PRB.% E QS,U6%(% F@% ^}/=U%2PU%x0?)B% E RTB %SUB#%TVB9% E" UWBF% E" VXBS% E" WYB`%XZBv%Y[B% E" Z\B % E" []B % E" \^B % E" ]_B %E"^`B %_aB%`bB% E" acB% E" bdB% E" ceB)%E"dfB %E"egB%E"fhB%E"giB%E"hjB%E"ikB%E"jlBe%E"kmBI%E"lnB-%E"moB%E"npB%E"oqB%E"prB%E"qsB%E"rtB %E"suBi!%E"tvBM"%E"uwB1#%E"vxB$%E"wyB$%E"xzB%%E"y{B&%Ep5z|,U6%(% F@% %/=U%2pU%x0?8B%{}B%|~B,% E" }B9% E" ~BF% E BS% E B`% E Bm% E Bz% E" B % E" B % E" B % E" B % E" B % E" B%E# B%E# B%B%B%E"B%E"Bf%E"B$%E"B%E"B%E"B^%E"B%E"B%E"B%Bw%E"B5%E"B%E"B%E"Bo%E"B-%E"B%E"B%E"Bg %E"B%!%E"B!%E"B"%E"B_#%E"B$%E"B$%E"B%%E"BW&%E"B'%E"B'%E"B(%E"BO)%E"B *%E"B*%E"B+%E"BG,%E"B-%E"B-%E"B.% E# ,U6%(% F@% %/=U%2P“U%x0?6B% B % E" B% B'%B=%BS%E"Bm%E"Bu% E" B % E" B %E %B %E"B %E"Bk %E"B)%E"B%E"B%E"Bc%E"B!%E"B%E"BU%E"B%E"B%E"B#%E"B%E"BW%E"B%E"B% E" B% E B% E B%EB% E" B% E" B% E" B%E"B%E %B%E %B!%E"B!%E"B"%E"BO#%E"B $%E"B$%E"B%%E"BG&%E"B'%E"B'%E"B(%B)%B*%E"B+%E"B,%E"B~-%E"Bb.%BF/%E",U6%(% F@% Л%:/=U%2pNU%x0?!B%B%B,%BB%BX% E" Be% E" Br% E" B%E"Bc%E"BG %E"B+ %E"B %E"B % E" B % E" B % E" B% E" B'% E" B4%BK% BX%E B<% BI%B_%Bu%E"BY%E"B=%E"B!%E"B%E"B% E" B% E"  B% E"  B% E"  B % E"   ,U6%(% F@% %y/=U%2P§U%x0?-B% E"   B % B#% B9%E" BP%E Bf%E BJ%E B.%E BD% E" BQ % E" B^ %E BB %BX % E" Be %E"B{%E B_% E Bl% E" By% E" B%E Bj%E  BN% E" !B[% E" "Bh% #B%!$B% E" "%B% E" #&B%E $'B%E %(Bw%E &)B[%E '*B?% E" (+BL% E" ),BY %E *-B=!% E +.BJ"% E" ,/BW#% E" -0Bd$%E .1BH%%E /2B,&%E 03B'% E 14B(% E" 25B*)% E" 36B7*% E 47BD+%E 58B(,%69,U6%(% F@% Ј%/=U%2p;U%x0?B%7:B%8;B,% E" 9<B9% E" :=BF%E";>B*%E"<?B%E"=@B%E">AB%E"?BB%E"@CB %E"ADB %E"BEBf % E" CFBs % E" DGB %E"EHBd%E"FIBH%E"GJB,%E"HKB% E" ILB%E JMB%E"KNB%E"LOB%E"MPB%E"NQB%E"ORBu%E"PSBY%E"QTB=%E"RUB!%E"SVB%TWB%UX,U6%(% F@% Е%/=U%2P HU%x0?*B%VYB%WZB,%X[BB%Y\BX% E" Z]Be% E" [^Br% E" \_B% E" ]`B% E" ^aB % E _bB % E `cB % E adB % E beB % E cfB% E dgB% E ehB% E fiB%gjB%hkB-% E" ilB:% E" jmBG% E" knBT% E" loBa% E" mpBn% E nqB{% E orB% E psB%E"qtB%ruB%svB%E"twB %E"uxBy!%E"vyBJ"%E"wzB#%E"x{B#%E"y|B$%E"z}B%%E"{~B_&%E"|B0'%E"}B(% E" ~B)% E" ,U6%(% F@% _%6/=U%2p U%x0?(B%B%B,%E"B %E"B%E"B%E"B~%E"BO%E"B %E"B%E"B%B %B %B %E"B %E"B %E"B)%E"B%E"B%E"B%E"Bm%E"B>%E"B%E"B%E"B% B%B%B%E"B%E"B%E"B]%E"B.%E"B%E"B%E"B%E"Br%E"BC %E"B!%E"B!%E"B"%E",U6%(% F@% %u/=U%2P ŸU%x0?+B%B%E"B,% E" B9% E" BF%E"B\% Bi%B %B%B %B~ %BO %B %B %B %B%Bd%B5%B%B%B%By%BJ%B%B%B%B%B_%B0%B%B%B%Bt%BE%B%B%B%B %BZ!%Bp"%B#% E" B$% E" B%% E" ,U6%(% F@% %Դ/=U%2p ’U%x0? B% B %B#% B0%B%B%B%Bt%BE%B%B%B % B %B % B % B %B%B%Bh%B~% B%B\%B-%B%B%B%Bq%B% B%Be%B6%B%,U6%(% F@% %/=U%2P ’U%x0? B% B %B#% B0%B%B%B%Bt%BE%B%B%B %B %BZ %B+ %B %B %B% Bo% B@% B% B% B% B% BU% B&% B% B%B%Bj%B;%B %,U6%(% F@% Ѐ%2/=U%2p3U%x0?*B%B%B%B% B% E" B*% E" B7% E" BD% E" BQ% E"  B^ %!Bt % "B % E" #B % E" $B % E"  %B% E !&B% E "'B% E #(B%$)B%%*B% E" &+B % E" ',B%(-B,%).BB% */BO% E" +0B\% E" ,1Bi%E"-2B'%E".3B%E"/4B%E"05Ba%E"16B%E"27B%E"38B %E"49BY!%E"5:B"%E"6;B"%E"7<B#%E"8=BQ$%E"9>B%%E":?B%%E";@B&%E"<A,U6%(% F@% `%q/=U%2PU%x0?1B%E"=BB%>CB%E"?DB%E"@EBP%E"AFB%E"BGB%E"CHB%E"DIBH%E"EJB%E"FKB%E"GLB%E"HMB@ %E"INB %E"JOB %E"KPBz %E"LQB8 %E"MRB %E"NSB %E"OTBr% E" PUB%E"QVB=%E"RWB%E"SXB%E"TYBw%E"UZB5%E"V[B%E"W\B%E"X]Bo%E"Y^B-%E"Z_B%E"[`B%E"\aBg%E"]bB%%E"^cB%E"_dB% E" `eB%afB% bgB% E" chB% E" diB% E" ejB % E" fkB"% E" glB#%hmB($%inB>%% E" joBK&% E" kpBX'% E" lqBe(% mr,U6%(% F@% %İ/=U%2pU%x0?$B% nsB %otB#% E" puB0% E" qvB=% E" rwBJ% E" sxBW% E" tyBd% E" uzBq% E" v{B~ % E" w|B % E" x}B % E" y~B %E"zBv %E"{BG%E"|B%E"}B%E"~B%E"B%E"B\%E"B-%E"B%E"B%E"B%E"Bq%E"BB%E"B%E"B%E"B%E"B%E"BW%E"B(%E"B%E"B%E"B% E" B% ,U6%(% F@% #%/=U%2PU%x0?B% E" B %B#%B9% E" BF% E" BS%E"Bi% E" Bv% E B% Ep5 B % Ep5 B % Ep5 B % E B % Ep5 B % E B% E B% E B%E"B% E B% E B% Ep5 B(% Ep5 B5% E BB%Ep5B\% Ep5 Bi% E Bv%B% E" ,U6%(% F@% v%./=U%2p)U%x0?B% E" B %B#%B9% E" BF% E BS% E B`% Ep5 Bm% Ep5 Bz% E" B % E" B % E" B % E" B % E" B % E" B% E" B% E" B% E" B% E B% E B % E" B% E B#% E B0% B=% uJ%e U e ? zU }?4}U88} ?FU88} E 6Ei}U88 ?FU88 E) |}U |?4|U87} ?FU87} E 6Ei|U87 ?FU87 E) |U |?4|U87} ?FU87} E 6Ei|U87 ?FU87 E) |uU |?4|U87} ?FU87} E 6Ei|U87 ?FU87 E) |U |?4|U87} ?FU87} E 6Ei|U87 ?FU87 E) |mU |?4|U87} ?FU87} E 6Ei|U87 ?FU87 E) uU |?4|U87} ?FU87} E /Ei|U87 ?FU87 B#% E" B$% E" ,U6%(% F@% Р%m/=U%2PSU%x0?*B%B% E" B#% E" B0% E" B=% E" BJ%|E"|B%E"B%E"BB%E"B %E"B % E B %B %B % E" B% E" B%E"B'%E"B%E"B%E"Ba%E"B%E"B%E"B%E" BY%E" B%E" B%E" B%E" BQ%E"B%E"B%E"B%E"BI%E"B%|E"|B%E"BA%E"BX% Be %B{!% &"%rEC r? EC ?,U88f?FU88f.Eh U88 ?FU88 .E= U88 ?FU88 UC |?,|U87f ?FU87f .Eh |U87 ?FU87 .E= |U87 ?FU87 UC |?,|U87f ?FU87f .Eh |U87 ?FU87 .E= |U87 ?FU87 UzC |?,|U87f ?FU87f .Eh |U87 ?FU87 .E= |U87 ?FU87 UC |?,|U87f ?FU87f .Eh |U87 ?FU87 .E= |U87 ?FU87 B)% E" HB+% IB,% J,U6%(% F@% %/=U%2p´U%x0?$B%KB%LB,%MBB% E" NBO% E" OB\% E" PBi% E" QBv% E" RB% E" SB % E" TB % E UB % E VB % Ep5 WB % Ep5 XB% Ep5 YB%ZB% [B% E" \ B% E" ] B% ^ B(%E"_ B0% E" ` B=% E" aBJ% E" bBW% E" cBd% E" dBq% E" eB~%E"fBb%E"gBF%E"hB*%E"iB %E"jB %E"kB!%E"lB"%E"mB#% E" n,U6%(% F@% %/=U%2PU%x0?5B% E" oB %pB#% qB0%E"rB%E"s B%E"t!Bj%E"u"B(%E"v#B%E"w$B%E"x%Bb%E"y&B %E"z'B %E"{(B %E"|)Br %E"}*B0 %E"~+B %E",B %-B% E" .B% E" /B% E" 0B% E" 1B%2B%E"3Br%E"4B0%E"5B%E"6B%E"7Bj%E"8B(%E"9B%E":B%E";Bb%E"<B %E"=B%E">B%E"?BZ%E"@B%E"AB%E"BB%E"CBR %E"DB!%E"EB!%E"FB"%E"GBJ#% E" HBW$% E IBd%% E" JBq&% E" KB~'% E" LB(% E MB)% E NB*% E OB+%E"Q,U6%(% F@% Y%*/=U%2p U%x0?B%RB%SB,% E" TB9% E" UBF% E" VBS% E WB`% E XBm%E"YB+%E"ZB%E"[B %E"\Be %E"]B# %E"^B % E" _B % E `B% E aB% E bB% Ep5 cB+% E dB8% E eBE% Ep5 fBR%Ep5hBl% Ep5 iBy% E jB% E kB%'Ep5'nB% Ep5 oB% Ep5 pB% q,U6%(% F@% D%i/=U%2PU%x0?;B%rB%sB,%tBB%uBX% vBe% wBr% xB%E"yB=%E"zB%E"{B %E"|Bw %E"}B5 %E"~B %E"B %E"Bo %E"B-%E"B%E"B%E"Bg%E"B%%E"B%E"B%E"B_%E"B%B3% B@% BM% BZ% Bg%E"B%%E"B%E"B%E"B_%E"B%E"B%E"B%E"BW%E"B %E"B %E"B!%E"BO"%E"B #%E"B#%E"B$%E"BG%%E"B&%E"B&%E"B'%E"B?(%E"B(%E"B)%E"By*%E"B7+%E"B+%E"B,%E"Bq-%E"B/.%E"B.% E" ,U6%(% F@% %/=U%2pyU%x0?*B% B %E"B%E"B%E"BG%E"B%E"B% E" B%E"B%E"BL%E"B %E"B%E"B %E"BD %E" B %E" B %E" B~ %E" B< %E" B %E"B%E"Bv%E"B4%E"B%E"B%E"Bn%E"B,%E"B%E"B%E"Bf%E"B$%E"B%E"B%E"B^%E"B%E"B%E"B%E"BV%E" B%E"!B%E""B%E"#BN%E"$B %E"%,U6%(% F@% Ѓ%/=U%2P6U%x0?;B%&B%'B,% (B9% E" )BF% E" *BS% E" +B`% E ,Bm% E -Bz% E .B %E"/BE %E"0B %E"1B %E"2B %E"3B= %E"4B %E"5B%E"6Bw%E"7B5%E"8B%E"9B%E":Bo%E";B-%E"<B%E"=B%E">Bg%E"?B%%E"@B%E"AB%E"BB_%E"CB%E"DB%E"EB%E"FBW%|E"|GB%E"HB%E"IBO%E"JB %E"KB%E"LB %E"MBG!%E"NB"%E"OB"%E"PB#%E"QB?$%E"RB$% E" SB &%E"TB&%E"UB'%E"VBD(%E"W B)%E"X B)%E"Y B~*%|E"|Z B+%E"[B,%E"\Bv-%E"]B4.%E"^B.%E"_B/%E"`,U6%(% F@% %&/=U%2pµU%x0?-B%aB%bB,% cB9% dBF%E"eB%E"fB%E"gB%E"hB>%E"iB%E"jB%E"kBx %E"lB6 %E"m B %E"n!B %E"o"Bp %E"p#B. %E"q$B %E"r%B%E"s&Bh%E"t'B&%E"u(B%E"v)B%E"w*B`%E"x+B%E"y,B%E"z-B%E"{.BX%E"|/B%E"}0B%E"~1B%E"2BP%E"3B%E"4B% E" 5B%E"6B%E"7BU%|E"|9B%E :B%E";BM%E"<B %E"=B %E">B!%E"?BE"%E"@B#%E"A,U6%(% F@% в%e/=U%2PeU%x0?)B%E"BB%E"CB|%E"DB:%E"EB%E"FB%E"GBt%E"HB2%E"IB%E"JB%E"KBl%E"LB*%E"MB%E"NB %E"OBd %E"PB" %E"QB %E"RB %E"SB\ %E"TB%E"UB%E"VB%E"WBT%E"XB%E"YB%E"ZB%E"[BL%E"\B %E"]B%E"^B%E"_BD%E"`B%E"aB%E"bB~%E"cB<%E"dB%E"eB%E"fBv%E"gB4%E"hB%E"iB%E"j,U6%(% F@% {%/=U%2p.U%x0?(B%kB%lB,% mB9% nBF%E"oB%E"pB%E"qB%E"rB>%E"sB%E"tB%E"uBx %E"vB6 %E"wB %E"xB %E"yBp %E"zB. %E"{B %E"|B%E"}Bh%E"~B&%E"B%E"B%E"B`%E"B%E"B%E"B%E"BX%E"B%E"B%E"B%E"BP%|E"|B%E"B%E"BH%E"B%E"B%E"B%E"B@%E"B%E",U6%(% F@% %/=U%2PšU%x0?:B%B% B#% B0% B=%B%B%Bw%B5%B%B%Bo %B- %B %B %Bg %B% % B2% B?% BL% BY% Bf%B$%B%B%B^%B%B%B%BV%B%B%B%BN%B %|B%BF% BS%B %B %B!%BK"%B #% B#% B$% BC%% B&% B&%B}'%B;(%B(%B)%Bu*%B3+%B+%B,%Bm-%B+.%,U6%(% F@% Ъ%"/=U%2p]U%x0?4B%B%B,%BB% BO% B\% E" Bi% E Bv% E  B%!B %E""BW %E"#B %E"$B %E"%B %E"&BO %E"'B %E"(B%E")B%E"*BG%E"+B%E",B%E"-B%E".B?%E"/B%E"0B%E"1By%E"2B7%E"3B%E"4B%E"5Bq%E"6B/%E"7B%E"8B%E"9Bi%E":B'%E";B%E"<B% =B% E >B%4E 4?B#% E @B$%E"AB%% E" BB&%E"CB'%E"DBE(%E"EB)%E"FB)%E"GB*%E"HB=+%E"IB+%E"JB,%E"KBw-%E"L,U6%(% F@% a%a/=U%2PU%x0?&B%E"MB% E" NB#% E OB0% E P B=% E Q BJ% E R BW%E"S B;%E"T B%E"UB%E"VBu %E"WB3 %E"XB %E"YB %E"ZBm %E"[B+ %E"\B %E"]B%E"^Be%E"_B#%E"`B%E"aB%E"bB]%E"cB%E"dB%E"eB%E"fBU%E"g B% E" h!B % E" i"B-%E"j#B%E"k$B%E"l%Bg%E"m&B%%E"n'B%E"o(B% E" p)B% E" q*B% E" r+,U6%(% F@% T%/=U%2p U%x0?9B%s,B% t-B#% E" u.B0% E" v/B=%E"w0BS%E"x1B%E"y2B%E"z3B%E"{4BK%E"|5B %E"}6B %E"~7B %E"8BC %E"9B %E":B %E";B} %E"<B;%E"=B%E">B%E"?Bu%E"@B3%E"AB%E"BB%E"CBm%E"DB+%E"EB%E"FB%E"GBe%E"HB#%E"IB%E"JB%E"KB]%E"LB%E"MB%E"NB%E"OBU%E"PB%E"QB%E"RB%E"SBM%E"TB %E"UB %E"VB!%E"WBE"%E"XB#%E"YB#%E"ZB$%E"[B=%%E"\B%%E"]B&%E"^Bw'%E"_B5(%E"`B(%E"aB)%E"bBo*%E"cB-+%E"d,U6%(% F@%  %/=U%2P!¼U%x0?-B% E" eB % E" fB% E" gB'%E"hB=%E"iB%E"jB%E"kBw%E"lB5%E"mB%E"nB%E"oBo %E"pB- %E"qB %E"rB %E"sBg %E"tB% %E"uB;%vBQ% E" wB^% xBk%yB%E"zB?%E"{B%E"|B%E"}By%E"~B7%E"B%E"B%E"Bq%E"B/%E"B%E"B%E"Bi%E"B'%E"B%E"B%E"Ba%E"B%E"B%E"B %E"BY!%E"B"%E"B"%E"B#%E",U6%(% F@% %|/=U%2p"yU%x0?*B%B%E"B|%E"B:%E"B%E"B%E"Bt%E"B2%E"B%E"B% E" B%E"By%E"B7 %E"B %E"B %E"Bq %E"B/ %E"B %E"B %E"Bi%E"B'%E"B%E"B%E"Ba%E"B%E"B%E"B%E"BY%E"B%E"B%E"B%E"BQ%E"B%E"B%E"B%E"BI%E"B%E"B%E"B%E"BA%E"B%E"B%E",U6%(% F@% Y%x]/=U%2P# U%x0?B% B % B%E"B%E"B%E"BT%E"B%E" B%E" B%E" BL%E" B %E" B%E"B %|E"|B %E"B %E"B~ %E"B< %E"B %E"B%E"Bv%E"B4%E"B%E"B%E"Bn%E"B,%E"B%E"B%E"Bf%E B$%,U6%(% F@% и%t/=U%2p$kU%x0?4B% B% !B#% "B0% E" #B=% E $BJ% E" %BW% E &Bd% E 'Bq%(B % )B % E" *B % E +B % E" ,B % E" -B%E".B%E"/BD%E"0B%E"1B%E"2B~%E"3B<%E"4B%E"5B%E"6Bv%E"7B4%E"8B%E"9B%E":Bn%E";B,%E"<B%E"=B%E">Bf%E"?B$%E"@B%E"AB%E"BB^%E"CB%E"DB%E"EB %E"FBV!%E"GB"%E"HB"%E"IB#%E"JBN$%E"KB %%E"LB%%E"MB&%E"NBF'%E"O B(%E"P B(%E"Q B)%E"R B>*%E"S ,U6%(% F@% a%p/=U%2P%U%x0?:B% TB % UB%E"VB%E"WB%E"XBT%E"YB%E"ZB%E"[B%E"\BL%E"]B %E"^B%E"_B %E"`BD %E"aB %E"bB %E"cB~ %E"dB< %E"eB %E"f B%E"g!Bv%E"h"B4%E"i#B%E"j$B%E"k%Bn%E"l&B,%E"m'B%E"n(B%E"o)Bf%E"p*B$% q+B1%r,B%E"s-B%E"t.Bk%E"u/B)%E"v0B%E"w1B%E"x2Bc%E"y3B!%E"z4B%E"{5B%E"|6B[%E"}7B %|E"|~9B!%|E"|;B#%E"<B#%E"=B$%E">BK%%E"?B &%E"@B&%E"AB'%E"BBC(%E"CB)%E"DB)%E"EB}*%E"FB;+%E"GB+%E"HB,%E"I,U6%(% F@% %l /=U%2p&šU%x0?:B%JB%KB,%LB%MB%NBf%OB$%PB%QB%RB^%SB%TB%UB %VBV %WB %XB %YB %ZBW %[B%\B%]B%^BO%_B %`B%aB%bBG%cB%dB%eB%fB?%gB%hB%iBy%jB7%kB%lB%mBq%nB/%oB%pB%qBi%rB'%sB%tB %uBa!%vB"%wB"%xB#%yBY$%zB%%{B%%|B&%}BQ'%~B(%B(%B)%BI*%B+%,U6%(% F@% %hY /=U%2P'¡U%x0?B%B%B|% B%BG%B%B%B%B?%B%E"B%E"By%E"B7 %E"B %E"B %Bq %B/ %B %B %Bi%B'%B%B%Ba%B%B%B%BY%B%B%B%,U6%(% F@% %d /=U%2p(ªU%x0?.B%B% B#% B0%BF%B%B%B%B>%B%B%Bx %B6 %B %B %Bp %B. %B %B%Bh%B&%B%B%B`%B%B%B%BX%B%B%B%BP%B%B%B% BH% B% B% B% B@%B%B%Bz %B8!%B!%B"%,U6%(% F@% H%` /=U%2P)U%x0?1B%B% B#% B0% E" B=% E" BJ% E" BW%B%B%B%BO %B % B %!B %"BG %#B %$B %%B%&B?%'B%(B%)By%*B7%+B%,B%-Bq%.B/%/B%0B%1Bi%2B'%3B%4B%5Ba%6B%7B%8B%9BY%:B%;B%<B%=BQ %>B!%?B!%@B"%ABI#%BB$%CB$%DB%%E,U6%(% F@% W%\ /=U%2p* U%x0?&B%FB% GB#% HB0% IB=% JBJ%KB%LB%M B%N BB%O B %P B %Q B| %RB: %SB %TB %UBt %VB2%WB%XB%YBl%ZB*%[B%\B%]Bd%^B"%_B%`B%aB\%bB%cB%d B%e!BT%f"B%g#B%h$B%i%BL%j&B %k',U6%(% F@% л%XU /=U%2P+nU%x0?+B%l(B% m)B#% n*B0%o+BF%p,B%q-B%r.B%s/B>%t0B%u1B%v2Bx %w3B6 %x4B %y5B %z6Bp %{7B. %|8B %}9B%~:Bh%;B&%<B%=B%>B`%?B%@B%AB%BBX%CB%DB%EB%FBP%GB%HB%IB%JBH%KB%LB%MB%NB@%OB%PB%QBz %R,U6%(% F@% %T /=U%2p,ºU%x0?"B% SB % TB% UB'% VB4%WB%XB%YBn%ZB,%[B%\B%]Bf %^B$ %_B %`B %aB^ %bB %cB %dB%eBV%fB%gB%hB%iBN%jB %kB%lB%mBF%nB%oB%pB%qB>%rB%sB% t,U6%(% F@% %P /=U%2P-|U%x0?+B%uB% vB% wB% xB% yB% E" zB% E" {B %|B%}B%~BF %B %B %B %B> %B %B %Bx%B6%B%B%Bp%B.%B%B%Bh%B&%B%B%B`%B%B%B%BX%B%B%B%BP%B%B%B%BH %B!%,U6%(% F@% %ZRoot Entry ®`V@CompObj<Ole persist elements" SfxDocumentInfo uBasicManager2 4StarBASIC SfxWindowsCSwNumRulesUStandardjSfxStyleSheetsSummaryInformation( 00SwPageStyleSheets$ 5StarWriterDocument&