LINUXキャラクターデバイスドライバの例(scull)

22124 ワード

要約:http://blog.chinaunix.net/uid-11829250-id-337300.html 【1.システム環境】このドライバはUBUTNTU 10.04 LTSでコンパイルされました。システムカーネルはlinux-2.32-24です。(uname-r命令を使用して現在のカーネルのバージョン番号を確認できます。)UBUTNTU 10.04 LTSをインストールする時、LINUXカーネルのソースがインストールされていませんので、www.kernel.orgでLINUXのソースを載せる必要があります。Linux-2.3.2.22 tar.bz 2をダウンロードします。(システムで実行されるLINUXカーネルのバージョンとできるだけ同じにしてください。)下のコマンドでカーネルをインストールします。1.カーネルを解凍するcd/us/src
tar jxvf linux-2.3.22 tar.bz 2
2.システムのincludeのためにリンクファイルcd/usr/include rm-rf asm linux scsi ln-s/usrc/linux-2.22/include/asm-generic asm ln-s/usrc/linux-2.6.22/inclux/clux.2
LINUX内のソースコードのインストールが完了しました。
/******************************************************************************
*Name: memdev.c
*Desc:              ,                 ,
*                
*Parameter: 
*Return:
*Author: yoyoba([email protected])
*Date: 2010-9-26
*Modify: 2010-9-26
********************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "memdev.h"

static mem_major = MEMDEV_MAJOR;

module_param(mem_major, int, S_IRUGO);

struct mem_dev *mem_devp; /*       */

struct cdev cdev; 

/*      */
int mem_open(struct inode *inode, struct file *filp)
{
    struct mem_dev *dev;

    /*      */
    int num = MINOR(inode->i_rdev);

    if (num >= MEMDEV_NR_DEVS) 
            return -ENODEV;
    dev = &mem_devp[num];

    /*                    */
    filp->private_data = dev;

    return 0; 
}

/*      */
int mem_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/*   */
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
  unsigned long p = *ppos;
  unsigned int count = size;
  int ret = 0;
  struct mem_dev *dev = filp->private_data; /*         */

  /*         */
  if (p >= MEMDEV_SIZE)
    return 0;
  if (count > MEMDEV_SIZE - p)
    count = MEMDEV_SIZE - p;

  /*        */
  if (copy_to_user(buf, (void*)(dev->data + p), count))
  {
    ret = - EFAULT;
  }
  else
  {
    *ppos += count;
    ret = count;

    printk(KERN_INFO "read %d bytes(s) from %d
"
, count, p); } return ret; } /* */ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; /* */ /* */ if (p >= MEMDEV_SIZE) return 0; if (count > MEMDEV_SIZE - p) count = MEMDEV_SIZE - p; /* */ if (copy_from_user(dev->data + p, buf, count)) ret = - EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "written %d bytes(s) from %d
"
, count, p); } return ret; } /* seek */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = offset; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + offset; break; case 2: /* SEEK_END */ newpos = MEMDEV_SIZE -1 + offset; break; default: /* can't happen */ return -EINVAL; } if ((newpos<0) || (newpos>MEMDEV_SIZE)) return -EINVAL; filp->f_pos = newpos; return newpos; } /* */ static const struct file_operations mem_fops = { .owner = THIS_MODULE, .llseek = mem_llseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, }; /* */ static int memdev_init(void) { int result; int i; dev_t devno = MKDEV(mem_major, 0); /* */ if (mem_major) result = register_chrdev_region(devno, 2, "memdev"); else /* */ { result = alloc_chrdev_region(&devno, 0, 2, "memdev"); mem_major = MAJOR(devno); } if (result < 0) return result; /* cdev */ cdev_init(&cdev, &mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; /* */ cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); /* */ mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL); if (!mem_devp) /* */ { result = - ENOMEM; goto fail_malloc; } memset(mem_devp, 0, sizeof(struct mem_dev)); /* */ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devp[i].size = MEMDEV_SIZE; mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devp[i].data, 0, MEMDEV_SIZE); } return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } /* */ static void memdev_exit(void) { cdev_del(&cdev); /* */ kfree(mem_devp); /* */ unregister_chrdev_region(MKDEV(mem_major, 0), 2); /* */ } MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit); /************************ *memdev.h ************************/ #ifndef _MEMDEV_H_ #define _MEMDEV_H_ #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 260 /* mem */ #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /* */ #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem */ struct mem_dev { char *data; unsigned long size; }; #endif /* _MEMDEV_H_ */
【3.ドライバモジュールをコンパイルする】Makefileファイルの内容は以下の通りです。
ifneq ($(KERNELRELEASE),)

obj-m:=memdev.o

else

KERNELDIR:=/lib/modules/$(shell uname -r)/build

PWD:=$(shell pwd)

default:

 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:

 rm -rf *.o *.mod.c *.mod.o *.ko

endif
rootに切り替えて、makeを実行する時、UBUTNTUが仮想マシンでインストールされている場合、makeを実行する時、uuntuとwindowsの共有ディレクトリの下で、エラーが発生します。root@VMUBUNTU:~胻make-C/lib/modules/2.62-24-generic/build M=/root modules make[1]:Entering directory /usr/src/linux-headers-2.6.32-24-generic'
CC [M] /root/memdev.o
/root/memdev.c:15: warning: type defaults to ‘int’ in declaration of ‘mem_major’
/root/memdev.c: In function ‘mem_read’:
/root/memdev.c:71: warning: format ‘%d’ expects type ‘int’, but argument 3 has type ‘long unsigned int’
/root/memdev.c: In function ‘mem_write’:
/root/memdev.c:99: warning: format ‘%d’ expects type ‘int’, but argument 3 has type ‘long unsigned int’
Building modules, stage 2.
MODPOST 1 modules
CC /root/memdev.mod.o
LD [M] /root/memdev.ko
make[1]: Leaving directory
/usr/src/linux-headers-2.62-24-generic’
lsは現在のディレクトリの内容を調べます。root@VMUBUNTU:~荌ls Makefile memdev.h memdev.mod.c memdev.o Module.symvers memdev.cm dev.ko memdev.mod. o modules.order
ここのmemdev.koは生成されたドライバモジュールです。insmoodコマンドでこのモジュールをカーネルに挿入します。root@VMUBUNTU:~萼insmom dev.ko
挿入されたmemdev.koドライバを確認します。root@VMUBUNTU:~荋cat/proc/devices Charcter devices:1 mem 4/dev/vc/0キティちゃん4キティS 5/dev/キティちゃん5/dev/consososolie 5/dev/ptmx 260 memdev 6 lp 7 vcs 10 misc 13 input 14 sound 21 sg 29 fp 136 alppu 116device 226 drm 251 hidraw 252 usbmon 253 bsg 254 rtc
Block devices:1 ramdisk 259 blkext 7 loop 8 sd 9 sd 11 sr 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 s 134 s d 135 sd 252 device-mapper 253 pkdd 254 p
memdevドライバが正確にカーネルに挿入されていることが見られます。マスタデバイス番号は260で、このデバイス番号はmemdev.hで定義されている嗳define MEMDEV_です。MAJOR 260ここで定義されている主装置番号がシステムで使用されている主装置番号と競合する場合、主装置番号の定義は以下の通りである。MAJOR 254では、insmoodコマンドを実行すると、次のようなエラーが発生します。root@VMUBUNTU:~芫insmo memdev.ko insmood:error inserting‘memdev.ko’:-1 Device or reource busy
現在のデバイスのメインデバイス番号を表示します。root@VMUBUNTU:~芫cat/proc/devices Charcter devices:1 mem 4/dev/vc/0キティちゃん4キティS 5/dev/キティ5/dev/consosolie 5/dev/ptmx 6 lp 7 vcs 10 miput 14 sound 21 sg 29 fb 99 dev 116 ptsdevice 226 drm 251 hidraw 252 usbmon 253 bsg 254 rtc
Block devices:1 ramdisk 259 blkext 7 loop 8 sd 9 sd 11 sr 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 s 134 s d 135 sd 252 device-mapper 253 pkdd 254 p
文字デバイスの254マスタデバイス番号は、rtcで使用されていることが判明したので、上記のエラーが発生します。解決方法は、マスターデバイス番号の定義をmemdev.hで修正するだけで良いです。【4.アプリケーションを作成し、このドライバをテストする】まず、そのドライバに対応するファイルノードをDev/ディレクトリの下で作成し、次のコマンドを使って作成します。root@VMUBUNTU:/dev萶mknod memdev c 260 0
作成したドライバノードファイルをlsで確認します。root@VMUBUNTU:/dev钾ls-al memdev crw-r–r–1 root root 260,0 2010-09-26 17:28 memdev
次のようなアプリケーションを作成してドライバをテストします。
/******************************************************************************
*Name: memdevapp.c
*Desc: memdev             。  memedev      ,   
*            。
*Parameter: 
*Return:
*Author: yoyoba([email protected])
*Date: 2010-9-26
*Modify: 2010-9-26
********************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
 int fd;
 char buf[]="this is a example for character devices driver by yoyoba!";//  memdev     

 char buf_read[4096]; //memdev         buf 


 if((fd=open("/dev/memdev",O_RDWR))==-1) //  memdev  

  printf("open memdev WRONG!
"
); else printf("open memdev SUCCESS!
"
); printf("buf is %s
"
,buf); write(fd,buf,sizeof(buf)); // buf memdev lseek(fd,0,SEEK_SET); // read(fd,buf_read,sizeof(buf)); // memdev buf_read printf("buf_read is %s
"
,buf_read); return 0; } root@VMUBUNTU:/mnt/xlshare# gcc -o mem memdevapp.c root@VMUBUNTU:/mnt/xlshare# ./mem open memdev SUCCESS! buf is this is a example for character devices driver by yoyoba! buf_read is this is a example for character devices driver by yoyoba!
ドライバが正常に動作していることを示します。【5.LINUXはどのようにmakeドライバモジュールですか?】Linuxカーネルは単体カーネルですが、モジュールを動的にロードすることによって、開発がとても便利です。じゃ、カーネルはどうやってコンパイルされますか?私たちはそのMakefileを分析して入手することができます。以下は簡単なハローカーネルモジュールのMakefileです。
ifneq ($(KERNELRELEASE),)
obj-m:=hello.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
        $(MAKE) -C $(KERNELDIR)  M=$(PWD) modules
clean:
        rm -rf *.o *.mod.c *.mod.o *.ko
ifは上记のmakefileを使ってハローモジュールを完成しました。そしてメークをすればいいです。もし私たちはハローモジュールのソースコードを/home/study/prog/mod/hello/下に置くと仮定します。私たちはこのディレクトリでメークを実行する時、メークはどのように行いますか?LDD 3第二章第四節「コンパイルとロード」では、このMakefileは二回実行されたと簡単に述べるだけですが、具体的なプロセスはどうなりますか?まず、メークの後にターゲットがないので、MakeはMakefileの一番目ではなく、先頭の目標をデフォルトの目標として実行します。そしてdefaultはmakeの目標となります。メークは実行します
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
shellはmake内部の関数であり、現在のカーネルバージョンは2.6.13-studyであると仮定して、2.6.13-studyという結果になりました。ここで実際に実行されているのはmake-C/lib/modules/2.6 3-study/build M=/home/study/prog/module/moduryメイキは2回実行されていることが分かります。最初の実行時はハローモジュールのソースコードを読むディレクトリ/home/s tudy/prog/mod/hello/下のMakefileです。二回目の実行時は/usr/src/linux/下のMakefileを実行する時です。しかし、多くの困った問題があります。1.このKERNELRELEASEも困ります。何ですか?この変数はホーム/study/prog/mod/he llo/Makefileには定義されていませんので、機能するのはelse...endifです。ただし、helloモジュールをカーネルソースコードに移動すると。例えば/usr/src/linux/driver/に置くと、KERNELRELEASEに定義があります。162 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)$(LOCALVERSION)があるとき、ハローモジュールは単独でmakeでコンパイルするのではなく、カーネルでmake modulesでコンパイルするのです。このようにして、Makefileは単独でコンパイルしてもカーネルの一部としてコンパイルしても正常に動作します。2.このObj-m:=hello.oはいつ実行されますか?実行中:
modules: 
862 .PHONY: modules 
863 modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) 
864 @echo ' Building modules, stage 2.'; 
865 $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost 
make-C/lib/modules/2.6.11 3-study/build M=/home/study/prog/mod/hello/modulesの場合、makeが/usr/src/linux/Makefileの中でターゲットを探していることが分かります。二つのstage:1.hello.ファイルをコンパイルします。2.ハロー.mod.o hello.koを生成する過程で、呼び出します。
make -f scripts/Makefile.build obj=/home/study/prog/mod/hello 
scripts/Makefile.buildには多くのファイルが含まれます。
011 -include .config 
012 
013 include $(if $(wildcard $(obj)/Kbuild), $(obj)/Kbuild, $(obj)/Makefile) 
その中には/home/study/prog/mod/hello/Makefileが存在します。したがって、Obj-m:=hello.o make modulesに関するより詳細なプロセスは、scripts/Makefile.modpostファイルの注釈において見つけることができる。makeの実行過程全体を見たいなら、make-nを実行してもいいです。