|
このページは「24環境」の情報です 第18回 EEPROM デバイスドライバ編(CAT709) †海老原祐太郎
2004/1/12
第16回 デバイスドライバ入門編(CAT709)を発展させ、今回はEEPROMのドライバを実装します。~
CAT709には512byte EEPROM内蔵RTC(時計IC)が実装されています。 仕様書より †CAT709の仕様書4ページ目より抜粋。
RTC9701JE内蔵の512バイトシリアルEEPROM
コンパイル方法 †第16回のcat709port.cに対して追加で書き足しました。minor番号3と4を追加しました。 基本的にはデバイスドライバの開発は開発機で行います。 説明するよりもコードを載せたほうが早いので、コードを載せます。 /* * cat709port.c * for linux-2.4 kernel * written by Y.Ebihara * 2004/1/12 Version 1.0 */ /* Makefile は次のようになる # cat709のカーネルを展開してビルドしたディレクトリを指定する KERNEL=/home/ebihara/cat709-root-dir/usr/src/linux CFLAGS=-O2 -I${KERNEL}/include CC=sh3-linux-gcc all:cat709port.o clean: rm *.o */ #define MODULE #define __KERNEL__ #define DEFAULT_MAJOR_NUM 241 #define DEVNAME "cat709port" /* control register */ #define PCCR 0xa4000104 #define PDCR 0xa4000106 #define PLCR 0xa4000114 /* data register */ #define PCDR 0xa4000124 #define PDDR 0xa4000126 #define PJDR 0xa4000130 #define PLDR 0xa4000134 enum miscport_minor{ DIPSW, // 0 LED1, // 1 LED2, // 2 EEPROM1, // 3 EEPROM2, // 4 MINOR_MAX }; #include <linux/module.h> #include <linux/fs.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/semaphore.h> // EEPROM 関連 #define EEPROM1_OFFSET 0 #define EEPROM1_SIZE 0x100 #define EEPROM2_OFFSET 0x100 #define EEPROM2_SIZE 0xf0 void rtc9701je_eeprom_write(unsigned char *from, int addr, int len); void rtc9701je_eeprom_read(unsigned char *to, int addr, int len); static struct semaphore eeprom_sem; /* -------------------------------------------------------- */ /* I/O access */ /* 実際のI/O操作を行う関数 */ /* -------------------------------------------------------- */ // inb(), outb() はcat709_port_map()によるアドレス変換の対象となる // 一方、ctrl_outb(), ctrl_inb()はアドレス変換の対象とならない。 // 従って物理番地を直接書きたいときは ctrl_ を使う(速度も速い) // unsigned char ctrl_inb(unsigned long 物理番地) // ctrl_outb(unsigned char データ, unsigned long 物理番地) static void init_cat709port(){ // initialize } /* LED OUTPUT */ /* led=1 or 2, data= 1 or 0 */ static void cat709_led(int led, int data){ int x; led &= 0x3; led <<= 6; x=ctrl_inb(PJDR); if(data) x &= ~led; else x |= led; ctrl_outb(x,PJDR); } /* DIPSW INPUT */ static int cat709_dipsw(void){ int x1,x2; x1 = ~ctrl_inb(PJDR) >> 1; x2 = x1 >> 1; return (x1 & 0x01) | (x2 & 0x0e); } /* -------------------------------------------------------- */ /* file operation method */ /* キャラクター型デバイスドライバ */ /* -------------------------------------------------------- */ // 大雑把に言うと ユーザープロセスはデバイスファイルを「ファイルとみなして」 // open() し read(), write()を行う。 // デバイスメソッドは プロセスから呼び出される『サブルーチン』と思えば // 大体あっている // 従って open()やread(), write() は カーネル空間のテキストだが // 『pidを持ったまま』プロセスのコンテキストとして走行する。 // このドライバをプロセスが open() したときに呼び出される static int cat709port_open(struct inode *inode, struct file *filp){ int minor; // マイナー番号取得マクロ minor=MINOR(inode->i_rdev); if(minor > MINOR_MAX){ printk("<1>cat709port open() error"); return -ENODEV; } // モジュールの『利用度』を1あげる MOD_INC_USE_COUNT; // filp->private_data は void* 型の変数で、プロセンス単位(openされる毎)の // データは private_data に保存する。 // *filpは すべてのデバイスメソッドで引数としてやってくるので // filp->private_data はすべてのデバイスメソッドから参照することができる。 // 変数がひとつで足りないときは 構造体の形をあらかじめ定義しておいて // open()時にkmalloc() で構造体の実態を作り、ポインタを private_data に格納する。 // もちろん デバイスをclose() するときに kfree()することを忘れないように filp->private_data = (void*)minor; return 0; } static int cat709port_close(struct inode *inode, struct file *filp){ // モジュールの『利用度』を1下げる MOD_DEC_USE_COUNT; return 0; } static int cat709port_read(struct file *filp, char *user_buf,size_t count,loff_t *ppos){ unsigned char buffer[255]; int len; int data; int minor; minor=(int)(filp->private_data); switch(minor){ case DIPSW: data=cat709_dipsw(); len=sprintf(buffer,"%d\n",data); // copy_to_user(転送先,転送元,バイト長) で // ユーザープロセスにデータを返す copy_to_user(user_buf,buffer,len); return len; // 転送バイト数を返す case EEPROM1: if(down_interruptible(&eeprom_sem)){ // セマフォを得られるまで待機 return -ERESTARTSYS; // セマフォの取得に失敗した } if((*ppos) + count > EEPROM1_SIZE) count=EEPROM1_SIZE - (*ppos); rtc9701je_eeprom_read(buffer,EEPROM1_OFFSET + (*ppos),count); copy_to_user(user_buf,buffer,count); *ppos += count; up(&eeprom_sem); // セマフォの開放 return count; case EEPROM2: if(down_interruptible(&eeprom_sem)){ // セマフォを得られるまで待機 return -ERESTARTSYS; // セマフォの取得に失敗した } if((*ppos) + count > EEPROM2_SIZE) count=EEPROM2_SIZE - (*ppos); rtc9701je_eeprom_read(buffer,EEPROM2_OFFSET + (*ppos),count); copy_to_user(user_buf,buffer,count); *ppos += count; up(&eeprom_sem); // セマフォの開放 return count; } return 0; // 戻り値は // 負:エラー // 0 :end-of-file に到達 // 正:転送したバイト数 // としなければならない } static int cat709port_write(struct file *filp, const char *user_buf,size_t count,loff_t *ppos){ char buffer[256]; int data; switch((int)(filp->private_data)){ case LED1: copy_from_user(buffer,user_buf,1); if(buffer[0]=='0') cat709_led(1, 0); else if(buffer[0]=='1') cat709_led(1, 1); return count; case LED2: copy_from_user(buffer,user_buf,1); if(buffer[0]=='0') cat709_led(2, 0); else if(buffer[0]=='1') cat709_led(2, 1); return count; case EEPROM1: if(down_interruptible(&eeprom_sem)){ // セマフォを得られるまで待機 return -ERESTARTSYS; // セマフォの取得に失敗した } if((*ppos) + count > EEPROM1_SIZE) count=EEPROM1_SIZE - (*ppos); copy_from_user(buffer,user_buf,count); rtc9701je_eeprom_write(buffer,EEPROM1_OFFSET + (*ppos),count); *ppos += count; up(&eeprom_sem); // セマフォの開放 return count; case EEPROM2: if(down_interruptible(&eeprom_sem)){ // セマフォを得られるまで待機 return -ERESTARTSYS; // セマフォの取得に失敗した } if((*ppos) + count > EEPROM2_SIZE) count=EEPROM2_SIZE - (*ppos); copy_from_user(buffer,user_buf,count); rtc9701je_eeprom_write(buffer,EEPROM2_OFFSET + (*ppos),count); *ppos += count; up(&eeprom_sem); // セマフォの開放 return count; } return count; // 戻り値は // 負:エラー // 0 :end-of-file に到達 // 正:転送したバイト数 // としなければならない // write()の時は、ユーザープロセスが書き出ししたかったバイト数(count)を // 全部返してあげるのでユーザープロセスは大満足する(^-^) } // file_operations構造体とはジャンプテーブルやベクターテーブルのようなもので // デバイスファイルを open() read() write() close() されたときに呼び出す // 関数一覧である static struct file_operations cat709port_fops = { owner:THIS_MODULE, // おまじない read:cat709port_read, // read write:cat709port_write, // write open:cat709port_open, // open release:cat709port_close // close }; /* -------------------------------------------------------- */ /* driver module load / unload */ /* -------------------------------------------------------- */ // モジュールがロードされたとき(insmod されたとき)は // init_module()が呼ばれる // 戻り値は // 負:エラー発生 // 0 :正常終了 // とする。負を返すとモジュールは常駐しない static int init_module(void){ printk("<1>cat709port hello\n"); sema_init(&eeprom_sem,1); // セマフォ初期化 // register_chrdev(メジャー番号, デバイス名, // ファイルオペレーションズ構造体へのポインタ) // で、カーネルに対してキャラクタ型デバイスドライバの登録を行う if(register_chrdev(DEFAULT_MAJOR_NUM,DEVNAME,&cat709port_fops) != 0){ printk("<1>cat709port register fail\n"); return -1; } // ハードウェア的な初期化 init_cat709port(); return 0; } // モジュールがアンロードされたときき (rmmod されたとき)は // cleanup_module()が呼び出される static void cleanup_module(void){ printk("<1>cat709port goodby\n"); unregister_chrdev(DEFAULT_MAJOR_NUM,DEVNAME); } MODULE_LICENSE("GPL"); /* ちなみにこのドライバの使い方は以下の通り ■ 準備 # rommode rw # mkdir /lib/modules/2.4.21/kernel/drivers/misc # cp cat709port.o /lib/modules/2.4.21/kernel/drivers/misc # depmod -a # modprobe cat709port メジャー番号241, マイナー番号0 # mknod /dev/cat709dipsw c 241 0 # chmod 444 /dev/cat709dipsw メジャー番号241, マイナー番号1 # mknod /dev/cat709led1 c 241 1 # chmod 666 /dev/cat709led1 メジャー番号241, マイナー番号2 # mknod /dev/cat709led2 c 241 2 # chmod 666 /dev/cat709led2 メジャー番号241, マイナー番号3 # mknod /dev/cat709eeprom1 c 241 3 # chmod 666 /dev/cat709eeprom1 メジャー番号241, マイナー番号4 # mknod /dev/cat709eeprom2 c 241 4 # chmod 666 /dev/cat709eeprom2 ■ 使い方 $ cat /dev/cat709dipsw 0〜15が読める $ echo 1 > /dev/cat709led1 LED1が点灯する $ echo 0 > /dev/cat709led2 LED2が消灯する $ cat /dev/cat709eeprom1 EEPROM1の領域を表示する $ echo "hello" > /dev/cat709eeprom1 EEPROM1に書き込みを行う */ Makefileは以下のようになります。 KERNEL=/home/ebihara/cat709-root-dir/usr/src/linux CFLAGS=-O2 -I${KERNEL}/include CC=sh3-linux-gcc all:cat709port.o clean: rm *.o # rmの前にはTABを入れてね コンパイルは make するだけです。 $ make $ file cat709port.o cat709port.o: ELF 32-bit LSB relocatable, Hitachi SH, version 1 (SYSV), not stripped デバイスドライバのロードと実行 ~ †以下の作業は CAT709 で行います。 ドライバを組み込む # insmod /NFSマウント/開発機のどこかのディレクトリ/cat709port.o cat709port hello 組み込まれたか確認する # lsmod Module Size Used by Not tainted cat709port 1468 0 ds 6332 0 (unused) sh3_ss 3212 3 pcmcia_core 34880 0 [ds sh3_ss] メジャー番号を調べる # cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 5 cua 10 misc 90 mtd 108 ppp 128 ptm 136 pts 162 raw 204 ttySC 205 cusc 241 cat709port ←241番に登録された 254 pcmcia そうしたらデバイスファイルも作ります。 # rommode rw メジャー番号241, マイナー番号3 # mknod /dev/cat709eeprom1 c 241 3 # chmod 666 /dev/cat709eeprom1 メジャー番号241, マイナー番号4 # mknod /dev/cat709eeprom2 c 241 4 # chmod 666 /dev/cat709eeprom2 # rommode ro 実際に使ってみます $ echo "hello" > /dev/cat709eeprom1 EEPROM1に書き込みを行う $ cat /dev/cat709eeprom1 EEPROM1空間が読める $ cat /dev/cat709eeprom2 console=ttySC0,115200 root=1f02 ro カーネル起動パラメータが読めます $ echo "console=ttySC0,115200 root=1f02 ro " > /dev/cat709_eeprom2 カーネル起動パラメータの変更ができます。 /dev/cat709eeprom1は256バイトのユーザーデータ用領域として使えます。 組 込 み †ドライバの動作検証ができたのでCAT709に保存します。 以下の作業は CAT709で行います。 # rommode rw # mkdir /lib/modules/2.4.21/kernel/drivers/misc # cp cat709port.o /lib/modules/2.4.21/kernel/drivers/misc # depmod -a # vi /etc/modules 1行追加する cat709port # rommode ro /etc/modules ファイルにモジュール名を追加しておくと、 詳しくは †ここで取り上げたのはlinuxデバイスドライバの一番簡単な例です。 |