ECEN 449: Microprocessor System Design

advertisement
ECEN 449: Microprocessor System Design
Department of Electrical and Computer Engineering
Texas A&M University
Prof. Sunil Khatri
TA: Monther Abusultan
(Lab exercise created by A Targhetta and P Gratz)
Laboratory Exercise #6
An Introduction to Linux Device Driver Development
Objective
The purpose of lab this week is to experience device driver creation in an embedded Linux environment.
Device drivers are a part of the operating system kernel which serve as the bridge between user applications
and hardware devices. Device drivers facilitate access and sharing of hardware devices under the control of
the operating system.
Similar to Lab 5, you will create a kernel module, which prints messages to the kernel’s message buffer.
However, this kernel module will also moderate user access to the multiplication peripheral created in Lab
3. You will then extend the capabilities of your kernel module, thereby creating a complete character device
driver. To test your multiplication device driver, you will also develop a simple Linux application which
utilizes the device driver, providing the same functionality as seen in Lab 3.
System Overview
The hardware system you will use this week is that which was built in Lab 4. Figure 1 depicts a simplified
view of the hardware and software you will create in this lab. Please note the hardware/software boundary in
the figure below. Above this boundary, the blocks represent compiled code executing within the MicroBlaze
processor. Below this boundary, the blocks represent hardware attached to the microprocessor. In our
particular case, the hardware is a multiplication peripheral attached to the MicroBlaze via the PLB bus.
1
2
Laboratory Exercise #6
Obviously, other hardware/software interactions exist in our system, but Figure 1 focuses on that which
you will provide. Also notice the existence of the kernel-space/user-space boundary. The kernel module
represents the character device driver executing in kernel space, while the user application represents the
code executing in user space, reading and writing to the device file, ‘/dev/multiplier’. The device file is not a
standard file in the sense that it does not reside on a disk, rather it provides an interface for user applications
to interact with the kernel.
User Application
/dev/multiplier
user space
kernel space
kernel module
software
hardware
multiplication peripheral
Figure 1: Hardware/Software System Diagram
Procedure
1. Compile a kernel module that reads and writes to the multiplication peripheral and prints the results
to the kernel message buffer.
(a) Create a lab6 directory and copy the ‘modules’ directory from your lab5 directory to your lab6
directory.
2
ECEN 449
Laboratory Exercise #6
3
(b) Navigate to the ‘modules’ directory and ensure the ‘Makefile’ points to the ‘linux-2.6.35.7’
kernel directory.
(c) Create a new kernel module source file called ‘multiply.c’.
(d) Copy the following skeleton code into ‘multiply.c’:
# include
# include
# include
# include
<l i n u x / module . h>
<l i n u x / k e r n e l . h>
<l i n u x / i n i t . h>
<asm / i o . h>
/∗
/∗
/∗
/∗
Needed
Needed
Needed
Needed
by a l l m o d u l e s ∗ /
f o r KERN ∗ and p r i n t k ∗ /
for
i n i t and
e x i t macros ∗ /
f o r IO r e a d s and w r i t e s ∗ /
# include ” x p a r a m e t e r s . h” /∗ needed f o r p h y s i c a l address of m u l t i p l i e r ∗/
/ ∗ from x p a r a m e t e r s . h ∗ /
# d e f i n e PHY ADDR XPAR MULTIPLY 0 BASEADDR / / p h y s i c a l a d d r e s s o f m u l t i p l i e r
/∗ s i z e of p h y s i c a l address range f o r m u l t i p l y ∗/
# d e f i n e MEMSIZE XPAR MULTIPLY 0 HIGHADDR − XPAR MULTIPLY 0 BASEADDR+1
void ∗ v i r t a d d r ; / / v i r t u a l address p o i n t i n g to m u l t i p l i e r
/ ∗ T h i s f u n c t i o n i s r u n upon module l o a d . T h i s i s where you s e t u p d a t a
s t r u c t u r e s and r e s e r v e r e s o u r c e s u s e d by t h e module . ∗ /
static int
i n i t m y i n i t ( void )
{
/∗ Linux k e r n e l ’ s v e r s i o n of p r i n t f ∗/
p r i n t k ( KERN INFO ” Mapping v i r t u a l a d d r e s s . . . \ n ” ) ;
/ ∗ map v i r t u a l a d d r e s s t o m u l t i p l i e r p h y s i c a l a d d r e s s ∗ /
/ / use ioremap
/∗ write 7 to r e g i s t e r 0 ∗/
p r i n t k ( KERN INFO ” W r i t i n g a 7 t o r e g i s t e r 0\ n ” ) ;
i o w r i t e 3 2 ( 7 , v i r t a d d r +0); / / base address + o f f s e t
/∗ Write 2 to r e g i s t e r 1 ∗/
p r i n t k ( KERN INFO ” W r i t i n g a 2 t o r e g i s t e r 1\ n ” ) ;
/ / use i o w r i t e 3 2
p r i n t k ( ” Read %d from r e g i s t e r 0\ n ” , i o r e a d 3 2 ( v i r t a d d r + 0 ) ) ;
p r i n t k ( ” Read %d from r e g i s t e r 1\ n ” , \\ u s e i o r e a d 3 2 ) ;
p r i n t k ( ” Read %d from r e g i s t e r 2\ n ” , i o r e a d 3 2 ( v i r t a d d r + 8 ) ) ;
/ / A non 0 r e t u r n means i n i t m o d u l e f a i l e d ; module can ’ t be l o a d e d .
return 0;
}
/ ∗ T h i s f u n c t i o n i s r u n j u s t p r i o r t o t h e module ’ s r e m o v a l f r o m t h e
s y s t e m . You s h o u l d r e l e a s e ALL r e s o u r c e s u s e d by y o u r module
h e r e ( o t h e r w i s e be p r e p a r e d f o r a r e b o o t ) . ∗ /
s t a t i c void
e x i t my exit ( void )
ECEN 449
3
4
Laboratory Exercise #6
{
p r i n t k ( KERN ALERT ” unmapping v i r t u a l a d d r e s s s p a c e . . . . \ n ” ) ;
iounmap ( ( v o i d ∗ ) v i r t a d d r ) ;
}
/ ∗ T h e s e d e f i n e i n f o t h a t can be d i s p l a y e d by m o d i n f o ∗ /
MODULE LICENSE ( ”GPL” ) ;
MODULE AUTHOR( ”ECEN449 S t u d e n t ( and o t h e r s ) ” ) ;
MODULE DESCRIPTION ( ” S i m p l e m u l t i p l i e r module ” ) ;
/ ∗ Here we d e f i n e w h i c h f u n c t i o n s we want t o u s e f o r i n i t i a l i z a t i o n
and c l e a n u p ∗ /
module init ( my init );
module exit ( my exit ) ;
(e) Some required function calls have been left out of the code above. Fill in the necessary code.
Consult Linux Device Drivers, 3rd Edition for help.
Note: When running Linux on the MicroBlaze, your code is operating in virtual memory, and
‘ioremap’ provides the physical to virtual address translation required to read and write to hardware from within virtual memory. ‘iounmap’ reverses this mapping and is essentially the cleanup function for ‘ioremap’.
(f) Add a ‘printk’ statement to your initialization routine that prints the physical and virtual addresses of your multiplication peripheral to the kernel message buffer.
(g) Compile your kernel module. Do not for get to source the ‘compile settings.csh’ file.
(h) Load the ‘multiply.ko’ module onto the CF card and mount the card within the XUP Linux
system using ‘/lib/modules/2.6.35.7’ as the mount point. Consult Lab 5 if this is unclear.
(i) Use ‘insmod’ to load the ‘multiply.ko’ kernel module into the XUP Linux kernel. Demonstrate
your progress to the TA.
2. With a functioning kernel module that reads and writes to the multiplication peripheral, you are now
ready to create a character device driver that provides applications running in user space access to
your multiplication peripheral.
(a) From within the ‘modules’ directory, create a character device driver called ‘multiplier.c’using
the following guidelines:
• Take a moment to examine ‘my chardev.c’, ‘my chardev mem.c’ and the appropriate header
files within ‘/homes/faculty/shared/ECEN449/module examples/’. Use the character device
driver examples provided in Linux Device Drivers, 3rd Edition and the laboratory directory
as a starting point for your device driver.
4
ECEN 449
Laboratory Exercise #6
5
• Use the ‘multiply.c’ kernel module created in Section 2 of this lab as a baseline for reading
and writing to the multiplication peripheral and mapping the multiplication peripheral’s
physical address to virtual memory.
• Within the initialization routine, you must register your character device driver after the
virtual memory mapping. The name of your device should be ‘multiplier’. Let Linux dynamically assign your device driver a major number and specify a minor number of 0. Print
the major number to the kernel message buffer exactly as done in the examples provided.
Be sure to handle device registration errors appropriately. You will be graded on this!
• In the exit routine, unregister the device driver before the virtual memory unmapping.
• For the open and close functions, do nothing except print to the kernel message buffer
informing the user when the device is opened and closed.
• Read up on ‘put user’ and ‘get user’ in Linux Device Drivers, 3rd Edition as you will be
using these system calls in your custom read and write functions.
• For the read function, your device driver must read bytes 0 through 11 within your peripheral’s address range and put them into the user space buffer. Note that one of the parameters
for the read function, ‘length’, specifies the number of bytes the user is requesting. Valid
values for this parameter include 0 through 12. Your function should return the number of
bytes actually transfered to user space (i.e. into the buffer pointed to by char* buf). You
may use ‘put user’ to transfer more than 1 byte at a time.
• For the write function, your device driver must copy bytes from the user buffer to kernel
space using the system call ‘get user’ to do the transfer and the variable ‘length’ as a specification of how many bytes to transfer. Furthermore, the device driver must write those
bytes to the multiplication peripheral . The return value of your write function should be
similar to that of your read function, returning the number of bytes successfully written to
your multiplication peripheral. Only write to valid memory locations (i.e. 0 through 7)
within your peripheral’s address space.
(b) Modify the ‘Makefile’ to compile ‘multiplier.c’ and run ‘make ARCH=microblaze’ in the terminal to create the appropriate kernel module.
(c) As with ‘multiply.ko’, load ‘multiplier.ko’ into your XUP Linux system.
(d) Use ‘dmesg’ to view the output of the kernel module after it has been loaded. Follow the instructions provided within the provided example kernel modules to create the ‘/dev/multiplier’
device node. Demonstrate your progress to the TA.
3. At this point, we need to create a user application that reads and writes to the device file, ‘/dev/multiplier’ to test our character device driver and provides the required functionality similar to that seen in
Lab 3.
(a) View the man pages on ‘open’, ‘close’, ‘read’, and ‘write’ from within the CentOS workstation.
You will use these system calls in your application code to read and write to the ‘/dev/multiplier’
ECEN 449
5
6
Laboratory Exercise #6
device file. Accessing the man pages can be done via the terminal. For example, to view the
man pages on ‘open’, type ‘man open’ in a terminal window.
(b) Within the ‘modules’ directory, create a source file called ‘devtest.c’ and copy the following
starter text into that file:
# include
# include
# include
# include
# include
# include
<s y s / t y p e s . h>
<s y s / s t a t . h>
< f c n t l . h>
< s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
i n t main ( ) {
unsigned i n t r e s u l t ;
in t fd ;
int i , j ;
/∗ f i l e descriptor ∗/
/∗ loop v a r i a b l e s ∗/
char i n p u t = 0 ;
/ ∗ open d e v i c e f i l e f o r r e a d i n g and w r i t i n g ∗ /
/ ∗ u s e ” open ” t o open ‘ / d e v / m u l t i p l i e r ’ ∗ /
/∗ handle error opening f i l e ∗/
i f ( f d == −1){
p r i n t f ( ” F a i l e d t o open d e v i c e f i l e ! \ n ” ) ;
r e t u r n −1;
}
while ( i n p u t != ’ q ’ ){ / ∗ c o n t i n u e u n l e s s u s e r e n t e r e d ’ q ’ ∗ /
f o r ( i = 0 ; i <=16; i ++){
f o r ( j = 0 ; j <=16; j ++){
/ ∗ w r i t e v a l u e s t o r e g i s t e r s u s i n g char dev ∗ /
/ ∗ u s e w r i t e t o w r i t e i and j t o p e r i p h e r a l ∗ /
/ ∗ r e a d i , j , and r e s u l t u s i n g c h a r d e v ∗ /
/ ∗ use read t o read from p e r i p h e r a l ∗ /
/∗ p r i n t unsigned i n t s to screen ∗/
p r i n t f ( ”%u ∗ %u = %u ” , r e a d i , r e a d j , r e s u l t ) ;
/∗ validate r e s u l t ∗/
i f ( r e s u l t == ( i ∗ j ) )
p r i n t f ( ” Result Correct ! ” ) ;
else
p r i n t f ( ” Result Incorrect ! ” ) ;
/ ∗ read from t e r m i n a l ∗ /
input=getchar ( ) ;
6
ECEN 449
Laboratory Exercise #6
7
}
}
}
close ( fd ) ;
return 0;
}
(c) Complete the skeleton code provided above using the specified system calls.
(d) Compile ‘devtest.c’ by executing the following command in the terminal window under the
‘modules’ directory:
>mb-linux-gcc -o devtest devtest.c
(e) Copy the executable file, ‘devtest’, on to the CF card and execute the application by typing the
following in the XUP terminal within the CF mount point directory:
>./devtest
(f) Examine the output as you hit the ‘enter’ key from the XUP terminal. Demonstrate your progress
to the TA.
Deliverables
1. [5 points.] Demo the working kernel module and device driver to the TA.
Submit a lab report with the following items:
2. [5 points.] Correct format including an Introduction, Procedure, Results, and Conclusion.
3. [4 points.] Commented C files.
4. [2 points.] The output of the XUP terminal (kermit) for parts 1 through 3 of lab.
5. [4 points.] Answers to the following questions:
(a) Given that the multiplier hardware uses memory mapped I/O (the processor communicates with
it through explicitly mapped physical addresses), why is the ioremap command required?
(b) Do you expect that the overall (wall clock) time to perform a multiplication would be better in
part 3 of this lab or in the original Lab 3 implementation? Why?
(c) Contrast the approach in this lab with that of Lab 3. What are the benefits and costs associated
with each approach?
(d) Explain why it is important that the device registration is the last thing that is done in the initialization routine of a device driver. Likewise, explain why un-registering a device must happen
first in the exit routine of a device driver.
ECEN 449
7
Download