1:APP打開的文件在內核中如何表示
1.1
int open(const char *pathname, int flags, mode_t mode);例如: fd = open(argv[1], O_RDWR);

1.2 打開字符設備節點時,內核中也有對應的 struct file
????????這個結構體中的結構體:struct file_operations *f_op,這是由驅動程序提供的。
2:如何編寫驅動程序
1:確定主設備號,也可以讓內核分配
2:定義自己的 file_operations 結構體
3:實現對應的 drv_open/drv_read/drv_write 等函數,填入 file_operations 結構體
4:把 file_operations 結構體告訴內核:register_chrdev
5:誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數
6:有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用unregister_chrdev
7:其他完善:提供設備信息,自動創建設備節點:class_create, device_create
3:代碼編寫
3.1:主要代碼分析
3.1.1:確定主設備號。
????????/* 1. 確定主設備號*/
static int major = 0;? ? ? ?
????????//后續在入口函數中,使用下列代碼:注冊函數進行分配
major = register_chrdev(0, "hello", &hello_drv);
3.1.2:定義自己的 file_operations 結構體
/* 2. 定義自己的file_operations結構體 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?*/
static struct file_operations hello_drv = {
? ? .owner = THIS_MODULE,? ? //必須存在
? ? .open = hello_drv_open,? ? ?//hello驅動程序的open函數,后續我們需要自己實現這個函數
? ? .read = hello_drv_read,? ??//hello驅動程序的read函數,后續我們需要自己實現這個函數
? ? .write = hello_drv_write,????//hello驅動程序的write函數,后續我們需要自己實現這個函數?
? ? .release = hello_drv_close,?//hello驅動程序的clos函數,后續我們需要自己實現這個函數
};
3.1.3:實現對應的 drv_open/drv_read/drv_write 等函數,??
注:我們現在編寫的函數屬于驅動函數,那么我們在串口掉用這些函數時,
????????串口那邊,我們屬于APP
? ? ? ? 而這些驅動屬于 “內核”
????????我們串口調用read函數? len = read(fd, buf, 1024);時,我們是需要把kernel_buf的數據讀到buf中。所以在這read代碼中,我們使用? err = copy_to_user(buf, kernel_buf, MIN(1024, size));這行代碼,將kernel_buf寫入到buf中。
????????我們串口調用write函數?write(fd, argv[2], len);時,是把argv[2]的數據寫入到kernel_buf中。所以在這write代碼中,我們使用? err = copy_from_user(kernel_buf, buf, MIN(1024, size));這行代碼,將buf也就是argv[2]寫入到kernel_buf中。
? ? ? ? open函數和close函數只是打印一下內核信息,不做數據處理。
/* 3. 實現對應的open/read/write等函數,填入file_operations結構體 ? ? ? ? ? ? ? ? ? */
static char kernel_buf[1024];
static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
? ? int err;
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
? ? err = copy_to_user(buf, kernel_buf, MIN(1024, size));
? ? return MIN(1024, size);
}
static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
? ? int err;
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
? ? err = copy_from_user(kernel_buf, buf, MIN(1024, size));
? ? return MIN(1024, size);
}
static int hello_drv_open(struct inode *node, struct file *file)
{
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
? ? return 0;
}
static int hello_drv_close(struct inode *node, struct file *file)
{
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
? ? return 0;
}
3.1.4:把 file_operations 結構體告訴內核:register_chrdev
3.1.5. 誰來注冊驅動程序啊?得有一個入口函數:?
? ? ? ? 3.4和3.5可同時在入口函數中實現,安裝驅動程序時,系統去調用這個入口函數,這是直接在入口函數中直接將結構體告訴內核
static int __init hello_init(void)
{
? ? int err;
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//3.4也就是這行代碼。在入口函數中進行 注冊驅動程序
? ? major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */
//? 下面的代碼屬于自動創建設備節點,這里先抄著使用
? ? hello_class = class_create(THIS_MODULE, "hello_class");
? ? err = PTR_ERR(hello_class);
? ? if (IS_ERR(hello_class))
? ? {
? ? ? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
? ? ? ? unregister_chrdev(major, "hello");
? ? ? ? return -1;
? ? }
? ? device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
? ? return 0;
}
3.1.6:有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用unregister_chrdev
static void __exit hello_exit(void)
{
? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//銷毀創建的節點,以及卸載驅動程序
? ? device_destroy(hello_class, MKDEV(major, 0));
? ? class_destroy(hello_class);
? ? unregister_chrdev(major, "hello");
}
3.1.7:其他完善:提供設備信息,自動創建設備節點:class_create, device_create
/* 7. 其他完善:提供設備信息,自動創建設備節點 */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
3.2:全部代碼
3.2.1:hello_drv.c
#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>/* 1. 確定主設備號 */
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;#define MIN(a, b) (a < b ? a : b)/* 3. 實現對應的open/read/write等函數,填入file_operations結構體 */
static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_to_user(buf, kernel_buf, MIN(1024, size));return MIN(1024, size);
}static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(kernel_buf, buf, MIN(1024, size));return MIN(1024, size);
}static int hello_drv_open(struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int hello_drv_close(struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 2. 定義自己的file_operations結構體 */
static struct file_operations hello_drv = {.owner = THIS_MODULE,.open = hello_drv_open,.read = hello_drv_read,.write = hello_drv_write,.release = hello_drv_close,
};/* 4. 把file_operations結構體告訴內核:注冊驅動程序 */
/* 5. 誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數 */
static int __init hello_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */hello_class = class_create(THIS_MODULE, "hello_class");err = PTR_ERR(hello_class);if (IS_ERR(hello_class)){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "hello");return -1;}device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */return 0;
}/* 6. 有入口函數就應該有出口函數:卸載驅動程序時,就會去調用這個出口函數 */
static void __exit hello_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(hello_class, MKDEV(major, 0));class_destroy(hello_class);unregister_chrdev(major, "hello");
}/* 7. 其他完善:提供設備信息,自動創建設備節點 */module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");
3.2.2 hello_drv_test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>/** ./hello_drv_test -w abc* ./hello_drv_test -r*/
int main(int argc, char **argv)
{int fd;char buf[1024];int len;/* 1. 判斷參數 */if (argc < 2) {printf("Usage: %s -w <string>\n", argv[0]);printf(" %s -r\n", argv[0]);return -1;}/* 2. 打開文件 */fd = open("/dev/hello", O_RDWR);if (fd == -1){printf("can not open file /dev/hello\n");return -1;}/* 3. 寫文件或讀文件 */if ((0 == strcmp(argv[1], "-w")) && (argc == 3)){len = strlen(argv[2]) + 1;len = len < 1024 ? len : 1024;write(fd, argv[2], len);}else{len = read(fd, buf, 1024); buf[1023] = '\0';printf("APP read : %s\n", buf);}close(fd);return 0;
}
3.3:測試
./hello_drv_test -w www.100ask.net // 把字符串“www.100ask.net”發給驅動程序./hello_drv_test -r // 把驅動中保存的字符串讀回