Linuxドライバ作成&&アプリケーションの呼び出し


Linuxドライバの開発は、組み込み学習に力を入れている多くの騒年の究極の夢だと信じています.技術の含有量にかかわらず、給料の待遇にかかわらず、彼女はすべて完璧に体現しています.もちろんcrk_13も同じ!しかし、魅力的なものほど期待できないことが多く、駆動開発の難しさに耳を傾けているのかもしれません.私の個人的な経歴から見ると、ドライバを書くにはLinuxのシステム全体に対して全面的な認識(システムプログラミング、ファイル操作、ハードウェア構造を含む)が必要ですが、ドアに入ってドライバ開発の大まかな手順を理解するには、伝説のような困難はありません.
まず、最も簡単なドライバです.永遠に変わらないhello_worldを例に、linuxドライバが作成する一般的な手順とルールについて説明します.
Linuxドライバの基本構造を簡単に説明します.2つのマクロを含める必要があります.
        module_init(init_func);
        module_exit(exit_func);
実装init_func()、exit_func()関数は、モジュールがロードされ、削除されるとそれぞれ呼び出されます.
簡単なhello_worldドライバ:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
 
static int __init hello_init()
{
    printk("Hello, world!
"); return 0; } static void __exit hello_exit() { printk("Goodbye, cruel world!
"); } module_init(hello_init); module_exit(hello_exit);

これは最も簡単なドライバで、コンパイルした後、生成したhelloをkoファイルは開発ボードにコピーされ、それぞれ次のコマンドが実行されます.
        insmod hello.ko#ロードモジュール、hello_が呼び出されますinit()#
rmmod hello#モジュールを削除するとhello_が呼び出されますexit()#
 
次に、S 5 PV 210のledドライバを例に、独自のドライバを作成し、使用することを学びます.
目的:開発ボード上のledランプ(1つのランプを例に、他のものと同様)をデバイスとしてドライバを作成し、呼び出す!
準備作業:①、ハードウェア構成:led 1(GPJ 0_3)②、カーネル:kernel、www.kernel.orgダウンロード
一、自分たちのカーネルをコンパイルする
自分のkernelでtar.gzを例に挙げます.
                ①、tar -xvzf kernel.tar.gz#カーネルソースを現在のディレクトリに解凍#
                ②、cd ./kernel#解凍が完了したら、kernelルートディレクトリに入ります.
                ③、cp ./arch/arm/configs/XXX_deconfig.config  .config#arch/arm/configs/ディレクトリからkernelルートディレクトリにプロファイルをコピーし、名前を変更します.config#
④、make zImage#コンパイル生成カーネルミラーファイル(約20分程度)#
OK、すべてがうまくいけば、私たちは自分でコンパイルしたカーネルzImageを手に入れました.
二、カーネルがあれば、私たちはドライバを書くことができます(前のステップが成功しなかったら、コードを書くことができないわけではありません.ただ、私たちのドライバはカーネルのためにサービスしています.私たちは彼女のためにカーネルを指定しなければなりません.彼は過去をコンパイルすることができません).
①、まず設備(つまり私たちのledランプ)に設備番号(この設備番号、従設備番号を含む)を割り当てる必要があります.私たちは一般的に動的分配法を採用しています.
                        dev_t  led_device_num;
                        alloc_chrdev_region(&led_device_num, 0, 1, "led_dev");#動的割当てデバイス番号#
②、設備番号があれば、相応の設備を見つけて、この設備に対して私たちが望んでいる操作を行うことができます!Linuxの目の中で、すべてはファイルです!ファイルにとって、最もよく使われる操作はopen、read、write、seek、closeなどにほかならない.そのため、私たちも同様の関数を提供して設備に対して操作を行って、供給用のプログラムは使用して、自分で勝手に関数を書いて機能を実現するべきではありません!
./include/linux/fs.hファイルには構造体が定義されています.file_operations、これは非常に重要な構造体であり、ドライバを作成するとき、この構造体で定義されたいくつかの関数の実現にほかならない.次のようになります.
                        static struct file_operations led_dev_fops =                         {                                 .read = led_dev_read,                                 .write = led_dev_write,                                 .open = led_dev_open,                                 .owner = THIS_MODULE                         };
                led_dev_read,led_dev_write,led_dev_Openはそれぞれread,write,open関数ポインタの初期化であり,以降,アプリケーションがledデバイス(現在作成している)に対してopen,read,write操作を行うと,"="の後の関数がそれぞれ呼び出され,もちろん,これらの関数数は後で実現する.
しかし、私たちはただ1つの構造体の変数を定義して、そして初期化を行いました.設備はどのように知っていますか.カーネルはどのようにシステムの関数を私たち自身が実現した関数と結びつけますか.したがって、この関数の対応関係をデバイスに伝える必要があります.
                        static struct cdev led_dev;#文字型デバイス変数を定義#
                        cdev_init (&led_dev, &led_dev_fops);#この文字デバイスを初期化します.主に関数ポインタの初期化です.
③、文字設備は最終的にカーネルによって呼び出され、前の仕事はまだドアを閉めて車を作っただけで、今車ができたので、出発しなければなりません.交通部門の許可を得る必要がありますか?同様に、カーネルにこのようなデバイスを登録する必要があることを伝えます.
                        cdev_add (&led_dev, led_dev_num, 1);#デバイスを登録#
④、上記の手順を完了した後、我々の初期化作業(即ち:デバイス番号を取得し、デバイスを登録する)が完了した後、我々自身のファイル操作関数、即ち:led_dev_open、led_dev_read、led_dev_write.
⑤、後処理.これを行うには、モジュールをアンインストールするときに、申請したデバイス番号を解放し、デバイスをアンインストールする必要があります.
                        cdev_del (&led_dev);#デバイスをアンインストール#unregister_chrdev_region(led_dev_num, 1);#デバイス番号を解放#
自分で書いたコードを添付します.
#include <linux/module.h>                                                       
#include <linux/kernel.h>                                                       
#include <linux/init.h>                                                         
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include <linux/io.h>                            
#include <linux/gpio.h> 

#include <linux/cdev.h>
#include <linux/fs.h>

static dev_t led_dev_num;
static struct cdev led_dev;  
static int copen(struct inode *node, struct file *filep)
{
    printk ("led_dev is open!
"); gpio_set_value(S5PV210_GPJ0(3), 0); //gpj0_3 ,led1 return 0; } static ssize_t cread (struct file *filep, char __user *buf, size_t count, loff_t *f_pos) { gpio_set_value(S5PV210_GPJ0(3), 1); //gpj0_3 ,led1 printk ("led_dev is read!
"); return 0; } static ssize_t cwrite (struct file *filep, const char __user *buf, size_t count, loff_t *f_pos) { printk ("led_dev is write!
"); return 0; } static struct file_operations led_dev_fops = { .read = cread, .write = cwrite, .open = copen, .owner = THIS_MODULE }; static int __init led_dev_init (void) { int res; res = alloc_chrdev_region(&led_dev_num, 0, 1, "led_dev"); // if (res < 0) return res; cdev_init (&led_dev, &led_dev_fops); // cdev_add (&led_dev, led_dev_num, 1); // s3c_gpio_cfgpin (S5PV210_GPJ0(3), S3C_GPIO_OUTPUT); // gpj0_3 printk ("led_dev is init
"); return 0; } static void __exit led_dev_exit (void) { cdev_del (&led_dev); // unregister_chrdev_region(led_dev_num, 1); // printk ("led_dev is exit
"); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("Dual BSD/GPL"); // , gpl , , , ! MODULE_AUTHOR("crk_13"); MODULE_DESCRIPTION("This is my first driver"); MODULE_SUPPORTED_DEVICE("none");

三、コンパイル.プログラムが作成されたらled_をコンパイルする必要がありますdev.c、ここにMakefileファイルを提供して、少し変更して、それからmakeでいいです! 
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y

# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif

#CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR)

ifneq ($(KERNELRELEASE),)
# call from kernel build system
#  obj-m                  
obj-m	:= led_dev.o

else
#                
KERNELDIR ?= /home/yw/kernel
PWD       := $(shell pwd)

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINCDIR=$(PWD)/../include modules

endif



clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

depend .depend dep:
	$(CC) $(CFLAGS) -M *.c > .depend


ifeq (.depend,$(wildcard .depend))
include .depend
endif

コンパイルが完了すると、現在のディレクトリの下にled_が生成されます.dev.koファイルは、開発ボード上でnfsを介してホストにマウントし、次の一連の操作を実行できます:1、insmod led_dev.ko#ロードモジュール#②、cat/proc/devices#デバイスled_を表示devのプライマリ・デバイス番号(動的に割り当てられているため、最初は知らなかった)#③、mknod/dev/led_dev c 250 0#デバイスノードを作成し、デバイス名は必ずled_dev#                 ④、ls/dev/led_dev-l#は、作成したノードが存在するかどうか、およびプライマリ、スレーブデバイス番号⑤、cat/dev/led_を表示します.dev#このコマンドを実行すると、open、read操作#⑥、rmmod led_を同時に実行することになります.dev#デバイスを削除し、アプリケーション呼び出しの実験を作成するときは、このステップを実行しないで、後でアプリケーションを実行した後、削除します.そうしないと、①に戻ってやり直します.
 
四、アプリケーションの作成実はアプリケーションは相対的にフォーマットが柔軟で、普通のファイルを操作するようにopen,readを呼び出したいにほかならない.のもう言わないで、コードを見てみましょう.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, const char* argv[])    //       ./main /dev/led_dev 10      /dev/led_dev  ,10       10s
{
    char buf[20];
    int fd = open (argv[1], O_RDWR);    //  led_dev_open  ,led1 

    sleep(atoi(argv[2]));    //  10s
    read (fd, buf, sizeof(buf));    //  led_dev_read  ,led1 

    close(fd);
    return 0;
}

アプリケーションの作成が完了し、コンパイルを開始し、実行:1、arm-linux-gcc main.c-o main#コンパイルmain.c、mainと名前を変更します.もちろん、便宜上、Makefile#②、./main/dev/led_dev 10#mainを実行し、/dev/led_を開くdevデバイス、遅延10 s#は現象を見ることができます:led 1は先に10 s明るくして、それから消灯します!私たちのledドライブができました!ハハ、もちろん、これはただのテンプレートで、規範と実用とは言えません.あなたは完全に彼女をもっとよくすることができますよ.