若該文為原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134533533文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-751810.html
紅胖子網(wǎng)絡(luò)科技博文大全:開(kāi)發(fā)技術(shù)集合(包含Qt實(shí)用技術(shù)、樹(shù)莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機(jī)、軟硬結(jié)合等等)持續(xù)更新中…
Linux系統(tǒng)移植和驅(qū)動(dòng)開(kāi)發(fā)專(zhuān)欄
上一篇:《Linux驅(qū)動(dòng)開(kāi)發(fā)筆記(三):基于ubuntu的helloworld驅(qū)動(dòng)源碼編寫(xiě)、makefile編寫(xiě)以及驅(qū)動(dòng)編譯加載流程測(cè)試》
下一篇:《Linux驅(qū)動(dòng)開(kāi)發(fā)筆記(五):驅(qū)動(dòng)連接用戶(hù)層與內(nèi)核層的文件操作集原理和Demo》文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-751810.html
前言
??驅(qū)動(dòng)的開(kāi)發(fā)需要先熟悉基本概念類(lèi)型,本篇講解linux雜項(xiàng)設(shè)備基礎(chǔ),還是基于虛擬機(jī)ubuntu去制作驅(qū)動(dòng),只需要虛擬機(jī)就可以嘗試編寫(xiě)注冊(cè)雜項(xiàng)設(shè)備的基本流程。
linux三大設(shè)備驅(qū)動(dòng)
- 字符設(shè)備:IO的傳輸過(guò)程是以字符為單位的,沒(méi)有緩沖,比如I2C(SDA、SCL),SPI(MISO、MOSI、SCLK、CS)。
- 塊設(shè)備:IO的傳輸過(guò)程是以塊為單位的,跟存儲(chǔ)相關(guān)的都屬于塊設(shè)備,比如tf卡,sd卡。
- 網(wǎng)絡(luò)設(shè)備:IO的傳輸以socket套接字來(lái)訪問(wèn)的。
雜項(xiàng)設(shè)備
- 雜項(xiàng)設(shè)備是屬于字符設(shè)備,可以自動(dòng)生成設(shè)備節(jié)點(diǎn),設(shè)備節(jié)點(diǎn)位于/dev/目錄下,是設(shè)備名稱(chēng),如/dev/ttyS9等。
- 主設(shè)備號(hào)相同,統(tǒng)一為10,次設(shè)備號(hào)不同,主設(shè)備相同可以節(jié)省內(nèi)核資源。
通過(guò)下列指令,可以查看系統(tǒng)雜項(xiàng)設(shè)備
cat /proc/misc
??在虛擬機(jī)上測(cè)試,查看雜項(xiàng):
??
- 設(shè)備號(hào)分為主設(shè)備號(hào)和次設(shè)備號(hào),主設(shè)備號(hào)是唯一的,次設(shè)備號(hào)不一定唯一。
通過(guò)下列指令,可以查看系統(tǒng)主設(shè)備號(hào):
cat /proc/devices
??
雜項(xiàng)設(shè)備描述結(jié)構(gòu)體
??ubuntu來(lái)說(shuō),自帶的/usr/src下的就是內(nèi)核的頭文件。
cd /usr/src/linux-headers-4.18.0-15
vi include/linux/miscdevice.h
??定位到之前ubuntu自帶的內(nèi)核頭文件下:
??
??
??查看到雜項(xiàng)設(shè)備的結(jié)構(gòu)體:
struct miscdevice {
int minor; // 次設(shè)備號(hào)
const char *name; // 設(shè)備節(jié)點(diǎn)名稱(chēng)(如/dev/ttyS8,則ttyS是名稱(chēng))
const struct file_operations *fops; // 文件操作集(非常重要)
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
??(注意:沒(méi)打注釋的,一般不管)
雜項(xiàng)設(shè)備文件操作集
cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h
??搜索到(vi則直接使用“/”):
??
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*setfl)(struct file *, unsigned long);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
} __randomize_layout;
??例如read函數(shù),那么就是打開(kāi)驅(qū)動(dòng)使用系統(tǒng)read,打開(kāi)這個(gè)設(shè)備驅(qū)動(dòng)的句柄,那么久會(huì)調(diào)用read函數(shù),其他的以此類(lèi)推,還比較好理解。
??以我們一個(gè)registerHelloWorld為例子,來(lái)簡(jiǎn)單說(shuō)明。
驅(qū)動(dòng)編寫(xiě)空模板準(zhǔn)備
??首先復(fù)制之前的hello world的驅(qū)動(dòng),改個(gè)名字為:registerMiscDev:
cd ~/work/drive
cp -arf hellowolrd registerMiscDev
??
cd registerMiscDev/
rm *.ko *.o *.order *.symvers
??這里刪除起來(lái)麻煩,修改makefile,添加clean:
??
??然后測(cè)試一下:
??
??繼續(xù)修改源碼文件名稱(chēng):
mv helloworld.c registerMiscDev.c
??修改完如下:
??
??然后修改makefile里面的(obj-m模塊名稱(chēng)改下),模板準(zhǔn)備好了
??
??下面基于registerMiscDev.c文件進(jìn)行注冊(cè)雜項(xiàng)設(shè)備,在修改.c文件:
??
#include <linux/init.h>
#include <linux/module.h>
static int registerMiscDev_init(void)
{
// 在內(nèi)核里面無(wú)法使用基礎(chǔ)c庫(kù)printf,需要使用內(nèi)核庫(kù)printk
printk("Hello, I’m hongPangZi, registerMiscDev_init\n");
return 0;
}
static void registerMiscDev_exit(void)
{
printk("bye-bye!!!\n");
}
MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);
雜項(xiàng)設(shè)備注冊(cè)流程Demo
步驟一:填充miscdevice結(jié)構(gòu)體
??在編寫(xiě)驅(qū)動(dòng)的時(shí)候,代碼中填充信息結(jié)構(gòu)體。
??添加頭文件miscdevice.h
#include <linux/miscdevice.h>
#include <linux/fs.h>
??
??然后填充雜項(xiàng)設(shè)備結(jié)構(gòu)體:
??
??(注意:開(kāi)始為“.”,結(jié)束為“,”,最后一行習(xí)慣加“,”了,這樣可以全部統(tǒng)一復(fù)制啥的,省的加沒(méi)加的)
struct miscdevice misc_dev {
.minor = MISC_DYNAMIC_MINRO, // 這個(gè)宏是動(dòng)態(tài)分配次設(shè)備號(hào),避免沖突
.name = "register_hongPangZi_misc, // 設(shè)備節(jié)點(diǎn)名稱(chēng)
.fops = misc_fops, // 這個(gè)變量記住,自己起的,步驟二使用
}
??
步驟二:填充file_operations結(jié)構(gòu)體
??在編寫(xiě)驅(qū)動(dòng)的時(shí)候,代碼中填充文件操作結(jié)構(gòu)體。
??
struct file_operations misc_fops {
.owner = THIS_MODULE
}
??
步驟三:注冊(cè)雜項(xiàng)設(shè)備并生成設(shè)備節(jié)點(diǎn)
??注冊(cè)到內(nèi)核:
static int registerMiscDev_init(void)
{
// 在內(nèi)核里面無(wú)法使用基礎(chǔ)c庫(kù)printf,需要使用內(nèi)核庫(kù)printk
printk("Hello, I’m hongPangZi, registerMiscDev_init\n");
int ret = 0;
ret = misc_register(misc_dev);
if(ret < 0)
{
printk("Failed to misc_register(misc_dev)\n");
return -1;
}
return 0;
}
??
??有注冊(cè)就有注銷(xiāo):
static int registerMiscDev_init(void)
{
// 在內(nèi)核里面無(wú)法使用基礎(chǔ)c庫(kù)printf,需要使用內(nèi)核庫(kù)printk
printk("Hello, I’m hongPangZi, registerMiscDev_init\n");
int ret = 0;
ret = misc_register(&misc_dev);
if(ret < 0)
{
printk("Failed to misc_register(misc_dev)\n");
return -1;
}
return 0;
}
??
??完整的文件源碼:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
struct file_operations misc_fops = {
.owner = THIS_MODULE,
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 這個(gè)宏是動(dòng)態(tài)分配次設(shè)備號(hào),避免沖突
.name = "register_hongPangZi_misc", // 設(shè)備節(jié)點(diǎn)名稱(chēng)
.fops = &misc_fops, // 這個(gè)變量記住,自己起的,步驟二使用
};
static int registerMiscDev_init(void)
{
// 在內(nèi)核里面無(wú)法使用基礎(chǔ)c庫(kù)printf,需要使用內(nèi)核庫(kù)printk
printk("Hello, I’m hongPangZi, registerMiscDev_init\n");
int ret = 0;
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);
步驟四:編譯make
make
??直接在驅(qū)動(dòng)工程目錄編譯:
??
??下面這個(gè)警告,實(shí)際上定義要在任何使用函數(shù)之前:
??
??修改下:
??
??
??編譯成功
??
步驟五:加載卸載驅(qū)動(dòng)測(cè)試
??將驅(qū)動(dòng)拷貝到開(kāi)發(fā)板或者目標(biāo)系統(tǒng),然后使用加載指令:
sudo insmod registerMiscDev.ko
??會(huì)打印入口加載的printk輸出。
??
??出現(xiàn)問(wèn)題可能原因一是內(nèi)核編譯使用的編譯器和模塊使用的編譯器版本不一致。ubuntu中printk終端打入內(nèi)核日志消息了,可以使用dmesg進(jìn)行查看:
dmesg
??
??然后查看是否加入了雜項(xiàng)設(shè)備節(jié)點(diǎn):
??
??然后注銷(xiāo):
sudo rmmod registerMiscDev.ko
??
??跟隨著,結(jié)點(diǎn)消失了:
??
入坑
入坑一:編譯報(bào)錯(cuò),結(jié)構(gòu)體之后未加分號(hào)
問(wèn)題
??編譯錯(cuò)誤,結(jié)構(gòu)體后面加分號(hào)
解決
??加分號(hào),腦袋有點(diǎn)蒙
??
入坑二:編譯錯(cuò)誤,文件操作指針問(wèn)題
問(wèn)題
??
解決
??這是寫(xiě)錯(cuò)了,是指針,需要加取地址&。
上一篇:《Linux驅(qū)動(dòng)開(kāi)發(fā)筆記(三):基于ubuntu的helloworld驅(qū)動(dòng)源碼編寫(xiě)、makefile編寫(xiě)以及驅(qū)動(dòng)編譯加載流程測(cè)試》
下一篇:《Linux驅(qū)動(dòng)開(kāi)發(fā)筆記(五):驅(qū)動(dòng)連接用戶(hù)層與內(nèi)核層的文件操作集原理和Demo》
若該文為原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134533533
到了這里,關(guān)于Linux驅(qū)動(dòng)開(kāi)發(fā)筆記(四):設(shè)備驅(qū)動(dòng)介紹、熟悉雜項(xiàng)設(shè)備驅(qū)動(dòng)和ubuntu開(kāi)發(fā)雜項(xiàng)設(shè)備Demo的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!