|
○ はじめに
今回は、ユーザプロセスから発行される 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 の解説は終わりです。キャラクタデバイスドライバとしての内容はひとまず終了して、次回からは他の話題を取り上げたいと思います。