|
|
○ はじめに
今回は、ユーザプロセスから発行される ioctl システムコールに対応してみます。ioctl は、read や write で行うことが適当でない、デバイスの設定を変えたりする場合に良く使われます。
ここでは、poll.c を改造して ioctl システムコールに対応させてみましょう。
○ サンプルプログラム
ioctl システムコールに対応したサンプルプログラム ioctl.c、 chardev_ioct.h です。 はじめに のページから、 全てのサンプルプログラムを 固めた tar ボールをダウンロードすることもできます。
簡単な試験プログラム ioctl_test.c も用意してみました。 ioctl.c をコンパイルして insmod で組み込み、ioctl_test.c の実行プログラムを実行すると
Tell driver to update internal value ... Query driver internal value ... Internal value = 0 Tell driver to update internal value via pointer ... Query driver internal value via pointer ... Internal value = 1 Reset reference counter ...
と表示されます。
--- ioctl.c ---
1 #include <asm/semaphore.h>
2 #include <asm/uaccess.h> /* copy_from_user, copy_to_user */
3 #include <linux/errno.h>
4 #include <linux/fs.h> /* inode, file, file_operations */
5 #include <linux/ioctl.h> /* ioctl */
6 #include <linux/kernel.h> /* printk() */
7 #include <linux/module.h>
8 #include <linux/poll.h> /* POLLIN ... */
9 #include <linux/wait.h> /* wait_queue_head_t */
10 #include <linux/sched.h> /* wait_event_interruptible, wake_up_interruptible */
11
12 #include "chardev_ioctl.h"
13
14
15 MODULE_AUTHOR( "Ryo" );
16 MODULE_DESCRIPTION( "Sample character device driver." );
17 MODULE_LICENSE( "GPL" );
18
19
20 static int devmajor = 77;
21 static char* devname = "chardev";
22 static char* msg = "module [poll.o]";
23
24
25 #define MAXBUF 32
26 static unsigned char devbuf[ MAXBUF ];
27 static int buf_pos;
28
29 static struct semaphore chardev_sem;
30 static wait_queue_head_t write_q;
31 static wait_queue_head_t read_q;
32
33
34 /*
35 * ログ出力フォーマット用関数
36 */
37 static void
38 log_format( char* body )
39 {
40 if ( current != NULL ) {
41 printk( KERN_INFO "%s : PID = %-5i cmdline = \"%-10s\" : %s\n",
42 msg, current->pid, current->comm, body );
43 }
44 else {
45 printk( KERN_INFO "%s : %s\n", msg, body );
46 }
47 }
48
49
50 /*
51 * open()
52 */
53 static int
54 chardev_open( struct inode* inode, struct file* filp )
55 {
56 log_format( "open() called" );
57 return 0;
58 }
59
60
61 /*
62 * release()
63 */
64 static int
65 chardev_release( struct inode* inode, struct file* filp )
66 {
67 log_format( "close() called" );
68 return 0;
69 }
70
71
72 /*
73 * read()
74 * ユーザプロセスに対して内部バッファの内容を転送する。
75 * 転送した内容は内部バッファから消える。
76 */
77 static ssize_t
78 chardev_read( struct file* filp, char* buf, size_t count, loff_t* pos )
79 {
80 int copy_len;
81 int i;
82
83 log_format( "read() called" );
84
85 if ( down_interruptible( &chardev_sem ) ) {
86 log_format( "down_interruptible for read failed" );
87 return -ERESTARTSYS;
88 }
89
90 while ( buf_pos == 0 ) {
91 up( &chardev_sem );
92
93 if ( filp->f_flags & O_NONBLOCK ) {
94 log_format( "read return because O_NONBLOCK is specified" );
95 return -EAGAIN;
96 }
97
98 log_format( "read process going to sleep" );
99
100 if ( wait_event_interruptible( read_q, ( buf_pos != 0 ) ) )
101 return -ERESTARTSYS;
102
103 if ( down_interruptible( &chardev_sem ) ) {
104 log_format( "down_interruptible for read failed" );
105 return -ERESTARTSYS;
106 }
107 }
108
109
110 log_format( "read process going to read" );
111
112 if ( count > buf_pos )
113 copy_len = buf_pos;
114 else
115 copy_len = count;
116
117 if ( copy_to_user( buf, devbuf, copy_len ) ) {
118 up( &chardev_sem );
119 log_format( "copy_to_user failed" );
120 return -EFAULT;
121 }
122
123 *pos += copy_len;
124
125 for ( i = copy_len; i < buf_pos; i ++ )
126 devbuf[ i - copy_len ] = devbuf[i];
127
128 buf_pos -= copy_len;
129
130 up( &chardev_sem );
131 wake_up_interruptible( &write_q );
132
133 return copy_len;
134 }
135
136
137 /*
138 * write()
139 * ユーザプロセスから転送された内容を内部バッファに書き込む。
140 */
141 static ssize_t
142 chardev_write( struct file* filp, const char* buf, size_t count, loff_t* pos )
143 {
144 int copy_len;
145
146 log_format( "write() called" );
147
148 if ( down_interruptible( &chardev_sem ) ) {
149 log_format( "down_interruptible for write failed" );
150 return -ERESTARTSYS;
151 }
152
153 while ( buf_pos == MAXBUF ) {
154 up( &chardev_sem );
155
156 if ( filp->f_flags & O_NONBLOCK ) {
157 log_format( "write return because O_NONBLOCK is specified" );
158 return -EAGAIN;
159 }
160
161 log_format( "write process going to sleep" );
162
163 if ( wait_event_interruptible( write_q, ( buf_pos != MAXBUF ) ) )
164 return -ERESTARTSYS;
165
166 if ( down_interruptible( &chardev_sem ) ) {
167 log_format( "down_interruptible for write failed" );
168 return -ERESTARTSYS;
169 }
170 }
171
172
173 log_format( "write process going to write" );
174
175 if ( count > ( MAXBUF - buf_pos ) )
176 copy_len = MAXBUF - buf_pos;
177 else
178 copy_len = count;
179
180 if ( copy_from_user( devbuf + buf_pos, buf, copy_len ) ) {
181 up( &chardev_sem );
182 log_format( "copy_from_user failed" );
183 return -EFAULT;
184 }
185
186 *pos += copy_len;
187 buf_pos += copy_len;
188
189 up( &chardev_sem );
190 wake_up_interruptible( &read_q );
191
192 return copy_len;
193 }
194
195
196 /*
197 * poll()
198 * select / poll システムコール発行時に実行される。
199 */
200 static unsigned int
201 chardev_poll( struct file* filp, poll_table* wait )
202 {
203 unsigned int retmask = 0;
204
205 log_format( "poll() called" );
206
207 poll_wait( filp, &read_q, wait );
208 poll_wait( filp, &write_q, wait );
209
210 if ( buf_pos != 0 ) {
211 retmask |= ( POLLIN | POLLRDNORM );
212 log_format( "POLLIN | POLLRDNORM" );
213 }
214
215 if ( buf_pos != MAXBUF ) {
216 retmask |= ( POLLOUT | POLLWRNORM );
217 log_format( "POLLOUT | POLLWRNORM" );
218 }
219
220 /*
221 読み込みデバイスが EOF の場合は retmask に POLLHUP を設定
222 デバイスがエラー状態である場合は POLLERR を設定
223 out-of-band データが読み出せる場合は POLLPRI を設定
224 */
225
226 return retmask;
227 }
228
229
230 /*
231 * ioctl()
232 * 複数プロセスが同一変数を操作するため、セマフォやスピンロック等での
233 * ロックを考慮すべきだが、ioctl の実装を例示したいだけなのでロックは
234 * 一切使っていない。。。
235 */
236 static int
237 chardev_ioctl(
238 struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg )
239 {
240 static int ioctl_value = 0;
241 int ret = 0;
242
243 log_format( "ioctl() called" );
244
245 switch( cmd ) {
246 case CHARDEV_IOCTL_TELL :
247 log_format( "IOCTL_TELL" );
248 ioctl_value = arg;
249 break;
250
251 case CHARDEV_IOCTL_SET :
252 log_format( "IOCTL_SET" );
253 if ( get_user( ioctl_value, ( int* )arg ) )
254 return -EFAULT;
255 break;
256
257 case CHARDEV_IOCTL_QUERY :
258 log_format( "IOCTL_QUERY" );
259 return ioctl_value;
260
261 case CHARDEV_IOCTL_GET :
262 log_format( "IOCTL_GET" );
263 if ( put_user( ioctl_value, ( int* )arg ) )
264 return -EFAULT;
265 break;
266
267 case CHARDEV_IOCTL_RESET :
268 log_format( "IOCTL_RESET" );
269
270 while ( MOD_IN_USE )
271 MOD_DEC_USE_COUNT;
272
273 MOD_INC_USE_COUNT;
274 break;
275
276 default:
277 return -EINVAL;
278 /* POSIX 標準では -ENOTTY らしい */
279 }
280
281 return 0;
282 }
283
284
285 static struct file_operations chardev_fops =
286 {
287 owner : THIS_MODULE,
288 read : chardev_read,
289 write : chardev_write,
290 poll : chardev_poll,
291 ioctl : chardev_ioctl,
292 open : chardev_open,
293 release : chardev_release,
294 };
295
296
297 /*
298 * モジュールの初期処理
299 * insmod 時に呼ばれる
300 */
301 int
302 init_module( void )
303 {
304 if ( register_chrdev( devmajor, devname, &chardev_fops ) ) {
305 log_format( "register_chrdev failed" );
306 return -EBUSY;
307 }
308
309 sema_init( &chardev_sem, 1 );
310 init_waitqueue_head( &write_q );
311 init_waitqueue_head( &read_q );
312
313 log_format( "loaded into kernel" );
314 return 0;
315 }
316
317
318 /*
319 * モジュールの解放処理
320 * モジュールの参照数が 0 であれば、rmmod 時に呼ばれる
321 */
322 void
323 cleanup_module( void )
324 {
325 if ( unregister_chrdev( devmajor, devname ) ) {
326 log_format( "unregister_chrdev failed" );
327 /* 対策無し ... */
328 }
329
330 log_format( "removed from kernel" );
331 }
332
333
334 /* End of ioctl.c */
--- chardev_ioctl.h ---
1 #ifndef ___chardev_ioctl_h_included_
2 #define ___chardev_ioctl_h_included_
3
4 #include <linux/ioctl.h>
5
6 /*
7 * /dev/chardev に対する ioctl() コマンドに使うマジックナンバー
8 *
9 * /usr/src/linux/Documentation/ioctl-number.txt に記述されている
10 * Code とぶつからないものを選ぶ
11 */
12 #define CHARDEV_IOCTL_MAGIC 0xAE
13
14
15 #define CHARDEV_IOCTL_TELL _IOC( _IOC_NONE, CHARDEV_IOCTL_MAGIC, 0, 0 )
16 #define CHARDEV_IOCTL_SET _IOC( _IOC_WRITE, CHARDEV_IOCTL_MAGIC, 1, sizeof( int ) )
17 #define CHARDEV_IOCTL_QUERY _IOC( _IOC_NONE, CHARDEV_IOCTL_MAGIC, 2, 0 )
18 #define CHARDEV_IOCTL_GET _IOC( _IOC_READ, CHARDEV_IOCTL_MAGIC, 3, sizeof( int ) )
19 #define CHARDEV_IOCTL_RESET _IOC( _IOC_NONE, CHARDEV_IOCTL_MAGIC, 4, 0 )
20
21
22 #endif
23
24
25 /* End of chardev_ioctl.h */
--- ioctl_test.c ---
1 #include <fcntl.h>
2 #include <stdlib.h>
3 #include <sys/ioctl.h>
4
5 #include "chardev_ioctl.h"
6
7
8 int
9 main( void )
10 {
11 int setval = 0;
12 int retval;
13 int fd;
14
15 if ( ( fd = open( "/dev/chardev", O_RDWR ) ) < 0 ) {
16 perror( "open" );
17 exit( 1 );
18 }
19
20
21 /*
22 * IOCTL_TELL
23 */
24 printf( "Tell driver to update internal value ...\n" );
25
26 if ( ioctl( fd, CHARDEV_IOCTL_TELL, setval ) == -1 )
27 perror( "ioctl( CHARDEV_IOCTL_TELL )" );
28
29
30 /*
31 * IOCTL_QUERY
32 */
33 printf( "Query driver internal value ...\n" );
34
35 if ( ( retval = ioctl( fd, CHARDEV_IOCTL_QUERY ) ) == -1 )
36 perror( "ioctl( CHARDEV_IOCTL_QUERY )" );
37
38 printf( "Internal value = %d\n", retval );
39 setval = retval + 1;
40
41
42 /*
43 * IOCTL_SET
44 */
45 printf( "Tell driver to update internal value via pointer ...\n" );
46
47 if ( ioctl( fd, CHARDEV_IOCTL_SET, &setval ) == -1 )
48 perror( "ioctl( CHARDEV_IOCTL_SET )" );
49
50
51 /*
52 * IOCTL_GET
53 */
54 printf( "Query driver internal value via pointer ...\n" );
55
56 if ( ioctl( fd, CHARDEV_IOCTL_GET, &retval ) == -1 )
57 perror( "ioctl( CHARDEV_IOCTL_GET )" );
58
59 printf( "Internal value = %d\n", retval );
60
61
62 /*
63 * IOCTL_RESET
64 */
65 printf( "Reset reference counter ...\n" );
66
67 if ( ioctl( fd, CHARDEV_IOCTL_RESET ) == -1 )
68 perror( "ioctl( CHARDEV_IOCTL_RESET )" );
69
70
71 close( fd );
72 return 0;
73 }
74
75
76 /* End of ioctl_test.c */
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。
ioctl.c は、前回の poll.c を基本として、ioctl システムコール対応用の処理を追加しています。ここでは変更点について説明します。
- 5、12 行目
ioctl 対応処理の実装に必要なヘッダファイルをインクルードしています。 12 行目では、ioctl コマンド定数を定義した自作のヘッダファイルをインクルードしています。
ユーザプロセスで使用する ioctl システムコールの関数プロトタイプは
#include <sys/ioctl.h> int ioctl( int fd, int request, ... );の様になります。第 2 引数にコマンドを指定する定数を、第 3 引数に コマンドの引数を直接値として、あるいはポインタを介してデータを渡します ( 引数が無くても良い )。
chardev_ioctl.h では以下の ioctl コマンド定数を定義しています。
定数名 ユーザプロセスから見た入出力方向 ioctl 第3引数の型 CHARDEV_IOCTL_TELL 出力 値 CHARDEV_IOCTL_SET 出力 ポインタ CHARDEV_IOCTL_QUERY 入力 値 CHARDEV_IOCTL_GET 入力 ポインタ CHARDEV_IOCTL_RESET 出力 無し CHARDEV_IOCTL_TELL / CHARDEV_IOCTL_SET はどちらも、デバイスドライバ に対して値を設定するコマンドです。TELL は直接値を通知し、SET はポインタ を第 3 引数に設定する点が異なります。
CHARDEV_IOCTL_QUERY / CHARDEV_IOCTL_GET はどりたも、デバイスドライバ から値を取得するコマンドです。QUERY は ioctl の返り値として値を取得し、 GET は第 3 引数に設定したポインタの参照先に値を格納する点が異なります。
CHARDEV_IOCTL_RESET はデバッグ用のコマンドで、カーネルモジュールの 参照カウンタ ( ユーザプロセスや他のカーネルモジュール等から参照されている 数を管理するカウンタ ) を、強制的に 1 にしてしまいます。これを実行した直後に ファイル記述子を閉じてしまえば参照カウンタは 0 になるため、他に参照している ユーザプロセス等があっても強制的に rmmod してしまうことが出来ます。
ユーザプロセスにおけるこれらのコマンドの詳しい使い方は、 ioctl_test.c を参照して下さい。
ところで各々のコマンド定数は、システム全体で唯一になることが求められます。 そのためにはまず、カーネルソースツリー内の Documentation/ioctl-number.txt を参照して、使われていないマジックナンバーを決定します。 chardev_ioctl.h では、0xAE を 使っています。この値は、使っているシステムに応じて決定して下さい。
次に、_IOC マクロを使って定数を作り出します。
#include <linux/ioctl.h> _IOC( direction, magic, number, size )direction はユーザプロセスとのデータ転送の方向を、以下の定数で指定します。
定数名 ユーザプロセスから見たデータ転送の方向 _IOC_NONE 無し ( ポインタではなく直接値をやりとりする場合は、これを使う ) _IOC_READ 入力 _IOC_WRITE 出力 _IOC_READ | _IOC_WRITE 入出力 magic は、先程決定したマジックナンバーを指定します。number は、 コマンドを識別するための通番を指定します。ビット幅が 8 ビットなので、 0 - 255 を指定します。
size は、データ転送を伴う場合の転送サイズを設定します。ただし、 コマンドから転送サイズをデコードして使うのでなければ、別に何を設定 しても良いみたいです。
- 230 〜 282 行目 ( chardev_ioctl )
今回追加した関数です。ioctl システムコールが発行された際に実行されます。 switch 文でコマンドに応じた処理を行っているだけです。chardev_ioctl の第 3 引数 cmd としてコマンド定数が、第 4 引数 arg としてユーザプロセスで ioctl システムコールの第 3 引数に設定した変数が渡されます。
コマンド 処理内容 CHARDEV_IOCTL_TELL arg を直接の設定値としているので、内部変数に設定します。 CHARDEV_IOCTL_SET arg は整数変数へのポインタとして扱います。このポインタの参照先から 内部変数にデータを転送するのですが、read / write の実装同様、直接代入 することはできません。ここでは、1 / 2 / 4 バイトのデータ転送を copy_from_user よりも早く行ってくれる get_user 関数を使っています。 get_user は成功すると 0、失敗すると -EFAULT を返します。 CHARDEV_IOCTL_QUERY ioctl の戻り値を返却値としているので、内部変数の値を return します。 CHARDEV_IOCTL_GET arg は整数変換へのポインタとして扱います。このポインタの参照先に対して、 1 / 2 / 4 バイトのデータ転送を copy_to_user よりも早く行ってくれる put_user 関数を使っています。put_user は成功すると 0 を、失敗すると -EFAULT を返します。 CHARDEV_IOCTL_RESET カーネルモジュールの参照カウンタを、強制的に 1 にします。これにより、 実行後にファイル記述子を閉じれば参照カウンタが 0 となり、他ユーザプロセス が参照中であっても rmmod することができます。本当の目的は、バグ等で 参照カウンタが狂ってしまい rmmod できなくなってしまった時に使いたいだけです。 MOD_IN_USE マクロは、参照カウンタが 0 で無い場合に真と評価されます。 MOD_DEC_USE_COUNT マクロは参照カウンタを 1 減算し、 MOD_INC_USE_COUNT マクロは参照カウンタを 1 増加します。 これらを使い、参照カウンタが 0 になるまで MOD_DEC_USE_COUNT で減算し、 最後に MOD_INC_USE_COUNT で 1 にしています。
- 285 〜 294 行目
struct file_opearations テーブルの ioctl タグに対して、新たに chardev_ioctl への関数ポインタを設定しています。これにより、ioctl システムコールが発行された際に chardev_ioctl 関数が実行されます。
以上で ioctl.c の解説は終わりです。キャラクタデバイスドライバとしての内容はひとまず終了して、次回からは他の話題を取り上げたいと思います。