0% found this document useful (0 votes)
122 views33 pages

Linux Device Driver Sum Up 2

The document discusses Linux device driver programming. It provides an overview of useful functions for device I/O, memory allocation, copying data between user and kernel space, and managing interrupts. It also describes driver basics like initialization routines and file operations tables. Key aspects covered include using timers, inodes to represent files, the virtual file system interface, and kernel notions of time.

Uploaded by

NguyenVan Thanh
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
122 views33 pages

Linux Device Driver Sum Up 2

The document discusses Linux device driver programming. It provides an overview of useful functions for device I/O, memory allocation, copying data between user and kernel space, and managing interrupts. It also describes driver basics like initialization routines and file operations tables. Key aspects covered include using timers, inodes to represent files, the virtual file system interface, and kernel notions of time.

Uploaded by

NguyenVan Thanh
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 33

Linuxデバイスドライバプログラミング

1.
2. Useful Functions
 Function are for doing device i/o, to/from the i/o ports

#include <asm/io.h>
char inb(int port);
char inw(int port);
char outb(char val, int port);
char outw(char val, int port);

 Function for allocating small amounts of memory (<4096 bytes)

#include <linux/malloc.h>
void *kmalloc(int size, int priority);
void kfree_s(void *obj, int size);
void kfree(void *obj);
the priority is either:
 GFP_KERNEL - for normal cases
 GFP_ATOMIC - for allocating mem inside interrupts

 Function for allocating larger amounts of memory, such as device buffers

#include <linux/mm.h>
void *vmalloc(long size);
void vfree(void *obj);
 #include <linux/kernel.h>
int printk(char *fmt, ...);

 to copy data to/from user space

#include <asm/segment.h>
unsigned char get_fs_byte(const char *addr);
unsigned short get_fs_word(const short *addr);
unsigned long get_fs_long(const int *addr);
void put_fs_byte(char val, char *addr);
void put_fs_word(short val, short *addr);
void put_fs_long(long val, int *addr);
void memcpy_fromfs(void *to, void *from, unsigned long len);
void memcpy_tofs(void *to, void *from, unsigned long len);

 to set, clear, save and restore the interrupt enable flag

#include <asm/system.h>
void sti();
void cli();
void save_flags(unsigned long flags);
void restore_flags(unsigned long flags);

3. Driver Basics

 need to write an intialization routine

1 In order that the device driver is correctly initialized when the operating
system is booted, the xxx_init() routine must be executed.

2 To ensure this happens, add the following line to the end of the
chr_drv_init() function in the
/usr/src/linux/driver/char/mem.c file:

mem_start = xxx_init(mem_start);

and resave the file back to disk.

3 xxx_init() should first call register_chrdev() to register itself


and avoid device number contention
4 Returns:

 0 if no other character device has registered with the same


major number.

 non-0 if the call fails, presumably because another


character device has already allocated that major number.

 need to write functions to fill in the struct file_operations structure

4. Using Interrupts
 need to write an interrupt service routine (ISR)

 keep in mind that interrupts are asynchronous - they can occur at


any time

void foo_interrupt(unsigned int irq, struct pt_reg *reg);

1 irq is the number of the irq which triggered this routine

2 regs is a copy of the CPU registers


5. Kernel modules
 from a source file mod.c of a kernel module, you compile it with gcc -Wall -O2
-c mod.c
 Once you get the compiled mod.o, you can become root and install it to a
running kernel using insmod mod.o, and remove it from the kernel using rmmod
mod.
 Most modules does not do a lot of things when being loaded. They just register
some functions to the kernel, so that when the functionalities are needed, those
functions are called
/* define functions of the module */
int init_module(void) {
register_something(...);
return 0; /* should return 0 if succeed, non-zero if failure */
}

void cleanup_module(void) {
unregister_something(...);
}
/* extra information of the module */
 Before you unload the module, you must unregister its major number. This can
be done by calling the function unregister_chrdev(unsigned int major, const
char *name). Again, negative values indicate error. The only possible error is
EINVAL
6. The in-kernel file structure
 /*<linux/fs.h>*/
 Represents open files in the kernel
 similar to a FILE* in userspace. The current position in the file is stored here.
 Created by the kernel on open call. Tuc la khi chung ta goi ham
open thi kernel se tao ra 1 file structure.
 When a device is opened, a file structure is allocated and filled by the kernel to
keep information like
1 current access pointer
2 associated directory entry
3 open mode
4 file operations functions
 The kernel will keep this structure until all references1 to it are removed, by
process being terminated, explicitly close() the file, or used dup2() which
implicitly closed the file.
 At that time the release method is called, and the kernel deallocate the file
structure.
 The file structure is defined in <linux/fs.h>
1 loff_t f_pos: The current file position.
2 struct file_operations *f_op; The operations associated with the file
3 void *private_data;
 If the device driver writerwants to keep some data specific for the file
structure, it will allocate a structure (using kmalloc) and put the
pointer to the structure here when the device is first opened.
 The open system call sets this pointer to null before calling the open
method for the driver.
 private_data is a useful resource for preserving state information
across system call
7. The file operations

 /*<linux/fs.h>*/
 Connects system calls with our drivers code by providing pointers
to the drivers functions.
 static int foo_open(struct inode *inode, struct file *filp)
1 Called when the file is opened.
2 The function should return 0 if it decides that the file opening is valid, and
return an error code (like EPERM) otherwise, explaining why the call
failed.
3 The device number can be found in the inode passed to this function, using
inode->i_rdev.
 static ssize_t foo_read(struct file *file, char * buffer, size_t count, loff_t *filp);
1 This is called when the user program tries to read from the file.
2 At most count bytes should be written to the user-provided buffer,
probably using copy_to_user or one of its variants
3 The code should increment the number *filp by the number of bytes read.
4 The return value of this function should be the number of bytes read, or 0
if it reaches the end of file, or a negative error code to indicate an error.
 static ssize_t foo_write(struct file *file, char * buffer, size_t count, loff_t
*filp);
1 This is called when the user program tries to write into the file.
2 At most count bytes should be copied from the user-provided buffer,
probably using copy_from_user or one of its variants.
3 The code should increment the number *filp by the number of bytes
written.
4 The return value of this function should be the number of bytes written, or
a negative error code to indicate error.

8. Inode

 /*<linux/fs.h>*/
 Represents files.
 Lots of fields but mostly 2 fields of interest for drivers if it is a
device file:
1 dev_t i_rdev; /*<linux/types.h>*/
2 struct cdev *i_cdev; cdev's an internal kernel stucture,
representing char devices
9. The File Systems
 VFS (Virtual File System)
 All file systems in Linux rely on the VFS to allow them not only to coexist, but
also to interoperate.
 This enables you to use standard Unix system calls to read and write to different
file systems on different media.
 The purpose of VFS is to maintain an interface between system calls concerning
files and the file management code proper
 When a process performs a system call on files, it is directed at the VFS
 The VFS is responsible for performing operations which are independent of
the format of the filesystem concerned
 Then, the VFS redirects the request to the module managing the file

 copy data in memory to disk


 The Flow of VFS
 inode
1 Every file and directory in the file system is described by one and only one inode.
2 The inodes for each block group are kept in the inode table together with an inode
(allocation) bitmap
3 Elements
 mode: permissions
 owner information: The user and group identifiers
 size
 timestamps: The time that the inode was created
 Datablocks
1 Pointers to the blocks that contains the data
2 The first 12 are direct pointers to data blocks
3 The last three entries are for indirect blocks
 File-System Control Blocks


10. Timers and Time Management
 Kernel notion of Time
1 Kernel can comprehend and manage time through tick rate of system   timer in
hardware
2 When the system timer goes off, it issues an interrupt that the kernel  handles via a
special interrupt handler
 The Tick Rate: HZ (Programmable Interval Timer)
1 The tick rate is programmed on system boot based on a static  preprocessor define,
HZ
2 The value of HZ differs for each supported architectures
3 The kernel defines the value in <asm/param.h>
#define HZ 1000 /* internal kernel time frequency */
4 When writing kernel code, never assume that HZ has any given value
 Jiffies
1 The global variable jiffies holds the number of ticks that have occurred since   the
system booted
extern unsigned long volatile jiffies; /* <linux/jiffies.h> */
on boot: jiffies = 0;
each timer interrupt: jiffies++;
2 convert forms of jiffies
 second to jiffies: (second*HZ)
 jiffies to second: (jiffies/HZ)
ex) unsigned long next_tick = jiffies + _1;/* one tick from now */
unsigned long later = jiffies + 5 * HZ; /* five second from now */
unsigned long timeout = jiffies + HZ/2; /* timeout in

/* … */
if (time_after(jiffies, timeout)) {
/* we did not time out, good … */
} else {
/* we time out, error … */
}
3 Internal Representation of Jiffies
 The jiffies variable has 32 bit in size on 32-bit architectures and 64-bit on 64-bit
architectures
 overflow: 49.7days (1000HZ / 32-bit)
4 macros for comparing tick counts:
 #define time_after(unknown, known) ((long)(known) – (long)(unknown) < 0)
 #define time_before(unknown, known) ((long)(unknown) – (long)(known) < 0)
 #define time_after_eq(unknown, known) ((long)(unkown) – (long)(known) >=
0)
 #define time_before_eq(unknown, known) ((long)(known) – (long)(unkown) >=
0)
5
 Timers
1 Purpose: to delay execution of some function until a specified later time
2 Timer usage sequence:
 specify an expiration time
 specify a function to execute upon expiration
 activate the timer
 Timer is destroyed once it expires
 No limit on the number of timers
3 represented by struct timer_list:

struct timer_list {
struct list_head entry; /* entry in linked list of
unsigned long expires; /* expiration value, in jif
spinlock_t lock; /* lock protecting this tim
void (*function)(unsigned long); /* the timer handler f
unsigned long data; /* lone argument to the han
struct tvec_t_base_s *base; /* internal timer fiel
};

4 Using:

1. define using timer


struct timer_list my_timer;

2. initialize the timer


init_timer(&my_timer);

3. setting the timer funtions


my_timer.expires = jiffies + delay;
my_timer.data = 0;
my_timer.function = my_function;

4. activate the timer


add_timer(&my_timer);
5 Timer Related Functions:
 Modify expiration of an active timer:
 mod_timer(&my_timer, jiffies + new_delay)
 Deactivate a timer:
 del_timer(&my_timer)

 Delaying Execution – Small Delays :


1 Purpose: kernel code (especially drivers) needs a way to delay execution for
sometime without using timers or bottom-half mechanism
 usually to allow hardware time to complete a given task
 network card driver should wait at least the two microseconds before continuing
2 the solutions (depending on the semantics of the delay)
 Busy Looping
 Small Delays
 schedule_timeout()
3 Busy looping: spin in a loop until the desired number of clock ticks pass

unsigned long delay = jiffies + 5 * HZ;

while (time_before(jiffies, delay))


;

 Busy Looping for 10 ticks: chu y rang jiffies is a volatile variable


 unsigned long delay = jiffies + 10;
 while (time_before (jiffies, delay));
 Busy looking for 2 seconds:
 unsigned long delay = jiffies + 2 * HZ;
 while (time_before (jiffies, delay));

4 Small Delays:
 if you need precise delays, these calls are your best choice
 requires very short and rather precise delays
 kernel provide two functions for microsecond and millisecond delays

void udelay(unsigned long usecs); /* udelay(150) *

void mdelay(unsigned long msecs); /* mdelay(150) *

 delays execution by busy looping for the specified number of microseconds


or milliseconds
 using the functions
 the udelay() function should be called only for small delays
 larger delays on fast machines might result in overflow
 as delays over 1 millisecond for longer durations mdelay() function should
be called
5 schedule_timeout(): nham muc dich dua 1 task vao trang thai sleep trong 1 khoang
thoi gian la bao lau, sau do kernel se wake_up task do’
 optimal method:
 task to sleep until at least the specified time has elapsed
 the kernel wakes the task up and places it back on the runqueue
 using the functions:
/* set task’state to interruptible sleep */
set_current_state(TASK_INTERRUPTIBLE); /*TASK_UNINTER
/* take a nap and wake up in “s” seconds */
schedule_timeout(s * HZ);
 sleeping on a waiting queue, with a timeout
 first occur want to make desirable to wait for a specific event or wait for a
specified time to elapse
 call schedule_timeout() instead of schedule() after placing itself on a wait
queue
11. System Call in Linux
 ioctl ドライバメソッドのプロトタイプ
 int (*ioctl) (struct inode* inode, struct file* filp,unsigned int cmd, unsigned long
arg);
 inode と filp はファイル記述子 fd に対応する値
 cmd はマクロでシンボリック名を用意する事が多 い
 arg は整数値でもポインタでも unsigned long として渡される
 su dung thu vien sau: include/asm/ioctl.h

12. ドライバとカーネルのインターフェース
 スケジューリング関連サービス
1 schedule():プロセスの再スケジューリング
 宣言:
#include <linux/sched.h>
asmlinkage void schedule(void);
 機能: プロセスの再スケジューリングを行います。OSは、現在のプロ
セス・コントロール・リストより優先度が最も高いプロセスを選択し、
そのプロセスに制御を渡 します(実行可能状態にします)。この関数
を呼んだプロセス以外に制御が渡った場合、コールしたプロセスは「ス
リープ状態」に遷移します。
2 sleep_on():プロセスをスリープ状態にする
 宣言:
#include <linux/sched.h>
void sleep_on(struct wait_queue **p);
void interruptible_sleep_on(struct wait_queue **p);
 機能:
 当該プロセスを「スリープ状態」に遷移させます。すなわち、他の
プロセスに制御を譲ります。この関数は、割込み禁止状態で発行し
なければなりません。Chuyen process vao trang thai sleed.
 スリープ状態のプロセスは、ドライバ内の sleep_on()を発行した次の
ステップに進まなくなります。永久に進まない場合そのプロセスは
俗に言う 「お亡くなりになった」状態になりますが、これを避け
るのが後述の wake_up()です。ドライバで sleep_on()を発行するのは、
一般的にハー ドウェアに I/O 命令を出して、その I/O 完了を待って
いるときです。Neu lenh sleep_on() duoc phat sinh thi process cua driver
se khogn the chuyen den buoc tiep theo, thuc hien cau lenh tiep theo duoc.
Chung ta se dung lenh wake_up() de thoat khoi tinh trang sleep_on(). Thuc
chat cua lenh sleep_on() nay la phat ra cac cau lenh I/O vao hardware, va
cho*` cho den khi cac I/O nay thuc hien xong
 DOS のようなシングルタスクの OS の場合は、ここで for()ループな
どで完了を待つのですが、Linux のようなマルチタスクの OS でこれ
をやると、 他のプロセスに制御が回らなくなり、システム全体が
重くなってしまうので、これを避けるために sleep_on()を使用します。
 sleep_on()と interruptible_sleep_on()の使い分けが、良く分かっていな
かったりします(^_^);。 interruptible_sleep_on()は、「上位プロセス
で SIGINT などを発行してシステムコールを中断できるようにす
る」のではないかと推測していますが、どうでしょう。誰か知って
たら教えてください。
3 wake_up():プロセスを起こす
 宣言:
#include <linux/sched.h>
void wake_up(struct wait_queue **q);
 機能: 「スリープ状態」(I/Oウェイト中)のプロセスを、「実行可
能状態」に遷移させます。スリープ中のプロセスのコンテキストは、引
数で識別されます。指定したプロセスがスリープ状態でなければ、なに
もしません。
 タイマー機能
1 add_timer():タイマー監視を開始する
 宣言:
#include <linux/timer.h>
void add_timer(struct timer_list * timer);
struct timer_list {
  struct timer_list *next; /* NULL のまま */
  struct timer_list *prev; /* NULL のまま */
  unsigned long expires; /* タイムアウト時間(10ms単位)*/
  unsigned long data; /* 任意。複数のイベントを1つの
    タイマーハンドラで管理する場合、この引数で認識する。*/
  void (*function)(unsigned long); /* タイマーハンドラ関数。
    引数として'data'が渡される。 */
};
 機能: カーネルのタイマーリストに監視したいイベントを追加します。
'expires' ×10ミリ秒経過すると、関数 'function' に制御が移ります。
2 del_timer():タイマー監視を中止する
 宣言:
#include <linux/timer.h>
int del_timer(struct timer_list * timer);
 機能: カーネルのタイマーリストからイベントを削除します。タイムア
ウトして 'function' が呼ばれた後は、実行する必要はありません。
3 udelay():μ 秒単位の遅延
 宣言:
#include <asm/delay.h>
__inline__ void udelay(unsigned long usecs);
 機能: 1ミリ秒以下の非常に短時間の遅延をしたい場合に使用します。
当該マクロを発行しても、スケジューリングは行われません
 メモリ操作関連
1 verify_area():ユーザ領域の妥当性検査
 宣言:
#include <linux/mm.h>
int verify_area(int type, const void * addr, unsigned long size);
 機能: 能 ユーザ領域(ユーザから渡された引数)の妥当性(整合性およ
び権限)を検査します。戻り値≦0の場合、ユーザ領域は使用不可(上
位の引数誤り)です。な お、ioctl()システムコールの第3引数(int また
は 任意のポインタ)がポインタの場合、そのポインタは連続したメモ
リ領域の先頭を指している必要があります。つまり、その引数が構造体
などのポインタであ り、そのメンバがさらにポインタである場合は、
メモリ空間が異なるため、ドライバ側ではその実体にはアクセスするこ
とができません。
2 memcpy_fromfs():ユーザ領域からのメモリ転送
 宣言:
#include <linux/asm-i386/segment.h>
void memcpy_fromfs(void *to, void *from, int size);
 機能: ユーザ領域からカーネル領域へのメモリコピーを行います。
※システムコールの引数にポインタが指定された場合、カーネル側(ド
ライバ)にポインタが渡されますが、ユーザAPとカーネルではメモリ
空間が異なるた め、ドライバ側ではポインタが指している領域に直接
にはアクセスできません。その場合当該関数を使用して、ポインタが指
している実体を、カーネル側で用意 した内部バッファにコピーしてか
らアクセスしてやる操作が必要となります。
 De chuyen du lieu tu user space vao kernel space: The device driver
copies data between the kernel'ys address space and the user
program'ys address space whenever the user makes a read() or
write() system call
 Several Linux routines such as, memcpy_*fs() and
put_fs*() enable device drivers to transfer data across the
usersystem boundary. Data may be transferred in bytes, words, or
in buffers of arbitrary sizes
 For example, memcpy_fromfs() transfers an arbitrary
number of bytes of data from user space to the device, while
get_fs_byte() transfers a byte of data from user space.
 Similarly, memcpy_tofs() and put_fs_byte() write
data to user space memory
3 memcpy_tofs():ユーザ領域へのメモリ転送
 宣言:
#include <linux/asm-i386/segment.h>
void memcpy_tofs(void *to, void *from, int size);
 機能: カーネル領域からユーザ領域へのメモリコピー。memcpy_fromfs()
を参照のこと。
4 memcmp():メモリ内容の比較
 宣言:
#include <asm/string.h>
inline int memcmp(const void * cs,const void * ct,size_t count)
 機能: memcmp(3) ライブラリ関数と同様です。カーネル空間内の変数を
比較します。
5 kmalloc():動的メモリ取得
 宣言:
#include <linux/malloc.h>
void *kmalloc(int size, int priority);
 機能: malloc(3) ライブラリ関数と同様です。カーネル空間内のヒープを
取得します。
 リソース関連
1 request_irq():IRQの使用宣言
 宣言:
#include <linux/sched.h>
#include <linux/signal.h>
int request_irq(
  unsigned int irq, /* IRQ番号 */
  void (*handler)(int, struct pt_regs *), /* 割り込みハンドラ */
  unsigned long flags, /* フラグ(SA_INTERRUPT) */
  const char *device); /* デバイス名 */
 機能: IRQと割り込みハンドラをカーネルに登録します。IRQの使
用状況は /proc/interrupt で確認できます。
2 free_irq():IRQの解放
 宣言:
#include <linux/sched.h>
void free_irq(unsigned int irq); /* arch/i386/kernel/irq.c */
 機能: 使用していたIRQを解放します。

3 request_dma():DMAチャネルの予約
 宣言:
#include <asm/dma.h> int request_dma(
  unsigned int dmanr, /* DMA番号 */
  char * device_id); /* デバイス名 */
 機能: DMAチャネルを予約します。使用状況は /proc/dma で確認できま
す。
4 free_dma():DMAの解放
 宣言:
#include <linux/sched.h>
free_dma(unsigned int dmanr);
 機能:
使用中のDMAを解放します。
5 request_region():I/Oポートの予約
 宣言:
#include <linux/ioport.h>
int request_region(
unsigned int from, /* 開始I/Oアドレス */
unsigned int extent, /* 使用バイト数 */
const char * device_id); /* デバイス名 */
 機能: I/Oポートアドレスを予約します。使用状況は /proc/ioports で確
認できます。

6 release_region():I/Oポートの解放
 宣言:
#include <linux/ioport.h>
int release_region(
unsigned int from, /* 開始I/Oアドレス */
unsigned int extent); /* 使用バイト数 */
 機能: 使用中のI/Oポートを解放します。

7 check_region():I/Oポートの検査
 宣言:
#include <linux/ioport.h>
int check_region(
unsigned int from, /* 開始I/Oアドレス */
unsigned int extent); /* 使用バイト数 */
 機能: I/Oポートが使用されているかどうかを検査します。

8 DMAサポートマクロ群
 宣言:
#include <asm/dma.h>
clear_dma_ff(DMAチャネル番号);
set_dma_mode(DMAチャネル番号, dma_mode); /* full address */
set_dma_addr(DMAチャネル番号, 先頭アドレス+転送済みバイト数);
set_dma_count(DMAチャネル番号, 転送カウント);
enable_dma(DMAチャネル番号);
 機能:
 能 static __inline__ void enable_dma(unsigned int dmanr)
指定のDMAチャネルを使用可能にします(単一マスク・ビットの
クリア)
 static __inline__ void disable_dma(unsigned int dmanr)
指定のDMAチャネルを使用不可にします(単一マスク・ビットの
設定)
 static __inline__ void clear_dma_ff(unsigned int dmanr)
'DMA Pointer Flip Flop'をクリアします。
 コーディング・サンプル
clear_dma_ff(DMA);
outb(下位バイト、アドレス);
outb(上位バイト、アドレス);
clear_dma_ff(DMA);
下位バイト = inb(アドレス);
上位バイト = inb(アドレス);
 この処理は、割り込み不可の状態で行なうこと!
 static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
指定のDMAチャネルのモード設定を行ないます。
 static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
指定のDMAチャネルに対して、転送アドレスとページビットの設
定を行ないます。DMAフリップフロップはクリアされているもの
と見なしています。
 static __inline__ void set_dma_count(unsigned int dmanr, unsigned int
count)
指定のDMAチャネルに対して、転送サイズの設定(DMA1−3
は最大64K、DMA5−7は最大128K)を行ないます。注
意:1を引く、またはワード転送の場合2で割る、などの計算は内
部で行っています。
 static __inline__ int get_dma_residue(unsigned int dmanr)
DMAの転送残りカウントを取得します。
 その他
1 register_chrdev():ハンドラの登録
 宣言:
#include <linux/fs.h>
int register_chrdev( /* linux/fs/devices.c */
unsigned int major, /* デバイスメジャー番号 */
const char * name, /* 内部デバイス名 */
struct file_operations *fops) /* ファイル操作構造体 */
struct file_operations { /* ファイル操作関数群管理構造体 */
  int (*lseek) (struct inode *, struct file *, off_t, int);
  int (*read) (struct inode *, struct file *, char *, int);
  int (*write) (struct inode *, struct file *, char *, int);
  int (*readdir) (struct inode *, struct file *, struct dirent *, int);
  int (*select) (struct inode *, struct file *, int, select_table *);
  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  void (*release) (struct inode *, struct file *);
  int (*fsync) (struct inode *, struct file *);
  int (*fasync) (struct inode *, struct file *, int);
  int (*check_media_change) (dev_t dev);
  int (*revalidate) (dev_t dev);
};
 機能: 仮想ファイルシステムにキャラクタ・デバイスとして登録します。
この後、割り込みハンドラ(ボトム・ハーフ・ルーチン)が有効になり
ます。
file_operations 構造体の各エントリは、アプリケーションが対応するシス
テムコールをコールした際の対応ハンドラとなります。
2 printk():カーネル用 printf()
 宣言:
#include <linux/kernal.h>
asmlinkage int printk(const char *fmt, ...); /* printk.c */
 機能: printf(3) のサブセットのようなものです。出力は起動時ならコン
ソールに表示され、起動後は syslogd デーモンを介して /var/log/syslog な
どに書き込まれます。

3 send_sig():自分自身(のプロセス)にシグナルを送る
 宣言:
#include <linux/sched.h>
int send_sig(unsigned long sig,struct task_struct * p,int priv) /* exit.c */
機能:
4 notify_parent():親プロセスに知らせる
 宣言:
#include <linux/sched.h>
void notify_parent(struct task_struct * tsk) /* exit.c */
 機能: 自分自身が死んだことを親プロセスに知らせます。
5 outb():ポートへの1バイト出力
 宣言:
#include <asm/io.h>
void outb(unsigned char value, unsigned address);
 機能
6 inb():ポートからの1バイト入力
 宣言:
#include <asm/io.h>
void inb(unsigned address)
 機能:
13. ドライバと上位APとのインターフェース
 file_operations 構造体: ドライバ内部の file_operations 構造体(register_chrdev() 参
照)は以下のように定義されています。この例を使って説明していきます。筆者
が使っていない引数などは、当然解説不能です(^_^);
全てのファイルシステムに全ての関数が定義されているわけではない.

 open():当該デバイスをアプリケーションが open(2) すると、wd33c_open() に制御


が移ります。この関数は、以下の引数を取ります。
  static int wd33c_open(
    struct inode * inode, /* inode 情報*/
    struct file * filp, /* file 情報*/
各構造体の定義は、/usr/include/linux/fs.h にあります。ちょっと長いですが、引用
します。
struct inode {
  kdev_t i_dev;
  unsigned long i_ino;
  umode_t i_mode;
  nlink_t i_nlink;
  uid_t i_uid;
  gid_t i_gid;
  kdev_t i_rdev;
  off_t i_size;
  time_t i_atime;
  time_t i_mtime;
  time_t i_ctime;
  unsigned long i_blksize;
  unsigned long i_blocks;
  unsigned long i_version;
  unsigned long i_nrpages;
  struct semaphore i_sem;
  struct inode_operations *i_op;
  struct super_block *i_sb;
  struct wait_queue *i_wait;
  struct file_lock *i_flock;
  struct vm_area_struct *i_mmap;
  struct page *i_pages;
  struct dquot *i_dquot[MAXQUOTAS];
  struct inode *i_next, *i_prev;
  struct inode *i_hash_next, *i_hash_prev;
  struct inode *i_bound_to, *i_bound_by;
  struct inode *i_mount;
  unsigned short i_count;
  unsigned short i_flags;
  unsigned char i_lock;
  unsigned char i_dirt;
  unsigned char i_pipe;
  unsigned char i_sock;
  unsigned char i_seek;
  unsigned char i_update;
  unsigned short i_writecount;
  union {
    struct pipe_inode_info pipe_i;
    struct minix_inode_info minix_i;
    struct ext_inode_info ext_i;
    struct ext2_inode_info ext2_i;
    struct hpfs_inode_info hpfs_i;
    struct msdos_inode_info msdos_i;
    struct umsdos_inode_info umsdos_i;
    struct iso_inode_info isofs_i;
    struct nfs_inode_info nfs_i;
    struct xiafs_inode_info xiafs_i;
    struct sysv_inode_info sysv_i;
    struct affs_inode_info affs_i;
    struct ufs_inode_info ufs_i;
    struct socket socket_i;
    void * generic_ip;
  } u;
};
なんかごちゃごちゃと並んでいますが、私は全く使っていないのでよくわかりま
せん。ブロック・デバイスのドライバを書く場合には必須になると思います。
struct file {
  mode_t f_mode; /* O_READ|O_WRITE など?*/
  loff_t f_pos;
  unsigned short f_flags; /* O_BINARY など?*/
  unsigned short f_count; /* 同時にオープンしているプロセス数? */
  unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
  struct file *f_next, *f_prev;
  int f_owner; /* pid or -pgrp where SIGIO should be sent */
  struct inode * f_inode;
  struct file_operations * f_op;
  unsigned long f_version;
  void *private_data; /* needed for tty driver, and maybe others */
};
 release():当該デバイスを open(2) している「す べての」アプリケーシ ョンが
close(2) するかまたは終了する時、wd33c_release() に制御が移ります。この関数は、
以下の引数を取ります。
static void wd33c_release(
    struct inode * inode, /* inode 情報*/
    struct file * filp); /* file 情報*/
当該ドライバの場合は、オープン中フラグを落としているくらいで、特に何も
行っていません。
 ioctl():当該デバイスを open(2) しているアプリケーションが ioctl(2) を発行すると、
wd33c_ioctl() に制御が移ります。この関数は、以下の引数を取ります。
  static int wd33c_ioctl(
    struct inode * inode, /* inode 情報*/
    struct file * filp, /* file 情報*/
    unsigned int iocmd, /* ユーザ定義コマンド */
    unsigned long ioarg); /* コマンドへの引数 */
14. アプリからデバスまで
 ファイルに対するシステムコール:
1 open ファイルを開ける
2 close 閉じる
3 read ファイルからデータを読む.
4 write ファイルにデータを書き込む.
5 ioctl デバイスの属性を変更したり,制御したりするのに使用する.例えば,
通信ポートのスピードを変更するなど.
これらはアプリケーションが利用する関数群である.

15. Phan biet RTOS va LINUX
 μ ITRON chang han thi truc tiep truy nhap xuong hardware, do vay khogn can
dinhnghia driver, cung khong can co API.
 Doi voi he dieu hanh Linux thi lai gom co 2 che do user mode va kernel mode. Nhung
ung dung application o user mode thi khogn the truy nhap truc tiep vao hardware,
application phai thong qua standard API(application interface), khoi dong device driver,
roi sau do moi co the truy nhap vao phan cung hardware duoc.
 ドライバーは、実行時にロード可能なモジュールとして作成されます。カーネルと一緒にコンパイルされ
て1つのオブジェクトとして組み込まれる「スタティックリンク方式」と、実行時に必要に応じてメモリへ展開

する「ローダブル方式」があります。

16. アプリケーションがデバイスのデータを読み取る処理の流れ
 Tu application su dung system call (la interface giua application va OS) de nham muc dich
truy xuat vao device. Chung ta co cac system call o application nhu sau:
1 open ファイルを開ける
2 close 閉じる
3 read ファイルからデータを読む.
4 write ファイルにデータを書き込む.
5 ioctl デバイスの属性を変更したり,制御したりするのに使用する.例えば,
通信ポートのスピードを変更するなど.
これらはアプリケーションが利用する関数群である
 chung ta khong the nhin thay device, ma chung ta chi co the nhin thay device gia tuong
thong qua 1 loai file dac biet「/dev/xxx」
 khi ma tu application access vao device thi kernel se su dung number duoc dinh nghia
cho device, tim kiem bang device driver table thogn qua major number. Major number
dung de phan biet cac device khac loai voi nhau. Ngoai ra con 1 tham so nua la minor
number. Khi muon phan biet cac thiet bi co cung major number hay noi cach khac la
cung loai thiet bi thi ta dung minor number. Minor number duoc ghi vao device special
file(inode) tuy nhien khi kernel goi device driver thi tham so minor number se duoc giao
cho thiet bi. Ve phai device thi minor number thuc chat la so thu tu cua port ma device
duoc gan vao.

 アプリケーションがデバイスのデータを読み取る処理の流れ:  デバイスドライバは以下の
3つの部分から構成されます。

1 Device Driver Initialization


 この登録とは、デバイススペシャルファイルに記述した「メジャー番号」と、そのデバイス操作
のための関数を結びつける事を言います。 Chu y rang viec initialization nay se duoc ghi

vao device special file. Gom co 3 thong so: major number, device name, function table
 In order that the device driver is correctly initialized when the
operating system is booted, the xxx_init() routine must be
executed.
 To ensure this happens, add the following line to the end of the
chr_drv_init() function in the
/usr/src/linux/driver/char/mem.c file:
mem_start = xxx_init(mem_start); and resave the file back to
disk.
 Chu y cac ham sau:
 Read:   デ バ イ ス か ら デ ー タ を 取 り 出 す た め に 使 用 し ま す 。 通 常 は 、
copy_to_user を使用してカーネル領域からユーザー領域にデータを転送
します。処理が成功した場合には読み出したバイト数を戻り値として返しま
す。
 Write:   デ ー タ を デ バ イ ス に 送 る た め に 使 用 し ま す 。 通 常 は 、
copy_from_user を使用してユーザー領域からカーネル領域にデータを転
送します。処理が成功した場合、書き込んだバイト数を戻り値として返しま
す。
2 Top Half:

Tuong ung voi cac system call o application chang han nhu system call open(); read();
thi kernel se goi cac function tuong ung xxx_open(); xxx_read();
3 Bottom Half:
Thong qua interrupt o hardware se khoi dong^ cac handler()
Thuc chat o day chinh la sudung clock phan cung「interrupible_sleep_on()」
4 Vi du:
  例えば、read()メソッドにより、外部のハードウエアから情報を読み取る場合、アプリケーショ
ンは標準 API をコールします。これを受けて、システムコールライブラリの read()メソッドが

コールされ、データ転送の要求を i/o デバイスに対して発行します。通常は、メモリ空間に

マッピングされた i/o デバイスのレジスタに、要求の書き込みを行うことで発行が行われます。

  i/o デバイスは、要求されたデータをデータ読み出しのためのレジスタ、あるいはメモリ空間
にセットし、準備が完了すると割り込みにより、カーネルへ通知を行います。

  通知を受けたカーネルは、トップハーフルーチンを呼び出し i/o デバイスが準備したデー


タの読み出しを行います。実際は、処理をボトムハーフルーチンに委ね、ここが i/o デバイス

のレジスタをアクセスして、データを取得します。同期転送の場合、 read()メソッドの中では、

処理の終了を待つ「interrupible_sleep_on()」がコールされています。この場合は、このボトム

ハーフルーチンの終了を受けて、カーネルが待ちの解除を行います。

17. カーネルへの割り込みハンドラ
 前述のように、i/o デバイスが動作を完了して、その終了をカーネルに通知するためには、
割り込みが使用されます。 Khi ma device ket thuc qua trinh xuly tin hieu, nham muc
dich thong bao cho kernel biet thi no phai su dung ngat
 この割り込みを受けて、デバイスドライバのトップハーフルーチンを起動するために、割り
込みハンドラの登録を行う必要があります。Vi trong phan bottom half thi chung ta co su
dung cau lenh 「interrupible_sleep_on()」tuc la se dung lai khogn hoat dong^, nham doi*
cho viec ghi/ doc du lieu ra phancung ket thuc. Khi viec ghi/doc nay ket thuc thi se
xuat hien ngat, ngat nay se phai lam nhiem vu wake_up_interruptible de cho
ham bottom half xu ly tiep. Do vay chung ta phai dang ky
ngat int request_irq o application va o kernel thi chung ta phai lam la
xxx_interrupt(int irq)
 登録には標準の API が用意されています。割り込みハンドラへのポインタ引数には、デバイスドライバの
トップハーフルーチンを設定します。

18. カーネルへのデバイスドライバの組み込み
 デバイスドライバを組み込んで使用する方法として、 Linux では2通りの方法が提供され
ています。Chung ta co 2 cach de co the dang ky device driver voi kernel
  1つは、デバイスドライバを、カーネルにスタティックリンクしてしまう方法です。Cach thu

nhat la bien device driver thanh static link voi kernel


1 これは、カーネルのコンパイル時に、ドライバ部分も合わせてコンパイルし、1つのオ
ブジェクトとしてリンクしてしまう方法です。Khi kernel compile thi ket hop voi device
driver de tro thanh 1 object
2  カーネルブート時に、デバイスドライバの登録と初期化の処理さえ行えば、直ぐに
使用可能になります。Khi kernel boot thi device driver se register va initialization
do vay co the su dung duoc ngay lap tuc
3  この方法では、スタティックなメモリ領域にドライバが常駐することになり、安全性の
高い組み込み方法ですが、メモリを多く消費してしまう欠点があります。 μITRON や
通常の RTOS のほとんどは、この方法で組み込みを行っています。 Chu y rang
phuong phap nay thi ta sedung static memory do vay se lam ton^ bo^ nho*’ nhung
lai dam bao duoc do^ an toan cao
  もう1つの方法として、Linux には「ローダブルモジュール方式がサポートされています。これは、必要
に応じてメモリへロードして使用し、不要になったら、メモリから取り除くことができるものです。普段使用

しないドライバモジュールは、メモリへ常駐させる必要が無いため、最小限のメモリでシステムを動作させ

ることが可能になります。

1 Most microkernel advantages due to modularity


2 Most modern operating systems implement kernel modules
 Uses object-oriented approach. Tuc la se compile thanh 1 object
 Each talks to the others over known interfaces

19.

You might also like