1. 程式人生 > >2018-2019-1 165308 165317 165331 實驗四 外設驅動程式設計

2018-2019-1 165308 165317 165331 實驗四 外設驅動程式設計

2018-2019-1 165308 165317 165331 實驗四 外設驅動程式設計

一、任務一

(一)要求
1、學習資源中全課中的“hqyj.嵌入式Linux應用程式開發標準教程.pdf”中的第十一章

2、提交康奈爾筆記的照片

二、任務二

(一)要求
在Ubuntu完成資源中全課中的“hqyj.嵌入式Linux應用程式開發標準教程.pdf”中的第十一章的test試驗
提交編譯,載入模組,解除安裝模組,測試執行的截圖(要多張,全屏,體現學號資訊)

(二)實驗步驟:

1、編寫程式碼


/* test_drv.c */

#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <linux/kernel.h>

#include <linux/slab.h>

#include <linux/types.h>

#include <linux/errno.h>

#include <linux/cdev.h>

#include <asm/uaccess.h>

#define TEST_DEVICE_NAME "test_dev"

#define BUFF_SZ 1024

/*全域性變數*/

static struct cdev test_dev;

unsigned int major =0;

static char *data = NULL;

/*讀函式*/

static ssize_t test_read(struct file *file,

char *buf, size_t count, loff_t *f_pos)

{

int len;

if (count < 0 )

return -EINVAL;

}
len = strlen(data);
count = (len > count)?count:len;
if (copy_to_user(buf, data, count)) /* ?核心??的數????使用者??*/
{
return -EFAULT;
}
return count;
}
/*寫函式*/
static ssize_t test_write(struct file *file, const char *buffer,
size_t count, loff_t *f_pos)
{
if(count < 0)
{
return -EINVAL;
}
memset(data, 0, BUFF_SZ);
count = (BUFF_SZ > count)?count:BUFF_SZ;
if (copy_from_user(data, buffer, count)) /* 將使用者緩衝的資料複製到核心空間*/
{
return -EFAULT;
}
return count;
}
/*開啟函式*/
static int test_open(struct inode *inode, struct file *file)
{
printk("This is open operation\n");
/* 分配並初始化緩衝區*/
data = (char*)kmalloc(sizeof(char) * BUFF_SZ, GFP_KERNEL);
if (!data)
{
return -ENOMEM;
}
memset(data, 0, BUFF_SZ);
return 0;
}
/*關閉函式*/
static int test_release(struct inode *inode,struct file *file)
{
printk("This is release operation\n");
if (data)
{
kfree(data); /* 釋放緩衝區*/
data = NULL; /* 防止出現野指標*/
}
return 0;
}
/* 建立、初始化字元裝置,並且註冊到系統*/
static void test_setup_cdev(struct cdev *dev, int minor,
struct file_operations *fops)
{
int err, devno = MKDEV(major, minor);
cdev_init(dev, fops);
dev->owner = THIS_MODULE;
dev->ops = fops;
err = cdev_add (dev, devno, 1);
if (err)
{
printk (KERN_NOTICE "Error %d adding test %d", err, minor);
}
}
/* 虛擬裝置的 file_operations 結構 */
static struct file_operations test_fops =
{
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.open = test_open,
.release = test_release,
};
/*模組註冊入口*/
int init_module(void)
{
int result;
dev_t dev = MKDEV(major, 0);
if (major)
{/* 靜態註冊一個裝置,裝置號先前指定好,並設定裝置名,用cat /proc/devices 來檢視 */
result = register_chrdev_region(dev, 1, TEST_DEVICE_NAME);
}
else
{
result = alloc_chrdev_region(&dev, 0, 1, TEST_DEVICE_NAME);
}
if (result < 0)
{
printk(KERN_WARNING "Test device: unable to get major %d\n", major);
return result;
}
test_setup_cdev(&test_dev, 0, &test_fops);
printk("The major of the test device is %d\n", major);
return 0;
}
/*解除安裝模組*/
void cleanup_module(void)
{
cdev_del(&test_dev);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk("Test device uninstalled\n");
}

2、編譯程式碼

通過make執行makefile以實現test_drv.c程式碼的編譯

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build /*核心程式碼編譯路徑*/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
obj-m := test_drv.o /* 將生成的模組為 test_drv.ko*/
endif

3、驅動模組載入和解除安裝

載入指令碼

test_drv_load
#!/bin/sh
# 驅動模組名稱
module="test_drv"
# 裝置名稱。在/proc/devices 中出現
device="test_dev"
# 裝置檔案的輸性
mode="664"
group="david"
# 刪除已存在的裝置節點
rm -f /dev/${device}
# 載入驅動模組
/sbin/insmod -f ./$module.ko $* || exit 1
# 查到裝置的主裝置號
major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"`
# 建立裝置檔案節點
mknod /dev/${device} c $major 0
# 設定裝置檔案屬性
chgrp $group /dev/${device}
chmod $mode /dev/${device}

解除安裝指令碼

test_drv_unload
#!/bin/sh
module="test_drv"
device="test_dev"
# 解除安裝驅動模組
/sbin/rmmod $module $* || exit 1
# 刪除裝置檔案
rm -f /dev/${device}
exit 0

4、執行

test.c

測試程式碼對驅動程式進行測試

/* test.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#define TEST_DEVICE_FILENAME "/dev/test_dev" /* 裝置檔名*/
#define BUFF_SZ 1024 /* 緩衝大小 */
int main()
{
int fd, nwrite, nread;
char buff[BUFF_SZ]; /*緩衝區*/
/* 開啟裝置檔案 */
fd = open(TEST_DEVICE_FILENAME, O_RDWR);
if (fd < 0)
{
perror("open");
exit(1);
}
do
{
printf("Input some words to kernel(enter 'quit' to exit):");
memset(buff, 0, BUFF_SZ);
if (fgets(buff, BUFF_SZ, stdin) == NULL)
{
perror("fgets");
break;
}
buff[strlen(buff) - 1] = '\0';
if (write(fd, buff, strlen(buff)) < 0) /* 向裝置寫入資料 */
{
perror("write");
break;
}
if (read(fd, buff, BUFF_SZ) < 0) /* 從裝置讀取資料 */
{
perror("read");
break;
}
else
{
printf("The read string is from kernel:%s\n", buff);
}
} while(strncmp(buff, "quit", 4));
close(fd);
exit(0);
}

(三)執行結果

在虛擬裝置驅動原始碼目錄下編譯並載入驅動模組。

$  make clean;make

$ ./test_drv_load

編譯並執行測試程式

$ gcc –o test test.c
$ ./test

解除安裝驅動程式

$ ./test_drv_unload

三、問題

因為事先問了其他很快做完的組需要注意些什麼,所以少走了一些彎路,提前在別的組幫助下降需要準備的都下載好了。
例如 出現 Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev 的時候

需要輸入命令列 sudo apt install libelf-dev 來完成缺少部分工具的下載

四、實驗感想

本次實驗學習了初步學習了嵌入式Linux裝置驅動的開發。
使用了康奈爾筆記,線上索/疑問以及總結方面得到更好鍛鍊。