字符設備驅動代碼編寫
一、驅動模塊代碼編寫
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/uaccess.h>
static int data;
static int chr_open (struct inode * inode, struct file * file){
printk("character device open\n");
return 0;
}
static int chr_release (struct inode * inode, struct file * file){
printk("character device close\n");
return 0;
}
ssize_t chr_read (struct file * file, char __user * buf, size_t size, loff_t * offset){
if(copy_to_user(buf, &data,sizeof(data))){
printk("copy failed\n");
return -EFAULT;
}
return sizeof(data);
}
ssize_t chr_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
if(copy_from_user(&data, buf, sizeof(data))){
printk("copy failed\n");
return -EFAULT;
}
return sizeof(data);
}
/* file_operations 用來存儲驅動內核模塊提供的對設備進行各種操作的函數的指針。
* 該結構體的每個域都對應着驅動內核模塊用來處理某個被請求的事務的函數的地址。
*/
static struct file_operations chr_fop = {
.owner = THIS_MODULE,
.open = chr_open,
.release = chr_release,
.read = chr_read,
.write = chr_write,
};
static struct cdev *my_chr_cdev;
static struct class *chr_class;
static dev_t device;
/* 驅動模塊的入口函數 */
static int chr_test_init(void){
if (device){
/* 靜態申請設備號 */
register_chrdev_region(device,1,"chr_dev");
}else{
/* 動態申請設備號 */
alloc_chrdev_region(&device,0, 1,"chr_dev");
}
/* 申請空間 */
my_chr_cdev = cdev_alloc();
/* 注冊字符設備驅動 */
cdev_init(my_chr_cdev,&chr_fop);
cdev_add(my_chr_cdev, device, 1);
/* 創建設備節點 */
chr_class = class_create(THIS_MODULE, "mydev");
class_device_create(chr_class, NULL, device, NULL,"mydev");
return 0;
}
/* 驅動模塊的退出函數 */
static void chr_test_exit(void){
/* 刪除設備節點 */
class_device_destroy(chr_class, device);
class_destroy(chr_class);
/* 注銷字符設備驅動 */
cdev_del(my_chr_cdev);
/* 釋放空間 */
cdev_put(my_chr_cdev);
/* 釋放設備號和相應的設備名 */
unregister_chrdev_region(device, 1);
}
/* 這個宏將 chr_test_init 函數修飾為模塊的入口函數 */
module_init(chr_test_init);
/* 這個宏將 chr_test_exit 函數修飾為模塊的退出函數 */
module_exit(chr_test_exit);
/* 遵循GPL協議 */
MODULE_LICENSE("GPL");
二、編寫Makefile
#內核源碼樹路徑
KERN_DIR = /work/system/kernel/source/linux-2.6.22.6
#目標文件
obj-m += first.o
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
三、測試應用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int buf;
int main(int argc, char** argv){
int fd;
if((fd = open("/dev/mydev",O_RDWR))<0){
printf("open device failed\n");
return -1;
}
/* 將數據寫到驅動程序,再讀出來驗證 */
for(int i=0; i<10; i++){
write(fd,&i,sizeof(i));
read(fd,&buf,sizeof(buf));
printf("read the data is:%d\n",buf);
sleep(1);
}
close(fd);
return 0;
}
四、編譯測試
1、編譯內核驅動
將源代碼和Makefile考到linux系統,然后執行
make
將frist.ko文件copy 到開發板上,這里我使用的是nfs(網絡文件系統)
cp first.ko /work/system/nfs/
2、編譯測試程序
編譯
arm-linux-gcc -o main main.c
將main文件copy 到開發板上
cp main /work/system/nfs/
3、測試
插入內核模塊
insmod frist.ko
查看模塊信息
lsmod
執行應用程序
./main·
五、小結
(在設備驅動中常用的命令)
加載模塊
insmod xxx.ko
查看內核中已插入的模塊
lsmod
卸載模塊
rmmod xxx
查看模塊的描述信息
modinfo xxx.ko