キャラクター デバイスは、Linux システムで一般的なデバイス タイプであり、キーボード、マウス、シリアル ポートなど、データをバイト単位で送信します。キャラクター デバイス ドライバーは Linux ドライバー開発の最も基本的なタイプで、デバイス番号、デバイス ファイル、ファイル操作構造、cdev 構造などの概念が含まれます。この記事では、デバイス番号の登録と登録解除、デバイスファイルの作成と削除、cdev 構造体の初期化と解放、ファイル操作関数の実装と登録など、Linux キャラクターデバイスドライバーフレームワークの原理と方法を紹介します。使用方法と注意事項を説明するための例。
Linux ではすべてがファイルであるため、デバイス ファイルとして、その操作メソッドのインターフェイスは struct file_operations
にカプセル化されます。ドライバーを作成するときは、ドライバーが利用できるように、対応するインターフェイスを実装する必要があります。 Linux カーネルは、ドライバーを作成するために多くの「登録コールバック」メカニズムを使用します。いわゆる登録コールバックは、デバイス ファイルを開くときに、実際に VFS を通じて対応する i ノードを見つけ、以前に作成されたこれを実行するという単純な理解です。 device file は inode に登録されている open 関数であり、他の関数も同様であるため、作成したドライバがアプリケーションで正常に動作するためには、まず対応するメソッドを実装する必要があります。次に、対応するデバイス ファイルを作成します。
冗長な文ですが、デバイス番号に静的アプリケーションを使用する場合、最大の問題は、既知のデバイス番号と競合しないことです。カーネルは、ドキュメント **"Documentation/devices.txt"* にどのマスターがあるかをすでに示しています。 ※デバイス番号を使用しています。このことから、2^12 の主要なデバイス番号のうち、使用できる範囲は 240-255 と 261-2^12-1 であることがわかり、動的に適用した理由も説明できます。場合によっては、デバイス番号が 250 になることがよくあります。また、このファイルからは「メジャーデバイス番号がデバイスの種類を表している」こともわかりますが、キャラクタ/ブロックデバイス自体は多くのカテゴリに分類できるため、カーネルは各カテゴリにメジャーデバイス番号を割り当てます。
Linux では各プロセスが独立したプロセス空間を持っており、カーネルデータがユーザープロセスにマッピングされていても、そのデータの PID が自動的にユーザープロセスの PID に変換される仕組みがあるため、カーネル空間とユーザー空間からデータを直接コピーすることはできず、特別なデータ コピー関数/マロスが必要です:
リーリーこれら 2 つの関数は、カーネル空間のデータを、関数をコールバックするユーザープロセスのユーザープロセス空間にコピーすることができ、この 2 つの関数により、カーネル内の読み書きにより、カーネル空間間のデータコピーを実現できます。そしてユーザースペース。
リーリーioctl は、ユーザーレベルの制御デバイス用に Linux によって特別に設計されたシステム コール インターフェイスです。このインターフェイスは非常に柔軟性があります。ユーザーがコマンドを通じて実現できるようにデバイスが意図している機能は、それを通じて実装できます。ioctl が動作しています対応する機能メソッド セットのポインターは long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
です。コマンドとパラメーターはドライバーによって完全に指定されます。通常、コマンドはヘッダー ファイルに書き込まれます。アプリケーション層とドライバー層が同じ通信プロトコルに準拠するために、Linux では、図
设备类型 序列号 方向 数据尺寸 8bit 8bit 2bit 13/14bit
“
设备类型字段为一个幻数,可以是0~0xff之间的数,内核中的”ioctl-number.txt“给出了一个推荐的和已经被使用的幻数(但是已经好久没人维护了),新设备驱动定义幻数的时候要避免与其冲突。
序列号字段表示当前命令是整个ioctl命令中的第几个,从1开始计数。
方向字段为2bit,表示数据的传输方向,可能的值是:**_IOC_NONE,_IOC_READ,_IOC_WRITE和_IOC_READ|_IOC_WRITE**。
数据尺寸字段表示涉及的用户数据的大小,这个成员的宽度依赖于体系结构,通常是13或14位。”
内核还定义了**_IO(),_IOR(),_IOW(),_IOWR()**这4个宏来辅助生成这种格式的命令。这几个宏的作用是根据传入的type(设备类型字段),nr(序列号字段)和size(数据长度字段)和方向字段移位组合生成命令码。
内核中还预定义了一些I/O控制命令,如果某设备驱动中包含了与预定义命令一样的命令码,这些命令会被当做预定义命令被内核处理而不是被设备驱动处理,有如下4种:
//mycmd.h ... #include #define CMDT 'A' #define KARG_SIZE 36 struct karg{ int kval; char kbuf[KARG_SIZE]; }; #define CMD_OFF _IO(CMDT,0) #define CMD_ON _IO(CMDT,1) #define CMD_R _IOR(CMDT,2,struct karg) #define CMD_W _IOW(CMDT,3,struct karg) ... //chrdev.c static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { static struct karg karg = { .kval = 0, .kbuf = {0}, }; struct karg *usr_arg; switch(cmd){ case CMD_ON: /* 开灯 */ break; case CMD_OFF: /* 关灯 */ break; case CMD_R: if(_IOC_SIZE(cmd) != sizeof(karg)){ return -EINVAL; } usr_arg = (struct karg *)arg; if(copy_to_user(usr_arg, &karg, sizeof(karg))){ return -EAGAIN; } break; case CMD_W: if(_IOC_SIZE(cmd) != sizeof(karg)){ return -EINVAL; } usr_arg = (struct karg *)arg; if(copy_from_user(&karg, usr_arg, sizeof(karg))){ return -EAGAIN; } break; default: ; }; return 0; }
插入的设备模块,我们就可以使用cat /proc/devices命令查看当前系统注册的设备,但是我们还没有创建相应的设备文件,用户也就不能通过文件访问这个设备。设备文件的inode应该是包含了这个设备的设备号,操作方法集指针等信息,这样我们就可以通过设备文件找到相应的inode进而访问设备。创建设备文件的方法有两种,手动创建或自动创建,手动创建设备文件就是使用mknod /dev/xxx 设备类型 主设备号 次设备号的命令创建,所以首先需要使用cat /proc/devices查看设备的主设备号并通过源码找到设备的次设备号,需要注意的是,理论上设备文件可以放置在任何文件加夹,但是放到**”/dev”才符合Linux的设备管理机制,这里面的devtmpfs是专门设计用来管理设备文件的文件系统。设备文件创建好之后就会和创建时指定的设备绑定,即使设备已经被卸载了,如要删除设备文件,只需要像删除普通文件一样rm**即可。理论上模块名(lsmod),设备名(/proc/devices),设备文件名(/dev)并没有什么关系,完全可以不一样,但是原则上还是建议将三者进行统一,便于管理。
除了使用蹩脚的手动创建设备节点的方式,我们还可以在设备源码中使用相应的措施使设备一旦被加载就自动创建设备文件,自动创建设备文件需要我们在编译内核的时候或制作根文件系统的时候就好相应的配置:
Device Drivers ---> Generic Driver Options ---> [*]Maintain a devtmpfs filesystem to mount at /dev [*] Automount devtmpfs at /dev,after the kernel mounted the rootfs
OR
制作根文件系统的启动脚本写入
mount -t sysfs none sysfs /sys mdev -s //udev也行
有了这些准备,只需要导出相应的设备信息到**”/sys”**就可以按照我们的要求自动创建设备文件。内核给我们提供了相关的API
class_create(owner,name); struct device *device_create_vargs(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, va_list vargs); void class_destroy(struct class *cls); void device_destroy(struct class *cls, dev_t devt);
有了这几个函数,我们就可以在设备的**xxx_init()和xxx_exit()**中分别填写以下的代码就可以实现自动的创建删除设备文件
/* 在/sys中导出设备类信息 */ cls = class_create(THIS_MODULE,DEV_NAME); /* 在cls指向的类中创建一组(个)设备文件 */ for(i= minor;i"%s%d",DEV_NAME,i); } /* 在cls指向的类中删除一组(个)设备文件 */ for(i= minor;i
通过本文,我们了解了Linux字符设备驱动框架的原理和方法,它们可以用来实现对字符设备的驱动和控制。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用静态或动态分配的设备号,使用标准或自定义的设备文件名,使用正确或简化的cdev初始化方式等。字符设备驱动框架是Linux驱动开发中最重要的一部分,它可以实现对字符设备的读写操作,也可以提升驱动程序的可移植性和可维护性。希望本文能够对你有所帮助和启发。
以上がLinux キャラクター デバイス ドライバー フレームワーク: 原則と方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。