Device file

advertisement
Linux Device Driver
2009/04/08
Reference
Book
Another Reference Book
Embedded Linux Primer: A Practical,
Real-World Approach By
Christopher Hallinan
Publisher: Prentice Hall Pub Date:
September 18, 2006 Print ISBN-10: 0-13167984-8 Print ISBN-13: 978-0-13167984-9 Pages: 576
Website
•
•
•
•
http://wiki.openwrt.org
http://www.linuxsir.org
http://www-128.ibm.com/developerworks/
http://lwn.net
Task of Device Driver
• Device initialization
• Hardware operation and management
• Data transfer between kernel space and
user space
• Data exchange between hardware and
kernel space
Function of device driver
User
space
Application
Kernel
Space
Buffer
Device Driver
Hardware
User space vs Kernel Space program
——From the point of view of CPU
Execution
core
INT
mode reg.
data
address
memory
access
surveillance
memory
address
mapping
Memory
User space vs Kernel Space program
——From the point of view of CPU
Soft
interrupt
用户应用程序
用户应用程序
User space
用户应用程序
program
Kernel schedule/
Kernel API
Scheduler,
change mode
reg. to enter
different mode
Mode reg.
内核程序
内核程序
Kernel
space
内核程序
program
Address Mapping and accessing of
the physical address
User
processs1
User
processs2
User
process 3
Virtual address
mapping
Virtual address
mapping
Virtual address
mapping
Physical Address space
user program access virtual using pointers
physical address of IO device cannot be accessed by
user program directly
Basic operation of device driver
• Device controller is often mapped into memory
address
Data
bus
CPU
Address
bus
D
Q
D
Q
D
Q
Address
matching
circuit
Device
circuits
Basic operation of device driver
Data
bus
CPU
D
Q
D
Q
D
Q
Address
bus
Address
matching
circuit
Device
circuits
User space vs Kernel Space
program
User space program
• Limited priority
• Virtual run
environment
– Logic address
– Key resource access
is difficult
• User invoke function
directly
Kernel Space program
• Highest priority
• Physical run
environment
– Virtual address
– Access all resource
• Kernel invoke function
Direct memory
access(/dev/kmem)
kmfd = open("/dev/kmem", O_RDONLY );
lseek( kmfd, offset, SEEK_SET );
read( kmfd, byteArray, byteArrayLen );
close(kmfd);
• memory is mapped into device file, and can be
memory
accessed by file read/write
• Can access kernel address(virtual address of kernel)
• Most started from 0xC0000000
offset
Access Physical address
directly(/dev/mem)
mem_fd = open("/dev/mem", O_RDONLY );
b=mmap(0, 0x10000,
PROT_READ|PROT_WRITE,MAP_SHARED,
mem_fd,0xA0000)
…
May not bye
close(memfd);
memory
device file
Pointer b
0xA0000
mmap mapping data in file into array
Physical memory(accessed by special file /dev/mem)
s mapped into array pointed by b
0xB0000
Note that value of B may not be 0xA0000, its value is
a virtual address coressponding to the physical
address 0xA0000
under Linux, /dev/mem is for special memory access,
such as video memory
Directly access IO port(/dev/port)
port_fd = open("/dev/port", O_RDWR);
lseek(port_fd, port_addr, SEEK_SET);
read(port_fd, …);
write(port_fd, …);
close(port_fd);
• Note, not use fopen/fread/fwrite/fclose,
because data operation by these
function is not fulfilled immediately
(buffered)
outb()/outw()/inb()/inw() Function
#include <stdio.h>
• outb(value, port); inb(port); // 8-bit
• outw(value, port); inw(port); // 16-bit
#include <unistd.h>
• access time is about 1us
#include <asm/io.h>
#define BASEPORT 0x378 // printer
int main()
{
ioperm(BASEPORT, 3, 1)); // get access permission
outb(0, BASEPORT);
usleep(100000);
printf("status: %d\n", inb(BASEPORT + 1));
ioperm(BASEPORT, 3, 0)); // give up
exit(0); • ioperm(from,num,turn_on)
• port address that can be obtained from ioperm is
}
0x000~0x3FF,use iopl() can obtain all port address
• must run as root
• use “gcc -02 –o xxx.elf xxx.c” to compile
Access memory directly by user
space program
——why we need device driver
• Share devices
• INT management
Safe device access method ——
Using Linux device driver
• Device driver can access device address by pointer
• device driver also use virtual address but more ‘real’
than user application(device address mapping can be
found in transplanting Linux Kernel)
Device Driver
Virtual address mapping Device address mapping
Device Driver
Virtual address mapping Device address mapping
Physical address space
Device address Space
Direct access IO port vs Using
device driver
•
•
•
•
Direct IO Access
User space
simple
poll mode, slow
(response time)
difficult to share
devices
Access by device driver
• Kernel space
• difficult to debug and
programming
• can use fast INT
mode, realtime
• easy to implement
device sharing
(managed by OS)
Device Classification in Linux
• Character device
– Mouse、Serial port、joystick
• Block device
– Printer , hard disk
• Network device
– Access by BSD Socket
Character device vs Block
device
Character device
Block device
• Read/write operation
is fulfilled immediately
• data buffer is optional
• need data buffer to
reduce device
write/read operation
• For slow device such
as hard disk
• ADC/DAC、button、
LED、sensor
Dynamically installable device
vs static linked device driver
• Static linked device driver
– Change configuration file, re-compile and
install kernel
• Dynamically instable device driver
– insmod
– rmmod
– lsmod
install
removal
query
Abstraction of devices under
Linux
Device file
• Open/Close/Read/Write
• Example
– /dev/mouse
– /dev/lp0
universal IF
Device Driver and File
Access by
open/read/write/close API
Similar
method
Device
File
Application
Create by
command
mknod
Find real
device river
by major
device
number
Device driver
installed by command
insmod (or statically
compiled into kernel)
Device Driver and File
User space User
application
Name of device file
Device File
device ID
read()
name of vice file
write()
open()
close()
device ID
Deevice Driver
Kernel Space
read()
write()
open()
close()
Register and unregister
Structure
of Device
Driver
device file operation
function (API)
(*open)()
(*write)()
(*flush)()
(*llseek)()
…
ISR
Example of LED device driver
CPU
struct file_operations LED_fops =
{
read:
LED_read,
write:
LED_write,
open:
LED_open,
release:
LED_release,
pointers of
};
Program list (1)
functions
int LED_init_module(void)
{
SET_MODULE_OWNER(&LED_fops);
LED_major = register_chrdev(0, "LED", &LED_fops);
LED_off();
invoked when device
LED_status=0;
is installed
return 0;
}
void LED_cleanup_module(void) invoked when device is uninstalled
{ unregister_chrdev(LED_major, "LED"); }
Tell kernel the name of function to b
module_init(LED_init_module);
invoked when install or uninstall dev
module_exit(LED_cleanup_module);
driver
program(2)
int LED_open(struct inode *inode, struct file
*filp)
{
printk("LED_open()\n");
MOD_INC_USE_COUNT; new vision Linux doesn’t
use this Macro
return 0;
}
int LED_release(struct inode *inode, struct
file *filp)
{
printk(“LED_release()\n“);
new vision Linux doesn’t
MOD_DEC_USE_COUNT;
use this Macro
return 0;
}
Program List(3)
ssize_t LED_read (struct file *filp, char *buf, size_t
count, loff_t *f_pos)
{
int i;
for (i=0; i<count; i++)
*((char*)(buf+i)) = LED_Status;
return count;
}
ssize_t LED_write(struct file *filp, const char *buf,
size_t count, loff_t *f_pos)
{
int i;
for (i=0; i<count; i++)
if (*((char*)(buf+i))) LED_on();
else LED_off();
return count;
}
(*((volatile unsigned int *)(0xXXXXXXXX))) |= MASK;
(*((volatile unsigned int *)(0xXXXXXXXX))) &=~MASK;
#ifndef
__KERNEL__
#define __KERNEL__
#endif
#ifndef
MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <sysdep.h>
#include <linux/ioctl.h>
#include <linux/in6.h>
#include <asm/checksum.h>
MODULE_AUTHOR("Rendong Ying");
int LED_major, LED_status;
Program
List(4)
Header
Device ID, uer can
manually write the
number or use
automatically assigned
number by kernel
Compile(Makefile)
CC = arm-elf-linux-gcc
LD = arm-elf-linux-ld
INCLUDE = /usr/local/src/bspLinux/include
LIB_INC = /usr/local/lib/gcc-lib/arm-elf-linux/2.95.3/include
CFLAGS = -O6 -Wall -DCONFIG_KERNELD -DMODULE -D__KERNEL__
-DLinux -nostdinc -I- -I . -I$(INCLUDE)
-idirafter $(LIB_INC)
LED.o: LED.c
$(CC) $(CFLAGS) -c LED.c
clean:
rm -f LED.o
generate *.o
install device driver and
create device file
• chmod +x /tmp/LED.o
• /sbin/insmod -f ./LED.o
• cat /proc/devices get device ID
instralled into the memory
• mknod /dev/Lamp c Num1 Num2
Num1 major device ID
Num2 Minor device IDforce install, ignore
version checking
can be
any value
assigned in
the program:
LED_major
install device driver
User space
/sbin/insmod -f ./LED.o
Kernel
Space
Device Driver
read()
write()
open()
close()
Create device file
User space
cat /proc/devices
mknod /dev/Lamp c
Num1 Major device
Num2 Minor device
obtain major device ID
Num1 Num2
ID
ID
device ID
Device
File
Device ID
Device Driver
Kernel Space
read()
write()
open()
close()
Test and use device driver
• command
open printk,you may read log information in
/var/log/messages
echo 8 > /proc/sys/kernel/printk
cat /dev/Lamp
cat > /dev/Lamp
• program
void main()
{
int fd=open(“/dev/Lamp, O_RDWR);
write(fd, &data, 1);
close(fd);
}
Use of device driver
User space
void main()
{
int fd=open(“/dev/Lamp, O_RDWR);
write(fd, &data, 1);
close(fd);
}
Device File
Device Driver
Kernel Space
read()
write()
open()
close()
Uninstall device
/sbin/rmmod LED
rm -f /dev/Lamp
remove device driver
delete device file
Function of
MOD_INC_USE_COUNT;
MOD_DEC_USE_COUNT;
Uninstall device
User space
rm -f /dev/Lamp
Device
File
/sbin/rmmod LED
Device Driver
Kernel Space
read()
write()
open()
close()
Complex
Device
Driver
Data in
user
space
Kernel
space buffer
register and unregister
(device and INT)
Divice operation API(function)
(*open)()
(*write)()
(*flush)()
(*llseek)()
…
ISR
Complex Device Driver
(USB Device)
Application of IRQ
INT number,
determined
by hardware,
kernel find
INT number
from INT
status reg.
Then call
registed
function on
that number
• if (request_irq(USB_INTR_SOURCE1,
ISR pointer
usb_ep1_int,
INT ID
Name
point to ISR
SA_INTERRUPT,
mask same
"USB EP1",
INT
0) < 0)
printk("Int. req. failed !\n");
• free_irq(USB_INTR_SOURCE0, 0);
cat /proc/interrupts
check register INT
ISR
• No return value
• Fats and small
Specific format for
function declaration
void usb_ep1_int(int
irq,
void *dev_id,
struct pt_regs *regs)
{
//…
}
ISR that receive data
void usb_ep1_int(int
irq,
void *dev_id,
struct pt_regs *regs)
{
read_data_from_hardware_FIFO();
send_data_to_buffer();
}
ISR that send data
void usb_ep2_int(int
irq,
void *dev_id,
struct pt_regs *regs)
{
read_data_from_buffer();
send_data_to_hardware_FIFO ();
}
read function
ssize_t usb_ep1_read (struct file *filp,
char
*buf,
size_t
count,
loff_t
*f_pos)
{
if (data_buffer_empty()) return 0;
else
copy_data_to_user_space();
return data_copyed;
}
copy_to_user(user_buf,
device_driver_buf,
size);
read function (blocking mode)
ssize_t usb_ep1_read (struct file *filp,
char
*buf,
size_t
count,
loff_t
*f_pos)
{
while(device_driver_buf_empty())
{
if (wait_event_interruptible(q_ep2,
device_driver_buf_not_empty))
return -ERESTARTSYS;
}
copy_data_to_user_space();
return data_copyed;
}
wait_queue_head_t
rq_EP2;
queue when multiple
user application
init_waitqueue_head(&rq_EP2); access this device
write function
ssize_t usb_ep2_write (struct file *filp,
char
*buf,
size_t
count,
loff_t
*f_pos)
{
if (data_buffer_full()) return 0;
else
copy_data_to_device_driver_buf();
if (no_transmission_now)
send_1st_data();
return data_copyed;
}
copy_from_user(device_driver_buf,
user_buf,
size);
Memory allocation by device
driver
• malloc ?
• kmalloc
• kfree
• vmalloc
• vfree
Prevent open device several times
int LED_flag;
int LED_init_module(void)
{
LED_flag=0;
…
}
int LED_open(struct inode *inode, struct file *filp)
{
if (LED_flag=0)
need semphora
{
LED_flag=1;
MOD_INC_USE_COUNT; to avoid access
conflict
return 0;
}
else
}
return -ENODEV;
int LED_release(struct inode *inode, struct file *filp)
{
LED_flag=0;
MOD_DEC_USE_COUNT;
return 0;
}
Manage several device by one
device driver
Application
Serial Port
Device Driver
UART 0
UART 0
Create device file
User space
cat /proc/devices
mknod /dev/Lamp c
Num1 Major device
Num2 Minor device
Device
File
device Device
file
File
Obtain device ID
Num1 Num2
ID
ID
• Minor dev. ID is used to identify
devices that is opened
Device Driver
read()
write()
open()
close()
Kernel Space
Manage several device by one
device driver
int dev_open(struct inode *inode,
struct file *filp)
{ int minor = MINOR(inode->i_rdev);
filp->private_data=sub_dev_dat[minor];
…
Allocate data space for each device
}
ssize_t dev_write(struct file *filp,
const char *buf,
size_t
count,
loff_t
*f_pos)
{
switch(*(filp->private_data))
{ … }
use data space allocated for the opened
}
device
Debug of Character device driver
Application
(using device)
设备文件 1
debug and monitor
Virtual Serial
port, canbe
access by
minicom
设备文件 2
Device Read
Debug Read
Device Write
Debug Write
Device Open
Debug Open
Device Close
Debug Close
Device Driver
Kernel
Related documents
Download