URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [net/] [wan/] [8253x/] [sab8253xds.txt] - Rev 1774
Go to most recent revision | Compare with Previous | Blame | View Log
*_Design Specification of SAB8253X ASLX Driver for Linux_*The effort to design and implement the ASLX SAB8253X Driver for Aurora<http://www.auroratech.com/> hardware with the functionality describedin *_Functional Specification of SAB8253X ASLX Driver for Linux<http://www.telfordtools.com/sab8253x/sab8253xfs.html>_* requiressolutions to seven separate problems:1. creating a development environment for maintaining and extendingthe driver,2. integrating the driver into the kernel sources,3. creating a file structure of the driver that aids understanding,4. crafting a reasonable and easy to use user interface,5. developing simple tools and example programs for the driver, and6. designing the driver data structures and7. designing the driver program logic._Development Environment_There are several possible approaches to creating a developmentenvironment. The development environments for many drivers seemto have consisted simply of a single machine, and the developerused only /printk()/ in the driver code to debug. I used such anenvironment to develop a driver for an IOP480 based intelligentadapter card.For a driver that provides the functionality described in the FunctionalSpecification, a more sophisticated development and debuggingenvironment is useful. (One could even occasionally wish for an ICE,but that level of resources was not available to me.)The development environment consisted of two 686 class machines, onwhich the Linux operating system was installed. One machine ran at 800Mhz, the other at 1Ghz. It probably would have been worthwhile to havedual processor machine, and one was added to the development environmentlater. The 800 Mhz machine hosted the remote gdb application. It ranRedhat Linux 7.0, but because the machine served only as an NFS andremote gdb host, the details of the Linux distribution on this machineare not particularly important.The target machine on which the driver was developed and debugged hostedSuse Linux 7.1 and was later upgraded to Suse Linux 7.3. Suse Linuxseemed to provide the most complete Linux distribution with the leasthassle in installation. (As the Suse distribution comes on 7 standardCDs or 1 DVD, there is a lot of value in having a DVD drive in thetarget machine [and the gdb host if it runs a Suse distribution].)The target and remote gdb machines are connected by a 100 Mbps Ethernetnetwork and by a serial crossover cable between the Com1 (ttyS0) ports.When I started developing the driver, I obtained the Linux 2.4.3 sourcesfrom The Linux Kernel Archives <http://www.kernel.org/>. Later as laterdistributions became stable, I switched to the Linux 2.4.6distribution. The sources were installed first in/home/martillo/kernel/linux-2.4.3 and then in/home/martillo/kernel/linux-2.4.6 on the remote gdb host machine, whichwas named frolix.The sources were imported into CVS on frolix, and the core directoryinto CVS was added manually because the cvs import function ignoresit. I consider it safer to maintain the CVS repository on the remotegdb host machine instead of the target machine because the targetmachine is likely to crash frequently, and its file system may be putinto bad states.I executed the following commands on the remote gdb host machine./home/martillo/kernel/linux-2.4.3/include/asm** *or* */home/martillo/kernel/linux-2.4.6/include/asm** *I edited the /etc/exports file to contain the following./ ylith(rw)/ fireball(rw)/ bohun(rw)/ indefatigable(rw)Ylith is the original 1 Ghz target machine. Fireball is a 400 Mhzcompact PCI target machine. Indefatigable is a dual 1 Ghz targetmachine. Bohun is a Solaris target machine used for another project.On the frolix, I started NFS with the following commands (contained in ashell script).*/etc/rc.d/init.d/nfs start**exportfs -va*If it had been a Suse Linux machine, I would have used the yast2 controlcenter to start NFS service.On ylith, I created an empty directory /frolix and executed thefollowing command.*mount frolix:/ /frolix** *At this point both the remote gdb host (frolix) and the targetdevelopment and debugging machine can refer to the same files by thesame paths. I could have guaranteed the same paths to the source fileson both machines if I had simply used /home/martillo as my homedirectory on frolix and exported /home from frolix to all the othermachines so that there would only be one /home directory for all themachines in my network. I was lazy about the network configuration.After making sure that the user martillo had read write accesspermissions to all the kernel sources, I built the kernel on the targetmachine from within xemacs with the following sequence of commands.From a shell window:Inside xemacs:*cd /frolix/home/martillo/kernel/linux-2.4.3*or*cd /frolix/home/martillo/kernel/linux-2.4.6*Then make sure that linux-2.4.x/include/asm-i386 is symbolically linked tolinux-2.4.x/include/asm.Execute the following emacs command.M-x compileThis command prompts for targets.In the development environment the most useful target string was usually/clean xconfig dep bzImage modules./ The target /xconfig /brings up aconfiguration window. In the basic development environment, it wasgenerally worthwhile to add SCSI CD ROM, SCSI legacy support, anEthernet driver and DOS file system supportThe target/ dep/ creates the dependencies (note that if the kernel treeis ever removed, the .depend and .hdepend files must be regenerated).The /bzImage /target builds the kernel. The /modules/ target generatesall the modules to be dynamically installed in the kernel via the*insmod* command.After building the kernel, installing the modules in the /lib treerequires the execution (as root) ofmake modules_installThe command *make install* *INSTALL_PATH=/boot* will install thecompressed kernel image as vmlinuz along with other files in the /bootpartition. I preferred to use a shell script with commands like thefollowing.cp /frolix/home/martillo/kernel/linux-2.4.6/arch/i386/boot/bzImage/boot/vmlinuz_246cp /frolix/home/martillo/kernel/linux-2.4.6/System.map/boot/System.map-2.4.6cp /frolix/home/martillo/kernel/linux-2.4.6/.config /boot/vmlinuz_246.configcp /frolix/home/martillo/kernel/linux-2.4.6/include/linux/autoconf.h/boot/vmlinuz_246.autoconf.hcp /frolix/home/martillo/kernel/linux-2.4.6/include/linux/version.h/boot/vmlinuz_246.version.hWhen the kernel comes from a linux-2.4.3 tree, the obvious substitutionsof 3 for 6 are required.Once all the modules and the kernel image are installed, the next stepin giving the system the ability to boot with the new linux-2.4.3 orlinux-2.4.6 kernel image is the modification of the lilo.conf file.I added the following directives to the lilo.conf file.image = /boot/vmlinuz_243label = linux_2.4.3root = /dev/hde7optionalimage = /boot/vmlinuz_246label = linux_2.4.6root = /dev/hde7optionalIn this case /dev/hde7 corresponds to the /boot partition, and theoptions linux_2.4.3 and linux_2.4.6 will be added to the boot menu oncethe *lilo* command has been executed. In other system setups the diskpartition that corresponds to /boot might have a different name like/dev/hda7.Once the new kernel successfully boots, the next step to creating adriver development and debugging environment is patching the kernel forremote gdb debugging.The necessary patch can be obtained from kgdb: Source level debugging oflinux kernel <http://kgdb.sourceforge.net/downloads.html>.Now the kernel can be built again with the extra step of configuring forremote gdb support in the kernel configuration menu.The following directives should be added to lilo.conf.image = /boot/vmlinuz_243label = debug243append = "gdb gdbttyS=0 gdbbaud=115200"root = /dev/hde7optionalimage = /boot/vmlinuz_246label = debug246append = "gdb gdbttyS=0 gdbbaud=115200"root = /dev/hde7optionalThen lilo can be executed. On reboot the boot menu will includeoptions for debug243 and debug246.To test the patch, select one of the debug options. Then, on the remotegdb host machine execute the following command.stty 115200 < /dev/ttyS0Start up an *xemacs* process. Execute the following commands within*xemacs.*M-x shellThen within the shell window execute the following command.cd /frolix/home/martillo/kernel/linux-2.4./X// /Then invoke the remote debugger.M-x gdbReply to the file prompt with *vmlinux.** *In the gdb window, execute the following command.target remote /dev/ttyS0The gdb window should break in gdbstub.c which will be displayed in thegdb source window.At this point, all the basic gdb remote debugging capabilities are readyto use.To access the hardware breakpoint capability of the i386 processor, thefollowing commands can be loaded directly or from a file with the*script* command.#Hardware breakpoints in gdb##Using ia-32 hardware breakpoints.##4 hardware breakpoints are available in ia-32 processors. These breakpoints#do not need code modification. They are set using debug registers.##Each hardware breakpoint can be of one of the#three types: execution, write, access.#1. An Execution breakpoint is triggered when code at the breakpointaddress is#executed.#2. A write breakpoint ( aka watchpoints ) is triggered when memory location#at the breakpoint address is written.#3. An access breakpoint is triggered when memory location at the breakpoint#address is either read or written.##As hardware breakpoints are available in limited number, use software#breakpoints ( br command in gdb ) instead of execution hardwarebreakpoints.##Length of an access or a write breakpoint defines length of the datatype to#be watched. Length is 1 for char, 2 short , 3 int.##For placing execution, write and access breakpoints, use commands#hwebrk, hwwbrk, hwabrk#To remove a breakpoint use hwrmbrk command.##These commands take following types of arguments. For arguments associated#with each command, use help command.#1. breakpointno: 0 to 3#2. length: 1 to 3#3. address: Memory location in hex ( without 0x ) e.g c015e9bc##Use the command exinfo to find which hardware breakpoint occured.#hwebrk breakpointno addressdefine hwebrkmaintenance packet Y$arg0,0,0,$arg1enddocument hwebrkhwebrk breakpointno addressPlaces a hardware execution breakpointend#hwwbrk breakpointno length addressdefine hwwbrkmaintenance packet Y$arg0,1,$arg1,$arg2enddocument hwwbrkhwwbrk breakpointno length addressPlaces a hardware write breakpointend#hwabrk breakpointno length addressdefine hwabrkmaintenance packet Y$arg0,1,$arg1,$arg2enddocument hwabrkhwabrk breakpointno length addressPlaces a hardware access breakpointend#hwrmbrk breakpointnodefine hwrmbrkmaintenance packet y$arg0enddocument hwrmbrkhwrmbrk breakpointnoRemoves a hardware breakpointend#exinfodefine exinfomaintenance packet qEenddocument exinfoexinfoGives information about a breakpoint.endOnce the above macros are define, the developer can set hardwarebreakpoints.The next step to creating a useful development and debugging environmentis to provide a shell script to for remote debugging of dynamicallyloaded modules. The following shell script (called *loadmodule.sh*)creates a gdb script called *load/ModuleName/* in/frolix/home/martillo/kernel/linux-2.4.6 when it is invoked (as root)with the following command.loadmodule.sh modulenameIn order to decrease the probability of confusion, I usually make a linkin kernel root directory, /frolix/home/martillo/kernel/linux-2.4.6, tothe location of the module to be debugged in the kernel tree. The abovecommand is invoked on the target machine (ylith) in the root directory.On the remote debug machine, in the gdb command window, whose workingdirectory should be the kernel root directory,/frolix/home/martillo/kernel/linux-2.4.6, the command, *scriptload/ModuleName/*, is invoked. Once the script is executed the symbolsfor the module are available for remote symbolic debugging.#!/bin/sh# This script loads a module on a target machine and generates a gdb script.# source generated gdb script to load the module file at appropriateaddresses# in gdb.## Usage:# Loading the module on target machine and generating gdb script)# [foo]$ loadmodule.sh <modulename>## Loading the module file into gdb# (gdb) source <gdbscriptpath>## Modify following variables according to your setup.# TESTMACHINE - Name of the target machine# GDBSCRIPTS - The directory where a gdb script will be generated## Author: Amit S. Kale (akale@veritas.com).## If you run into problems, please check files pointed to by following# variables.# ERRFILE - /tmp/<modulename>.errs contains stderr output of insmod# MAPFILE - /tmp/<modulename>.map contains stdout output of insmod# GDBSCRIPT - $GDBSCRIPTS/load<modulename> gdb script.TESTMACHINE=ylithGDBSCRIPTS=/frolix/home/martillo/kernel/linux-2.4.6if [ $# -lt 1 ] ; then {echo Usage: $0 modulefileexit} ; fiMODULEFILE=$1MODULEFILEBASENAME=`basename $1`if [ $MODULEFILE = $MODULEFILEBASENAME ] ; then {MODULEFILE=`pwd`/$MODULEFILE} fiERRFILE=/tmp/$MODULEFILEBASENAME.errsMAPFILE=/tmp/$MODULEFILEBASENAME.mapGDBSCRIPT=$GDBSCRIPTS/load$MODULEFILEBASENAMEfunction findaddr() {local ADDR=0x$(echo "$SEGMENTS" | \grep "$1" | sed 's/^[^ ]*[ ]*[^ ]*[ ]*//' | \sed 's/[ ]*[^ ]*$//')echo $ADDR}function checkerrs() {if [ "`cat $ERRFILE`" != "" ] ; then {cat $ERRFILE} fi}#load the module#echo Copying $MODULEFILE to $TESTMACHINE#*rcp $MODULEFILE root@${TESTMACHINE}:echo Loading module $MODULEFILE#rsh -l root $TESTMACHINE /sbin/insmod -m ./`basename $MODULEFILE` \# > $MAPFILE 2> $ERRFILE &/sbin/insmod -m ./`basename $MODULEFILE` $2 . . > $MAPFILE 2> $ERRFILE &sleep 5checkerrsNUMLINES=`grep -n '^$' $MAPFILE | sed -e 's/:.*//g'`SEGMENTS=`head -n $NUMLINES $MAPFILE | tail -n $(eval expr $NUMLINES - 1)`TEXTADDR=$(findaddr "\\.text[^.]")LOADSTRING="add-symbol-file $MODULEFILE $TEXTADDR"SEGADDRS=`echo "$SEGMENTS" | awk '//{if ($1 != ".text" && $1 != ".this" &&$1 != ".kstrtab" && $1 != ".kmodtab") {print " -s " $1 " 0x" $3 " "}}'`LOADSTRING="$LOADSTRING $SEGADDRS"echo Generating script $GDBSCRIPTecho $LOADSTRING > $GDBSCRIPTWith the addition of the above shell script, the driver development anddebugging environment is almost complete. Other useful tools fordeveloping and debugging this type of serial driver would include aWanalyzer (I used an Interview 7700 and an HP 4952A in developing thisdriver), a breakout box that displays interface signal states and (fordeveloping the serial Ethernet-like network driver) several WAN LAN VLANrouters as described in *Packet Switching Software and Platforms<http://members.aol.com/Telford001/vrouter2g.html>*, *Routing in aBridged Network <http://members.aol.com/Telford001/routetti2.html>, **AWAN SUBSYSTEM for a High Performance Packet Switch<http://members.aol.com/Keleustes/syncdob.html>* and *A New HighPerformance Architecture for Routers, Bridges and LAN Switches (SoftwareDefined Internetworking)<http://members.aol.com/Ishtar7713/private/sdi4.html>.*_Integration into the Kernel Sources_The driver has its own directory, {kernel rootdirectory}/drivers/net/wan/8253x, in the 2.4.* kernel source tree.To facilitate the automatic build of the 8253x driver, the followingstandard kernel files were modified.1. {kernel root directory}/drivers/net/wan/Config.in towhich the linetristate ' Aurora Technology, Inc. synchronous asynchronous PCI cardsV2' CONFIG_ATI_XX20was added,2. {kernel root directory}/drivers/net/wan/Makefile towhich the following lines were added,subdir-$(CONFIG_ATI_XX20) += 8253xifeq ($(CONFIG_ATI_XX20),y)obj-y += 8253x/ASLX.oendifWhen the driver is built as a dynamically loaded module, the followingmacro commands in the file 8253xini.c puts the module entry points inthe special module entry point segment.module_init(auraXX20_probe);module_exit(auraXX20_cleanup);The sources are provided to the users in a patch file, tentatively named8253x.patch <http://www.telfordtools.com/sab8253x/8253x.patch>.To install it the user sets his directory to the top level of the kernelsources and executes the following command._File Structure of the ASLX Driver Source Code_The following files are present in the driver directory.8253x.h8253xdbg.c8253xmac.c8253xsyn.cPciRegs.hcrc32.hsp502.h8253xcfg.c8253xini.c8253xnet.c8253xtty.cReg9050.hcrc32dcl.hring.h8253xctl.h8253xioc.h8253xplx.c8253xint.ccrc32.cendian.hMakefileAmcc5920.c8253xmcs.h8253xmcs.c8253xchr.c8253xutl.cThe source code is divided functionally among the files of the ASLX driver.8253xcfg.c is the source for a user application that configures 8253xcontrol registers to provide clocking. 8253xmac.c is the source for auser application that sets a pseudo-MAC address for the network driver.8253xini.c contains the initialization/probe logic.8253xint.c contains the common interrupt logic.8253xtty.c contains the asynchronous TTY logic.8253xsyn.c contains the synchronous TTY logic.8253xnet.c contains the network driver logic.8253xchr.c contains the character driver logic.8253xdbg.c contains some debugging functions.8253xutl.c contains most of the functions that are common among thedifferent driver functional subunits.8253xplx.c contains some functions specific to the PLX9050 (a PCI bridgechip) and specifically to reading and reprogramming the associatedserial EEPROM.amcc5920.c contains some functions specific to the AMCC5920 (a PCIbridge chip) and specifically to reading and reprogramming theassociated serial EEPROM.8253xmcs.c contains functions specific to programming the multichannelserver (mostly G-LINK related logic, programming the sp502 driver chipand reading or programming the serial EEPROM associated with theinterface cards contained within the MCS unit).crc32.c contains logic to append a CRC32 to a pseudo MAC frame that isgenerated by the network driver.8253x.h contains symbols, structures and macros that relate mostly tothe 8253x chips and ports.8253xctl.h contains symbols, structures and macros that relate mostly tothe adapter cards.8253xmcs.h contains symbols and structures that relate mostly to themultichannel server. A lot of this file relates to G-LINK.sp502.h contains symbols and structures that relate to the programmingof the hardware interface line drivers of the 3500 adapter cards of themultichannel server.8253xioc.h contains symbols and structures that relate to private ioctls.PciRegs.h contains symbols and structures that relate to PCIconfiguration space.Reg9050.h contains symbols and structures that relate to the PLX9050 PCIinterface chip and its serial epromcrc32.h, crc32dcl.h and .endian.h contain symbols, structures and macrosthat relate to generating a correct CRC32.ring.h contains symbols and structures that relate to the network driverframe transmission ring and frame reception.The Makefile is a standard Linux kernel Makefile whose structure isdictated by the current Linux build formalism._Using the ASLX Driver _possible. If it is built as a dynamically loadable module, the user(or relevant system configuration file) invokes /insmod /to load theASLX.o file.The following parameters can be set on the /insmod/ command line.MODULE_PARM(xx20_minorstart, "i");/*when statically linked autodectedotherwise 128 by default*/MODULE_PARM(sab8253xc_major, "i");/*major dev for character device, bydefault dynamic */MODULE_PARM(auraXX20n_debug, "i");/*turns on debugging messages, defaultoff*/MODULE_PARM(auraXX20n_name, "s"); /*base network driver name = 8253x000*/MODULE_PARM(sab8253xn_listsize, "i"); /*transmit ring size default 32*/MODULE_PARM(sab8253xc_name, "s");/*registered name for char driver =sab8253xc*/MODULE_PARM(sab8253x_default_sp502_mode, "i");The asynchronous TTY functionality can immediately be used withoutextra configuration. [Note that immediate use of the WMS3500 productsis possible because the default value of sab8253x_default_sp502_modeis SP502_RS232_MODE (== 1). If a different default mode is needed, itcan be set as options in the /etc/modules.conf file. OFF = 0.RS232 = 1, RS422 = 2, RS485 = 3, RS449 = 4, EIA530 = 5 and V.35 = 6, asdefined in 8253xioc.h.]The MAKETERMS script below parses the /proc/tty/driver/auraserial fileto make the asynchronous TTY device files in the /dev directory.TTYDEV=$1MDEVS=`cat /proc/tty/driver/auraserial | gawk '{print $1}' | sed -e'/[a-zA-Z]/d' | sed -e 's/://'`for i in $MDEVSdoTTYNAME=/dev/ttyS${TTYDEV}mknod $TTYNAME c 4 $iTTYDEV=$((${TTYDEV}+1))doneThe MAKEPROTO script below provides a prototype to modify the/etc/inittab file so that an agetty process can be spawned on everyother /dev/ttyS* at 9600 bpsTTYDEV=$1MDEVS=`cat /proc/tty/driver/auraserial | gawk '{print $1}' | sed -e'/[a-zA-Z]/d' | sed -e 's/://'`LEADCHAR=""for i in $MDEVSdoNAME=S${TTYDEV}TTYNAME=ttyS${TTYDEV}echo ${LEADCHAR}${NAME}:35:respawn:/sbin/agetty 9600 ${TTYNAME}TTYDEV=$((${TTYDEV}+1))if [ -z "$LEADCHAR" ]thenLEADCHAR="#"elseLEADCHAR=""fidoneIf loopback cables are connected between successive TTY ports on eachAurora adapter card or unit, the commandwould connect to the login that was spawned on /dev/ttyS{n-1}.The script MAKESTERMS (viz below) creates synchronous TTY dev files forall the Aurora serial ports.TTYDEV=$1MDEVS=`cat /proc/tty/driver/auraserial | gawk '{print $1}' | sed -e'/[a-zA-Z]/d' | sed -e 's/://'`for i in $MDEVSdoTTYNAME=/dev/sttyS${TTYDEV}mknod $TTYNAME c 5 $iTTYDEV=$((${TTYDEV}+1))doneThe MAKESPROTO script below creates a prototype with which to modify the/etc/inittab file to spawn an agetty process on every other/dev/sttyS{N} device.TTYDEV=$1MDEVS=`cat /proc/tty/driver/auraserial | gawk '{print $1}' | sed -e'/[a-zA-Z]/d' | sed -e 's/://'`LEADCHAR=""for i in $MDEVSdoNAME=sS${TTYDEV}TTYNAME=sttyS${TTYDEV}echo ${LEADCHAR}${NAME}:35:respawn:/sbin/agetty 9600 ${TTYNAME}TTYDEV=$((${TTYDEV}+1))if [ -z "$LEADCHAR" ]thenLEADCHAR="#"elseLEADCHAR=""fidoneThe simplest way to use these terminals with the agetty process thatcomes with the Linux distribution is to leave externally clocked (thedefault) the terminals on which agetty has been spawned.The loopback cable can be connected to a port on which agetty is notbeing run. The clockside of the cable is connected to this port. Theuser can run the MAKECLOCKING script below.echo 8253xcfg $1 -n 64 158 56 4 192 140 158253xcfg $1 -n 64 158 56 4 192 140 15The numbers on the 8253xcfg command line are new (decimal) values forthe channel control, mode and baud rate registers. The file 8253xioc.hand sab8253x manuals from Siemens/Infineon can assist in explaining thereasoning behind these values. The 8253xcfg sets the mode, channelcontrol and baudrate generator registers of the port specified by/dev/sttyS{N-1} which is the argument $1 of this script file. 8253xcfgis a simple example program that is included with the driver sources.It is described in the next section of this document.At this point, the user could execute the following command to connectsynchronously to the peer synchronous TTY port.To turn off internal clocking use the following command.To use an ASLX network device the following commands would be used.*MAKECLOCKING /dev/sttyS*/{N} [if the interface is to provide clock]/*stty */{speed} /*< /dev/sttyS*/{N} [if the interface is to provide clock]/To set the MAC address, which defaults to 00:00:00:00:00:00 and whichconsequently must be changed, use the following command.*ifconfig 8253x*/{mdev} /*hw ether*/ {mac address} [as root]/[Note that the 8253x{mdev} interface must not be running when the abovecommand is executed.]To set the IP address, use the command./ /*ifconfig 8253x*/{mdev} {ipadress} [as root]/[Note that the two ifconfig commands can be combined on one line. Ifthey are executed separately the MAC address command must be executedbefore the IP address command.]After the completion of the above commands, assuming there is an activenetwork peer that uses the same serial Ethernet frame structure, itshould be possible to ping or telnet to the peer networking device.{mdev} is the minor device number (in decimal, 3 digits includingleading 0s required) associated with /dev/sttyS{N}.//* *To disable the network interface use the following command.*ifconfig 8253x*/{mdev} /*down*/ [as root]/If there is a need to disable clocking on a serial port, theMAKENONCLOCKING shell script is invoked with the TTY device as anargument as follows.MAKENONCLOCKING /dev/ttyS{N}The shell script contains the following commands.echo 8253xcfg $1 -n 64 152 0 4 192 140 2558253xcfg $1 -n 64 152 0 4 192 140 255The numbers on the 8253xcfg command line are new (decimal) values forthe channel control, mode and baud rate registers. The file 8253xioc.hand sab8253x manuals from Siemens/Infineon can assist in explaining thereasoning behind these values. The 8253xcfg sets the mode, channelcontrol and baudrate generator registers of the port specified by/dev/sttyS{N-1} which is the argument $1 of this script file. 8253xcfgis a simple example program that is included with the driver sources.It is described in the next section of this document._Simple Tools and Example Programs_The tools and example programs supplied with the SAB8253X ASLX driverare the following.1. eprom90502. 8253xcfg3. 8253xspeed4. 8253xpeer5. 8253xmodeeprom9050This program performs the bit-banging necessary to read and to programthe serial eprom of the PLX9050.To access the serial eprom on an adapter card the program opens up a TTYdevice on the adapter card, whose serial eprom is to be modified.This TTY device can either be synchronous, asynchronous or callout. TheTTY device is passed as an argument when the program is invoked.The program uses the ATIS_IOCGSEP9050 IOCTL to get the current serialeprom values and the ATIS_IOCSSEP9050 IOCTL to set the new serial epromvalues.Here is the source code of the program./** Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.***/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include "8253xioc.h"#include "Reg9050.h"/* This application shows how to load the *//* channel control, mode and rx framelength *//* check registers via an ioctl.*/int main(int argc, char **argv){int fd;unsigned short oldeeprom[EPROM9050_SIZE], neweeprom[EPROM9050_SIZE];char buffer[200];int count;int value;unsigned short *pointer;unsigned short *pointerold;int noprompt = 0;int epromindex;if(argc < 2){fprintf(stderr, "Syntax: %s {portname} [-n] {prom values}.\n", *argv);exit(-1);}fd = open(argv[1], O_RDWR);if(fd < 0){perror("open failed.");exit(-2);}if((argc > 2) && !strcmp("-n", argv[2])){noprompt = 1;}/* get the current values */if(ioctl(fd, ATIS_IOCGSEP9050, &oldeeprom) < 0){perror("ioctl failed.");exit(-3);}/* set up the existing values as defaults */memcpy(neweeprom, oldeeprom, sizeof(oldeeprom));/* gather all new values from thecommand line *//* or via tty input.*/for(count = (2+noprompt), pointer = neweeprom; count < argc; ++count,++pointer){*pointer = atoi(argv[count]);}pointer = neweeprom;pointerold = oldeeprom;for(epromindex = 0; epromindex < EPROM9050_SIZE; ++epromindex){fprintf(stderr, "LOCATION %i [%4.4x/%4.4x]: ", epromindex,*pointerold, *pointer);if(!noprompt){if(count = read(0, buffer, 150), count <= 0){exit(0);}buffer[count] = '\0';if(buffer[0] != '\n'){sscanf(buffer, "%x", &value);*pointer = (unsigned short) value;}}else{fprintf(stderr, "\n");}++pointerold;++pointer;}/* This ioctl does the actual registerload. */if(ioctl(fd, ATIS_IOCSSEP9050, neweeprom) < 0){perror("ioctl failed.");exit(-3);}fflush(stdout);}With the above program it is possible to change PCI vendor and device IDvalues of the adapter card. At that point the card would no longer bevisible to the driver. To correct the vendor and device IDs use the*lspci* Linux command to find out what new values are and recompile thedriver code after modifying the symbols that correspond to the correctvendor and device Ids of the card to the new values. (I should make thevendor and device IDs module parameters that can be set from the*insmod* command line.) The eprom9050 can be invoked to write thedevice and vendor IDs to the correct values. Of course, then the cardwill no longer be visible to the new version of the driver, and theoriginal version of the driver must be used to communicate with this card.8253xcfgThe 8253xcfg command provides access to images of the channel control,mode and baud rate generator registers of the serial port that isspecified by the minor device number (port number = minor device numbercallout) specified on the command line by which 8253xcfg is invoked.The next time the port is initialized (usually on the first open afterevery process that currently has the port open has closed it) theseregisters are set with the values of their images. The 8253xcfg commandcan make a synchronous port clocking or non-clocking. Note that eventhough 8253xcfg operates on a TTY device, the open that finally sets theregisters with the values from the images can be either a synchronousTTY, a network device or a synchronous character device open. Theprogram uses the ATIS_IOCGPARAMS IOCTL to get the current values of theimages of the registers and employs the ATIS_IOCSPARAMS IOCTL to set thecurrent values of the images of the registers. Here is the source ofthe 8253xcfg program./** Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.***/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include "8253xioc.h"char *prompts[] ={"ccr0","ccr1","ccr2","ccr3","ccr4","mode","rlcr",0};/* This application shows how to load the *//* channel control, mode and rx framelength *//* check registers via an ioctl.*/int main(int argc, char **argv){int fd;struct channelcontrol ccontrolold, ccontrolnew;char buffer[200];int count;int value;unsigned char *pointer;unsigned char *pointerold;char **promptpointer = prompts;int noprompt = 0;if(argc < 2){fprintf(stderr, "Syntax: %s {portname} [-n] [ccr0 [ccr1 [ccr2[ccr3 [ccr4 [mode [rlcr]]]]]]].\n", *argv);exit(-1);}fd = open(argv[1], O_RDWR);if(fd < 0){perror("open failed.");exit(-2);}if((argc > 2) && !strcmp("-n", argv[2])){noprompt = 1;}/* get the current values */if(ioctl(fd, ATIS_IOCGPARAMS, &ccontrolold) < 0){perror("ioctl failed.");exit(-3);}/* set up the existing values as defaults */ccontrolnew = ccontrolold;/* gather all new values from thecommand line *//* or via tty input.*/for(count = (2+noprompt), pointer = (unsigned char*) &ccontrolnew;count < argc; ++count, ++pointer){*pointer = atoi(argv[count]);}pointer = (unsigned char*) &ccontrolnew;pointerold = (unsigned char*) &ccontrolold;while(*promptpointer){fprintf(stderr, "%s [%2.2x/%2.2x]: ",*promptpointer, *pointerold,*pointer);if(!noprompt){if(count = read(0, buffer, 150), count <= 0){exit(0);}buffer[count] = '\0';if(buffer[0] != '\n'){sscanf(buffer, "%x", &value);*pointer = (unsigned char) value;}}else{fprintf(stderr, "\n");}++pointerold;++pointer;++promptpointer;}/* This ioctl does the actual registerload. */if(ioctl(fd, ATIS_IOCSPARAMS, &ccontrolnew) < 0){perror("ioctl failed.");exit(-3);}fflush(stdout);}8253xspeedThis program sets the custom baud rate of a serial port. If the custombaud rate of a serial port is non-zero, and if the standard baud ratehas been set to 38,400 bps, the next time the port is initialized, theport will run at the custom baud rate. If the custom baud rate were 0,the port would run at the standard baud rate. The 8352xpeed program isinvoked with a TTY device (either asynchronous, synchronous or callout),but the effect also applies to the network device and the synchronouscharacter device that correspond to the same physical serial port.Here is the source code for the 8253xspeed./** Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.***/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include "8253xioc.h"int main(int argc, char **argv){int fd;unsigned long oldspeed, newspeed;char buffer[200];int count;long value;int noprompt = 0;int epromindex;if(argc < 2){fprintf(stderr, "Syntax: %s {portname} [-n] {new speed}.\n", *argv);exit(-1);}fd = open(argv[1], O_RDWR);if(fd < 0){perror("open failed.");exit(-2);}if((argc > 2) && !strcmp("-n", argv[2])){noprompt = 1;}/* get the current values */if(ioctl(fd, ATIS_IOCGSPEED, &oldspeed) < 0){perror("ioctl failed.");exit(-3);}/* set up the existing values as defaults */newspeed = oldspeed;/* gather all new values from the command line *//* or via tty input.*/if(argc == (noprompt + 3)){newspeed = atoi(argv[count]);}fprintf(stderr, "speed [%ld/%ld]: ", oldspeed, newspeed);if(!noprompt){if(count = read(0, buffer, 150), count <= 0){exit(0);}buffer[count] = '\0';if(buffer[0] != '\n'){sscanf(buffer, "%ld", &newspeed);}}else{fprintf(stderr, "\n");}/* This ioctl does the actual register load. */if(ioctl(fd, ATIS_IOCSSPEED, &newspeed) < 0){perror("ioctl failed.");exit(-3);}fflush(stdout);}The ATIS_IOCGSPEED gets the value the custom baud rate for a serial portwhile ATIS_IOCSSPEED sets the value of the custom baud rate for a serialport.8253xpeerThe 8253xpeer example program reads and writes packets to the serialport in synchronous mode. The synchronous character driver to someextent emulates the getmsg/putmsg functionality found in Solaris. Thisdriver returns only one packet at a time to read and returns ENOMEM ifthe receive buffer is not large enough to receive the current packet.The driver assumes that the data from a write is to be packetized into asingle packet. The driver can provide asynchronous notification thatthere is no more data queued to be transmitted at the serial port. Thisasynchronous notification informs the application program that a lowpriority packet can now be written to the driver. Such functionality isuseful to protocols like LAPB that distinguish low priority informationframes from high priority control frames.To try out this program find the major device number associated with the8253xc device in the /proc/devices file. Select two ports to looptogether. Connect them with a synchronous loopback cable. Then executethe following command for each of the ports.mknod /dev//DevName1/ c /major-dev-num minor-dev-num-1// /mknod /dev//DevName2/ c /major-dev-num minor-dev-num-2/Minor-dev-num-[1/2] correspond to the selected ports.Select one of the ports to be clocking (the clocking end of the loopbackcable should connect to this port) and apply MAKECLOCKING to thecorresponding TTY. Use stty or 8253xspeed and stty to set the speed onthe corresponding TTY port.Then in one window run *8253xpeer /dev//DevName1/* and in another windowexecute *8253xpeer /dev//DevName2./* It should now be possible to sendand receive data in each of the windows.Here is the program source./** Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.***/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include "8253xioc.h"#include <sys/poll.h>struct pollfd pollarray[2];char buffer[8192];int main(int argc, char **argv){int fd;int status;int prompt = 1;int count;if(argc != 2){fprintf(stderr, "Syntax: %s {portname}\n", *argv);exit(-1);}fd = open(argv[1], O_RDWR);if(fd < 0){perror("open failed.");exit(-2);}do{if(prompt){printf("Enter data: ");fflush(stdout);prompt = 0;}pollarray[0].fd = 0;pollarray[0].events = POLLIN;pollarray[0].revents = 0;pollarray[1].fd = fd;pollarray[1].events = POLLIN|POLLOUT;pollarray[1].revents = 0;status = poll(pollarray, 2, 10);switch(status){case 0:break;case 1:case 2:if(pollarray[0].revents == POLLIN){if(count = read(0, buffer, 150), count <= 0){perror("unable to read stdio.\n");exit(0);}buffer[count] = '\0';if(count){if(pollarray[1].revents & POLLOUT){if(write(pollarray[1].fd, buffer, count) <= 0){perror("unable to write protodevice.\n");exit(-1);}}else{printf("Write of protodevice would block.\n");fflush(stdout);}}prompt = 1;}if(pollarray[1].revents & POLLIN){if(count = read(pollarray[1].fd, buffer, 8192), count <= 0){perror("unable to read protodevice.\n");exit(0);}buffer[count] = '\0';printf("\nRead: %s", buffer);fflush(stdout);prompt = 1;}break;default:break;}}while(status >= 0);}8253xmodeThe 8253xmode program sets the signaling mode of port on a multichannelserver 3500 extension board which has a programmable Sipex sp502physical driver chip for each port.The command syntax is the following*8253xmode* /dev//{dev name} {mode}/where mode is one of the following.* off* 232* 422* 485* 530* v.35Note the minor devices associated with a multiserver increasemonotonically starting from the first connector on the upper left cornerif you are facing the connector side of the multiserver. The numberinggoes from left to right and top to bottom without gaps Thus, thenumbers on the multiserver itself may not map to the minor device numberas port number + minor device number of first port if the multiserver isnot fully populated.Here is the source for the 8253xmode program./* -*- linux-c -*- *//** Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.***/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include "8253xioc.h"static char *signaling[] ={"OFF","RS232","RS422","RS485","RS449","RS530","V.35"};/* This application shows how to set sigmode* on those devices that support software* programmable signaling. */int main(int argc, char **argv){int fd;unsigned int oldmode, newmode;if(argc != 3){fprintf(stderr, "Syntax: %s {portname} {new mode}.\n",*argv);fprintf(stderr, "{new mode} = off | 232 | 422 | 485 |449 | 530 | v.35\n");exit(-1);}fd = open(argv[1], O_RDWR);if(fd < 0){perror("open failed.");exit(-2);}if(!strcmp("off", argv[2])){newmode = SP502_OFF_MODE;}else if(!strcmp("232", argv[2])){newmode = SP502_RS232_MODE;}else if(!strcmp("422", argv[2])){newmode = SP502_RS422_MODE;}else if(!strcmp("485", argv[2])){newmode = SP502_RS485_MODE;}else if(!strcmp("449", argv[2])){newmode = SP502_RS449_MODE;}else if(!strcmp("530", argv[2])){newmode = SP502_EIA530_MODE;}else if(!strcmp("v.35", argv[2])){newmode = SP502_V35_MODE;}else{fprintf(stderr, "Unknown mode %s.\n", argv[2]);fprintf(stderr, "Syntax: %s {portname} {new mode}.\n",*argv);fprintf(stderr, "{new mode} = off | 232 | 422 | 485 |449 | 530 | v.35\n");exit(-1);}/* get the current values */if(ioctl(fd, ATIS_IOCGSIGMODE, &oldmode) < 0){perror("ATIS_IOCGSIGMODE ioctl failed.");exit(-3);}fprintf(stderr, "old mode = %s.\n", signaling[oldmode]);if(ioctl(fd, ATIS_IOCSSIGMODE, &newmode) < 0){perror("ATIS_IOCSSIGMODE ioctl failed.");exit(-3);}/* get the current values */if(ioctl(fd, ATIS_IOCGSIGMODE, &oldmode) < 0){perror("ATIS_IOCGSIGMODE ioctl failed.");exit(-3);}fprintf(stderr, "new mode = %s.\n", signaling[oldmode]);fflush(stdout);}The 8253xmode program uses the ATIS_IOCSSIGMODE ioctl to set the newphysical signaling mode and employs the ATIS_IOCGSIGMODE ioctl to getthe original value and to verify the new mode._Logic Structure of the ASLX Driver_/Data Structure Summary/The key data structures that enable the driver logic are:multichannel server)transmission of data.the transmission of data.SAB_PORT structure are used to track all the sk_buffs that are currentlyin use at each port.the TTY driver access low level routines for either asynchronous TTY,synchronous TTY and callout functionality. The SAB_PORT serves as theprivate data structure associated with each 8253x TTY, which on a givenopen can instantiate itself as a synchronous TTY, an asynchronous TTY oras a call out device.structure. The SAB_PORT serves as the private data structure associatedwith each 8253x network interface.structure. The SAB_PORT serves as the private data structure associatedwith each 8253x character interface.Thus the fundamental driver functionalities, the TTY device, the networkinterface and the character device, share the port structure; and theport structure contains the data is used to arbitrate driverfunctionality access to a given physical port. All the above driverspecific structures are dynamically allocated at driver initialization.They are maintained on lists (in come cases several lists, e.g., global,per board, by interrupt+by board type and per chip lists). The globalport list is used to map minor device numbers sequentially to ports.The per chip port list is used in interrupt processing. Likewise the byinterrupt+by board type board lists are also used in interrupt processing.The use and definition of the tty_struct (TTY and callout drivers),net_device (network drivers) and file_operations (character drivers)structures are found in the Linux.Quick Overview of Standard Linux TTY, Network and Character Devicesand Interaction with the ASLX Driver DesignThe basic design of TTY/callout drivers, network drivers and characterdrivers is specified by the Linux interface.The TTY driver uses the standard circular transmit buffer as found inserial.c, which handles the PC com ports) while received characters passinto a line disciple via the standard flip buffer logic found in serial.cThe network and character driver parts just follow the design describedin /Linux Device Drivers/ by Alessandro Rubini.All the driver functionalities use a circular transmit buffer descriptorring and sk_buffers for receiving and transmitting data. This uniformapproach to transception simplifies the driver logic immensely.There is no use of a transmit done interrupt equivalent (unnecessary fora non-dma design). The driver can be compiled to free up transmittedsk_buffs in the interrupt handler or in write routines. Investigationshows that the driver performs better when transmitted buffers are freedoutside of the interrupt handlers. The sk_* routines are in generalfairly performance costly. As a further optimization, when sk_buffs arefreed in the write routines, if system write gets ahead of thetransmitter, the program logic will try to avoid releasing transmittedbuffers (in the TTY driver) but will try to reuse them if possible.When data is received, chains of receive buffers are passed back to thecharacter device read routine or to a flush-to-line-discipline function(defined in the ASLX driver to override the default flush_to_ldiscroutine defined in tty_io.c) in the case of the TTY driverfunctionalities. The network driver just invokes the /netif_rx()/routine at interrupt level to receive packets. Currently, the networkdriver pre-allocates a receive buffer, but such pre-allocation is notnecessary, and no other driver functionalities make use ofpre-allocation of buffers.From Standard Driver Architectures to the ASLX DriverThe main difficulties to be overcome in the ASLX design fit into twocategories.1. No other Linux driver attempts to combine multiple TTYfunctionalities with network and character driver functionalities.2. Even though the members of this Aurora product line all usebasically the same Siemens/Infineon interface chip, the details ofaccessing this chip differ radically over the product line. The adaptercards use the PLX 9050 PCI bridge chip while the multichannel serversuse the AMCC 5920 PCI bridge chip. In the latter case there is asomewhat complex G-Link hardware protocol used for communication betweenthe host adapter card and the expansion chassis that hosts the extensionboards where the serial interface chips are located. The adapter cardsdiffer in detecting and causing modem signal changes./Creating a Uniform Device Programming Interface/The most painful aspect of of creating a multifunction driver for theAurora hardware is the difference of each Aurora adapter card or unitfrom every other Aurora adapter card or unit.Signals are handled differently on ESCC2 (SAB82532) versus ESCC8(SAB82538) based devices. Interrupt processing is different on ESCC2,ESCC8 and multichannel server devices. The multichannel servers use anAMCC bridge chip while the other devices use a PLX bridge chip.Multichannel servers and all other Aurora adapter cards access deviceregisters completely differently.There are a set of data structures and macros that simplify access tocontrol registers for serial ports and that try to provide uniformity inline control signal handling, which is complex because some signals aredefined in the serial port interface of the 8253X serial interface chipwhile other signals are handled through the general parallel ports ofthe 8253X chip.The bitwise definition of the line control signals differ on theparallel ports of the 82532 and the 82538 based cards (includingmultichannel server units).The macros (courtesy Francois Wautier) below provide a common interfacefor raising and lowering control signals as well as for querying them./** Raise a modem signal y on port x, tmpval must exist! */#define RAISE(xx,y) \{ \unsigned char __tmpval__; \__tmpval__= (xx)->readbyte((xx),(xx)->y.reg);\if((xx)->y.inverted)\__tmpval__ &= ~((xx)->y.mask);\else\__tmpval__ |= (xx)->y.mask;\__tmpval__ |= (xx)->y.cnst;\(xx)->y.val=1;\(xx)->writebyte((xx),(xx)->y.reg,__tmpval__);\}/** Lower a modem signal y on port x, __tmpval__ must exist! */#define LOWER(xx,y) \{\unsigned char __tmpval__; \__tmpval__= (xx)->readbyte((xx),(xx)->y.reg);\if((xx)->y.inverted)\__tmpval__ |= (xx)->y.mask;\else\__tmpval__ &= ~((xx)->y.mask);\__tmpval__ |= (xx)->y.cnst;\(xx)->y.val=0;\(xx)->writebyte((xx),(xx)->y.reg,__tmpval__);\}#define ISON(xx,y) \((xx)->y.inverted !=(((xx)->readbyte((xx),(xx)->y.reg)&(xx)->y.mask)==(xx)->y.mask))The inverted, cnst, and mask fields of the modem signal structure (y)are specific to the type of serial communications controller. Thereadbyte and writebyte functions are specific to the different types ofAurora hardware.To hide the details of accessing control and data registers, theSAB_PORT structure has a fields whose values are the functions to read adevice register, to write a device register,to read a device FIFO andto write a device FIFO (as well as to read and to write short words,functions that could be used in implementing the readfifo and writefiforoutines).Here is readfifo for non-multichannel server hardware./**************************************************************************** aura_readfifo: Function to read the FIFO on a 4X20P, 8X20P or Sunserial*** Parameters :* port: The port being accessed* buf: The address of a buffer where we should put* what we read* nbytes: How many chars to read.** Return value : none** Prerequisite : The port must have been opened** Remark :** Author : fw** Revision : Oct 13 2000, creation***************************************************************************/void aura_readfifo(struct sab_port *port, unsigned char *buf, unsignedint nbytes){int i;unsigned short *wptr = (unsigned short*) buf;int nwords = ((nbytes+1)/2);for(i = 0; i < nwords; i ++){wptr[i] = readw(((unsigned short *)port->regs));}}Here is the readfifo function for multichannel servers.void wmsaura_readfifo(struct sab_port *port, unsigned char *buf,unsigned int nbytes){#ifdef FIFO_DIRECTunsigned short fifo[32/2]; /* this array is word aligned* buf may not be word aligned*/unsigned int nwords;int i;int wcount;unsigned int address;if (nbytes == 0){return;}wcount = ((nbytes + 1) >> 1);/* Read the thing into the local FIFO and copy it out. */address = (unsigned int) port->regs;for(i = 0; i < wcount; ++i){fifo[i] = readw((unsigned short*)(address + CIMCMD_RDFIFOW));}memcpy((unsigned char*) buf, (unsigned char*) &(fifo[0]), (unsignedint) nbytes);#else /* FIFO_DIRECT */unsigned short fifo[32/2];int i;int wcount;SAB_BOARD *bptr;unsigned int channel;if (nbytes == 0){return;}bptr = port->board;wcount = ((nbytes + 1) >> 1);channel = (((unsigned char*) port->regs) - bptr->CIMCMD_REG); /*should be properly shifted *//** Trigger a cache read by writing the nwords - 1 to the* magic place.*/writeb((unsigned char) wcount, bptr->MICCMD_REG + (MICCMD_CACHETRIG +channel));/** Now, read out the contents.*/channel >>= 1;for(i = 0; i < wcount; ++i){fifo[i] = readw((unsigned short*)(bptr->FIFOCACHE_REG + (channel +(i << 1))));}memcpy((unsigned char*) buf, (unsigned char*) &(fifo[0]), (unsignedint) nbytes);#endif /* !FIFO_DIRECT */}Except at initialization time the driver code accesses serialcommunications controller device registers only through fields in theport structure. The details of accessing the different types ofhardware are almost completely hidden from the driver program logic.The only exception is the interrupt handler, which must understand someof the details of the multichannel server. Nevertheless, as is madeclear in the following section, from the standpoint of processinginterrupts there are really only two types of Aurora hardware, thatwhich is ESCC2 based and that which is ESCC8 based. The details of thedifference of the two types of hardware is contained solely within the8253xint.c file which also contains the logic by which an interrupt froma multichannel server can be processed almost exactly like an interruptfrom an 8520P adapter card./Code Structure Summary/The SAB8253X driver code has the following logic components1. a probe/initialization logic,a. bridge initialization/EEPROM parsing logic,b. structure allocation logic,c. interrupt request logic,2. the core logic, which handles all the non-interrupt processingof the driver functionality,a. per port startup/shutdown logic,b. driver arbitration logic,c. skbuffer management logic,3. the interrupt handler logic,a. common interrupt port polling logic,b. skbuffer management logic,4. the termination/driver unload logic,a. interrupt shutdown logic,b. device shutdown logic,c. structure deallocation logic./Probe/Initialization Logic/The probe/initialization logic sets up the CRC structures for thenetwork driver and then the tty_struct for the synchronous andasynchronous TTY drivers. Initializing the tty_struct involves settingup pointers to the standard functions that the Linux TTY driver invokesas well as some standard data and the /proc/tty/driver/auraserialfunction and data.The probe/initialization logic identifies all the multiport serialadapters, compact PCI adapters and multiserver adapters in the systemand puts them on a list of board structures. After identifying all theboards, initializing the bridge chips and analyzing (possibly rewriting)the serial EEPROM, the logic sets up all the chips on the boards and allthe ports on the chips.All chips are linked together in a list. All ports are linked togetherin a list. The port list is linked together so that minor device Ncorresponds to the Nth element of the port list. A board structurepoints to a list of chips on the board as well as a list of all ports onthe board. Likewise a chip structure points to a list of all ports onthe chip. Chip structures point back to the board structure associatedwith the board on which the chip resides. Likewise port structurespoint back to the board and to the chip on which they reside. Thisinterconnected list structure facilitates access to one type ofstructure when a routine has been passed a pointer to another type ofstructure. All these structures are dynamically allocated via/kmalloc()/. When the driver is unloaded, memory is released by walkingthe list.After setting up the board, chip and port structures, initialization ofthe tty_struct is completed at this point because the maximum possiblenumber of serial TTYs is now known. The, the standard network devicestructure that is associated with each port is allocated andinitialized. The network devices point to the associated portstructure. The network devices are chained via a pointer in theassociated port structure. This chaining facilitates release of thenetwork device structure memory when the driver is unloaded.Each network device is registered after its network device structure isinitialized. Network device initialization invokes the network deviceinitialization, which installs the standard network functions in thenetwork device structure and which sets up the sk_buff transmit ring aswell as the receive sk_buf. Unlike the Solaris driver, when a completeframe is received with no errors, it is immediately passed into thenetwork layer. Thus, there is no receive ring of sk_buffs as one mightfind in the driver of a device that could carry out chained DMA (e.g. aDEC Tulip or an Hitachi SCA).All sk_buffs associated with a network device that are currently in useby the network driver are linked together in an sk_buff list (viz corelogic). This list facilitates release of all sk_buff associated with anetwork device when that network device is shut down. This list may bea problem if it becomes possible for a single sk_buff to be usedsimultaneously by multiple network devices. Currently, sk_buff headersare not shared among network devices although sk_buff data can beshared. Thus, for the nonce this logic works correctly.After completing of network device initialization, theprobe/initialization logic register the character driver. Next, theprobe/initialization creates three lists of boards for each interrupt(0-31). One list contains 82532 based boards at that interrupt level.The next list contains 82538 based adapter cards at that interruptlevel. The third list contains all multichannel server units at thatinterrupt level.Next the probe/initialization logic checks the lists associated witheach interrupt level. If either list is non-null, theprobe/initialization logic requests that the general interrupt handlerbe installed at this interrupt level and then it turns on PLX9050 orAMCC5920 interrupts (as needed) into the Linux host for each cardassociated with that interrupt level. Thus, the interrupt handler pollsall boards/ports at a given interrupt level when the interrupt occurs.This approach is more efficient that installing one interrupt per boardand avoids some internal Linux limits on the number of interrupthandlers that can be installed per interrupt.At this point probe/intialization is complete and any serial port may beused for asynchronous TTY, synchronous TTY, call out, network deviceservice or synchronous character device service./Core Logic/The main problem of the core logic is arbitration of access to theserial port, when and by which part of the driver a port is started andwhen the port is shut down.The asynchronous callout, asynchronous TTY, synchronous TTY devices,synchronous character device and network device follow the following rules.1. If there is an established point-to-pointconnection, only the current process or process group that owns theserial port may open the device.2. If the asynchronous callout is open, opens of asingle TTY or character device type block until the connection completes.3. Network device opens never block, and the networklayer opens a network device only once.4. If a connection that belongs to a TTY device orcharacter device hangs up, eventually all opens of that device will close.5. Hangup of on a network device does not guarantee aclose of the device, but a flag bit is set that permits a call out opento restore the connection (note that a one-to-one map of TTY devices,callout devices, character devices and network devices is implied.)The network device open and block_til_ready* functions invoked from TTYopens enforce this arbitration.The logic works as follows:* The cua device associated with a port may only be opened one.* If a TTY or character device is open, the cua device may not beopened.* If the cua device is open, the network device cannot be opened andthe TTY or character device block (multiple opens from the sameprocess or process group are allowed).* If the network device is open, the TTY or character devices cannotbe opened while the cua device blocks.* A hangup sends an interrupt to the TTY or character device whilethe network device shuts down its port and a (network blocked) cuadevice proceeds.* On the close of the cua device, blocked TTY and character devicesproceed and the network device restarts its port.When an open completes, the transmit_chars, receive_chars, check_statusfunctions and associated data fields are set in the port structure,these are used in the interrupt handler that never changes until thedriver is unloaded.When a port is not in use, the asynchronous version of these fields andfunctions are set in the port structure. Some values must be present,and the asynchronous ones are probably the least dangerous.Besides the arbitration problem, the core logic addresses buffermanagement for the network and character drivers.The network layer passes a transmit sk_buff to the network driver. Thebuffer is inserted in the transmit ring or sets a transmit congestionflag. In either case, transmit is initialized if not already inprogress. If the sk_buff is successfully inserted, it is also linkedinto the driver sk_buff list which tracks all sk_buffs used by thenetwork driver. On network device close all sk_buffs on the per portsk_buff list are released. This sk_buff list mechanism is used to avoidmemory leaks.The TTY and character drivers packetize write data in an sk_buff (eachwrite creates a single sk_buff) and inserts it in the transmit ring ifpossible or blocks (returns a failure in the case of TTY drivers).Transmit sk_buffs are also linked into the driver sk_buff list. Thislist is a convenience to assist deallocation during driver close.The TTY and character drivers also maintains a receive sk_buff list.When the user application invokes the read system call, the data fromone sk_buff is passed up to the user application (or an error if theread were not invoked with sufficient buffer space).The character driver can be configured via IOCTL to send asynchronoususer notification when the transmit ring empties (a design choice forthe standard driver fasync functionality). This character driver designemulates the Solaris putmsg/getmsg interface as far as possible andmakes it possible to implement in a user application protocols likeLAPB, which require that low priority packets only be queued to thedriver when the driver currently has no frames in the process oftransmission or queued for transmission.On character device close all sk_buffs on the per port sk_buff and perport receive sk_buff list are released. This double sk_buff listmechanism is used to avoid memory leaks./Interrupt Handler Logic/The following code comprises the combined interrupt and board/portpolling logic. The actual handler is /sab8253x_interrupt()/. Itwalks through the 82532, adapter 82538 adapter and multichannelserver lists at the interrupt level that is being processed andinvokes inline /sab82532_interrupt()/ and /sab82538_interrupt()/.Note that it temporary modifies multichannel server lists so thatit like an 8520P adapter card to the /sab8253x_interrupt()/. Thislogic works because the granularity of the PCI interruptassociated with the Aurora hardware is basically either a list ofESCC2s (the 4520P and 4520CP adapter cards) or a single ESCC8 (an8520P or one ESCC8 on a multichannel server remote card after theinterrupt status has been queried).The current interrupt sources are identified and processed. Ifany characters are available to be received, they are received onefifo at a time into the TTY flip buffer (a structure that issupposed to decrease TTY latency) or into an sk_buf (somethinglike a Solaris mblk/dblk structure( in the case of the network orcharacter driver. Then if there are characters in the TTYcircular transmit buffer or in the network or character driversk_buff, they are loaded into the transmit fifo, one fifo at atime. Finally, modem control status is checked. If a hang up isdetected, the serial driver hang up is scheduled at kernelscheduler priority (a slight difference from the standard serialdriver which processes hang-ups at interrupt level) in order toavoid certain race conditions in the TTY driver that can occur onfast machines with many serial ports. /do_serial_hangup()/invokes the TTY hangup routines in the case of TTY driver. For anetwork connection in the event of a line disconnection, carrieris marked as off on the interface and the blocked cua device iswoken so that it can proceed after the network port has shut down.static void __inline__ sab82532_interrupt(int irq, void *dev_id, structpt_regs *regs){struct sab_port *port;struct sab_chip *chip=NULL;struct sab_board *bptr = (struct sab_board*) dev_id;union sab8253x_irq_status status;unsigned char gis;for(chip = bptr->board_chipbase; chip != NULL; chip =chip->next_by_board){port= chip->c_portbase;gis = READB(port, gis); /* Global! */status.stat=0;/* Since the PORT interrupt are global,* we do check all the ports for this chip*//* A 2 ports chip */if(!(gis & SAB82532_GIS_MASK)){continue; /* no interrupt on this chip */}if (gis & SAB82532_GIS_ISA0){status.sreg.isr0 = READB(port, isr0);}else{status.sreg.isr0 = 0;}if (gis & SAB82532_GIS_ISA1){status.sreg.isr1 = READB(port, isr1);}else{status.sreg.isr1 = 0;}if (gis & SAB82532_GIS_PI){status.sreg.pis = READB(port, pis);}else{status.sreg.pis = 0;}if (status.stat){if (status.images[ISR0_IDX] & port->receive_test){(*port->receive_chars)(port, &status); /* when the fifois full *//* no time to schedulethread*/}if ((status.images[port->dcd.irq] & port->dcd.irqmask) ||(status.images[port->cts.irq] & port->cts.irqmask) ||(status.images[port->dsr.irq] & port->dsr.irqmask) ||(status.images[ISR1_IDX] & port->check_status_test)){(*port->check_status)(port, &status); /* this stuff shouldbe *//* be moveable to scheduler *//* thread*/}if (status.images[ISR1_IDX] & port->transmit_test){(*port->transmit_chars)(port, &status); /* needs to bemoved to task */}}/* Get to next port on chip */port = port->next_by_chip;/* Port B */if (gis & SAB82532_GIS_ISB0){status.images[ISR0_IDX] = READB(port, isr0);}else{status.images[ISR0_IDX] = 0;}if (gis & SAB82532_GIS_ISB1){status.images[ISR1_IDX] = READB(port,isr1);}else{status.images[ISR1_IDX] = 0;}/* DO NOT SET PIS. IT was reset! */if (status.stat){if (status.images[ISR0_IDX] & port->receive_test){(*port->receive_chars)(port, &status);}if ((status.images[port->dcd.irq] & port->dcd.irqmask) ||(status.images[port->cts.irq] & port->cts.irqmask) ||(status.images[port->dsr.irq] & port->dsr.irqmask) ||(status.images[ISR1_IDX] & port->check_status_test)){(*port->check_status)(port, &status);}if (status.images[ISR1_IDX] & port->transmit_test){(*port->transmit_chars)(port, &status);}}}}static void __inline__ sab82538_interrupt(int irq, void *dev_id, structpt_regs *regs){struct sab_port *port;struct sab_chip *chip=NULL;struct sab_board *bptr = (struct sab_board*) dev_id;union sab8253x_irq_status status;unsigned char gis,i;chip = bptr->board_chipbase;port= chip->c_portbase;gis = READB(port, gis); /* Global! */status.stat=0;/* Since the PORT interrupt are global,* we do check all the ports for this chip*//* 8 ports chip */if(!(gis & SAB82538_GIS_MASK)){return;}if(gis & SAB82538_GIS_CII){ /* A port interrupt! *//* Get the port */int portindex;portindex = (gis & SAB82538_GIS_CHNL_MASK);port = chip->c_portbase;while(portindex){port = port->next_by_chip;--portindex;}status.images[ISR0_IDX] = READB(port,isr0);status.images[ISR1_IDX] = READB(port,isr1);if (gis & SAB82538_GIS_PIC){status.images[PIS_IDX] =(*port->readbyte)(port,((unsigned char *)(port->regs)) +SAB82538_REG_PIS_C);}else{status.images[PIS_IDX] = 0;}if (status.stat){if (status.images[ISR0_IDX] & port->receive_test){(*port->receive_chars)(port, &status);}if ((status.images[port->dcd.irq] & port->dcd.irqmask) ||(status.images[port->cts.irq] & port->cts.irqmask) ||(status.images[port->dsr.irq] & port->dsr.irqmask) ||(status.images[ISR1_IDX] & port->check_status_test)){(*port->check_status)(port, &status);}/** We know that with 8 ports chip, the bit corresponding tochannel* number is used in the parallel port... So we clear it* Not too elegant!*/status.images[PIS_IDX] &= ~(1 << (gis&SAB82538_GIS_CHNL_MASK));if (status.images[ISR1_IDX] & port->transmit_test){(*port->transmit_chars)(port, &status);}}}/** Now we handle the "channel interrupt" case. The chip manual for the* 8 ports chip states that "channel" and "port" interrupt are set* independently so we still must check the parrallel port** We should probably redesign the whole thing to be less AD HOC that we* are now... We know that port C is used for DSR so we only checkthat one.* PIS for port C was already recorded in status.images[PIS_IDX], so we* check the ports that are set*/if (status.images[PIS_IDX]){for(i=0, port = chip->c_portbase;i < chip->c_nports;i++, port=port->next_by_chip){if(status.images[PIS_IDX] & (0x1 << i)){ /* Match *//* Checking DSR */if(port->dsr.inverted){port->dsr.val = (((*port->readbyte)(port, port->dsr.reg) &port->dsr.mask) ? 0 : 1);}else{port->dsr.val = ((*port->readbyte)(port, port->dsr.reg) &port->dsr.mask);}port->icount.dsr++;wake_up_interruptible(&port->delta_msr_wait);}}}}/** This is the serial driver's generic interrupt routine*/void sab8253x_interrupt(int irq, void *dev_id, struct pt_regs *regs){extern SAB_BOARD *AuraBoardESCC2IrqRoot[];extern SAB_BOARD *AuraBoardESCC8IrqRoot[];extern SAB_BOARD *AuraBoardMCSIrqRoot[];AURA_CIM *cim;SAB_CHIP *chip;SAB_PORT *port;register SAB_BOARD *boardptr;register unsigned char intrmask;unsigned char stat;SAB_CHIP *save_chiplist;SAB_PORT *save_portlist;if((irq < 0) || (irq >= NUMINTS)){printk(KERN_ALERT "sab8253x: bad interrupt value %i.\n", irq);return;}/* walk through all the cards on the interrupt that occurred. */for(boardptr = AuraBoardESCC2IrqRoot[irq]; boardptr != NULL; boardptr= boardptr->next_on_interrupt){sab82532_interrupt(irq, boardptr, regs);}for(boardptr = AuraBoardESCC8IrqRoot[irq]; boardptr != NULL; boardptr= boardptr->next_on_interrupt){sab82538_interrupt(irq, boardptr, regs);}for(boardptr = AuraBoardMCSIrqRoot[irq]; boardptr != NULL; boardptr =boardptr->next_on_interrupt){while(1){writeb(0, (unsigned char*)(boardptr->CIMCMD_REG +CIMCMD_WRINTDIS)); /* prevent EBs from raising* any more ints through the* host card */stat = ~(unsigned char) /* active low !!!!! */readw((unsigned short*)(((unsigned char*)boardptr->CIMCMD_REG) +CIMCMD_RDINT)); /* read out the ints *//* write to the MIC csr to reset the PCIinterrupt */writeb(0, (unsigned char*)(boardptr->MICCMD_REG +MICCMD_MICCSR)); /* reset the interrupt generation* hardware on the host card*//* now, write to the CIM interrupt enato re-enable interrupt generation */writeb(0, (unsigned char*)(boardptr->CIMCMD_REG +CIMCMD_WRINTENA)); /* allow EBs to request ints* through the host card */if(!stat){break;}cim = boardptr->b_cimbase; /* cims in reverse order */for(intrmask = boardptr->b_intrmask;intrmask != 0;intrmask <<= 2, stat <<=2){if(cim == NULL){break; /* no cim no ports */}if((intrmask & 0xc0) == 0) /* means no cim for these ints */{ /* cim not on list do not go to next */continue;}save_portlist = boardptr->board_portbase;save_chiplist = boardptr->board_chipbase;/* the goal is temporarily to make thestructures* look like 8x20 structures -- thus ifI find* a bug related to escc8s I need fix it in* only one place. */switch(stat & 0xc0) /* possible ints */{default:break;case 0x80: /* esccB */chip = cim->ci_chipbase;if(!chip){printk(KERN_ALERT "aura mcs: missing cim.\n");break;}chip = chip->next_by_cim;if(!chip){printk(KERN_ALERT "aura mcs: missing 2nd cim.\n");break;}port = chip->c_portbase;boardptr->board_portbase = port;boardptr->board_chipbase = chip;sab82538_interrupt(irq, boardptr, regs);break;case 0x40: /* esccA */chip = cim->ci_chipbase;if(!chip){printk(KERN_ALERT "aura mcs: missing cim.\n");break;}port = chip->c_portbase;boardptr->board_portbase = port;boardptr->board_chipbase = chip;sab82538_interrupt(irq, boardptr, regs);break;case 0xc0: /* esccB and esccA */chip = cim->ci_chipbase;if(!chip){printk(KERN_ALERT "aura mcs: missing cim.\n");break;}port = chip->c_portbase;boardptr->board_portbase = port;boardptr->board_chipbase = chip;sab82538_interrupt(irq, boardptr, regs);chip = cim->ci_chipbase;if(!chip){printk(KERN_ALERT "aura mcs: missing cim.\n");break;}chip = chip->next_by_cim;if(!chip){printk(KERN_ALERT "aura mcs: missing 2nd cim.\n");break;}port = chip->c_portbase;boardptr->board_portbase = port;boardptr->board_chipbase = chip;sab82538_interrupt(irq, boardptr, regs);break;}boardptr->board_portbase = save_portlist;boardptr->board_chipbase = save_chiplist;cim = cim->next_by_mcs;}}}}Note that if there is no transmit in process when thewrite/hard_start_transmit routine is invoked, transmit is intitiatedfrom the core logic as if it took place in the interrupt handler. Thisapproach differs from the ASE driver, which used to force an interrupt,and then start the transmission. Francois Wautier may have fixed thatlogic to start the transmission either in the STREAMS put or serviceroutine./Driver Unload Logic/The driver unload logic inverts the probe intialization logic.At this point no ports should be in use and in fact every port shouldhave been put in the 8253x powered down state when each port underwentits last close (or hangup which can actually complete after a close).The tty driver is cleaned up, some dynamic TTY data structures aredeallocated, the bottom half associated with this driver is removed andall TTY ports are deregistered.The PLX or AMCC interrupts to the Linux host are disabled on each board,and all interrupt handlers are freed.Next all board and chip structures are deallocated (including physicalmemory to virtual memory mappings associated with PLX9050 or AMCC 5920and chip registers). Then all network device structures aredeallocated. (Note that all lingering sk_buffs were freed during theclose of the network device, which must have completed before the unloadof the driver module.) And finally the port structures aredeallocated. In deallocating the port structures, network drivertransmit rings and the receive sk_buff descriptor are deallocated ifthey are present. (They were only allocated if the port had ever beenused for a network device.) At this point the driver has beengracefully unloaded./* cleanup module/free up virtual memory *//* space*/void cleanup_module(void){SAB_BOARD *boardptr;SAB_CHIP *chipptr;SAB_PORT *portptr;int intr_val;extern void sab8253x_cleanup_ttydriver(void);printk(KERN_ALERT "auraXX50n: unloading AURAXX50 driver.\n");sab8253x_cleanup_ttydriver(); /* clean up tty *//* unallocate and turn off ints */for(intr_val = 0; intr_val < NUMINTS; ++intr_val){if((AuraBoardESCC2IrqRoot[intr_val] != NULL) ||(AuraBoardESCC8IrqRoot[intr_val] != NULL)){for(boardptr = AuraBoardESCC2IrqRoot[intr_val]; boardptr !=NULL; boardptr = boardptr->next_on_interrupt){writel(PLX_INT_OFF, &(boardptr->b_bridge->intr));}for(boardptr = AuraBoardESCC8IrqRoot[intr_val]; boardptr !=NULL; boardptr = boardptr->next_on_interrupt){writel(PLX_INT_OFF, &(boardptr->b_bridge->intr));}free_irq(intr_val, &AuraBoardESCC2IrqRoot[intr_val]); /* freeup board int* notethat if two boards* sharean int, two int*handlers were registered**/}}/* disable chips and free board memory*/while(AuraBoardRoot){boardptr = AuraBoardRoot;for(chipptr = boardptr->board_chipbase; chipptr != NULL; chipptr =chipptr->next_by_board){(*chipptr->int_disable)(chipptr); /* make sure no ints cancome int */}AuraBoardRoot = boardptr->nextboard;if(boardptr->virtbaseaddress0){DEBUGPRINT((KERN_ALERT"auraXX50n: unmapping virtual address %p.\n",(void*)boardptr->virtbaseaddress0));iounmap((void*)boardptr->virtbaseaddress0);boardptr->virtbaseaddress0 = 0;}if(boardptr->virtbaseaddress2){DEBUGPRINT((KERN_ALERT"auraXX50n: unmapping virtual address %p.\n",(void*)boardptr->virtbaseaddress2));iounmap((void*)boardptr->virtbaseaddress2);boardptr->virtbaseaddress2 = 0;}kfree(boardptr);}while(AuraChipRoot) /* free chip memory */{chipptr = AuraChipRoot;AuraChipRoot = chipptr->next;kfree(chipptr);}while(Sab8253xRoot) /* free up network stuff */{SAB_PORT *priv;priv = (SAB_PORT *)Sab8253xRoot->priv;unregister_netdev(Sab8253xRoot);#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)kfree(Sab8253xRoot.name);#endifkfree(Sab8253xRoot);Sab8253xRoot = priv->next_dev;}while(AuraPortRoot) /* free up port memory */{portptr = AuraPortRoot;AuraPortRoot = portptr->next;if(portptr->dcontrol2.receive){kfree(portptr->dcontrol2.receive);}if(portptr->dcontrol2.transmit){kfree(portptr->dcontrol2.transmit);}kfree(portptr);}}_Design Summary_The 10 key data structures are shared by all the logic of the system.The logic enforces both exclusive and cooperative access to the physicalhardware on the basis of certain rules established by the core logic atdevice/port open time. While the driver is mostly self-configuring, thedata structure sharing simplifies the user interface because an ioctl toone driver functionality (e.g., the TTY functionality) configures therest of the driver functionality (e.g., the network and character devicefunctionality). In other words, a single serial port acts virtually asthree arbitrated devices: a TTY device, which may be asynchronous,synchronous or call out, a network device or a character device. Yet,except at the time of initialization, time of driver unload and veryearly in interrupt processing all hardware details are concealed and thedriver logic is applied to an abstract, simplified port entity. Thus,the user application interfaces to three abstract virtual devices, whichhave a single configuration interface (otherwise it might be possible tohave an inconsistent configuration) and not to a complex real singleport in the context of an equally complex adapter card or unit. Thissimplification makes it possible to provide a high degree of serialfunctionality across the family of Aurora synchronous/asynchronous PCIhardware through a straightforward uniform application interface.
Go to most recent revision | Compare with Previous | Blame | View Log
