花了半年时间在QNX系统上,这是一个RTOS,这个系统是高安全级别的系统,在核物理站/天文空间站/电站/地铁/交通运输(飞机/汽车/地铁)等工业系统领域占有70%以上的市场份额。

背景:本文将我个人在QNX上移植内核和开发驱动以及应用程序的部分经验记录在此,因公司商业机密,部分源码不便公开。我会框架性的讲解开发思路。为了简化文章复杂性,我只讨论相同板子的平台驱动转移,我手中是at91sam9260-ek的板子。外部设备是公司硬件部单独添加的。

目的:利用已有的Linux驱动,简化QNX的驱动编写。

大致思路:Linux的驱动是基于模块的,每个驱动作为内核的扩展放进内存中,QNX的驱动就是一个进程,需要将模块改为一个应用程序。调整内核与驱动的通信机制。

以RTC驱动为例子:

Linux的驱动开发框架为:

1.填充read/write/ioctl/probe等驱动回调函数,设置对应的接口。

static struct file_operations rtc8025_fops = {

.owner   = THIS_MODULE,

.open    = rtc_open,

.release = rtc_release,

.read    = rtc_read,

.write   = rtc_write,

.ioctl   = rtc_ioctl,

};

2.编写每个部分的硬件相关代码。以write为例:

static ssize_t rtc_write(struct file *filp, __user const char  *buf,

size_t len,loff_t *ppos){

char buff[16];

VR_TIME *tm;

tm = kmalloc(sizeof(VR_TIME),GFP_KERNEL);

if ( NULL == tm ){

printk("Memory error!\n");

return -1;

}

if ( copy_from_user(buff, buf, len) )

return -EFAULT;

buff[len] = '\0';

set_time_value(tm, buff);

set_sys_time(tm);

kfree(tm);

return len;

}

3.注册设备的initial接口函数与exit接口函数。

static __init int rtc8025_init(void){

int ret;

printk("%s Driver Version: %s\n", DRIVER_NAME, VERSION);

ret = register_chrdev(major,DRIVER_NAME,&rtc8025_fops);

if ( ret

printk("unable to register %s\n",DRIVER_NAME);

return ret;

}

init_gpio();

msleep(10);

init_rtc();

return 0;

}

static __exit void rtc8025_exit(void){

i2c_stime(0xe0, 0x20);

i2c_stime(0xf0, 0);

i2c_stime(0x70, 0x00);

unregister_chrdev(major, DRIVER_NAME);

printk("%s unregister!\n", DRIVER_NAME);

}

module_init(rtc8025_init);

module_exit(rtc8025_exit);

其他具体实现就不在这里描述了。

原文参考自web开发网:http://www.software8.co/wzjs/

为了将原有的Linux驱动移植到QNX上,需要改动read/write以及ioctl的函数接口参数。并将与Linux内核相关的函数去掉(通常不会在QNX里面用到)以read函数代码为例:

size_t rtc_read(char *buf, size_t len, int *ppos){

VR_TIME *tm = (VR_TIME *)buf;

static unsigned char tmp = 0;

tmp = i2c_rtime(0x00) & 0x7f;     tm->second = BCD2DEC(tmp);

tmp = i2c_rtime(0x10) & 0x7f;      tm->minute = BCD2DEC(tmp);

tmp = i2c_rtime(0x20) & 0x3f;      tm->hour   = BCD2DEC(tmp);

tmp = i2c_rtime(0x40) & 0x3f;      tm->day    = BCD2DEC(tmp);

tmp = i2c_rtime(0x50) & 0x1f;      tm->month  = BCD2DEC(tmp);

tmp = i2c_rtime(0x60);             tm->year   = BCD2DEC(tmp)+ 1920;

return 0;

}

结构体VR_TIME是自定义的。

经过多次驱动的编写和调整,我发现还是Linux的那种回调机制在移植的时候代码改动最方便。因此,我自己也定义了一个结构体,用于模仿Linux的驱动框架,整合两个系统的差异。

struct driver_interface ;

struct driver_interface get_driver_interface(){

struct driver_interface drv;

drv.drv_init  = rtc_init;

drv.drv_read  = rtc_read;

drv.drv_write = rtc_write;

drv.drv_ioctl = rtc_ioctl;

return drv;

}

这个框架差异整合的结构体为:

struct driver_interface{

void (*drv_init)(void);

size_t (*drv_read)(char *buf, size_t len, int *ppos);

size_t (*drv_write)( const char *buf, size_t len, int *ppos);

int (*drv_ioctl)(unsigned int cmd, unsigned long arg);

};

struct driver_interface get_driver_interface();

然后我们来看看QNX的驱动框架,根据这个框架,我们将自己的drv整合进去,于是便有了下面的代码:

文件名为:driver_main.c

/*

*  driver_main.c

*

*  This module contains the source code for the /dev/rtc device.

*

*  This module contains all of the functions necessary.

*

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "linux2qnx_drv.h"

//#include "MMA8452Q.h"

#include "rtc.h"

#define EXAMPLE_NAME "/dev/rtc"

struct driver_interface drv;

/* change to something else if sharing a target */

// #define EXAMPLE_NAME "/dev/dagexample"

void options (int argc, char *argv[]);

/*

*  these prototypes are needed since we are using their names in main ()

*/

int io_open (resmgr_context_t *ctp, io_open_t  *msg, RESMGR_HANDLE_T *handle, void *extra);

int io_read (resmgr_context_t *ctp, io_read_t  *msg, RESMGR_OCB_T *ocb);

int io_write(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);

int    io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb);

/*

*  our connect and I/O functions

*/

resmgr_connect_funcs_t  connect_funcs;

resmgr_io_funcs_t       io_funcs;

/*

*  our dispatch, resource manager and iofunc variables

*/

dispatch_t              *dpp;

resmgr_attr_t           rattr;

dispatch_context_t      *ctp;

iofunc_attr_t           ioattr;

char    *progname = "rtc_driver";

int     optv;                               // -v for verbose operation

int main (int argc, char *argv[]){

printf ("%s:  starting

\n", progname);

int pathID;

options (argc, argv);

drv = get_driver_interface();

drv.drv_init();

/*

* allocate and initialize a dispatch structure for use by our

* main loop

*/

dpp = dispatch_create ();

if (dpp == NULL) {

fprintf (stderr, "%s:  couldn't dispatch_create: %s\n",

progname, strerror (errno));

exit (1);

}

/*

* set up the resource manager attributes structure, we'll

* use this as a way of passing information to resmgr_attach().

* For now, we just use defaults.

*/

memset (&rattr, 0, sizeof (rattr)); /* using the defaults for rattr */

/*

* initialize the connect functions and I/O functions tables to

* their defaults by calling iofunc_func_init().

*

* connect_funcs, and io_funcs variables are already declared.

*

*/

iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,

_RESMGR_IO_NFUNCS, &io_funcs);

/* over-ride the connect_funcs handler for open with our io_open,

* and over-ride the io_funcs handlers for read and write with our

* io_read and io_write handlers

*/

connect_funcs.open = io_open;

io_funcs.read = io_read;

io_funcs.write = io_write;

io_funcs.devctl = io_devctl;

/* initialize our device description structure

*/

iofunc_attr_init (&ioattr, S_IFCHR | 0666, NULL, NULL);

/*

*  call resmgr_attach to register our prefix with the

*  process manager, and also to let it know about our connect

*  and I/O functions.

*

*  On error, returns -1 and errno is set.

*/

pathID = resmgr_attach (dpp, &rattr, EXAMPLE_NAME, _FTYPE_ANY, 0,

&connect_funcs, &io_funcs, &ioattr);

if (pathID == -1) {

fprintf (stderr, "%s:  couldn't attach pathname: %s\n",

progname, strerror (errno));

exit (1);

}

ctp = dispatch_context_alloc (dpp);

while (1) {

if ((ctp = dispatch_block (ctp)) == NULL) {

fprintf (stderr, "%s:  dispatch_block failed: %s\n",

progname, strerror (errno));

exit (1);

}

dispatch_handler (ctp);

}

}

/*

*  io_open

*

*  we are called here when the client does an open.

*  It is up to us to establish a context (in this

*  case NULL will do just fine), and return a status

*  code.

*/

int io_open (resmgr_context_t *ctp, io_open_t *msg,

RESMGR_HANDLE_T *handle, void *extra){

if (optv) {

printf ("%s:  in io_open\n", progname);

}

return (iofunc_open_default (ctp, msg, handle, extra));

}

/*

*  io_read

*

*  At this point, the client has called their library "read"

*  function, and expects zero or more bytes.  Currently our

*  /dev/example resource manager returns zero bytes to

*  indicate EOF -- no more bytes expected.

*

*  After our exercises, it will return some data.

*/

int io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb){

int status;

int nb = sizeof(VR_TIME);

static VR_TIME data;

if (optv) {

printf ("%s:  in io_read\n", progname);

}

if ((status = iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {

if (optv) printf("read failed because of error %d\n", status );

return status;

}

// No special xtypes

if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {

return ENOSYS;   // causes MsgError( ctp->rcvid, ENOSYS );

}

drv.drv_read((char*)&data, sizeof(data), 0);

nb = min( nb, msg->i.nbytes );

_IO_SET_READ_NBYTES (ctp, nb); // ctp->status = nb

SETIOV( ctp->iov, &data, nb );

if (nb > 0)

ocb->attr->flags |= IOFUNC_ATTR_ATIME;

return _RESMGR_NPARTS (1); // causes MsgReplyv( ctp->rcvid, ctp->status, ctp->iov, 1 );

}

/*

*  io_write

*

*  At this point, the client has called their library "write"

*  function, and expects that our resource manager will write

*  the number of bytes that they have specified to some device.

*

*  Currently, for /dev/example, all of the clients writes always

*  work -- they just go into Deep Outer Space.

*

*  After our updates, they will be displayed on standard out.

*/

int io_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb){

int status;

int nb;

VR_TIME *buf;

if (optv) {

printf ("\n\n\n%s:  in io_write, of %d bytes\n\n\n", progname, msg->i.nbytes);

}

if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK)

return status;

// No special xtypes

if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {

return ENOSYS;

}

/* first process any data already in the receive buff */

// skip the io_write header to get to the data

buf = (VR_TIME *)(msg+1);

// calculate number of bytes of client data in receive buffer

nb = ctp->info.msglen - (ctp->offset + sizeof(*msg) );

status = drv.drv_write( (char*)buf, sizeof(*buf), 0 );

_IO_SET_WRITE_NBYTES (ctp, nb);

// if we actually handled any data, mark that a write was done for

// time updates (POSIX stuff)

if (nb > 0)

ocb->attr->flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;

return _RESMGR_NPARTS (0);

}

int    io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb){

int     nbytes, status;

union {  /* See note 1 */

XYZunion8  arg_info_data;

int     data32;

/*

 other devctl types you can receive */

} *rx_data;

/*

Let common code handle DCMD_ALL_* cases.

You can do this before or after you intercept devctls, depending

on your intentions.  Here we aren't using any predefined values,

so let the system ones be handled first. See note 2.

*/

if ((status = iofunc_devctl_default(ctp, msg, ocb)) !=

_RESMGR_DEFAULT) {

return(status);

}

status = nbytes = 0;

/*

Note this assumes that you can fit the entire data portion of

the devctl into one message.  In reality you should probably

perform a MsgReadv() once you know the type of message you

have received to get all of the data, rather than assume

it all fits in the message.  We have set in our main routine

that we'll accept a total message size of up to 2 KB, so we

don't worry about it in this example where we deal with ints.

*/

/* Get the data from the message. See Note 3. */

rx_data = _DEVCTL_DATA(msg->i);

/*

Three examples of devctl operations:

SET: Set a value (int) in the server

GET: Get a value (int) from the server

SETGET: Set a new value and return the previous value

*/

_Int32t linux_cmd = (msg->i.dcmd);

if((linux_cmd & SETVAL) && ((GETVAL & linux_cmd))){  // SETGET is SETVAL|GETVAL == 0x800000000|0x40000000

linux_cmd &= ~SETGET;

linux_cmd -= (sizeof(XYZunion8)<<16);

//printf("SG = 0x%0x", SETGET);

drv.drv_ioctl(linux_cmd, (unsigned long)&(rx_data->arg_info_data) );

nbytes = sizeof( rx_data->arg_info_data );

}else if(linux_cmd & GETVAL){

linux_cmd &= ~GETVAL;

linux_cmd -= (sizeof(int)<<16);

printf("G = 0x%x", GETVAL);

printf(", linux_cmd=%d, data address =%d\n", linux_cmd, (int)&(rx_data->data32));

drv.drv_ioctl(linux_cmd, (unsigned long )&(rx_data->data32));

nbytes = sizeof(&rx_data->data32);

}else if(linux_cmd & SETVAL ){

linux_cmd &= ~SETVAL;

linux_cmd -= (sizeof(int)<<16);

printf("S = 0x%x", SETVAL);

printf(", linux_cmd=%d, data=%d\n", linux_cmd, rx_data->data32);

drv.drv_ioctl(linux_cmd, (int)(rx_data->data32) );

}else{

printf("undefined functionality\n");

//TODO

return -1;

}

/* Clear the return message. Note that we saved our data past

this location in the message. */

memset(&msg->o, 0, sizeof(msg->o));

/*

If you wanted to pass something different to the return

field of the devctl() you could do it through this member.

See note 5.

*/

msg->o.ret_val = status;

/* Indicate the number of bytes and return the message */

msg->o.nbytes = nbytes;

return(_RESMGR_PTR(ctp, &msg->o, sizeof(msg->o) + nbytes));

}

/*

*  options

*

*  This routine handles the command line options.

*  For our simple /dev/example, we support:

*      -v      verbose operation

*/

void options (int argc, char *argv[]){

optv = 0;

int     opt;

while ((opt = getopt (argc, argv, "v")) != -1) {

if( opt == 'v' ){

optv++;

}

}

}

这样,我们的Linux驱动就能基本原封不动的移植到QNX里面。

这是rtc.h:

#include

#include "MMA8452Q.h"

#ifndef RTC_COMMON_H_

#define RTC_COMMON_H_

#define GETVAL _POSIX_DEVDIR_FROM

#define SETVAL _POSIX_DEVDIR_TO

#define SETGET _POSIX_DEVDIR_TOFROM

typedef struct tagVRTIME{

int year;

int month;

int day;

int hour;

int minute;

int second;

}VR_TIME;

#endif /* RTC_COMMON_H_ */

由于QNX使用了微内核架构,而驱动是一个进程,因此使用的进程间通信,对于ioctl要做特别处理,read和write可以直接使用,但是ioctl目前没有用到,在我的驱动中,后面加入了加速计传感器的驱动,需要ioctl进行扩展操作,但我最终使用QNX自己的devctl函数,看一下这个函数的使用代码。

#define GETVAL _POSIX_DEVDIR_FROM

#define SETVAL _POSIX_DEVDIR_TO

#define SETGET _POSIX_DEVDIR_TOFROM

static int  inline DEVCTL_CMD( int code){

if((code & _POSIX_DEVDIR_TO) && (code & _POSIX_DEVDIR_FROM))

return (sizeof(XYZunion8)<<16)+code;

if(code & _POSIX_DEVDIR_TO)

return (sizeof(int)<<16) + code;

if(code & _POSIX_DEVDIR_FROM)

return (sizeof(int)<<16) + code;

return    code;

}

static void get_xyz( int *x, int *y, int *z ){

XYZunion8  _XYZdata8;

memset( &_XYZdata8, 0, sizeof( _XYZdata8 ) );

devctl( fd, DEVCTL_CMD(SETGET|CMD_READ_XYZ8), &_XYZdata8, sizeof(_XYZdata8), NULL);

*x = ( int )_XYZdata8.Byte.Xdata8;

*y = ( int )_XYZdata8.Byte.Ydata8;

*z = ( int )_XYZdata8.Byte.Zdata8;

//printf( "x=%4d, y=%4d, z=%4d\n", *x, *y, *z);

}

这样,就可以在QNX的应用程序中和驱动进行通信了。

最后附加上额外的两个源文件,叫做linux2qnx.c/linux2qnx.h用以整合两边对纯硬件的操作(地址映射与寄存器操作)的差异。

Linux2qnx.c:

/*

* linux2qnx_drv.c

*

*  Created on: Feb 18, 2013

*      Author: mark

*/

#include

#include "linux2qnx_drv.h"

uintptr_t ioremap(uint64_t io, size_t len){

return mmap_device_io(len, io);

}

void iowrite16(  uint16_t val, uintptr_t port ){

out16(port, val);

}

uint_t ioread16(uintptr_t port){

return in16(port);

}

void ssleep(unsigned int  seconds){

sleep(seconds);

}

void iowrite32(uint32_t val, uintptr_t port){

out32(port, val);

}

uint_t ioread32(uintptr_t port){

return in32(port);

}

linux2qnx.h:

/*

* linux2qnx_drv.h

*

*  Created on: Feb 18, 2013

*      Author: mark

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#ifndef LINUX2QNX_DRV_H_

#define LINUX2QNX_DRV_H_

#define GETVAL _POSIX_DEVDIR_FROM

#define SETVAL _POSIX_DEVDIR_TO

uintptr_t ioremap(uint64_t io, size_t len);

void iowrite16(  uint16_t val, uintptr_t port );

uint_t ioread16(uintptr_t port);

uint_t ioread32(uintptr_t port);

void iowrite32(uint32_t val, uintptr_t port);

void ssleep(unsigned int  seconds);

struct driver_interface{

void (*drv_init)(void);

size_t (*drv_read)(char *buf, size_t len, int *ppos);

size_t (*drv_write)( const char *buf, size_t len, int *ppos);

int (*drv_ioctl)(unsigned int cmd, unsigned long arg);

};

struct driver_interface get_driver_interface();

#endif /* LINUX2QNX_DRV_H_ */

qnx 设备驱动开发_移植LINUX的外围设备驱动到QNX系统中相关推荐

  1. Linux驱动开发_设备文件系统详解

    目录 何为设备管理器? Linux下dev的作用 Devfs sysfs kobject udev proc 何为设备管理器? 设备管理器就是负责管理这台电脑上的外设,当我们通过电脑提供的USB口插入 ...

  2. Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)

    一.前言 块设备主要为存储设备设计的框架. 在前面章节Linux下驱动开发_块设备驱动开发(内存模拟存储) 里介绍了块设备驱动编写思路,并且利用内存模拟了硬件存储,完成了块设备驱动开发测试.这一篇文章 ...

  3. linux内核驱动开发 培训,嵌入式Linux驱动开发培训 - 华清远见教育集团官网

    9.LINUX下USB驱动开发基础 9.1 USB规范介绍 9.2 USB主机控制器 9.3 USB HUB 9.4 USB设备状态 9.5 USB描述符 9.6 USB请求 9.7 USB通讯数据格 ...

  4. wdm设备驱动程序开发pdf_世界顶级Linux技术大师力作1000页Linux开发实战

    20世纪90年代初,Linux操作系统诞生,随着虚拟化.云计算.大数据.容器技术的出现和人工智能时代的来临,Linux 以迅雷不及掩耳之势飞速发展,占据着整个服务器行业的半壁江山,但同时也面临着巨大的 ...

  5. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

  6. 逆变器方案(高频 工频)正弦波逆变驱动开发板,完全兼容EG8010驱动板

    逆变器方案(高频 工频)正弦波逆变驱动开发板,完全兼容EG8010驱动板,高频机工频机通用,资料包含源代码,带中文注释,原理图,PCB图,高质量开发板代码,可以直接上机运行 ID:4215006632 ...

  7. qnx 设备驱动开发_在QNX操作系统实现一个设备驱动程序

    1 概述 QNX操作系统是加拿大黑莓公司旗下的QNX公司推出的嵌入式硬实时操作系统,属于类UNIX操作系统.Shell命令行的操作方式和操作命令与Linux系统几乎相同. QNX操作系统属于微内核架构 ...

  8. linux设备驱动之串口移植,linux设备驱动之UART移植

    对于串口驱动的移植准备自己分析一下源代码的,但是发现自己好多地方都只知道一 些皮毛,不明白其中的道理,所以我上网搜的时候发现有好多人写了很多很好的文章了,下面我转载的这篇就非常不错,一个困恼我好久的问 ...

  9. linux设备驱动之串口移植,Linux设备驱动之UART驱动结构

    一.对于串口驱动Linux系统中UART驱动属于终端设备驱动,应该说是实现串口驱动和终端驱动来实现串口终端设备的驱动.要了解串口终端的驱动在Linux系统的结构就先要了解终端设备驱动在Linux系统中 ...

  10. RK3399 驱动开发 _ 04 - Android系统中通过fdt文件系统反编译查看设备中真实生效的设备树配置信息

    文章目录 前言 一.设备树保存在文件系哪里? 二.文件系统设备树dts编译与反编译 2.1 设备树原始 dtb 格式 2.2 从设备中获取目标文件fdt 2.3 在ubuntu系统中将fdt反编译成d ...

最新文章

  1. Maya阿诺德室外环境灯光照明和渲染技术学习视频教程
  2. PostgreSQL 表达式索引 - 语法注意事项
  3. FPGA的发展历史及相关名词解释
  4. WPF TextBox只能输入数字
  5. java并发编程(更新)
  6. 大龄打工者的出路在哪里
  7. phpcms加载系统类与加载应用类的区别
  8. 关于一次生产环境项目线程数一直增长问题排查记录
  9. 计算机的色彩在哪调整w10,win10电脑显示器颜色不对如何调整
  10. 康托尔点集matlab实数,康托尔(Cantor)是如何证明实数集是不可数的
  11. java培训班值得去吗?
  12. Java下载excel模板
  13. Sprite Renderer
  14. 计算机专业梦想作文,我的理想是电脑工程师作文
  15. 商会管理系统_沈阳写字间丨商会总部大厦
  16. Android Zxing 扫描器 扫描框、扫描线定制样式
  17. 30岁的问题,为什么有人说程序员只能干到30岁。
  18. 塞瓦维斯特定理(不定方程)
  19. 云计算发展前景好不好 学了后能胜任哪些岗位
  20. va_start函数的使用

热门文章

  1. 围棋人机大战属于计算机在什么方面的应用,人民日报评围棋人机大战:胜出的是我们自己...
  2. 如何用计算机画地形地貌图,地形图是如何绘制出来的
  3. 北航计算机学院博士开题,【北航毕设开题报告】北航博士开题报告格式.doc
  4. python调用手机蓝牙_python bluetooth蓝牙信息获取蓝牙设备类型的方法
  5. 暗通道去雾算法原理及实现
  6. 7本最经典的逻辑思维书籍推荐
  7. 雷蛇鼠标 雷云3 驱动无法启动 Razer Syncapse 3 Failed to start
  8. Maven仓库的下载和配置settings.xml文件
  9. python xlwt_Python模块xlwt对excel进行写入操作
  10. js中继承的几种方式