Nuttx相關(guān)的歷史文章:
介紹
Nuttx支持多種設(shè)備驅(qū)動(dòng)嗜价,包括:
- 字符設(shè)備驅(qū)動(dòng)约谈,比如串口設(shè)備、觸摸屏設(shè)備、ADC/DAC锅铅、PWM、CAN晾嘶、正交編碼器幽邓、Timer、RTC撤蟆、Watchdog奕塑、Keyboard/Keypad等;
- 塊設(shè)備驅(qū)動(dòng)家肯;
- 其他特殊設(shè)備驅(qū)動(dòng)龄砰,比如Ethernet、SPI讨衣、I2C换棚、Frame Buffer、LCD反镇、MTD固蚤、SDIO、USB等歹茶;
總體來(lái)說(shuō)夕玩,Nuttx中的驅(qū)動(dòng)機(jī)制相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,它并沒(méi)有提供像Linux系統(tǒng)那樣復(fù)雜的驅(qū)動(dòng)模型機(jī)制惊豺,比如Device燎孟、Driver、Bus扮叨、Class等缤弦。Nuttx只是簡(jiǎn)單的通過(guò)驅(qū)動(dòng)注冊(cè)接口,將驅(qū)動(dòng)注冊(cè)進(jìn)文件系統(tǒng)中彻磁,并實(shí)現(xiàn)file_operations
操作函數(shù)集碍沐,上層應(yīng)用便能通過(guò)標(biāo)準(zhǔn)的系統(tǒng)調(diào)用,進(jìn)而調(diào)用到低層的驅(qū)動(dòng)衷蜓。
我將以字符設(shè)備驅(qū)動(dòng)來(lái)闡述整個(gè)驅(qū)動(dòng)的機(jī)制累提。
數(shù)據(jù)結(jié)構(gòu)與接口
數(shù)據(jù)結(jié)構(gòu)
應(yīng)用層通過(guò)系統(tǒng)調(diào)用來(lái)訪問(wèn)驅(qū)動(dòng):系統(tǒng)調(diào)用->vfs->驅(qū)動(dòng),因此首先需要了解一下磁浇,驅(qū)動(dòng)注冊(cè)進(jìn)文件系統(tǒng)時(shí)所涉及到的數(shù)據(jù)結(jié)構(gòu)斋陪。數(shù)據(jù)結(jié)構(gòu)的相關(guān)定義在include/nuttx/fs/fs.h
文件中。
首先,驅(qū)動(dòng)注冊(cè)后无虚,會(huì)創(chuàng)建一個(gè)inode
缔赠,對(duì)應(yīng)到設(shè)備文件上:
/* This structure represents one inode in the Nuttx pseudo-file system */
struct inode
{
FAR struct inode *i_peer; /* Link to same level inode */
FAR struct inode *i_child; /* Link to lower level inode */
int16_t i_crefs; /* References to inode */
uint16_t i_flags; /* Flags for inode */
union inode_ops_u u; /* Inode operations */
#ifdef CONFIG_FILE_MODE
mode_t i_mode; /* Access mode flags */
#endif
FAR void *i_private; /* Per inode driver private data */
char i_name[1]; /* Name of inode (variable) */
};
其中i_flags
字段用于標(biāo)記該inode
對(duì)應(yīng)的為什么文件,典型的有驅(qū)動(dòng)友题、消息隊(duì)列等嗤堰,專門有宏定義來(lái)設(shè)置或者判斷這個(gè)字段是否為驅(qū)動(dòng)文件:
#define INODE_IS_DRIVER(i) INODE_IS_TYPE(i,FSNODEFLAG_TYPE_DRIVER)
#define INODE_SET_DRIVER(i) INODE_SET_TYPE(i,FSNODEFLAG_TYPE_DRIVER)
struct inode
結(jié)構(gòu)體中inode_ops_u
用于描述操作函數(shù)集,這個(gè)字段是一個(gè)聯(lián)合體度宦,可以是字符設(shè)備驅(qū)動(dòng)踢匣、塊設(shè)備驅(qū)動(dòng)、掛載點(diǎn)等的操作函數(shù)集戈抄。驅(qū)動(dòng)操作函數(shù)集如下:
struct file_operations
{
/* The device driver open method differs from the mountpoint open method */
int (*open)(FAR struct file *filep);
/* The following methods must be identical in signature and position because
* the struct file_operations and struct mountp_operations are treated like
* unions.
*/
int (*close)(FAR struct file *filep);
ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
off_t (*seek)(FAR struct file *filep, off_t offset, int whence);
int (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
/* The two structures need not be common after this point */
#ifndef CONFIG_DISABLE_POLL
int (*poll)(FAR struct file *filep, struct pollfd *fds, bool setup);
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int (*unlink)(FAR struct inode *inode);
#endif
};
這個(gè)函數(shù)集离唬,由低層的驅(qū)動(dòng)來(lái)實(shí)現(xiàn),并且設(shè)置進(jìn)設(shè)備文件對(duì)應(yīng)的inode
中划鸽,當(dāng)系統(tǒng)調(diào)用操作設(shè)備文件時(shí)输莺,便能根據(jù)設(shè)備文件對(duì)應(yīng)的inode
來(lái)找到對(duì)應(yīng)的函數(shù)了。
接口
驅(qū)動(dòng)注冊(cè)的時(shí)候裸诽,會(huì)調(diào)用register_driver()
接口:
/****************************************************************************
* Name: register_driver
*
* Description:
* Register a character driver inode the pseudo file system.
*
* Input parameters:
* path - The path to the inode to create
* fops - The file operations structure
* mode - inmode priviledges (not used)
* priv - Private, user data that will be associated with the inode.
*
* Returned Value:
* Zero on success (with the inode point in 'inode'); A negated errno
* value is returned on a failure (all error values returned by
* inode_reserve):
*
* EINVAL - 'path' is invalid for this operation
* EEXIST - An inode already exists at 'path'
* ENOMEM - Failed to allocate in-memory resources for the operation
*
****************************************************************************/
int register_driver(FAR const char *path, FAR const struct file_operations *fops,
mode_t mode, FAR void *priv)
{
FAR struct inode *node;
int ret;
/* Insert a dummy node -- we need to hold the inode semaphore because we
* will have a momentarily bad structure.
*/
inode_semtake();
ret = inode_reserve(path, &node);
if (ret >= 0)
{
/* We have it, now populate it with driver specific information.
* NOTE that the initial reference count on the new inode is zero.
*/
INODE_SET_DRIVER(node);
node->u.i_ops = fops;
#ifdef CONFIG_FILE_MODE
node->i_mode = mode;
#endif
node->i_private = priv;
ret = OK;
}
inode_semgive();
return ret;
}
這個(gè)接口完成以下幾個(gè)操作:
- 根據(jù)
path
(一般對(duì)應(yīng)設(shè)備文件模闲,比如/dev/xxxx
),來(lái)查找是否存在對(duì)應(yīng)的inode
崭捍,如果沒(méi)有的話尸折,那為path
創(chuàng)建一個(gè)inode
; - 將實(shí)際驅(qū)動(dòng)實(shí)現(xiàn)的
struct file_operations fops
更新到path
對(duì)應(yīng)的inode
中,此外還設(shè)置權(quán)限殷蛇; - 將
priv
數(shù)據(jù)設(shè)置進(jìn)inode
中实夹,這個(gè)一般存放驅(qū)動(dòng)的私有數(shù)據(jù);
ADC驅(qū)動(dòng)
下面將以一個(gè)實(shí)際的驅(qū)動(dòng)粒梦,ADC驅(qū)動(dòng)
亮航,來(lái)分析一下流程。
在Nuttx的驅(qū)動(dòng)代碼中匀们,你會(huì)發(fā)現(xiàn)經(jīng)常會(huì)把驅(qū)動(dòng)分成兩部分缴淋,一個(gè)是upper half
,一個(gè)是lower half
:
-
upper half
:上半部分提供了應(yīng)用程序級(jí)的通用接口泄朴,也就是實(shí)現(xiàn)了file_operations
中的函數(shù)集重抖,比如針對(duì)ADC驅(qū)動(dòng)
,專門有drivers/analog/adc.c
來(lái)描述上半部分的操作祖灰,這個(gè)對(duì)于所有的ADC
設(shè)備都是相同的钟沛; -
lower half
:下半部分基于特定平臺(tái)的驅(qū)動(dòng)程序,用于實(shí)現(xiàn)硬件級(jí)的控制局扶,比如寄存器的操作等恨统。arch/arm/src/lpc43xx/lpc43_adc.c
文件實(shí)現(xiàn)了特定的硬件驅(qū)動(dòng)叁扫;
整體的框架如下圖所示:
- 芯片相關(guān),代表了
lower half
畜埋,針對(duì)硬件的實(shí)際操作莫绣,并且在中斷處理函數(shù)中,會(huì)去回調(diào)upper half
的回調(diào)函數(shù)悠鞍⊥米郏可以在這個(gè)回調(diào)函數(shù)中做一些處理,比如通過(guò)消息隊(duì)列的機(jī)制狞玛,統(tǒng)治上層應(yīng)用已經(jīng)收到了數(shù)據(jù); - 通用框架涧窒,代表了
upper half
心肪,對(duì)接上層的系統(tǒng)調(diào)用,并且在實(shí)現(xiàn)file_operations
函數(shù)集的時(shí)候纠吴,會(huì)去調(diào)用lower half
的接口硬鞍; - 板級(jí)部分,這個(gè)部分其實(shí)是將
upper half
和lower half
進(jìn)行綁定戴已,建立連接并注冊(cè)進(jìn)文件系統(tǒng)中固该,這個(gè)接口最終會(huì)在系統(tǒng)boot的階段調(diào)用;
具體的驅(qū)動(dòng)代碼就不貼了糖儡。
其他的驅(qū)動(dòng)實(shí)現(xiàn)伐坏,機(jī)制都大體類似蒂培,分成兩部分婆瓜,上半部分對(duì)接應(yīng)用系統(tǒng)調(diào)用,下半部分對(duì)應(yīng)實(shí)際的低層硬件操作偶摔,這種分層是一種合理的做法金闽,上半部分做成通用的框架纯露,不需要改動(dòng),下半部分針對(duì)不同硬件實(shí)現(xiàn)具體的操作接口即可了代芜。