前言
驅動作爲橋樑,用戶層調用預定義名稱的系統函數與系統內核交互,而用戶層與系統層不能直接進行數據傳遞,進行本篇主要就是理解清楚驅動如何讓用戶編程來實現與內核的數據交互傳遞。
- 設備節點是應用層(用戶層)與內核層交互;
- 使用預先的結構體進行操作,如系統open函數對應了驅動中文件操作及的open指針結構體:struct file_operations;
- 文件操作集結構體,填充結構體對應指針,填充自己使用到的就行了,多餘的可以不填充,調用也不會崩潰或返回錯誤,會返回0;
那麼如何將應用層的輸入寫入進去可用,如何將內核層的數據通過read返回出來,就是本篇學習了。
首先複製之前的testFileOpts的驅動,改個名字爲:testFileOpts:
cd ~/work/drive/
ls
cp -arf 003_testFileOpts 004_testReadWrite
cd 004_testReadWrite/
make clean
ls
mv testFileOpts.c testReadWrite.c
vi Makefile
ls
其中修改makefile裏面的模塊名稱(obj-m模塊名稱),模板準備好了
gedit Makefile
下面基於testReadWrite.c文件進行註冊雜項設備,修改.c文件:
gedit testReadWrite.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
return 0;
}
// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
printk("int misc_release(struct inode * pInde, struct file * pFile)\n");
return 0;
}
// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
return 0;
}
// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
return 0;
}
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read,
.write = misc_write,
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 這個宏是動態分配次設備號,避免衝突
.name = "register_hongPangZi_testReadWrite", // 設備節點名稱
.fops = &misc_fops, // 這個變量記住,自己起的,步驟二使用
};
static int registerMiscDev_init(void)
{
int ret;
// 在內核裏面無法使用基礎c庫printf,需要使用內核庫printk
printk("Hello, I’m hongPangZi, registeraMiscDev_init\n");
ret = misc_register(&misc_dev);
if(ret < 0)
{
printk("Failed to misc_register(&misc_dev)\n");
return -1;
}
return 0;
}
static void registerMiscDev_exit(void)
{
misc_deregister(&misc_dev);
printk("bye-bye!!!\n");
}
MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);
內核層和用戶層不能中是不能直接與用戶數據交互,需要使用內核函數copy_to_user和copy_from_user。
在內核中可以使用printk,memset,memcpy,strlen等函數。
頭文件是:linux/uaccess.h(我們這是ubuntu,不是arm)
可以在內核根目錄下搜索下:
find . -type f -exec grep -l "copy_to_user(void" {} \;
static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
簡化下:
static unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
參數分別是,複製到的地址(內核空間),從什麼地址複製(用戶空間),複製長度;
static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)
簡化下:
static unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
參數分別是,複製到的地址(用戶空間),從什麼地址複製(內核空間),複製長度;
#include <linux/uaccess.h> // Demo_004 add
static char kBuf[256] = {0x00}; // Demo_004 add
// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
memcpy(kBuf, "init kBuf", sizeof("init kBuf"));
printk("kBuf = %s\n", kBuf);
return 0;
}
// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
if(copy_to_user(pUser, kBuf, strlen(kBuf)) != 0)
{
printk("Failed to copy_to_user(pUser, kBuf, strlen(kBuf)\n");
return -1;
}
return 0;
}
// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
if(copy_from_user(kBuf, pUser, size) != 0)
{
printk("Failed to copy_from_user(kBuf, pUser, size)\n");
return -1;
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int fd = -1;
char buf[32] = {0};
int ret = -1;
const char devPath[] = "/dev/register_hongPangZi_testReadWrite";
fd = open(devPath, O_RDWR);
if(fd < 0)
{
printf("Failed to open %s\n", devPath);
return -1;
}else{
printf("Succeed to open %s\n", devPath);
}
// 讀取
ret = read(fd, buf, sizeof(buf) < 0);
if(ret < 0)
{
printf("Failed to read %s\n", devPath);
close(fd);
return 0;
}else{
printf("Succeed to read [%s]\n", buf);
}
// 修改內容
memset(buf, 0x00, sizeof(buf));
memcpy(buf, "Get you content", strlen("Get you content"));
// 寫入
ret = write(fd, buf, sizeof(buf));
if(ret < 0)
{
printf("Failed to write %s\n", devPath);
close(fd);
return 0;
}else{
printf("Succeed to write [%s]\n", buf);
}
// 讀取
ret = read(fd, buf, sizeof(buf) < 0);
if(ret < 0)
{
printf("Failed to read %s\n", devPath);
close(fd);
return 0;
}else{
printf("Succeed to read [%s]\n", buf);
}
close(fd);
printf("exit\n");
fd = -1;
return 0;
}
make
sudo insmod testReadWrite.ko
gcc test.c
sudo ./a.out
測試結果與預期相同