AppleRS232Serial.h   [plain text]

 * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.2 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * and read it before using this file.
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * Please see the License for the specific language governing rights and 
 * limitations under the License.
#ifndef __AppleRS232Serial__
#define __AppleRS232Serial__
#define LDEBUG		0			// for debugging
#define USE_ELG		1			// to event log - LDEBUG must also be set
#define	LOG_DATA	0			// logs data to the IOLog - LDEBUG must also be set
#define DUMPALL		0			// Dumps all the data to the log - LOG_DATA must also be set

#define Sleep_Time	300			// 20 ms per iolog

    #if USE_ELG
        #include "XTrace.h"
        #define XTRACE(x, y, msg)                    						\
        do											\
        {											\
            if (gXTrace)									\
            {											\
                static char *__xtrace = 0;              					\
                if (__xtrace)									\
                    gXTrace->LogAdd(gTraceID, (UInt32)(x), (UInt32)(y), __xtrace);    		\
                else										\
                    __xtrace = gXTrace->LogAdd(gTraceID, (UInt32)(x), (UInt32)(y), msg, false);	\
            }											\
        } while(0)
        #define ELG(x, y, msg) XTRACE(x, y, msg)
        #define XTRACE2(x, y, msg) XTRACE_HELPER(gXTrace, gTraceID, x, y, "AppleRS232Serial: "  msg, true)
    #else /* not USE_ELG */
        #define ELG(A,B,STRING)	{IOLog( "AppleRS232Serial: %8x %8x " STRING "\n", (unsigned int)(A), (unsigned int)(B) );IOSleep(Sleep_Time);}
    #endif /* USE_ELG */
    #if LOG_DATA
        #define LogData(D, C, b)	SerialLogData((UInt8)D, (UInt32)C, (char *)b)
    #else /* not LOG_DATA */
        #define LogData(D, C, b)
    #endif /* LOG_DATA */
#else /* not LDEBUG */
    #define ELG(A,B,S)
    #define LogData(D, C, b)
    #undef USE_ELG
    #undef LOG_DATA
#endif /* LDEBUG */

#define ALERT(A,B,STRING) do { \
	    IOLog( "AppleRS232Serial: %8x %8x " STRING "\n", (unsigned int)(A), (unsigned int)(B) );\
	} while(0)
#define ALERT(A,B,STRING) IOLog( "AppleRS232Serial: %8x %8x " STRING "\n", (unsigned int)(A), (unsigned int)(B) )

#define getDebugFlags()			(getDebugFlagsTable(getPropertyTable()))

#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOTimerEventSource.h>

#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOConditionLock.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/platform/AppleMacIODevice.h>

#include <IOKit/serial/IOSerialDriverSync.h>
#include <IOKit/serial/IORS232SerialStreamSync.h>

#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IOFilterInterruptEventSource.h>

#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/ppc/IODBDMA.h>

#include "SccQueue.h"
#include "Z85C30.h"

//#define TRACE ON

	kSerialOut = 0,		    // debug only, used for LogData() direction
	kSerialIn = 1,
	kSerialOther = 2

    R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15


#define BUFFER_SIZE_DEFAULT	(16*1024)	    // tx and rx queues are always this big

#define SPECIAL_SHIFT		(5)
#define SPECIAL_MASK		((1<<SPECIAL_SHIFT) - 1)
#define STATE_ALL		(PD_RS232_S_MASK | PD_S_MASK )
#define FLOW_RX_AUTO   	 	(PD_RS232_A_RFR | PD_RS232_A_DTR | PD_RS232_A_RXO )
#define FLOW_TX_AUTO    	(PD_RS232_A_CTS | PD_RS232_A_DSR | PD_RS232_A_TXO | PD_RS232_A_DCD )
#define CAN_NOTIFY		(PD_RS232_N_MASK )
#define EXTERNAL_MASK   	(PD_S_MASK | (PD_RS232_S_MASK & ~PD_RS232_S_LOOP) )
#define DEFAULT_AUTO		(PD_RS232_A_DTR | PD_RS232_A_RFR | PD_RS232_A_CTS | PD_RS232_A_DSR )
#define DEFAULT_NOTIFY		(0x00 )
#define IDLE_XO	   		0
#define NEEDS_XOFF 		1
#define SENT_XOFF 		-1
#define NEEDS_XON  		2
#define SENT_XON  		-2

#define	CONTINUE_SEND		1
#define	PAUSE_SEND		2

    // Interrupts

typedef enum InterruptTypes
    kTxInterrupts		= 4,  		//SCC chip level
    kRxInterrupts		= 5,
    kSccInterrupts		= 6,		// sets/clears chip interrupts
} InterruptTypes;

//#define channelDataOffsetRISC		0x010
#define channelControlOffsetRISC	0x000
//#define channelDataOffset		4
//#define channelControlOffset		0

enum InterruptAssignments
    kIntChipSet			= 0,
//#define DMABufferSize		4096

enum ParityType
    NoParity 			= 0,

enum SerialPortSelector
    serialPortA			= 0,
    serialPortB			= 1

#define MIN_BAUD (50 << 1)
#define kDefaultBaudRate	9600
#define kMaxBaudRate		230400

typedef struct SerialDBDMAStatusInfo
    IODBDMAChannelRegisters	*dmaBase;                 	// address of DBDMA channel regs
    IODBDMADescriptor 		*dmaChannelCommandArea;    	// room for DBDMA channel program commands (shared pool)
    IODBDMADescriptor		*dmaChannelCommandAreaPhysical;	// DBDMA channel program, physical address of above
    UInt32			dmaNumberOfDescriptors;    	// number of dbdma command descriptors allocated in above
    UInt8			*dmaTransferBuffer;        	// alloc'd buffer for read/writes (always PAGE_SIZE for now)
    UInt32			dmaTransferSize;           	// byte count to move (tx) or amount that has been read in (rx)
    IOBufferMemoryDescriptor	*dmaTransferBufferMDP; 	   	// memory descriptor for alloc & memory prepare of dmaTransferBuffer
} SerialDBDMAStatusInfo;

typedef struct BufferMarks {		    // high/low/size for tx/rx queues
    UInt32	BufferSize;		    // currenty fixed at 16k per queue (BUFFER_SIZE_DEFAULT)
    UInt32	HighWater;		    // currently fixed at 2/3 of buffersize
    UInt32	LowWater;		    // currently fixed at 1/3 of buffersize
} BufferMarks;

enum {					    // indexes/counts for manipulating/allocating the rx/tx dbdma command chains
	kRxDBDMACmd_First_Read = 0,		// a single byte read to generate an interrupt (when desired)
	kRxDBDMACmd_Main_Read,			// then a big read for the rest of the buffer
	kRxDBDMACmd_Stop,			// finally a stop
	kNumberOfRxDBDMACommands,		// count of commands we need per rx dbdma command set

	kTxDBDMACmd_Write = 0,			// tx has a write and a stop

enum {						// sizes of the dbdma transfer buffers
	kRxDBDMABufferSize = 4096,		// each of the two rx buffers is this big
	kTxDBDMABufferSize = 4096		// one tx buffer

class AppleRS232Serial;

typedef struct
    UInt32			State;
    UInt32			WatchStateMask;
    AppleRS232Serial		*RS232;
        // queue control structures
    CirQueue			RX;
    CirQueue			TX;
    BufferMarks			RXStats;
    BufferMarks			TXStats;
        // UART configuration info
    UInt32			CharLength;	    // 5 thru 8 bits per byte
    UInt32			StopBits;	    // 1, 1.5 or 2 stop bits (x<<1)
    UInt32			TX_Parity;
    UInt32			RX_Parity;
    UInt32			BaudRate;	    // [MIN_BAUD .. kMaxBaudRate]
    bool                	MinLatency;	    // true iff low latency req (not impl yet)

        // flow control state & configuration
    UInt8			XONchar;				// defaults to ^Q
    UInt8			XOFFchar;				// defaults to ^S
    UInt32			SWspecial[(0x100)>>SPECIAL_SHIFT];	// a bit to flag each of [0..255] byte codes as special.  not impl
    UInt32			FlowControl;				// notify-on-delta & auto_control
    UInt32			FlowControlState;			// tx flow control state, one of PAUSE_SEND if paused or CONTINUE_SEND if not blocked
    bool			DCDState;
    bool			CTSState;
    int				RXOstate;				// Indicates our receive state
    int				TXOstate;				// Indicates our transmit state, if we have received any Flow Control
    bool			xOffSent;				// init false, set true if sw flow control and we've sent an xoff
    bool			DTRAsserted;				// init true, set false if DTR flow control and DTR is cleared to hold back rx
    bool			RTSAsserted;				// init true, set false if RTS flow control and RTS is cleared to hold back rx
    bool			aboveRxHighWater;
    bool			BreakState;
    mach_timespec		DataLatInterval;
    mach_timespec		CharLatInterval;			// saved & returned but not impl'd
    bool			AreTransmitting;

        // chip dependent
    UInt8			baudRateGeneratorLo;
    UInt8			baudRateGeneratorHi;
    UInt32			rtxcFrequency;
    UInt8			lastWR[kNumSCCWR];

    IOVirtualAddress		ChipBaseAddress;		// virtual address of the chip
    IOPhysicalAddress		ChipBaseAddressPhysical;	// physical addr of the chip
    IOVirtualAddress		ControlRegister;		// virtual address of the control reg
    //UInt8			*DataRegister;
    //UInt32			ConfigWriteRegister;
    SerialPortSelector		whichPort;			// serialPortA or serialPortB

    IOBufferMemoryDescriptor	*dmaChannelCommandAreaMDP; 	// descriptor for alloc & memory prepare of rx/tx channel command pool
    SerialDBDMAStatusInfo	TxDBDMAChannel;			// tx dma info
    SerialDBDMAStatusInfo	rxDBDMAChannels[2];		// rx is double buffered now 
    int				activeRxChannelIndex;		// index to either rxDBDMAChannels[0] or rxDBDMAChannels[1]
    bool			lastCTSState;			// for midi cts insanity check
    UInt32			lastCTSTime;
    UInt32			ctsTransitionCount;
    IOTimerEventSource		*rxTimer;
} PortInfo_t, SccChannel;

static inline UInt32 tval2long(mach_timespec val)
   return ((val.tv_sec *NSEC_PER_SEC) + val.tv_nsec);

static inline mach_timespec long2tval(UInt32 val)
    mach_timespec tval;

    tval.tv_sec  = val / NSEC_PER_SEC;
    tval.tv_nsec = val % NSEC_PER_SEC;
    return tval;

#include "SccChip.h"

    UInt8 Asciify(UInt8 i);
    void SerialLogData(UInt8 Dir, UInt32 Count, char *buf);

class AppleRS232Serial : public IOSerialDriverSync

    bool		portOpened;
    AbsoluteTime	startingTime;


	// Instance variables
    IOWorkLoop			*fWorkLoop;		// holds the workloop for this driver
    IOCommandGate		*fCommandGate;		// and the command gate

    PortInfo_t			fPort;
    IOService			*fProvider;

    thread_call_t		fdmaStartTransmissionThread;
    thread_call_t		dmaRxHandleCurrentPositionThread;

    IOInterruptEventSource		*sccInterruptSource;
    IOFilterInterruptEventSource	*txDMAInterruptSource;
    IOFilterInterruptEventSource	*rxDMAInterruptSource;
    IOInterruptEventSource		*txDMAInterruptSource;
    IOInterruptEventSource		*rxDMAInterruptSource;

        // IOSerialStreamSync methods
    virtual	IOReturn	acquirePort(bool sleep, void *refCon);
    virtual	IOReturn	releasePort(void *refCon);
    virtual	UInt32		getState(void *refCon);
    virtual	IOReturn	setState(UInt32 state, UInt32 mask, void *refCon);
    virtual	IOReturn	watchState(UInt32 *state, UInt32 mask, void *refCon);
    virtual     UInt32		nextEvent(void *refCon);
    virtual	IOReturn	executeEvent(UInt32 event, UInt32 data, void *refCon);
    virtual	IOReturn	requestEvent(UInt32 event, UInt32 *data, void *refCon);
    virtual	IOReturn	enqueueEvent(UInt32 event, UInt32 data, bool sleep, void *refCon);
    virtual	IOReturn	dequeueEvent(UInt32 *event, UInt32 *data, bool sleep, void *refCon);
    virtual	IOReturn	enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep, void *refCon);
    virtual	IOReturn	dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min, void *refCon);
    virtual	IOWorkLoop	*getWorkLoop() const;

        // Static stubs for IOCommandGate::runAction
    static	IOReturn	acquirePortAction(OSObject *owner, void *arg0, void *, void *, void *);
    static	IOReturn	releasePortAction(OSObject *owner, void *, void *, void *, void *);
    static	IOReturn	getStateAction(OSObject *owner, void *, void *, void *, void *);
    static	IOReturn	setStateAction(OSObject *owner, void *arg0, void *arg1, void *, void *);
    static	IOReturn	watchStateAction(OSObject *owner, void *arg0, void *arg1, void *, void *);
    static	IOReturn	executeEventAction(OSObject *owner, void *arg0, void *arg1, void *, void *);
    static	IOReturn	requestEventAction(OSObject *owner, void *arg0, void *arg1, void *, void *);
    static	IOReturn	enqueueEventAction(OSObject *owner, void *arg0, void *arg1, void *, void *);
    static	IOReturn	enqueueDataAction(OSObject *owner, void *arg0, void *arg1, void *arg2, void *arg3);
    static	IOReturn	dequeueDataAction(OSObject *owner, void *arg0, void *arg1, void *arg2, void *arg3);
        // Gated methods called by the Static stubs

    virtual	IOReturn	acquirePortGated(bool sleep);
    virtual	IOReturn	releasePortGated(void);
    virtual	UInt32		getStateGated(void);
    virtual	IOReturn	setStateGated(UInt32 state, UInt32 mask);
    virtual	IOReturn	watchStateGated(UInt32 *state, UInt32 mask);
    virtual	IOReturn	executeEventGated(UInt32 event, UInt32 data);
    virtual	IOReturn	requestEventGated(UInt32 event, UInt32 *data);
    virtual	IOReturn	enqueueDataGated(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep);
    virtual	IOReturn	dequeueDataGated(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min);

        // IOKit methods
    virtual	bool		start(IOService *provider);
    virtual	void		stop(IOService *provider);

        // Registry & RS232 handling methods
                void 		shutDown();

    static	void		interruptOccurred(OSObject *obj, IOInterruptEventSource *src, int count);
                bool		createSerialStream(IOService *provider);

    static	bool		allocateRingBuffer(CirQueue *Queue, size_t BufferSize, IOWorkLoop *workloop);
    static	void		freeRingBuffer(CirQueue *Queue);

                bool		initializePort(PortInfo_t *port);
    static	void		setStructureDefaults(PortInfo_t *port);

                void		CheckQueues(PortInfo_t *port);

        // Interrupt handler
    static	void		handleInterrupt(OSObject *target, void *refCon, IOService *nub, int source);
    static	void		handleDBDMATxInterrupt(OSObject *target, void *refCon, IOService *nub, int source);
    static	void		handleDBDMARxInterrupt(OSObject *target, void *refCon, IOService *nub, int source);
    static	void		timeoutHandler(OSObject *owner, IOTimerEventSource *sender);

    static	bool		interruptFilter(OSObject *owner, IOFilterInterruptEventSource *source);
    static	void		interruptHandler(OSObject *owner, IOInterruptEventSource *source, int count);
    // power management
    bool			initForPM(IOService *policyMaker);
    IOReturn			setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice);
    unsigned long		powerStateForDomainState(IOPMPowerFlags domainState );
    unsigned long		maxCapabilityForDomainState(IOPMPowerFlags domainState);
    unsigned long		initialPowerStateForDomainState(IOPMPowerFlags domainState);

    static void			handleSetPowerState(thread_call_param_t param0, thread_call_param_t param1);
    static IOReturn		setPowerStateGated(OSObject *owner, void *arg0, void *arg1, void *arg2, void *arg3);

    // private power state stuff
    thread_call_t		fPowerThreadCall;
    bool			fWaitForGatedCmd;
    unsigned int		fCurrentPowerState;     // current power state (0 or 1)