Nuttx驱动机制

Nuttx相关的历史文章:

介绍

Nuttx支持多种设备驱动,包括:

  • 字符设备驱动,比如串口设备、触摸屏设备、ADC/DAC、PWM、CAN、正交编码器、Timer、RTC、Watchdog、Keyboard/Keypad等;
  • 块设备驱动;
  • 其他特殊设备驱动,比如Ethernet、SPI、I2C、Frame Buffer、LCD、MTD、SDIO、USB等;

总体来说,Nuttx中的驱动机制相对来说比较简单,它并没有提供像Linux系统那样复杂的驱动模型机制,比如Device、Driver、Bus、Class等。Nuttx只是简单的通过驱动注册接口,将驱动注册进文件系统中,并实现file_operations操作函数集,上层应用便能通过标准的系统调用,进而调用到低层的驱动。

我将以字符设备驱动来阐述整个驱动的机制。

数据结构与接口

数据结构

应用层通过系统调用来访问驱动:系统调用->vfs->驱动,因此首先需要了解一下,驱动注册进文件系统时所涉及到的数据结构。数据结构的相关定义在include/nuttx/fs/fs.h文件中。
首先,驱动注册后,会创建一个inode,对应到设备文件上:

/* 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字段用于标记该inode对应的为什么文件,典型的有驱动、消息队列等,专门有宏定义来设置或者判断这个字段是否为驱动文件:

#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结构体中inode_ops_u用于描述操作函数集,这个字段是一个联合体,可以是字符设备驱动、块设备驱动、挂载点等的操作函数集。驱动操作函数集如下:

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
};

这个函数集,由低层的驱动来实现,并且设置进设备文件对应的inode中,当系统调用操作设备文件时,便能根据设备文件对应的inode来找到对应的函数了。

接口
驱动注册的时候,会调用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;
}

这个接口完成以下几个操作:

  1. 根据path(一般对应设备文件,比如/dev/xxxx),来查找是否存在对应的inode,如果没有的话,那为path创建一个inode;
  2. 将实际驱动实现的struct file_operations fops更新到path对应的inode中,此外还设置权限;
  3. priv数据设置进inode中,这个一般存放驱动的私有数据;

ADC驱动

下面将以一个实际的驱动,ADC驱动,来分析一下流程。
在Nuttx的驱动代码中,你会发现经常会把驱动分成两部分,一个是upper half,一个是lower half

  • upper half:上半部分提供了应用程序级的通用接口,也就是实现了file_operations中的函数集,比如针对ADC驱动,专门有drivers/analog/adc.c来描述上半部分的操作,这个对于所有的ADC设备都是相同的;
  • lower half:下半部分基于特定平台的驱动程序,用于实现硬件级的控制,比如寄存器的操作等。arch/arm/src/lpc43xx/lpc43_adc.c文件实现了特定的硬件驱动;

整体的框架如下图所示:


adc驱动框架
  • 芯片相关,代表了lower half,针对硬件的实际操作,并且在中断处理函数中,会去回调upper half的回调函数。可以在这个回调函数中做一些处理,比如通过消息队列的机制,统治上层应用已经收到了数据;
  • 通用框架,代表了upper half,对接上层的系统调用,并且在实现file_operations函数集的时候,会去调用lower half的接口;
  • 板级部分,这个部分其实是将upper halflower half进行绑定,建立连接并注册进文件系统中,这个接口最终会在系统boot的阶段调用;

具体的驱动代码就不贴了。
其他的驱动实现,机制都大体类似,分成两部分,上半部分对接应用系统调用,下半部分对应实际的低层硬件操作,这种分层是一种合理的做法,上半部分做成通用的框架,不需要改动,下半部分针对不同硬件实现具体的操作接口即可了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Linux系统一般有4个主要部分:内核、shell、文件系统和应用程序。 内核、shell和文件系统一起形成了基本...
    请爱护小动物阅读 2,545评论 0 22
  • 转载自:http://blog.csdn.net/hguisu/article/details/6122513原作...
    miaoiao阅读 1,507评论 0 7
  • Linux系统一般有4个主要部分: 内核、shell、文件系统和应用程序。内核、shell和文件系统一起形成了基本...
    偷风筝的人_阅读 3,241评论 1 17
  • 此刻夜凉,平静,毫无喧嚣。枕月入眠,安宁,毫无悬念。
    筱乔MM阅读 153评论 0 1
  • 今天看到消息,物理学家霍金去世了,享年 76 岁。 一开始很震惊, 印象中的霍金先生虽然身患残疾,但身残志坚,生命...
    知鱼君阅读 171评论 0 1