第一次写Nuttx系统的驱动,用惯了rt-thread、FreeRTOS等RTOS或裸机的驱动编写。写Nuttx驱动感觉好蹩脚,顺便记录一下(by the way: 先完成,再完善)
Nuttx作为类linux的RTOS,驱动结构、风格与linux很相似
例如串口设备、ADC、DAC、CAN、Timer、PWM、编码器、RTC、看门狗、按键等等
例如网卡、SPI、IIC、LCD、SDIO、USB、MIPI等
Nuttx通过驱动注册接口,将驱动注册到文件系统中,并实现file_operations
操作函数,应用层只需通过标准系统调用,即可调用底层驱动。
底层驱动有分为上半部分(upper_half)和下半部分(lower_half)
本质理解:驱动 = 总线 + 功能
总线:GPIO、SPI、IIC、CAN、USB、串口等;
功能:读写数据、存储、使能、传输等;
struct file_operations
在fs.h
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 */
int (*poll)(FAR struct file *filep, struct pollfd *fds, bool setup);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int (*unlink)(FAR struct inode *inode);
#endif
};
// 与 struct file_operations 相关的数据结构
struct file
{
int f_oflags; /* Open mode flags */
off_t f_pos; /* File position */
FAR struct inode *f_inode; /* Driver or file system interface */
FAR void *f_priv; /* Per file driver private data */
};
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
struct inode
{
FAR struct inode *i_parent; /* Link to parent level 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_PSEUDOFS_ATTRIBUTES
mode_t i_mode; /* Access mode flags */
uid_t i_owner; /* Owner */
gid_t i_group; /* Group */
struct timespec i_atime; /* Time of last access */
struct timespec i_mtime; /* Time of last modification */
struct timespec i_ctime; /* Time of last status change */
#endif
FAR void *i_private; /* Per inode driver private data */
char i_name[1]; /* Name of inode (variable) */
};
要操作设备,第一步就是要打开相应的设备文件,即使用open()
打开设备,其返回一个文件描述符fd。打开设备号之后对该设备的操作可以通过fd来完成。
应用中open()
以设备的节点路径和操作权限为参数,操作进入VFS,调用fs_open.c
中的open()
函数,通过设备路径找到对应的inode
节点,在进程的文件描述符链表中寻找并分配空闲可用的描述符fd
和文件file
,最后调用设备节点inode
中的文件操作file_operation
中的函数open()
。应用程序调用成功时,返回本次分配的文件描述符fd
,发生错误时,返回-1,错误码记录在errno
中。
关闭设备文件,调用file_operations
的close()
函数,释放设备文件、文件描述符fd
。
从设备读取数据。
往设备写数据。
参数同read
查找或调整文件读写位置。
用于执行设备特定命令,如设置设备属性、配置设备寄存器等。
查询指定的一组文件是否可读或可写。
首先初始化信号量,用于实现阻塞,直到文件可读或可写(亦可设置超时时间)
file_operation
的poll()
函数设计中,如果文件可读、写:
用于已挂载的设备或文件卸载,字符设备一般不涉及;常见于块设备。
Nuttx将驱动设备文件化,即VFS。
struct file_operations
设备文件操作的方法,通过register_driver接口
将驱动设备挂到对应的struct inode
节点中,struct inode
描述 了每个设备节点的位置和数据。当系统调用操作设备文件时,根据对应文件的inode
就能索引到对应的函数。
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.
*/
ret = inode_semtake();
if (ret < 0)
{
return ret;
}
ret = inode_reserve(path, mode, &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;
node->i_private = priv;
ret = OK;
}
inode_semgive();
return ret;
}
/dev/key
int unregister_driver(FAR const char *path)
{
int ret;
ret = inode_semtake();
if (ret >= 0)
{
ret = inode_remove(path);
inode_semgive();
}
return ret;
}