700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > qnx 设备驱动开发_移植LINUX的外围设备驱动到QNX系统中

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

时间:2022-05-23 15:52:34

相关推荐

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

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

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

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

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

以RTC驱动为例子:

Linux的驱动开发框架为:

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

staticstructfile_operationsrtc8025_fops={

.owner=THIS_MODULE,

.open=rtc_open,

.release=rtc_release,

.read=rtc_read,

.write=rtc_write,

.ioctl=rtc_ioctl,

};

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

staticssize_trtc_write(structfile*filp,__userconstchar*buf,

size_tlen,loff_t*ppos){

charbuff[16];

VR_TIME*tm;

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

if(NULL==tm){

printk("Memoryerror!\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);

returnlen;

}

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

static__initintrtc8025_init(void){

intret;

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

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

if(ret

printk("unabletoregister%s\n",DRIVER_NAME);

returnret;

}

init_gpio();

msleep(10);

init_rtc();

return0;

}

static__exitvoidrtc8025_exit(void){

i2c_stime(0xe0,0x20);

i2c_stime(0xf0,0);

i2c_stime(0x70,0x00);

unregister_chrdev(major,DRIVER_NAME);

printk("%sunregister!\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_trtc_read(char*buf,size_tlen,int*ppos){

VR_TIME*tm=(VR_TIME*)buf;

staticunsignedchartmp=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;

return0;

}

结构体VR_TIME是自定义的。

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

structdriver_interface;

structdriver_interfaceget_driver_interface(){

structdriver_interfacedrv;

drv.drv_init=rtc_init;

drv.drv_read=rtc_read;

drv.drv_write=rtc_write;

drv.drv_ioctl=rtc_ioctl;

returndrv;

}

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

structdriver_interface{

void(*drv_init)(void);

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

size_t(*drv_write)(constchar*buf,size_tlen,int*ppos);

int(*drv_ioctl)(unsignedintcmd,unsignedlongarg);

};

structdriver_interfaceget_driver_interface();

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

文件名为:driver_main.c

/*

* driver_main.c

*

*Thismodulecontainsthesourcecodeforthe/dev/rtc device.

*

*Thismodulecontainsallofthefunctionsnecessary.

*

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include"linux2qnx_drv.h"

//#include"MMA8452Q.h"

#include"rtc.h"

#defineEXAMPLE_NAME"/dev/rtc"

structdriver_interfacedrv;

/*changetosomethingelseifsharingatarget*/

//#defineEXAMPLE_NAME"/dev/dagexample"

voidoptions(intargc,char*argv[]);

/*

*theseprototypesareneededsinceweareusingtheirnamesinmain()

*/

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

intio_read(resmgr_context_t*ctp,io_read_t*msg,RESMGR_OCB_T*ocb);

intio_write(resmgr_context_t*ctp,io_write_t*msg,RESMGR_OCB_T*ocb);

intio_devctl(resmgr_context_t*ctp,io_devctl_t*msg,RESMGR_OCB_T*ocb);

/*

*ourconnectandI/Ofunctions

*/

resmgr_connect_funcs_tconnect_funcs;

resmgr_io_funcs_tio_funcs;

/*

*ourdispatch,resourcemanagerandiofuncvariables

*/

dispatch_t*dpp;

resmgr_attr_trattr;

dispatch_context_t*ctp;

iofunc_attr_tioattr;

char*progname="rtc_driver";

intoptv;//-vforverboseoperation

intmain(intargc,char*argv[]){

printf("%s:starting

\n",progname);

intpathID;

options(argc,argv);

drv=get_driver_interface();

drv.drv_init();

/*

*allocateandinitializeadispatchstructureforusebyour

*mainloop

*/

dpp=dispatch_create();

if(dpp==NULL){

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

progname,strerror(errno));

exit(1);

}

/*

*setuptheresourcemanagerattributesstructure,we'll

*usethisasawayofpassinginformationtoresmgr_attach().

*Fornow,wejustusedefaults.

*/

memset(&rattr,0,sizeof(rattr));/*usingthedefaultsforrattr*/

/*

*initializetheconnectfunctionsandI/Ofunctionstablesto

*theirdefaultsbycallingiofunc_func_init().

*

*connect_funcs,andio_funcsvariablesarealreadydeclared.

*

*/

iofunc_func_init(_RESMGR_CONNECT_NFUNCS,&connect_funcs,

_RESMGR_IO_NFUNCS,&io_funcs);

/*over-ridetheconnect_funcshandlerforopenwithourio_open,

*andover-ridetheio_funcshandlersforreadandwritewithour

*io_readandio_writehandlers

*/

connect_funcs.open=io_open;

io_funcs.read=io_read;

io_funcs.write=io_write;

io_funcs.devctl=io_devctl;

/*initializeourdevicedescriptionstructure

*/

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

/*

*callresmgr_attachtoregisterourprefixwiththe

*processmanager,andalsotoletitknowaboutourconnect

*andI/Ofunctions.

*

*Onerror,returns-1anderrnoisset.

*/

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

&connect_funcs,&io_funcs,&ioattr);

if(pathID==-1){

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

progname,strerror(errno));

exit(1);

}

ctp=dispatch_context_alloc(dpp);

while(1){

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

fprintf(stderr,"%s:dispatch_blockfailed:%s\n",

progname,strerror(errno));

exit(1);

}

dispatch_handler(ctp);

}

}

/*

*io_open

*

*wearecalledherewhentheclientdoesanopen.

*Itisuptoustoestablishacontext(inthis

*caseNULLwilldojustfine),andreturnastatus

*code.

*/

intio_open(resmgr_context_t*ctp,io_open_t*msg,

RESMGR_HANDLE_T*handle,void*extra){

if(optv){

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

}

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

}

/*

*io_read

*

*Atthispoint,theclienthascalledtheirlibrary"read"

*function,andexpectszeroormorebytes.Currentlyour

*/dev/exampleresourcemanagerreturnszerobytesto

*indicateEOF--nomorebytesexpected.

*

*Afterourexercises,itwillreturnsomedata.

*/

intio_read(resmgr_context_t*ctp,io_read_t*msg,RESMGR_OCB_T*ocb){

intstatus;

intnb=sizeof(VR_TIME);

staticVR_TIMEdata;

if(optv){

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

}

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

if(optv)printf("readfailedbecauseoferror%d\n",status);

returnstatus;

}

//Nospecialxtypes

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

returnENOSYS;//causesMsgError(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);//causesMsgReplyv(ctp->rcvid,ctp->status,ctp->iov,1);

}

/*

*io_write

*

*Atthispoint,theclienthascalledtheirlibrary"write"

*function,andexpectsthatourresourcemanagerwillwrite

*thenumberofbytesthattheyhavespecifiedtosomedevice.

*

*Currently,for/dev/example,alloftheclientswritesalways

*work--theyjustgointoDeepOuterSpace.

*

*Afterourupdates,theywillbedisplayedonstandardout.

*/

intio_write(resmgr_context_t*ctp,io_write_t*msg,RESMGR_OCB_T*ocb){

intstatus;

intnb;

VR_TIME*buf;

if(optv){

printf("\n\n\n%s:inio_write,of%dbytes\n\n\n",progname,msg->i.nbytes);

}

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

returnstatus;

//Nospecialxtypes

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

returnENOSYS;

}

/*firstprocessanydataalreadyinthereceivebuff*/

//skiptheio_writeheadertogettothedata

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

//calculatenumberofbytesofclientdatainreceivebuffer

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

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

_IO_SET_WRITE_NBYTES(ctp,nb);

//ifweactuallyhandledanydata,markthatawritewasdonefor

//timeupdates(POSIXstuff)

if(nb>0)

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

return_RESMGR_NPARTS(0);

}

intio_devctl(resmgr_context_t*ctp,io_devctl_t*msg,RESMGR_OCB_T*ocb){

intnbytes,status;

union{/*Seenote1*/

XYZunion8arg_info_data;

intdata32;

/*

otherdevctltypesyoucanreceive*/

}*rx_data;

/*

LetcommoncodehandleDCMD_ALL_*cases.

Youcandothisbeforeorafteryouinterceptdevctls,depending

onyourintentions.Herewearen'tusinganypredefinedvalues,

soletthesystemonesbehandledfirst.Seenote2.

*/

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

_RESMGR_DEFAULT){

return(status);

}

status=nbytes=0;

/*

Notethisassumesthatyoucanfittheentiredataportionof

thedevctlintoonemessage.Inrealityyoushouldprobably

performaMsgReadv()onceyouknowthetypeofmessageyou

havereceivedtogetallofthedata,ratherthanassume

itallfitsinthemessage.Wehavesetinourmainroutine

thatwe'llacceptatotalmessagesizeofupto2KB,sowe

don'tworryaboutitinthisexamplewherewedealwithints.

*/

/*Getthedatafromthemessage.SeeNote3.*/

rx_data=_DEVCTL_DATA(msg->i);

/*

Threeexamplesofdevctloperations:

SET:Setavalue(int)intheserver

GET:Getavalue(int)fromtheserver

SETGET:Setanewvalueandreturnthepreviousvalue

*/

_Int32tlinux_cmd=(msg->i.dcmd);

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

linux_cmd&=~SETGET;

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

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

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

nbytes=sizeof(rx_data->arg_info_data);

}elseif(linux_cmd&GETVAL){

linux_cmd&=~GETVAL;

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

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

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

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

nbytes=sizeof(&rx_data->data32);

}elseif(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("undefinedfunctionality\n");

//TODO

return-1;

}

/*Clearthereturnmessage.Notethatwesavedourdatapast

thislocationinthemessage.*/

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

/*

Ifyouwantedtopasssomethingdifferenttothereturn

fieldofthedevctl()youcoulddoitthroughthismember.

Seenote5.

*/

msg->o.ret_val=status;

/*Indicatethenumberofbytesandreturnthemessage*/

msg->o.nbytes=nbytes;

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

}

/*

*options

*

*Thisroutinehandlesthecommandlineoptions.

*Foroursimple/dev/example,wesupport:

*-vverboseoperation

*/

voidoptions(intargc,char*argv[]){

optv=0;

intopt;

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

if(opt=='v'){

optv++;

}

}

}

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

这是rtc.h:

#include

#include "MMA8452Q.h"

#ifndefRTC_COMMON_H_

#defineRTC_COMMON_H_

#defineGETVAL_POSIX_DEVDIR_FROM

#defineSETVAL_POSIX_DEVDIR_TO

#defineSETGET_POSIX_DEVDIR_TOFROM

typedefstructtagVRTIME{

intyear;

intmonth;

intday;

inthour;

intminute;

intsecond;

}VR_TIME;

#endif/*RTC_COMMON_H_*/

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

#defineGETVAL_POSIX_DEVDIR_FROM

#defineSETVAL_POSIX_DEVDIR_TO

#defineSETGET_POSIX_DEVDIR_TOFROM

staticintinlineDEVCTL_CMD(intcode){

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;

returncode;

}

staticvoidget_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

*

*Createdon:Feb18,

*Author:mark

*/

#include

#include"linux2qnx_drv.h"

uintptr_tioremap(uint64_tio,size_tlen){

returnmmap_device_io(len,io);

}

voidiowrite16(uint16_tval,uintptr_tport){

out16(port,val);

}

uint_tioread16(uintptr_tport){

returnin16(port);

}

voidssleep(unsignedintseconds){

sleep(seconds);

}

voidiowrite32(uint32_tval,uintptr_tport){

out32(port,val);

}

uint_tioread32(uintptr_tport){

returnin32(port);

}

linux2qnx.h:

/*

*linux2qnx_drv.h

*

*Createdon:Feb18,

*Author:mark

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#ifndefLINUX2QNX_DRV_H_

#defineLINUX2QNX_DRV_H_

#defineGETVAL_POSIX_DEVDIR_FROM

#defineSETVAL_POSIX_DEVDIR_TO

uintptr_tioremap(uint64_tio,size_tlen);

voidiowrite16(uint16_tval,uintptr_tport);

uint_tioread16(uintptr_tport);

uint_tioread32(uintptr_tport);

voidiowrite32(uint32_tval,uintptr_tport);

voidssleep(unsignedintseconds);

structdriver_interface{

void(*drv_init)(void);

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

size_t(*drv_write)(constchar*buf,size_tlen,int*ppos);

int(*drv_ioctl)(unsignedintcmd,unsignedlongarg);

};

structdriver_interfaceget_driver_interface();

#endif/*LINUX2QNX_DRV_H_*/

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。