• 正文
    • mydevice.c源碼
    • 完整mydevice.c示例源碼
    • 編譯
    • 編寫測試應(yīng)用源碼device_app.c
    • 測試
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡-字符驅(qū)動測試示例

03/17 13:40
420
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼2_字符驅(qū)動mydevice

mydevice.c源碼

(一)首先包含頭文件

#include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數(shù)據(jù)訪問函數(shù)的頭文件

#include <linux/cdev.h> ????????//包含字符設(shè)備頭文件

(二)聲明變量

#define DEVICE_NAME "mydevice" ?// 設(shè)備名稱

static dev_t dev_num; ???????????//分配的設(shè)備號

struct ?cdev my_cdev; ?????????//字符設(shè)備指針

int major; ?//主設(shè)備號

int minor; ?//次設(shè)備號

(三)驅(qū)動模塊入口和出口函數(shù)

module_init(mydevice_init); // 指定驅(qū)動程序的初始化函數(shù)

module_exit(mydevice_exit); // 指定驅(qū)動程序的清理函數(shù)

(四)入口函數(shù)和出口函數(shù)實(shí)現(xiàn)

static int __init mydevice_init(void)

{

int ret;

// 在這里執(zhí)行驅(qū)動程序的初始化操作

// 注冊字符設(shè)備驅(qū)動程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num); ??????//獲取主設(shè)備號

minor=MINOR(dev_num); ??????//獲取次設(shè)備號

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops); ???//初始化字符設(shè)備結(jié)構(gòu)體

cdev_add(&my_cdev,dev_num,1); ?//將字符設(shè)備添加到內(nèi)核中

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

// 刪除字符設(shè)備

cdev_del(&my_cdev);

// 注銷字符設(shè)備驅(qū)動程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

入口函數(shù)mydevice_init(void)中主要有以下幾個函數(shù)的調(diào)用:

(1)調(diào)用int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);函數(shù)進(jìn)行設(shè)備號分配。

dev:指向dev_t類型的指針,用于接收分配的設(shè)備號范圍的起始設(shè)備號;

firstminor:指定次設(shè)備號的起始值。次設(shè)備號用于區(qū)分同一主設(shè)備號下的不同設(shè)備實(shí)例;

count:指定要分配的設(shè)備號數(shù)量;

name:指定字符設(shè)備的名稱,這是一個用于標(biāo)識設(shè)備的字符串,可以在/proc/devices中找到;

(2)調(diào)用MAJOR(dev)獲取主設(shè)備號:

MAJOR(dev)宏接受一個dev_t類型的設(shè)備號作為參數(shù),并返回該設(shè)備號的主設(shè)備號。

(3)調(diào)用MINOR(dev)獲取次設(shè)備號:

MINOR(dev)宏接受一個dev_t類型的設(shè)備號作為參數(shù),并返回該設(shè)備號的次設(shè)備號。

(4)調(diào)用void cdev_init(struct cdev *cdev, const struct file_operations *fops);函數(shù)初始化字符設(shè)備結(jié)構(gòu)體。

cdev:指向要初始化的struct cdev結(jié)構(gòu)體的指針;

fops:指向包含設(shè)備操作函數(shù)的struct file_operations結(jié)構(gòu)體的指針;

(5)調(diào)用int cdev_add(struct cdev *p, dev_t dev, unsigned int count);函數(shù)將字符設(shè)備添加到內(nèi)核中。

p:指向要添加的struct cdev結(jié)構(gòu)體的指針;

dev:指定要添加的設(shè)備號。通常是由alloc_chrdev_region或register_chrdev_region函數(shù)分配得到的設(shè)備號;

count:指定要添加的設(shè)備號數(shù)量;

cdev_add函數(shù)將字符設(shè)備添加到內(nèi)核中,并與指定的設(shè)備號相關(guān)聯(lián)。當(dāng)成功添加字符設(shè)備后,它將被分配一個主設(shè)備號和次設(shè)備號,并成為系統(tǒng)中的一個可用設(shè)備;

注意:在調(diào)用cdev_add之前,應(yīng)先使用cdev_init函數(shù)對struct cdev進(jìn)行初始化,并設(shè)置正確的文件操作函數(shù)。

出口函數(shù)mydevice_exit(void)中主要有以下兩個函數(shù)的調(diào)用:

(1)調(diào)用void unregister_chrdev(unsigned int major, const char *name);函數(shù)注銷已注冊的字符設(shè)備。

major:要注銷的字符設(shè)備的主設(shè)備號;

name:字符設(shè)備的名稱;

unregister_chrdev函數(shù)將指定的字符設(shè)備從內(nèi)核中注銷,并釋放相關(guān)的資源。注銷后,該字符設(shè)備將不再可用;

(2)調(diào)用void cdev_del(struct cdev *p);函數(shù)刪除字符設(shè)備。

p:指向要刪除的struct cdev結(jié)構(gòu)體的指針;

cdev_del函數(shù)從內(nèi)核中刪除指定的字符設(shè)備,并釋放相關(guān)的資源。刪除后,該字符設(shè)備將不再可用;

(五)定義fops結(jié)構(gòu)體

static struct file_operations fops = {

.owner = THIS_MODULE,

.open = device_open,

.release = device_release,

.read = device_read,

.write = device_write,

};

file_operations結(jié)構(gòu)體定義如下:

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

int (*open) (struct inode *, struct file *);

int (*release) (struct inode *, struct file *);

// 其他成員函數(shù)...

};

file_oprations結(jié)構(gòu)中定義了非常多的成員,但是對于大多數(shù)字符驅(qū)動而言,只需要實(shí)現(xiàn),其中很少的幾個方法即可。下面將忽略不常用的成員,對常用成員進(jìn)行介紹:

(1)owner:指向持有該結(jié)構(gòu)體的模塊的指針;

(2)llseek:用于實(shí)現(xiàn)文件的定位(seek)操作;

(3)read:用于實(shí)現(xiàn)從文件讀取數(shù)據(jù)的操作;

(4)write:用于實(shí)現(xiàn)向文件寫入數(shù)據(jù)的操作;

(5)open:用于打開文件時的處理操作;

(6)release:用于關(guān)閉文件時的處理操作;

(六)設(shè)備操作函數(shù)的實(shí)現(xiàn)

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備打開的操作

printk(KERN_INFO "This is device_open.n");

return 0;

}

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備關(guān)閉的操作

printk(KERN_INFO "This is device_release.n");

return 0;

}

static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備讀取的操作

printk(KERN_INFO "This is device_read.n");

return 0;

}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備寫入的操作

printk(KERN_INFO "This is device_write.n");

return 0;

}

(七)聲明模塊信息

MODULE_LICENSE("GPL"); // 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple Hello World driver"); // 指定模塊的描述信息

完整mydevice.c示例源碼

#include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數(shù)據(jù)訪問函數(shù)的頭文件

#include <linux/cdev.h> ????????//包含字符設(shè)備頭文件

#define DEVICE_NAME "mydevice" ?// 設(shè)備名稱

static dev_t dev_num; ??//分配的設(shè)備號

struct ?cdev my_cdev; ?????????//字符設(shè)備指針

int major; ?//主設(shè)備號

int minor; ?//次設(shè)備號

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備打開的操作

printk(KERN_INFO "This is device_open.n");

return 0;

}

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備關(guān)閉的操作

printk(KERN_INFO "This is device_release.n");

return 0;

}

static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備讀取的操作

printk(KERN_INFO "This is device_read.n");

return 0;

}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備寫入的操作

printk(KERN_INFO "This is device_write.n");

return 0;

}

static struct file_operations fops = {

.owner = THIS_MODULE,

.open = device_open,

.release = device_release,

.read = device_read,

.write = device_write,

};

static int __init mydevice_init(void)

{

int ret;

// 在這里執(zhí)行驅(qū)動程序的初始化操作

// 注冊字符設(shè)備驅(qū)動程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num); ??//獲取主設(shè)備號

minor=MINOR(dev_num); ??//獲取次設(shè)備號

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops); ??//初始化字符設(shè)備結(jié)構(gòu)體

cdev_add(&my_cdev,dev_num,1); ??//將字符設(shè)備添加到內(nèi)核中

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

// 刪除字符設(shè)備

cdev_del(&my_cdev);

// 注銷字符設(shè)備驅(qū)動程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

module_init(mydevice_init);

module_exit(mydevice_exit);

MODULE_LICENSE("GPL"); ?????// 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple character device driver"); // 指定模塊的描述信息

編譯

復(fù)制helloworld驅(qū)動中的Makefile文件,將其中的hello.o修改為mydevice.o,效果如下:

設(shè)置環(huán)境變量,編譯:

?. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/02_字符驅(qū)動/mydevice$?make

將生成的mydevice.ko文件拷貝到開發(fā)板中。

編寫測試應(yīng)用源碼device_app.c

驅(qū)動中已經(jīng)給應(yīng)用層提供了open、read、write的接口,接下來應(yīng)用層就可以進(jìn)行相應(yīng)的系統(tǒng)調(diào)用。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <errno.h>

#include <fcntl.h>

#define DEV_NAME "/dev/mydevice"

int main(int argc, char *argv[])

{

int reg;

int fd = 0;

int dat = 0;

fd = open (DEV_NAME, O_RDWR);

if (fd < 0) {

perror("Open "DEV_NAME" Failed!n");

exit(1);

}

reg = read(fd, &dat, 1);

if (reg<0) {

perror("read "DEV_NAME" Failed!n");

exit(1);

}

dat = 0;

reg = write(fd, &dat, 1);

if (reg<0) {

perror("write "DEV_NAME" Failed!n");

exit(1);

}

close(fd);

return 0;

}

設(shè)置環(huán)境變量:

?. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

編譯,將生成的device_app文件拷貝到開發(fā)板中:

elf@ubuntu:~/work/test/02_字符驅(qū)動/device_app$ $CC device_app.c -o device_app

測試

(一)加載驅(qū)動

root@ELF1:~# insmod mydevice.ko

major number: 246

minor number: 0

Device registered successfully.

(二)可以看到生成的主設(shè)備號為246,次設(shè)備號為0。我們需要使用主設(shè)備號和次設(shè)備號手動創(chuàng)建/dev/下的設(shè)備節(jié)點(diǎn)。

root@ELF1:~# mknod /dev/mydevice c 246 0

mknod的基本語法如下:

mknod <device_file> <file_type> <major> <minor>

<device_file>:指定要創(chuàng)建的設(shè)備文件節(jié)點(diǎn)的路徑和名稱;

<file_type>:指定設(shè)備文件的類型,可以是b(塊設(shè)備)或c(字符設(shè)備);

<major>:指定設(shè)備文件的主設(shè)備號;

<minor>:指定設(shè)備文件的次設(shè)備號;

(三)運(yùn)行測試應(yīng)用程序

root@ELF1:~# ./device_app

This is device_open.

This is device_read.

This is device_write.

This is device_release.

(四)卸載驅(qū)動

root@ELF1:~# rmmod mydevice.ko

Device unregistered.

相關(guān)推薦