BSP and Device Driver Development Guide: On-Line Applications Research Corporation
BSP and Device Driver Development Guide: On-Line Applications Research Corporation
BSP and Device Driver Development Guide: On-Line Applications Research Corporation
COPYRIGHT c 1988 - 2013. On-Line Applications Research Corporation (OAR). The authors have used their best efforts in preparing this material. These efforts include the development, research, and testing of the theories and programs to determine their effectiveness. No warranty of any kind, expressed or implied, with regard to the software or the material contained in this document is provided. No liability arising out of the application or use of any product described in this document is assumed. The authors reserve the right to revise this material and to make changes from time to time in the content hereof without obligation to notify anyone of such revision or changes. The RTEMS Project is hosted at http: / / www . rtems . com. Any inquiries concerning RTEMS, its related support components, its documentation, or any custom services for RTEMS should be directed to the contacts listed on that site. A current list of RTEMS Support Providers is at http://www.rtems.com/oarsupport.
Table of Contents
1 2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Target Dependent Files . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1 2.2 2.3 2.4 2.5 2.6 2.7 CPU Dependent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Board Dependent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Peripheral Dependent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Questions to Ask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CPU Dependent Executive Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CPU Dependent Support Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Board Support Package Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 4 4 5 5
Makefiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1 3.2 Makefiles Used During The BSP Building Process . . . . . . . . . . . . . . 8 Creating a New BSP Make Customization File . . . . . . . . . . . . . . . . . . 9
Linker Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.1 4.2 4.3 4.4 4.5 What is a "linkcmds" file? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Program Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Image of an Executable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example Linker Command Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Initialized Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 12 12 18
ii
Initialization Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.1 7.2 7.3 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Required Global Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Board Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.1 Start Code - Assembly Language Initialization . . . . . . . . . . . . 7.3.2 boot card() - Boot the Card . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.3 bsp start() - BSP Specific Initialization . . . . . . . . . . . . . . . . . . . 7.3.4 RTEMS Pretasking Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.5 RTEMS Predriver Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.6 Device Driver Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.7 RTEMS Postdriver Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4 The Interrupt Vector Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4.1 Interrupt Vector Table on the gen68340 BSP . . . . . . . . . . . . . . 7.5 Chip Select Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6 Integrated Processor Registers Initialization . . . . . . . . . . . . . . . . . . . 7.7 Data Section Recopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.8 The RTEMS Configuration Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 27 28 29 29 30 31 31 31 31 32 32 32 33 33 33
Console Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.1 8.2 8.3 8.4 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Termios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Driver Functioning Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Serial Driver Functioning Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.1 Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.2 Termios and Polled IO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.3 Termios and Interrupt Driven IO . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.4 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.5 Opening a serial device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.6 Closing a Serial Device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.4.7 Reading Characters from a Serial Device . . . . . . . . . . . . . . . . . . 8.4.8 Writing Characters to a Serial Device . . . . . . . . . . . . . . . . . . . . . 8.4.9 Changing Serial Line Parameters . . . . . . . . . . . . . . . . . . . . . . . . . 35 35 36 36 37 39 40 42 43 45 46 46 47
Clock Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
9.1 9.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clock Driver Global Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.1 Major and Minor Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2.2 Ticks Counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.4 System shutdown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.5 Clock Interrupt Subroutine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.6 IO Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 49 49 49 49 50 50 50
iii
10
Timer Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
51 51 51 51 52
10.1 Benchmark Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.1 benchmark timer initialize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.2 Read timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.3 benchmark timer disable subtracting average overhead ............................................................ 10.2 gen68340 UART FIFO Full Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
12
ATA Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
59 59 59 60 60 62
12.1 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.3 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4 ATA Driver Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4.1 ATA Driver Main Internal Data Structures . . . . . . . . . . . . . . 12.4.2 Brief ATA Driver Core Overview . . . . . . . . . . . . . . . . . . . . . . . .
13
14
iv
15
Networking Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Learn about the network device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Understand the network scheduling conventions . . . . . . . . . . . . . . Network Driver Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Attach Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Start Function. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Initialization Function. . . . . . . . . . . . . . . . . . . . . . . Write the Driver Transmit Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Receive Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Interrupt Handler . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver IOCTL Function . . . . . . . . . . . . . . . . . . . . . . . . . . . Write the Driver Statistic-Printing Function . . . . . . . . . . . . . . . . . 71 71 71 72 73 74 74 74 75 75 75 75
15.1 15.2 15.3 15.4 15.5 15.6 15.7 15.8 15.9 15.10 15.11 15.12
16
16.1 Shared Memory Configuration Table . . . . . . . . . . . . . . . . . . . . . . . . . 16.2 Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.1 Convert Address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.2 Get Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.3 Locking Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.3.1 Initializing a Shared Lock . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.3.2 Acquiring a Shared Lock . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.3.3 Releasing a Shared Lock . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.3 Installing the MPCI ISR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
17.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.2 Driver Function Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.2.1 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.2.2 Opening the Frame Buffer Device . . . . . . . . . . . . . . . . . . . . . . . 17.2.3 Closing the Frame Buffer Device . . . . . . . . . . . . . . . . . . . . . . . . 17.2.4 Reading from the Frame Buffer Device . . . . . . . . . . . . . . . . . . 17.2.5 Writing to the Frame Buffer Device . . . . . . . . . . . . . . . . . . . . . 17.2.6 Frame Buffer IO Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
Analog Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Major and Minor Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Analog Driver Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Initialize an Analog Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Open a Particular Analog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Close a Particular Analog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Read from a Particular Analog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write to a Particular Analog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reset DACs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reinitialize DACS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Get Last Written Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 87 88 88 88 88 88 89 89 89
18.1 18.2 18.3 18.4 18.5 18.6 18.7 18.8 18.9 18.10
19
Discrete Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Major and Minor Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discrete I/O Driver Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . Initialize a Discrete I/O Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Open a Particular Discrete Bitfield . . . . . . . . . . . . . . . . . . . . . . . . . . . Close a Particular Discrete Bitfield . . . . . . . . . . . . . . . . . . . . . . . . . . . Read from a Particular Discrete Bitfield . . . . . . . . . . . . . . . . . . . . . . Write to a Particular Discrete Bitfield . . . . . . . . . . . . . . . . . . . . . . . . Disable Discrete Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Enable Discrete Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reinitialize Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Get Last Written Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 91 92 92 92 92 92 93 93 93 93
19.1 19.2 19.3 19.4 19.5 19.6 19.7 19.8 19.9 19.10 19.11
Chapter 1: Introduction
1 Introduction
Before reading this documentation, it is strongly advised to read the RTEMS Development Environment Guide to get acquainted with the RTEMS directory structure. This document describes how to do a RTEMS Board Support Package, i.e. how to port RTEMS on a new target board. Discussions are provided for the following topics: RTEMS Board Support Package Organization Makefiles and the Linker Command Script Board Initialization Sequence Device Drivers Including: Console Driver Clock Driver Timer Driver Real-Time Clock Driver Non-Volatile Memory Driver Networking Driver Shared Memory Support Driver Analog Driver Discrete Driver
The original version of this manual was written by Geoffroy Montel <g montel@yahoo.com>. When he started development of the gen68340 BSP, this manual did not exist. He wrote the initial version of this manual as the result of his experiences. At that time, this document was viewed internally as the most important "missing manual" in the RTEMS documentation set. The gen68340 BSP is a good example of the life of an RTEMS BSP. It is based upon a part not recommended for new designs and none of the core RTEMS Project team members have one of these boards. Thus we are unlikely to perform major updates on this BSP. So as long as it compiles and links all tests, it will be available. The RTEMS Project team members are always trying to identify common code across BSPs and refactoring the code into shared routines. As part of this effort, the we will enhance the common BSP Framework. Not surprisingly, not every BSP takes advantage of every feature in the framework. The gen68340 does not take advantage of as many features as the ERC32 BSP does. So in many ways, the ERC32 is a better example BSP at this point. But even the ERC32 BSP does not include examples of every driver template and framework available to the BSP author. So in this guide we will try to point out good examples from other BSPs. Our goal is for you to be able to reuse as much code as possible and write as little board specific code as possible.
Within each CPU dependent directory inside the executive proper is a file named CPU.h which contains information about each of the supported CPU models within that family.
There may be other directories in the BSP tree and the name should be indicative of the functionality of the code within that directory. The build order of the BSP is determined by the Makefile structure. This structure is discussed in more detail in the Chapter 3 [Makefiles], page 7 chapter. NOTE: This manual refers to the gen68340 BSP for numerous concrete examples. You should have a copy of the gen68340 BSP available while reading this piece of documentation. This BSP is located in the following directory: c/src/lib/libbsp/m68k/gen68340 Later in this document, the $BSP340 ROOT label will be used to refer to this directory.
Chapter 3: Makefiles
3 Makefiles
This chapter discusses the Makefiles associated with a BSP. It does not describe the process of configuring, building, and installing RTEMS. This chapter will not provide detailed information about this process. Nonetheless, it is important to remember that the general process consists of four phases as shown here:
During the bootstrap phase, you are using the configure.ac and Makefile.am files as input to GNU autoconf and automake to generate a variety of files. This is done by running the bootstrap script found at the top of the RTEMS source tree. During the configure phase, a number of files are generated. These generated files are tailored for the specific host/target combination by the configure script. This set of files includes the Makefiles used to actually compile and install RTEMS. During the build phase, the source files are compiled into object files and libraries are built. During the install phase, the libraries, header files, and other support files are copied to the BSP specific installation point. After installation is successfully completed, the files generated by the configure and build phases may be removed.
When adding new include files, you will be adding to the set of include_HEADERS. When you finish editing the Makefile.am file, do not forget to run bootstrap -p to regenerate the preinstall.am. The Makefile.am also specifies which source files to build. By convention, logical components within the BSP each assign their source files to a unique variable. These variables which define the source files are collected into a single variable which instructs the GNU autotools that we are building libbsp.a. This fragment from the SPARC/ERC32 BSP shows how the startup related, miscellaneous support code, and the console device driver source is managed in the Makefile.am. startup_SOURCES = ../../sparc/shared/bspclean.c ../../shared/bsplibc.c \
Chapter 3: Makefiles
../../shared/bsppredriverhook.c \ ../../shared/bsppost.c ../../sparc/shared/bspstart.c \ ../../shared/bootcard.c ../../shared/sbrk.c startup/setvec.c \ startup/spurious.c startup/erc32mec.c startup/boardinit.S clock_SOURCES = clock/ckinit.c ... noinst_LIBRARIES = libbsp.a libbsp_a_SOURCES = $(startup_SOURCES) $(console_SOURCES) ... When adding new files to an existing directory, do not forget to add the new files to the list of files to be built in the corresponding XXX_SOURCES variable in the Makefile.am and run bootstrap. Some BSPs use code that is built in libcpu. If you BSP does this, then you will need to make sure the objects are pulled into your BSP library. The following from the SPARC/ERC32 BSP pulls in the cache, register window management and system call support code from the directory corresponding to its RTEMS_CPU model. libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/cache.rel \ ../../../libcpu/@RTEMS_CPU@/reg_win.rel \ ../../../libcpu/@RTEMS_CPU@/syscall.rel NOTE: The Makefile.am files are ONLY processed by bootstrap and the resulting Makefile.in files are only processed during the configure process of a RTEMS build. Therefore, when developing a BSP and adding a new file to a Makefile.am, the already generated Makefile will not automatically include the new references unless you configured RTEMS with the --enable-maintainer-mode option. Otherwise, the new file not being be taken into account!
10
RTEMS_CPU=m68k RTEMS_CPU_MODEL=m68340 include $(RTEMS_ROOT)/make/custom/default.cfg # This is the actual bsp directory used during the build process. RTEMS_BSP_FAMILY=gen68340 # This contains the compiler options necessary to select the CPU model # and (hopefully) optimize for it. CPU_CFLAGS = -mcpu=cpu32 # optimize flag: typically -O2 CFLAGS_OPTIMIZE_V = -O2 -g -fomit-frame-pointer The make customization files have generally grown simpler and simpler with each RTEMS release. Beginning in the 4.9 release series, the rules for linking an RTEMS application are shared by all BSPs. Only BSPs which need to perform a transformation from linked ELF file to a downloadable format have any additional actions for program link time. In 4.8 and older, every BSP specified the "make executable" or make-exe rule and duplicated the same actions. It is generally easier to copy a make/custom file from a BSP similar to the one being developed.
11
4 Linker Script
4.1 What is a "linkcmds" file?
The linkcmds file is a script which is passed to the linker at linking time. This file describes the memory configuration of the board as needed to link the program. Specifically it specifies where the code and data for the application will reside in memory. The format of the linker script is defined by the GNU Loader ld which is included as a component of the GNU Binary Utilities. If you are using GNU/Linux, then you probably have the documentation installed already and are using these same tools configured for native use. Please visit the Binutils project http://sourceware.org/binutils/ if you need more information.
12
NOTE: Many programs and support libraries unknowingly assume that the .bss section and, possibly, the application heap are initialized to zero at program start. This is not required by the ISO/ANSI C Standard but is such a common requirement that most BSPs do this. That brings us up to the notion of the image of an executable: it consists of the set of the sections that together constitute the application.
13
* NOTE: The default may be overridden by passing an argument to ld. */ RamSize = DEFINED(RamSize) ? RamSize : 4M; /* * * * * * * * * * * * * * */
Set the amount of RAM to be used for the application heap. Objects allocated using malloc() come from this area. Having a tight heap size is somewhat difficult and multiple attempts to squeeze it may be needed reducing memory usage is important. If all objects are allocated from the heap at system initialization time, this eases the sizing of the application heap. NOTE 1: The default may be overridden by passing an argument to ld. NOTE 2: The TCP/IP stack requires additional memory in the Heap. NOTE 3: The GNAT/RTEMS run-time requires additional memory in the Heap.
Set the size of the starting stack used during BSP initialization until first task switch. After that point, task stacks allocated by RTEMS are used. NOTE: The default may be overridden by passing an argument to ld.
Starting addresses and length of RAM and ROM. The addresses must be valid addresses on the board. The Chip Selects should be initialized such that the code addresses are valid.
14
The following defines the order in which the sections should go. It also defines a number of variables which can be used by the application program. NOTE: Each variable appears with 1 or 2 leading underscores to ensure that the variable is accessible from C code with a single underscore. Some object formats automatically add a leading underscore to all C global symbols.
SECTIONS { /* * Make the RomBase variable available to the application. */ _RamSize = RamSize; __RamSize = RamSize; /* * Boot PROM */
rom : { _RomBase = .; __RomBase = .; } >rom /* * Dynamic RAM - set the RamBase variable to the start of the RAM. */ ram : { _RamBase = .; __RamBase = .; } >ram /* *
15
*/ .text : { /* * Create a symbol for each object (.o). */ CREATE_OBJECT_SYMBOLS /* * Put all the object files code sections here. */ *(.text) . = ALIGN (16); /* go to a 16-byte boundary */
/* * C++ constructors and destructors * * NOTE: See the CROSSGCC mailing-list FAQ for * more details about the "[......]". */ __CTOR_LIST__ = .; [......] __DTOR_END__ = .; /* * Declares where the .text section ends. */ etext = .; _etext = .; } >rom /* * Exception Handler Frame section */ .eh_fram : { . = ALIGN (16); *(.eh_fram) } >ram /* *
16
*/ .gcc_exc : { . = ALIGN (16); *(.gcc_exc) } >ram /* * Special variable to let application get to the dual-ported * memory. */ dpram : { m340 = .; _m340 = .; . += (8 * 1024); } >ram /* * Initialized Data section goes in RAM */ .data : { copy_start = .; *(.data) . = ALIGN (16); _edata = .; copy_end = .; } >ram /* * Uninitialized Data section goes in ROM */ .bss : { /* * M68K specific: Reserve some room for the Vector Table * (256 vectors of 4 bytes). */ M68Kvec = .; _M68Kvec = .; . += (256 * 4); /* *
17
*/ clear_start = .; /* * Put all the object files uninitialized data sections * here. */ *(.bss) *(COMMON) . = ALIGN (16); _end = .; /* * Start of the Application Heap */ _HeapStart = .; __HeapStart = .; . += HeapSize; /* * The Starting Stack goes after the Application Heap. * M68K stack grows down so start at high address. */ . += StackSize; . = ALIGN (16); stack_init = .; clear_end = .; /* * The RTEMS Executive Workspace goes here. RTEMS * allocates tasks, stacks, semaphores, etc. from this * memory. */ _WorkspaceBase = .; __WorkspaceBase = .; } >ram }
18
Step 1
In Step 1, the program is linked together using the BSP linker script. In Step 2, a copy is made of the .data section and placed after the .text section so it can be placed in PROM. This step is done after the linking time. There is an example of doing this in the file $RTEMS ROOT/make/custom/gen68340.cfg: # make a PROM image using objcopy m68k-rtems-objcopy \ --adjust-section-vma .data= \ m68k-rtems-objdump --section-headers \ $(basename $@).exe \ | awk [...] \ $(basename $@).exe NOTE: The address of the "copy of .data section" is created by extracting the last address in the .text section with an awk script. The details of how this is done are not relevant. Step 3 shows the final executable image as it logically appears in the targets non-volatile program memory. The board initialization code will copy the ""copy of .data section" (which are stored in ROM) to their reserved location in RAM.
19
20
some particularly cryptic part of the software in that directory or provide rationale on the implementation.
5.3 times
This file contains the results of the RTEMS Timing Test Suite. It is in a standard format so that results from one BSP can be easily compared with those of another target board. If a BSP supports multiple variants, then there may be multiple times files. Usually these are named times.VARIANTn.
21
22
On some BSPs, it prints a message indicating that the application completed execution and waits for the user to press a key before resetting the board. The PowerPC/gen83xx and PowerPC/gen5200 BSPs do this when they are built to support the FreeScale evaluation boards. This is convenient when using the boards in a development environment and may be disabled for production use.
23
rtems_isr_entry set_vector( /* returns old vector rtems_isr_entry handler, /* isr routine rtems_vector_number vector, /* vector number int type /* RTEMS or RAW intr ) { if the type is RAW install the raw vector else use rtems_interrupt_catch to install the vector perform any interrupt controller necessary to unmask the interrupt source return the previous handler }
*/ */ */ */
NOTE: The i386, PowerPC and ARM ports use a Programmable Interrupt Controller model which does not require the BSP to implement set_vector. BSPs for these architectures must provide a different set of support routines.
25
26
27
7 Initialization Code
7.1 Introduction
The initialization code is the first piece of code executed when theres a reset/reboot. Its purpose is to initialize the board for the application. This chapter contains a narrative description of the initialization process followed by a description of each of the files and routines commonly found in the BSP related to initialization. The remainder of this chapter covers special issues which require attention such as interrupt vector table and chip select initialization. Most of the examples in this chapter will be based on the SPARC/ERC32 and m68k/gen68340 BSP initialization code. Like most BSPs, the initialization for these BSP is divided into two subdirectories under the BSP source directory. The BSP source code for these BSPs is in the following directories: c/src/lib/libbsp/m68k/gen68340 c/src/lib/libbsp/sparc/erc32 Both BSPs contain startup code written in assembly language and C. The gen68340 BSP has its early initialization start code in the start340 subdirectory and its C startup code in the startup directory. In the start340 directory are two source files. The file startfor340only.s is the simpler of these files as it only has initialization code for a MC68340 board. The file start340.s contains initialization for a 68349 based board as well. Similarly, the ERC32 BSP has startup code written in assembly language and C. However, this BSP shares this code with other SPARC BSPs. Thus the Makefile.am explicitly references the following files for this functionality. ../../sparc/shared/start.S NOTE: In most BSPs, the directory named start340 in the gen68340 BSP would be simply named start or start followed by a BSP designation.
28
The above figure illustrates the flow from assembly language start code to the shared bootcard.c framework then through the C Library, RTEMS, device driver initialization phases, and the context switch to the first application task. After this, the application executes until it calls exit, rtems_shutdown_executive, or some other normal termination initiating routine and a fatal system state is reached. The optional bsp_fatal_extension initial extension can perform BSP specific system termination. The routines invoked during this will be discussed and their location in the RTEMS source tree pointed out as we discuss each.
29
The general rule of thumb is that the start code in assembly should do the minimum necessary to allow C code to execute to complete the initialization sequence. The initial assembly language start code completes its execution by invoking the shared routine boot_card(). The label (symbolic name) associated with the starting address of the program is typically called start. The start object file is the first object file linked into the program image so it is ensured that the start code is at offset 0 in the .text section. It is the responsibility of the linker script in conjunction with the compiler specifications file to put the start code in the correct location in the application image.
30
It invokes the BSP specific routine bsp_pretasking_hook. On most BSPs which utilize the framework, this routine does nothing. If RTEMS_DEBUG is enabled, then the RTEMS debug mask level is inialized appropriately. It invokes the RTEMS directive rtems_initialize_before_drivers() to initialize the MPCI Server thread in a multiprocessor configuration and execute API specific extensions. It invokes the BSP specific routine bsp_predriver_hook. For most BSPs, the implementation of this routine does nothing. It invokes the RTEMS directive rtems_initialize_device_drivers() to initialize the statically configured set of device drivers in the order they were specified in the Configuration Table. It invokes the BSP specific routine bsp_postdriver_hook. For most BSPs, the implementation of this routine does nothing. However, some BSPs use this hook and perform some initialization which must be done at this point in the initialization sequence. This is the last opportunity for the BSP to insert BSP specific code into the initialization sequence. It invokes the RTEMS directive rtems_initialize_start_multitasking() which initiates multitasking and performs a context switch to the first user application task and may enable interrupts as a side-effect of that context switch. The context switch saves the executing context. The application runs now. The directive rtems shutdown executive() will return to the saved context. The exit() function will use this directive. After a return to the saved context a fatal system state is reached. The fatal source is RTEMS FATAL SOURCE EXIT with a fatal code set to the value passed to rtems shutdown executive(). The enabling of interrupts during the first context switch is often the source for fatal errors during BSP development because the BSP did not clear and/or disable all interrupt sources and a spurious interrupt will occur. When in the context of the first task but before its body has been entered, any C++ Global Constructors will be invoked. Thats it. We just went through the entire sequence.
31
is distinct from the application heap used for malloc(). Many BSPs place the RTEMS Workspace area at the end of RAM although this is certainly not a requirement. After completing execution, this routine returns to the boot_card() routine.
32
33
35
8 Console Driver
8.1 Introduction
This chapter describes the operation of a console driver using the RTEMS POSIX Termios support. Traditionally RTEMS has referred to all serial device drivers as console device drivers. A console driver can be used to do raw data processing in addition to the "normal" standard input and output device functions required of a console. The serial driver may be called as the consequence of a C Library call such as printf or scanf or directly via the read or write system calls. There are two main functioning modes: console: formatted input/output, with special characters (end of line, tabulations, etc.) recognition and processing, raw: permits raw data processing. One may think that two serial drivers are needed to handle these two types of data, but Termios permits having only one driver.
8.2 Termios
Termios is a standard for terminal management, included in the POSIX 1003.1b standard. As part of the POSIX and Open Group Single UNIX Specification, is commonly provided on UNIX implementations. The Open Group has the termios portion of the POSIX standard online at http://opengroup.org/onlinepubs/007908775/xbd/termios.html. The requirements for the <termios.h> file are also provided and are at http://opengroup.org/onlinepubs/007908775/xsh/termios.h.html. Having RTEMS support for Termios is beneficial because: from the users side because it provides standard primitive operations to access the terminal and change configuration settings. These operations are the same under UNIX and RTEMS. from the BSP developers side because it frees the developer from dealing with buffer states and mutual exclusions on them. Early RTEMS console device drivers also did their own special character processing. it is part of an internationally recognized standard. it makes porting code from other environments easier. Termios support includes: raw and console handling, blocking or non-blocking characters receive, with or without Timeout. At this time, RTEMS documentation does not include a thorough discussion of the Termios functionality. For more information on Termios, type man termios on a Unix box or point a web browser at http://www.freebsd.org/cgi/man.cgi.
36
In polled mode, the processor blocks on sending/receiving characters. This mode is not the most efficient way to utilize the UART. But polled mode is usually necessary when one wants to print an error message in the event of a fatal error such as a fatal error in the BSP. This is also the simplest mode to program. Polled mode is generally preferred if the serial port is to be used primarily as a debug console. In a simple polled driver, the software will continuously check the status of the UART when it is reading or writing to the UART. Termios improves on this by delaying the caller for 1 clock tick between successive checks of the UART on a read operation. In interrupt driven mode, the processor does not block on sending/receiving characters. Data is buffered between the interrupt service routine and application code. Two buffers are used to insulate the application from the relative slowness of the serial device. One of the buffers is used for incoming characters, while the other is used for outgoing characters. An interrupt is raised when a character is received by the UART. The interrupt subroutine places the incoming character at the end of the input buffer. When an application asks for input, the characters at the front of the buffer are returned. When the application prints to the serial device, the outgoing characters are placed at the end of the output buffer. The driver will place one or more characters in the UART (the exact number depends on the UART) An interrupt will be raised when all the characters have been transmitted. The interrupt service routine has to send the characters remaining in the output buffer the same way. When the transmitting side of the UART is idle, it is typically necessary to prime the transmitter before the first interrupt will occur.
37
The following list describes the basic flow. the application programmer uses standard C library call (printf, scanf, read, write, etc.), C library (e.g. RedHat (formerly Cygnus) Newlib) calls the RTEMS system call interface. This code can be found in the cpukit/libcsupport/src directory. Glue code calls the serial driver entry routines.
8.4.1 Basics
You need to include the following header files in your Termios device driver source file:
38
#include <unistd.h> #include <termios.h> #include <rtems.h> #include <rtems/libio.h> #include <rtems/console.h> You need to provide a data structure for the Termios driver interface. The functions are described later in this chapter. The functions should return zero on succes and minus one in case of an error. Currently the return value will be not checked from the Termios infrastructure in most cases. One notable exception is the polled read function, here is the return value important. If you want to use polled IO it should look like the following. You may also have a look at c/src/lib/libbsp/shared/console-polled.c for a shared implementation of the basic framework. Termios must be told the addresses of the functions that are to be used for simple character IO, i.e. pointers to the my_driver_poll_read and my_driver_poll_write functions described later in Section 8.4.2 [Console Driver Termios and Polled IO], page 39. static const rtems_termios_callbacks my_driver_callbacks_polled = { .firstOpen = my_driver_first_open, .lastClose = my_driver_last_close, .pollRead = my_driver_poll_read, .write = my_driver_poll_write, .setAttributes = my_driver_set_attributes, .stopRemoteTx = NULL, .startRemoteTx = NULL, .outputUsesInterrupts = TERMIOS_POLLED }; For an interrupt driven implementation you need the following. The driver functioning is quite different in this mode. There is no device driver read function to be passed to Termios. Indeed a console_read call returns the contents of Termios input buffer. This buffer is filled in the driver interrupt subroutine, see also Section 8.4.3 [Console Driver Termios and Interrupt Driven IO], page 40. The driver is responsible for providing a pointer to the my_driver_interrupt_write function. static const rtems_termios_callbacks my_driver_callbacks_interrupt = { .firstOpen = my_driver_first_open, .lastClose = my_driver_last_close, .pollRead = NULL, .write = my_driver_interrupt_write, .setAttributes = my_driver_set_attributes, .stopRemoteTx = NULL, .startRemoteTx = NULL, .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN }; You can also provide callback functions for remote transmission control. This is not covered in this manual, so thay are set to NULL in the above examples.
39
Normally the device specific data structures are stored in a table which is indexed by the minor number. You may need an entry for the Termios handler pointer in your data structure. For simplicity of the console initialization example the device name is also present. /* Driver specific data structure */ typedef struct { const char *device_name; struct rtems_termios_tty *tty; } my_driver_entry; /* * This table contains the driver specific data. It is later * indexed by the minor number. */ static my_driver_entry my_driver_table [MY_DRIVER_DEVICE_NUMBER];
40
The my_driver_poll_read routine is responsible for reading a single character from the serial device specified by minor. If no character is available, then the routine should return minus one.
static int my_driver_poll_read(int minor) { my_driver_entry *e = &my_driver_table [minor]; /* * There is no need to check the minor number since it is derived * from a file descriptor. The upper layer takes care that it is * in a valid range. */ /* Check if a character is available */ if (my_driver_can_read_char(e)) { /* Return the character */ return my_driver_read_char(e); } else { /* Return an error status */ return -1; } }
The my_driver_interrupt_handler is responsible for processing asynchronous interrupts from the UART. There may be multiple interrupt handlers for a single UART. Some UARTs can generate a unique interrupt vector for each interrupt source such as a character has been received or the transmitter is ready for another character.
In the simplest case, the my_driver_interrupt_handler will have to check the status of the UART and determine what caused the interrupt. The following describes the operation of an my_driver_interrupt_handler which has to do this:
41
static void my_driver_interrupt_handler( rtems_vector_number vector, void *arg ) { my_driver_entry *e = (my_driver_entry *) arg; char buf [N]; int n = 0; /* * Check if we have received something. The function reads the * received characters from the device and stores them in the * buffer. It returns the number of read characters. */ n = my_driver_read_received_chars(e, buf, N); if (n > 0) { /* Hand the data over to the Termios infrastructure */ rtems_termios_enqueue_raw_characters(e->tty, buf, n); } /* * Check if we have something transmitted. The functions returns * the number of transmitted characters since the last write to the * device. */ n = my_driver_transmitted_chars(e); if (n > 0) { /* * Notify Termios that we have transmitted some characters. It * will call now the interrupt write function if more characters * are ready for transmission. */ rtems_termios_dequeue_characters(e->tty, n); } } The my_driver_interrupt_write function is responsible for telling the device that the n characters at buf are to be transmitted. The return value may be arbitrary since it is not checked from Termios. It is guaranteed that n is greater than zero. This routine is invoked either from task context with disabled interrupts to start a new transmission process with exactly one character in case of an idle output state or from the interrupt handler to refill the transmitter. If the routine is invoked to start the transmit process the output state will become busy and Termios starts to fill the output buffer. If the transmit interrupt arises before Termios was able to fill the transmit buffer you will end up with one interrupt per character. On error, the function should return -1. On success, it should return 0, since it the interrupt handler will report the actual number of characters transmitted.
42
static ssize_t my_driver_interrupt_write(int minor, const char *buf, size_t n) { my_driver_entry *e = &my_driver_table [minor]; /* * There is no need to check the minor number since it is derived * from a file descriptor. The upper layer takes care that it is * in a valid range. */ /* * Tell the device to transmit some characters from buf (less than * or equal to n). When the device is finished it should raise an * interrupt. The interrupt handler will notify Termios that these * characters have been transmitted and this may trigger this write * function again. You may have to store the number of outstanding * characters in the device data structure. */ /* * Termios will set n to zero to indicate that the transmitter is * now inactive. The output buffer is empty in this case. The * driver may disable the transmit interrupts now. */ return 0; }
8.4.4 Initialization
The driver initialization is called once during the RTEMS initialization process. The console_initialize function may look like this:
43
rtems_device_driver console_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_device_minor_number i = 0; /* * Initialize the Termios infrastructure. If Termios has already * been initialized by another device driver, then this call will * have no effect. */ rtems_termios_initialize(); /* Initialize each device */ for (i = 0; i < MY_DRIVER_DEVICE_NUMBER; ++i) { my_driver_entry *e = &my_driver_table [i]; /* * Register this device in the file system. In order to use the * console (i.e. being able to do printf, scanf etc. on stdin, * stdout and stderr), some device must be registered * as "/dev/console" (CONSOLE_DEVICE_NAME). */ sc = rtems_io_register_name (e->device_name, major, i); RTEMS_CHECK_SC(sc, "Register IO device"); /* * Initialize this device and install the interrupt handler if * necessary. You may also initialize the device in the first * open call. */ } return RTEMS_SUCCESSFUL; }
44
The console_open function has to inform Termios of the low-level functions for serial line support. rtems_device_driver console_open( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { struct rtems_termios_callbacks *callbacks = &my_driver_callbacks_polled; /* * Check the minor number. Termios does currently not check * the return value of the first open call so the minor * number must be checked here. */ if (MY_DRIVER_IS_MINOR_INVALID(minor)) { return RTEMS_INVALID_NUMBER; } /* * Depending on the IO mode you need to pass a different set of * callback functions to Termios. */ if (MY_DRIVER_USES_INTERRUPTS(minor)) { callbacks = &my_driver_callbacks_interrupt; } return rtems_termios_open(major, minor, arg, callbacks); } During the first open of the device Termios will call my_driver_first_open.
45
static int my_driver_first_open(int major, int minor, void *arg) { my_driver_entry *e = &my_driver_table [minor]; struct rtems_termios_tty *tty = ((rtems_libio_open_close_args_t *) arg)->iop->data1; /* Check minor number */ if (MY_DRIVER_IS_MINOR_INVALID(minor)) { return -1; } /* Connect the TTY data structure */ e->tty = tty; /* * You may add some initialization code here. */ /* * Sets the inital baud rate. This should be set to the value of * the boot loader. */ return rtems_termios_set_initial_baud(e->tty, MY_DRIVER_BAUD_RATE); }
46
static int my_driver_last_close(int major, int minor, void *arg) { my_driver_entry *e = &my_driver_table [minor]; /* * There is no need to check the minor number since it is derived * from a file descriptor. The upper layer takes care that it is * in a valid range. */ /* Disconnect the TTY data structure */ e->tty = NULL; /* * The driver may do some cleanup here. */ return 0; }
47
rtems_device_driver console_write( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { return rtems_termios_write(arg); }
48
static int my_driver_set_attributes( int minor, const struct termios *t ) { my_driver_entry *e = &my_driver_table [minor]; /* * There is no need to check the minor number since it is derived * from a file descriptor. The upper layer takes care that it is * in a valid range. */ /* * Inspect the termios data structure and configure the device * appropriately. The driver should only be concerned with the * parts of the structure that specify hardware setting for the * communications channel such as baud, character size, etc. */ return 0; }
49
9 Clock Driver
9.1 Introduction
The purpose of the clock driver is to provide a steady time basis to the kernel, so that the RTEMS primitives that need a clock tick work properly. See the Clock Manager chapter of the RTEMS Application C Users Guide for more details. The clock driver is located in the clock directory of the BSP.
9.3 Initialization
The initialization routine is responsible for programming the hardware that will periodically generate an interrupt. A programmable interval timer is commonly used as the source of the clock tick. The device should be programmed such that an interrupt is generated every m microseconds, where m is equal to rtems_configuration_get_microseconds_per_tick(). Sometimes the periodic interval timer can use a prescaler so you have to look carefully at your users manual to determine the correct value. You must use the RTEMS primitive rtems_interrupt_catch to install your clock interrupt service routine: rtems_interrupt_catch (Clock_ISR, CLOCK_VECTOR, &old_handler);
50
Since there is currently not a driver entry point invoked at system shutdown, many clock device drivers use the atexit routine to schedule their Clock_exit routine to execute when the system is shutdown. By convention, many of the clock drivers do not install the clock tick if the ticks_per_ timeslice field of the Configuration Table is 0.
9.6 IO Control
Prior to RTEMS 4.9, the Shared Memory MPCI Driver required a special IOCTL in the Clock Driver. This is no longer required and the Clock Driver does not have to provide an IOCTL method at all.
51
10 Timer Driver
The timer driver is primarily used by the RTEMS Timing Tests. This driver provides as accurate a benchmark timer as possible. It typically reports its time in microseconds, CPU cycles, or bus cycles. This information can be very useful for determining precisely what pieces of code require optimization and to measure the impact of specific minor changes. The gen68340 BSP also uses the Timer Driver to support a high performance mode of the on-CPU UART.
52
to initialize and read the benchmark timer. This is used by the tmoverhd test to determine the overhead required to initialize and read the timer. void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) { disable the subtract overhead feature } The benchmark_timer_find_average_overhead variable is used to indicate the state of the "subtract overhead feature".
53
/* /* /* /* /* /* /* /* /*
54
unsigned long RTC_Count = (sizeof(RTC_Table)/sizeof(rtc_tbl)); rtems_device_minor_number RTC_Minor; bool dmv177_icm7170_probe(int minor) { volatile unsigned16 *card_resource_reg; card_resource_reg = (volatile unsigned16 *) DMV170_CARD_RESORCE_REG; if ( (*card_resource_reg & DMV170_RTC_INST_MASK) == DMV170_RTC_INSTALLED ) return TRUE; return FALSE; }
11.2 Initialization
The rtc_initialize routine is responsible for initializing the RTC chip so it can be used. The shared libchip implementation of this driver supports multiple RTCs and bases its initialization order on the order the chips are defined in the RTC_Table. Each chip defined in the table may or may not be present on this particular board. It is the responsibility of the deviceProbe to indicate the presence of a particular RTC chip. The first RTC found to be present is considered the preferred RTC. In the shared libchip based implementation of the driver, the following actions are performed:
55
rtems_device_driver rtc_initialize( rtems_device_major_number major, rtems_device_minor_number minor_arg, void *arg ) { for each RTC configured in RTC_Table if the deviceProbe for this RTC indicates it is present set RTC_Minor to this device set RTC_Present to TRUE break out of this loop if RTC_Present is not TRUE return RTEMS_INVALID_NUMBER to indicate that no RTC is present register this minor number as the "/dev/rtc" perform the deviceInitialize routine for the preferred RTC chip for RTCs past this one in the RTC_Table if the deviceProbe for this RTC indicates it is present perform the deviceInitialize routine for this RTC chip register the configured name for this RTC } The deviceProbe routine returns TRUE if the device configured by this entry in the RTC_ Table is present. This configuration scheme allows one to support multiple versions of the same board with a single BSP. For example, if the first generation of a board had Vendor As RTC chip and the second generation had Vendor Bs RTC chip, RTC Table could contain information for both. The deviceProbe configured for Vendor As RTC chip would need to return TRUE if the board was a first generation one. The deviceProbe routines are very board dependent and must be provided by the BSP.
11.3 setRealTimeToRTEMS
The setRealTimeToRTEMS routine sets the current RTEMS TOD to that of the preferred RTC. void setRealTimeToRTEMS(void) { if no RTCs are present return invoke the deviceGetTime routine for the preferred RTC set the RTEMS TOD using rtems_clock_set }
56
11.4 setRealTimeFromRTEMS
The setRealTimeFromRTEMS routine sets the preferred RTC TOD to the current RTEMS TOD. void setRealTimeFromRTEMS(void) { if no RTCs are present return obtain the RTEMS TOD using rtems_clock_get invoke the deviceSetTime routine for the preferred RTC }
11.5 getRealTime
The getRealTime returns the preferred RTC TOD to the caller. void getRealTime( rtems_time_of_day *tod ) { if no RTCs are present return invoke the deviceGetTime routine for the preferred RTC }
11.6 setRealTime
The setRealTime routine sets the preferred RTC TOD to the TOD specified by the caller. void setRealTime( rtems_time_of_day *tod ) { if no RTCs are present return invoke the deviceSetTime routine for the preferred RTC }
11.7 checkRealTime
The checkRealTime routine returns the number of seconds difference between the RTC TOD and the current RTEMS TOD.
57
obtain the RTEMS TOD using rtems_clock_get get the TOD from the preferred RTC using the deviceGetTime routine convert the RTEMS TOD to seconds convert the RTC TOD to seconds return the RTEMS TOD in seconds - RTC TOD in seconds }
59
12 ATA Driver
12.1 Terms
ATA device - physical device attached to an IDE controller
12.2 Introduction
ATA driver provides generic interface to an ATA device. ATA driver is hardware independent implementation of ATA standard defined in working draft "AT Attachment Interface with Extensions (ATA-2)" X3T10/0948D revision 4c, March 18, 1996. ATA Driver based on IDE Controller Driver and may be used for computer systems with single IDE controller and with multiple as well. Although current implementation has several restrictions detailed below ATA driver architecture allows easily extend the driver. Current restrictions are: Only mandatory (see draft p.29) and two optional (READ/WRITE MULTIPLE) commands are implemented Only PIO mode is supported but both poll and interrupt driven The reference implementation cpukit/libblock/src/ata.c. for ATA driver can be found in
12.3 Initialization
The ata_initialize routine is responsible for ATA driver initialization. The main goal of the initialization is to detect and register in the system all ATA devices attached to IDE controllers successfully initialized by the IDE Controller driver. In the implementation of the driver, the following actions are performed:
60
rtems_device_driver ata_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { initialize internal ATA driver data structure for each IDE controller successfully initialized by the IDE Controller driver if the controller is interrupt driven set up interrupt handler obtain information about ATA devices attached to the controller with help of EXECUTE DEVICE DIAGNOSTIC command for each ATA device detected on the controller obtain device parameters with help of DEVICE IDENTIFY command register new ATA device as new block device in the system } Special processing of ATA commands is required because of absence of multitasking environment during the driver initialization. Detected ATA devices are registered in the system as physical block devices (see libblock library description). Device names are formed based on IDE controller minor number device is attached to and device number on the controller (0 - Master, 1 - Slave). In current implementation 64 minor numbers are reserved for each ATA device which allows to support up to 63 logical partitions per device. controller minor 0 0 1 1 ... device number 0 1 0 1 ... device name hda hdb hdc hdd ... ata device minor 0 64 128 172
61
/* ATA request */ typedef struct ata_req_s { Chain_Node link; char type; ata_registers_t regs; uint32_t cnt; uint32_t cbuf; uint32_t pos; blkdev_request *breq;
/* link in requests chain */ /* request type */ /* ATA command */ /* Number of sectors to be exchanged */ /* number of current buffer from breq in use */ /* current position in cbuf */ /* blkdev_request which corresponds to the * ata request */ rtems_id sema; /* semaphore which is used if synchronous * processing of the ata request is required */ rtems_status_code status; /* status of ata request processing */ int error; /* error code */ } ata_req_t;
ATA driver supports separate ATA requests queues for each IDE controller (one queue per controller). The following structure contains information about controllers queue and devices attached to the controller: /* * This structure describes controller state, devices configuration on the * controller and chain of ATA requests to the controller. */ typedef struct ata_ide_ctrl_s { bool present; /* controller state */ ata_dev_t device[2]; /* ata devices description */ Chain_Control reqs; /* requests chain */ } ata_ide_ctrl_t; Driver uses array of the structures indexed by the controllers minor number. The following structure allows to map an ATA device to the pair (IDE controller minor number device is attached to, device number on the controller): /* * Mapping of rtems ATA devices to the following pairs: * (IDE controller number served the device, device number on the controller) */ typedef struct ata_ide_dev_s { int ctrl_minor;/* minor number of IDE controller serves rtems ATA device */ int device; /* device number on IDE controller (0 or 1) */ } ata_ide_dev_t; Driver uses array of the structures indexed by the ATA devices minor number. ATA driver defines the following internal events:
62
/* ATA driver events */ typedef enum ata_msg_type_s { ATA_MSG_GEN_EVT = 1, /* ATA_MSG_SUCCESS_EVT, /* ATA_MSG_ERROR_EVT, /* ATA_MSG_PROCESS_NEXT_EVT /* } ata_msg_type_t;
general event */ success event */ error event */ process next ata request event */
63
13.1 Introduction
The IDE Controller driver is responsible for providing an interface to an IDE Controller. The capabilities provided by this driver are: Read IDE Controller register Write IDE Controller register Read data block through IDE Controller Data Register Write data block through IDE Controller Data Register The reference implementation for an IDE Controller driver can be found in $RTEMS_SRC_ ROOT/c/src/libchip/ide. This driver is based on the libchip concept and allows to work with any of the IDE Controller chips simply by appropriate configuration of BSP. Drivers for a particular IDE Controller chips locate in the following directories: drivers for well-known IDE Controller chips locate into $RTEMS_SRC_ROOT/c/src/libchip/ide, drivers for IDE Controller chips integrated with CPU locate into $RTEMS_SRC_ ROOT/c/src/lib/libcpu/myCPU and drivers for custom IDE Controller chips (for example, implemented on FPGA) locate into $RTEMS_SRC_ROOT/c/src/lib/libbsp/myBSP. There is a README file in these directories for each supported IDE Controller chip. Each of these README explains how to configure a BSP for that particular IDE Controller chip.
13.2 Initialization
IDE Controller chips used by a BSP are statically configured into IDE_Controller_Table. The ide_controller_initialize routine is responsible for initialization of all configured IDE controller chips. Initialization order of the chips based on the order the chips are defined in the IDE_Controller_Table. The following actions are performed by the IDE Controller driver initialization routine: rtems_device_driver ide_controller_initialize( rtems_device_major_number major, rtems_device_minor_number minor_arg, void *arg ) { for each IDE Controller chip configured in IDE_Controller_Table if (BSP dependent probe(if exists) AND device probe for this IDE chip indicates it is present) perform initialization of the particular chip register device with configured name for this chip }
64
65
void ide_controller_read_data_block( rtems_device_minor_number minor, unsigned16 block_size, blkdev_sg_buffer *bufs, uint32_t *cbuf, uint32_t *pos ) { get IDE Controller chip configuration information from IDE_Controller_Table by minor number invoke read data block routine for the chip }
67
There is currently only one non-volatile device driver included in the RTEMS source tree. The information provided in this chapter is based on drivers developed for applications using RTEMS. It is hoped that this driver model information can form the basis for a standard non-volatile memory driver model that can be supported in future RTEMS distribution.
memory
68
attributes
is a pointer to a memory type specific attribute block. Some of the fields commonly contained in this memory type specific attribute structure area: use protection algorithm is set to TRUE to indicate that the protection (i.e. locking) algorithm should be used for this area of non-volatile memory. A particular type of non-volatile memory may not have a protection algorithm. access is an enumerated type to indicate the organization of the memory devices in this memory area. The following is a list of the access types supported by the current driver implementation: simple unsigned8 simple unsigned16 simple unsigned32 simple unsigned64 single unsigned8 at offset 0 in an unsigned16 single unsigned8 at offset 1 in an unsigned16 single unsigned8 at offset 0 in an unsigned32 single unsigned8 at offset 1 in an unsigned32 single unsigned8 at offset 2 in an unsigned32 single unsigned8 at offset 3 in an unsigned32 is the depth of the progamming FIFO on this particular chip. Some chips, particularly EEPROMs, have the same programming algorithm but vary in the depth of the amount of data that can be programmed in a single block.
depth
number of partitions is the number of logical partitions within this area. Partitions is the address of the table that contains an entry to describe each partition in this area. Fields within each element of this table are defined as follows: offset is the offset of this partition from the base address of this area.
69
length
By dividing an area of memory into multiple partitions, it is possible to easily divide the non-volatile memory for different purposes.
70
The driver reads length bytes starting at offset into the partition and places them at buffer. The result is returned in status. After the read operation is complete, the user supplied "disable reads handler" is invoked to protect the memory area again.
71
15 Networking Driver
15.1 Introduction
This chapter is intended to provide an introduction to the procedure for writing RTEMS network device drivers. The example code is taken from the Generic 68360 network device driver. The source code for this driver is located in the c/src/lib/libbsp/m68k/gen68360/network directory in the RTEMS source code distribution. Having a copy of this driver at hand when reading the following notes will help significantly.
72
structures and resources. To ensure the consistency of these structures the tasks execute only when they hold the network semaphore (rtems_bsdnet_semaphore). The transmit and receive tasks must abide by this protocol. Be very careful to avoid deadly embraces with the other network tasks. A number of routines are provided to make it easier for the network driver code to conform to the network task scheduling conventions. void rtems_bsdnet_semaphore_release(void) This function releases the network semaphore. The network driver tasks must call this function immediately before making any blocking RTEMS request. void rtems_bsdnet_semaphore_obtain(void) This function obtains the network semaphore. If a network driver task has released the network semaphore to allow other network-related tasks to run while the task blocks, then this function must be called to reobtain the semaphore immediately after the return from the blocking RTEMS request. rtems_bsdnet_event_receive(rtems_event_set, rtems_option, rtems_ interval, rtems_event_set *) The network driver task should call this function when it wishes to wait for an event. This function releases the network semaphore, calls rtems_event_receive to wait for the specified event or events and reobtains the semaphore. The value returned is the value returned by the rtems_event_receive.
73
Application level code including network servers such as the FTP daemon are not part of the BSD kernel network code and should not be compiled with the BSD network flags. They should include <stdlib.h> and not define the network stack visibility macros.
ifp->if_unit
ifp->if_mtu ifp->if_flags
ifp->if_snd.ifq_maxlen The maximum length of the queue of packets waiting to be sent to the driver. This is normally set to ifqmaxlen. ifp->if_init ifp->if_start ifp->if_ioctl ifp->if_output The address of the driver initialization function. The address of the driver start function. The address of the driver ioctl function. The address of the output function. Ethernet devices should set this to ether_output.
RTEMS provides a function to parse the driver name in the configuration structure into a device name and unit number.
74
int rtems_bsdnet_parse_driver_name ( const struct rtems_bsdnet_ifconfig *config, char **namep ); The function takes two arguments; a pointer to the configuration structure and a pointer to a pointer to a character. The function parses the configuration name entry, allocates memory for the driver name, places the driver name in this memory, sets the second argument to point to the name and returns the unit number. On error, a message is printed and -1 is returned. Once the attach function has set up the above entries it must link the driver data structure onto the list of devices by calling if_attach. Ethernet devices should then call ether_ ifattach. Both functions take a pointer to the devices ifnet structure as their only argument. The attach function should return a non-zero value to indicate that the driver has been successfully configured and attached.
75
77
*/ */ */ */ */ */
78
convert
is the address of a routine which converts from native format to neutral format. Ideally, the neutral format is the same as the native format so this routine is quite simple. is either INTR MODE or POLLED MODE to indicate how the node will be informed of incoming messages.
is the information required to cause an interrupt on a node. This structure contains the following fields: address is the address to write at to cause an interrupt on that node. For a polled node, this should be NULL. is the value to write to cause an interrupt. is the length of the entity to write on the node to cause an interrupt. This can be 0 to indicate polled operation, 1 to write a byte, 2 to write a sixteen-bit entity, and 4 to write a thirty-two bit entity.
value length
16.2 Primitives
16.2.1 Convert Address
The Shm_Convert_address is responsible for converting an address of an entity in the shared memory area into the address that should be used from this node. Most targets will simply return the address passed to this routine. However, some target boards will have a special window onto the shared memory. For example, some VMEbus boards have special address windows to access addresses that are normally reserved in the CPUs address space. void *Shm_Convert_address( void *address ) { return the local address version of this bus address }
79
80
void Shm_Lock( Shm_Locked_queue_Control *lq_cb ) { disable processor interrupts set Shm_isrstat to previous interrupt disable level while ( TRUE ) { atomically attempt to acquire the lock if the lock was acquired return delay some small period of time } }
81
17.1 Introduction
The purpose of the frame buffer driver is to provide an abstraction for graphics hardware. By using the frame buffer interface, an application can display graphics without knowing anything about the low-level details of interfacing to a particular graphics adapter. The parameters governing the mapping of memory to displayed pixels (planar or linear, bit depth, etc) is still implementation-specific, but device-independent methods are provided to determine and potentially modify these parameters. The frame buffer driver is commonly located in the console directory of the BSP and registered by the name /dev/fb0. Additional frame buffers (if available) are named /dev/fb1, /dev/fb2, etc. To work with the frame buffer, the following operation sequence is used: open(), ioctls() to get the frame buffer info, read() and/or write(), and close().
82
rtems_device_driver frame_buffer_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; printk( "frame buffer driver initializing..\n" ); /* * Register the device */ status = rtems_io_register_name("/dev/fb0", major, 0); if (status != RTEMS_SUCCESSFUL) { printk("Error registering frame buffer device!\n"); rtems_fatal_error_occurred( status ); } /* * graphics hardware initialization goes here for non-console * devices */ return RTEMS_SUCCESSFUL; }
Thread safety of the frame buffer driver is implementation-dependent. The VGA driver shown below uses a mutex to prevent multiple open() operations of the frame buffer device.
The frame_buffer_open() function returns RTEMS SUCCESSFUL when the device driver is successfully opened, and RTEMS UNSATISFIED if the device is already open:
83
rtems_device_driver frame_buffer_close( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { if (pthread_mutex_unlock(&mutex) == 0){ /* restore previous state. for VGA this means return to text mode. * leave out if graphics hardware has been initialized in * frame_buffer_initialize() */ ega_hwterm(); printk( "FBVGA close called.\n" ); return RTEMS_SUCCESSFUL; } return RTEMS_UNSATISFIED; } In the previous example, the function ega_hwinit() takes care of hardware-specific initialization.
84
rtems_device_driver frame_buffer_read( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; rw_args->bytes_moved = ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ? (fb_ memcpy(rw_args->buffer, (const void *) (fb_fix.smem_start + rw_args->offset), rw_args return RTEMS_SUCCESSFUL; }
rtems_device_driver frame_buffer_write( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; rw_args->bytes_moved = ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ? (fb_ memcpy( (void *) (fb_fix.smem_start + rw_args->offset), rw_args->buffer, rw_args->byt return RTEMS_SUCCESSFUL; }
85
rtems_device_driver frame_buffer_control( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { rtems_libio_ioctl_args_t *args = arg; printk( "FBVGA ioctl called, cmd=%x\n", args->command switch( args->command ) { case FBIOGET_FSCREENINFO: args->ioctl_return = get_fix_screen_info( ( struct break; case FBIOGET_VSCREENINFO: args->ioctl_return = get_var_screen_info( ( struct break; case FBIOPUT_VSCREENINFO: /* not implemented yet*/ args->ioctl_return = -1; return RTEMS_UNSATISFIED; case FBIOGETCMAP: args->ioctl_return = get_palette( ( struct fb_cmap break; case FBIOPUTCMAP: args->ioctl_return = set_palette( ( struct fb_cmap break; default: args->ioctl_return = 0; break; } return RTEMS_SUCCESSFUL; } See rtems/fb.h for more information on the list of ioctls and data structures they work with. );
fb_fix_screeninfo * ) args->b
fb_var_screeninfo * ) args->b
* ) args->buffer );
* ) args->buffer );
87
18 Analog Driver
The Analog driver is responsible for providing an interface to Digital to Analog Converters (DACs) and Analog to Digital Converters (ADCs). The capabilities provided by this class of device driver are: Initialize an Analog Board Open a Particular Analog Close a Particular Analog Read from a Particular Analog Write to a Particular Analog Reset DACs Reinitialize DACS Most analog devices are found on I/O cards that support multiple DACs or ADCs on a single card. There are currently no analog device drivers included in the RTEMS source tree. The information provided in this chapter is based on drivers developed for applications using RTEMS. It is hoped that this driver model information can form the basis for a standard analog driver model that can be supported in future RTEMS distribution.
88
89
call. By passing the voltage to the device driver, the caller is freed from having to know the number of bits in the analog and board dependent conversion algorithm.
91
19 Discrete Driver
The Discrete driver is responsible for providing an interface to Discrete Input/Outputs. The capabilities provided by this class of device driver are: Initialize a Discrete I/O Board Open a Particular Discrete Bitfield Close a Particular Discrete Bitfield Read from a Particular Discrete Bitfield Write to a Particular Discrete Bitfield Reset DACs Reinitialize DACS
Most discrete I/O devices are found on I/O cards that support many bits of discrete I/O on a single card. This driver model is centered on the notion of reading bitfields from the card. There are currently no discrete I/O device drivers included in the RTEMS source tree. The information provided in this chapter is based on drivers developed for applications using RTEMS. It is hoped that this driver model information can form the discrete I/O driver model that can be supported in future RTEMS distribution.
From the above, it should be clear that a single device driver can support multiple copies of the same board in a single system. The minor number is used to distinguish the devices. By providing a way to easily access a particular bitfield from the device driver, the application is insulated with knowing how to mask fields in and out of a discrete I/O.
92
is the base address of a board. is an array of the values that should be written to each output word on the board during initialization. This allows the driver to start with the boards output in a known state.
93
The value written is an unsigned32 number representing the value to be written to the specified bitfield. This value is stored in the argument_block passed in to the call. NOTE: Some discrete I/O drivers have a special minor number used to access all discrete I/O bits on the board. If this special minor is used, then the area pointed to by argument_ block must be the correct size.
95
Concept Index
97
Concept Index
There are currently no Concept Index entries.