Linux Kernel 學習筆記5:字元裝置
(本章基於:linux-4.4.0-37)
本章介紹如何註冊一個字元裝置,並通過裝置節點對這個字元裝置進行open、close、read、write等操作。
一、字元設備註冊
相關標頭檔案:linux/cdev.h
1、初始化
void cdev_init(struct cdev *cdev, struct file_operations *fops);
1) 待初始化裝置
2) Fops結構
cdev_init(&my_dev,&hello_fops);
hello_dev.owner= THIS_MODULE; //必要的成員初始化
hello_dev.ops =&hello_fops;
2、新增裝置
intcdev_add(struct cdev *dev, dev_t num, unsigned int count);
1) 待新增的裝置
2) 裝置響應的第一個裝置號
3) 響應的裝置個數
result = cdev_add(&hello_dev,MKDEV(hello_major, hello_minor), 1);
3、刪除裝置voidcdev_del(struct cdev *dev);
1) 待刪除的裝置
cdev_del(&hello_dev);
二、定義檔案操作
相關標頭檔案:linux/fs.h
使用struct file_operations 結構定義檔案操作
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_release,
};
三、介面解析
int (*open)(struct inode *, struct file *);
open方法主要進行下列操作
1) 檢查裝置特定的錯誤;
2) 如果他是第一次開啟,初始化該裝置;
int (*release)(struct inode *, struct file *);
release主要用來釋放open時所申請的各種資源,並關閉裝置。
注意:並不是每次close()都會執行release,fork出的子程序可以不用open來建立開啟檔案的拷貝,但每個拷貝都會在程序終止時關閉。核心中維持一個檔案結構被使用多少次的計數,當計數為0 時才真正呼叫release,這樣的機制保證了一次open只進行一次release。(備註:flush在每次close時都會被呼叫,但一般驅動很少實現flush)。
ssize_t (*read)(struct file *file, char __user *user, size_t size, loff_t *offp);
ssize_t (*write)(struct file *fule, const char __user *user, size_t size, loff_t *offp);
file: 檔案指標;
count:請求傳輸資料大小;
user:指向持有被寫入資料的快取,或放入新資料的空快取;
offp:當前存取的檔案位置;
四、核心空間、使用者空間資料互動
static inlinelong copy_from_user(void *to,
const void __user * from, unsignedlong n)
從使用者空間from拷貝n位元組資料到to
static inlinelong copy_to_user(void __user *to,
const void *from, unsigned long n)
從核心空間from拷貝n位元組資料到使用者空間to
這兩個函式返回未拷貝資料的大小,也就是說返回0 表示已拷貝所有資料。
例:
hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
static dev_t devId;
static struct class *cls = NULL;
static struct cdev myDev;
static int
my_open(struct inode *inode, struct file *file)
{
printk(KERN_WARNING "open success!\n");
return 0;
}
static int
my_release(struct inode *inode, struct file *file)
{
printk(KERN_WARNING "close success!\n");
return 0;
}
static int
my_read(struct file *file, char __user *user, size_t size, loff_t *loff)
{
char buf[] = "read kernel data!";
size_t readSize;
(sizeof(buf) > size)? (readSize = size) : (readSize = sizeof(buf));
if(copy_to_user(user, buf, readSize) != 0) {
return -EFAULT;
}
return readSize;
}
static int
my_write(struct file *file, const char __user *user, size_t size, loff_t *loff)
{
char buf[64];
size_t writeSize;
(sizeof(buf) - 1 > size) ? (writeSize = size) : (writeSize = sizeof(buf) - 1);
memset(buf, 0, sizeof(buf));
if(copy_from_user(buf, user, writeSize) != 0) {
return -EFAULT;
}
printk(KERN_ALERT "kernel read data:%s\n", buf);
return writeSize;
}
//定義檔案操作
static struct file_operations myFops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_release,
};
static void
hello_cleanup(void)
{
cdev_del(&myDev);
device_destroy(cls, devId);
class_destroy(cls);
unregister_chrdev_region(devId, 1);
}
static __init int hello_init(void)
{
int result;
//動態註冊裝置號
if(( result = alloc_chrdev_region(&devId, 0, 1, "stone-alloc-dev") ) != 0) {
printk(KERN_WARNING "register dev id error:%d\n", result);
goto err;
} else {
printk(KERN_WARNING "register dev id success!\n");
}
//動態建立裝置節點
cls = class_create(THIS_MODULE, "stone-class");
if(IS_ERR(cls)) {
printk(KERN_WARNING "create class error!\n");
goto err;
}
if(device_create(cls, NULL, devId, "", "hello%d", 0) == NULL) {
printk(KERN_WARNING "create device error!\n");
goto err;
}
//字元設備註冊
myDev.owner = THIS_MODULE; //必要的成員初始化
myDev.ops = &myFops;
cdev_init(&myDev, &myFops);
//新增一個裝置
result = cdev_add(&myDev, devId, 1);
if(result != 0) {
printk(KERN_WARNING "add cdev error!\n");
goto err;
}
printk(KERN_ALERT "hello init success!\n");
return 0;
err:
hello_cleanup();
return -1;
}
static __exit void hello_exit(void)
{
hello_cleanup();
printk(KERN_WARNING "helloworld exit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stone");
使用者層應用程式:
a.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int
main(void)
{
int fd;
char buf[100];
int size;
fd = open("/dev/hello0", O_RDWR);
if(!fd) {
perror("open");
exit(-1);
}
memset(buf, 0, sizeof(buf));
size = read(fd, buf, sizeof(buf));
if(size < 0) {
close(fd);
printf("read error!\n");
exit(1);
}
printf("read %d:%s\n", size, buf);
size = write(fd, "write data to kernel!\n", 23);
if(size < 0) {
close(fd);
printf("read error!\n");
exit(1);
}
printf("write %d\n", size);
close(fd);
return 0;
}