转载请注明出处http://blog.csdn.net/alvin_jack/article/details/70665371
在Nuttx中,字符型设备具有以下的属性(以串口为例):
文章均出自个人理解,错误之处敬请指出;
Nuttx的驱动主要分为两层,向上为抽象为文件的接口层,具体接口如下,向下为驱动接口层,通过dev、ops实现对底层寄存器的操作,以及对驱动模块的配置;
1)include/nuttx/fs/fs.h所有的结构和API接口需要以字符型设备正常运行,具体的定义在该头文件中;每个设备驱动必须实现struct file_operations这个数据结构的实例,具体如下:
struct file_operations{
int open (FAR struct file *filep);
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);
int poll(FAR struct file *filep, struct pollfd *fds, bool setup);
}
2)drivers/serial/serial.c,这里就是用来填充之前已经定义的file_operations结构,换句话说就是对该结构的实现;
而实现函数,例如uart_open(FAR struct file *filep):
static int uart_open(FAR struct file *filep)
{
//获取设备的路径,并根据路径在虚拟文件系统中找到该设备驱动结构体变量(即dev)
FAR struct inode *inode = filep->f_inode;
FAR uart_dev_t *dev = inode->i_private;
uint8_t tmp;
int ret;
/* If the port is the middle of closing, wait until the close is finished.
* If a signal is received while we are waiting, then return EINTR.
*/
ret = uart_takesem(&dev->closesem, true);
if (ret < 0)
{
/* A signal received while waiting for the last close operation. */
return ret;
}
#ifdef CONFIG_SERIAL_REMOVABLE
/* If the removable device is no longer connected, refuse to open the
* device. We check this after obtaining the close semaphore because
* we might have been waiting when the device was disconnected.
*/
if (dev->disconnected)
{
ret = -ENOTCONN;
goto errout_with_sem;
}
#endif
/* Start up serial port */
/* Increment the count of references to the device. */
tmp = dev->open_count + 1;
if (tmp == 0)
{
/* More than 255 opens; uint8_t overflows to zero */
ret = -EMFILE;
goto errout_with_sem;
}
/* Check if this is the first time that the driver has been opened. */
if (tmp == 1)
{
irqstate_t flags = enter_critical_section();
/* If this is the console, then the UART has already been initialized. */
if (!dev->isconsole)
{
/* Perform one time hardware initialization */
ret = uart_setup(dev);
if (ret < 0)
{
leave_critical_section(flags);
goto errout_with_sem;
}
}
/* In any event, we do have to configure for interrupt driven mode of
* operation. Attach the hardware IRQ(s). Hmm.. should shutdown() the
* the device in the rare case that uart_attach() fails, tmp==1, and
* this is not the console.
*/
ret = uart_attach(dev);
if (ret < 0)
{
uart_shutdown(dev);
leave_critical_section(flags);
goto errout_with_sem;
}
/* Mark the io buffers empty */
dev->xmit.head = 0;
dev->xmit.tail = 0;
dev->recv.head = 0;
dev->recv.tail = 0;
/* Initialize termios state */
#ifdef CONFIG_SERIAL_TERMIOS
dev->tc_iflag = 0;
if (dev->isconsole)
{
/* Enable \n -> \r\n translation for the console */
dev->tc_oflag = OPOST | ONLCR;
}
else
{
dev->tc_oflag = 0;
}
#endif
#ifdef CONFIG_SERIAL_DMA
/* Notify DMA that there is free space in the RX buffer */
uart_dmarxfree(dev);
#endif
/* Enable the RX interrupt */
uart_enablerxint(dev);
leave_critical_section(flags);
}
/* Save the new open count on success */
dev->open_count = tmp;
errout_with_sem:
uart_givesem(&dev->closesem);
return ret;
}
里面调用的函数例如uart_setup、uart_givesem、uart_dmarxfree都是来自于uart_ops_s的数据结构中的,而uart_ops_s中的数据结构则是对uart的外设寄存器组进行操作,从而实现串口的功能;
#define uart_setup(dev) dev->ops->setup(dev)
#define uart_shutdown(dev) dev->ops->shutdown(dev)
#define uart_attach(dev) dev->ops->attach(dev)
#define uart_detach(dev) dev->ops->detach(dev)
#define uart_enabletxint(dev) dev->ops->txint(dev, true)
#define uart_disabletxint(dev) dev->ops->txint(dev, false)
#define uart_enablerxint(dev) dev->ops->rxint(dev, true)
#define uart_disablerxint(dev) dev->ops->rxint(dev, false)
#define uart_rxavailable(dev) dev->ops->rxavailable(dev)
#define uart_txready(dev) dev->ops->txready(dev)
#define uart_txempty(dev) dev->ops->txempty(dev)
#define uart_send(dev,ch) dev->ops->send(dev,ch)
#define uart_receive(dev,s) dev->ops->receive(dev,s)
3)uart_dev_s,包含了uart_ops_s以及串口的状态值(如中断信号量、传输标志位...),uart_dev_s是需要被系统的驱动调用的,也就是说在初始化的时候会通过(uart_register())进行注册,将之前的填充好的数据结构与虚拟文件系统进行关联;
struct uart_dev_s
{
/* State data */
uint8_t open_count; /* Number of times the device has been opened */
volatile bool xmitwaiting; /* true: User waiting for space in xmit.buffer */
volatile bool recvwaiting; /* true: User waiting for data in recv.buffer */
#ifdef CONFIG_SERIAL_REMOVABLE
volatile bool disconnected; /* true: Removable device is not connected */
#endif
bool isconsole; /* true: This is the serial console */
#ifdef CONFIG_SERIAL_TERMIOS
/* Terminal control flags */
tcflag_t tc_iflag; /* Input modes */
tcflag_t tc_oflag; /* Output modes */
tcflag_t tc_lflag; /* Local modes */
#endif
/* Semaphores */
sem_t closesem; /* Locks out new open while close is in progress */
sem_t xmitsem; /* Wakeup user waiting for space in xmit.buffer */
sem_t recvsem; /* Wakeup user waiting for data in recv.buffer */
#ifndef CONFIG_DISABLE_POLL
sem_t pollsem; /* Manages exclusive access to fds[] */
#endif
/* I/O buffers */
struct uart_buffer_s xmit; /* Describes transmit buffer */
struct uart_buffer_s recv; /* Describes receive buffer */
#ifdef CONFIG_SERIAL_DMA
/* DMA transfers */
struct uart_dmaxfer_s dmatx; /* Describes transmit DMA transfer */
struct uart_dmaxfer_s dmarx; /* Describes receive DMA transfer */
#endif
/* Driver interface */
FAR const struct uart_ops_s *ops; /* Arch-specific operations */
FAR void *priv; /* Used by the arch-specific logic */
/* The following is a list if poll structures of threads waiting for
* driver events. The 'struct pollfd' reference for each open is also
* retained in the f_priv field of the 'struct file'.
*/
#ifndef CONFIG_DISABLE_POLL
struct pollfd *fds[CONFIG_SERIAL_NPOLLWAITERS];
#endif
};
4)register_driver,串口注册函数,完成之前所填充的数据结构uart_dev_s和uart_ops_s与虚拟文件系统关联
int register_driver( FAR const char *path, \
FAR const struct file_operations *fops,\
mode_t mode,\
FAR void *priv);
eg:
register_driver(/dev/ttyS0, \注册到系统虚拟文件系统的地址
&g_serialops, \填充的file_operations数据结构
0666, \端口的权限
dev); \填充的uart_dev_s数据结构
到这里一个串口的驱动就算完成了,对应用层来说,所调用file_operations中的函数去驱动的硬件,是已经在初始化的时候就做好了的;基于串口的应用开发也就不需要再去关心硬件驱动层面的开发了;
一般来说,file_operations和dev(uart_dev_s)之间的关系也就是从注册开始建立起联系的,一般的板卡会存在多个dev(uart_dev_s),而file_operations针对同一类设备来说只会是存在一个实例,这些驱动一般OS都会事先写好,然后用户通过配置文件进行选择;