wince驱动分析

对于wince 驱动或者linux驱动,或者其他操作系统驱动。基本上就是两个部分,1,访问硬件寄存器。2,编写操作系统接口。从访问硬件上来说,可以有总线驱动,一般的I/O驱动等。从操作系统来说,就wince而言,可以分为,built-driver和stream driver,也可以从另外的一个角度,分为 Layered Device Driver 和 Monolithic driver。总之,驱动的叫法和种类很多,一般都要根据具体开发的驱动再仔细研究。其实大多数工作都是要看E文的datasheet,了解硬件的工作原理,对CPU工作方式有一定的理解,问题就不大了。这里简单介绍一下如何编写wince下的stream driver。首先讲一下框架,很简单,按照wince的联机帮助文档,完成下面的接口函数就好了。 XXX_Close (Device Manager) XXX_Deinit (Device Manager) XXX_Init (Device Manager) XXX_IOControl (Device Manager) XXX_Open (Device Manager) 。。。。无非就是一些读写,IOCTL,POWER管理的接口而已。下面就是写的一个FOO driver,从它慢慢谈起。FOO取代了上面函数中的XXX,FOO就是说不明白的意思。呵呵。实际上,不需要实现上面的所有函数。 FOO.h #pragma once #define SETFNAME(n) LPCWSTR pszFname = L##n #define FOO_DEBUG #ifdef FOO_DEBUG #define DEBUG_OUT(x,y) RETAILMSG(x,y) #elif #define DEBUG_OUT(x,y) #endif #define FOO_DEV_NAME L"Foo1:" typedef struct { char* pBuffer1; char* pBuffer2; int nLen; }COPY_STRUCT; // {1F9A46C2-F419-4285-A78D-2553023C3C35} DEFINE_GUID(DEVICE_IFC_FOO_GUID, 0x1f9a46c2, 0xf419, 0x4285, 0xa7, 0x8d, 0x25, 0x53, 0x2, 0x3c, 0x3c, 0x35); //define IOCTL code #define IOCTL_FOO_COPYCMD / CTL_CODE(FILE_DEVICE_UNKNOWN ,0x350,METHOD_BUFFERED ,FILE_ANY_ACCESS) #define IOCTL_FOO_LOCKBUS / CTL_CODE(FILE_DEVICE_UNKNOWN,0x351,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_FOO_UNLOCKBUS / CTL_CODE(FILE_DEVICE_UNKNOWN,0x352,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_FOO_XER / CTL_CODE(FILE_DEVICE_UNKNOWN,0X353,METHOD_BUFFERED,FILE_ANY_ACCESS) 这里面定义了一个GUID,暂时驱动没有使用。还定义了IOCTL的code,代表具体的功能。 FOO.C #include #include #include #include #include #include #include "foo.h" #ifdef DEBUG DBGPARAM dpCurSettings = { TEXT("FOO"), { TEXT("Error"),TEXT("Warning"),TEXT("Init"),TEXT("IST"), TEXT("Foo1"),TEXT("Foo2"),TEXT("Fooxx"),TEXT("Fooyy"), TEXT(""),TEXT("Resume"),TEXT(""),TEXT(""), TEXT(""),TEXT(""),TEXT(""),TEXT(""), }, 0x0007 // ZONE_ERROR | ZONE_WARN | ZONE_INIT }; #endif // DEBUG #define FOO_DEV_COOKIE 'FooD' #define FOO_INS_COOKIE 'FooI' #define LOCK() EnterCriticalSection(cs) #define UNLOCK() LeaveCriticalSection(cs) typedef struct foo_dev { DWORD foo_val; DWORD dwCookie; long instance; //Critical section // CRITICAL_SECTION cs; }FOO_DEV; typedef struct foo_instance { FOO_DEV *pDev; DWORD dwCookie; }FOO_INSTANCE; static CRITICAL_SECTION cs; BOOL FOO_Deinit(DWORD dwContext); /** * Function FOO_Init * * Description * brief handles initialization of the matrix keypad driver. */ DWORD FOO_Init(DWORD dwContext) { SETFNAME("FOO::Init"); DWORD rc = (DWORD)NULL; FOO_DEV* pFoo; pFoo = LocalAlloc(LPTR,sizeof(struct foo_dev)); if(pFoo==NULL) { DEBUG_OUT(1,(TEXT("Foo_init error,not enough mem!/r/n"))); goto cleanUp; } //initialize pFoo->dwCookie = FOO_DEV_COOKIE; //initialize the critcal section InitializeCriticalSection(&cs); rc = (DWORD)pFoo; cleanUp: if(rc==0) { FOO_Deinit((DWORD)pFoo); DEBUG_OUT(1,(TEXT("Foo_Init error/r/n"))); } return rc; } /** * Function: FOO_Deinit * * Description * brief handles deinitialization of the remote driver. */ BOOL FOO_Deinit(DWORD dwContext) { SETFNAME("FOO::Deinit"); FOO_DEV *pDevice = (FOO_DEV*)dwContext; // Delete critical section DeleteCriticalSection(&cs); if(pDevice!=NULL) LocalFree(pDevice); DEBUG_OUT(1,(TEXT("FOO_Deinit/r/n"))); return TRUE; } /** * Function: FOO_Open * * Description * * Note * This call must succeed for FOO1: to open an IOCTL channel */ DWORD FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode) { SETFNAME("FOO::Open"); DWORD rc = (DWORD)NULL; FOO_DEV *pFoo = (FOO_DEV *)dwContext; FOO_INSTANCE *pIns=NULL; DEBUG_OUT(1, (TEXT("%s/r/n"), pszFname)); if(pFoo==NULL) { DEBUG_OUT(1,(TEXT("Open error:error parameter/r/n"))); goto cleanUp; } pIns = LocalAlloc(LPTR,sizeof(struct foo_instance)); if(pIns==NULL) { DEBUG_OUT(1,(TEXT("Open error:error parameter/r/n"))); goto cleanUp; } pIns->dwCookie = FOO_INS_COOKIE; pIns->pDev = pFoo; // Increment number of open instances InterlockedIncrement(&pFoo->instance); rc = (DWORD)pIns; cleanUp: if(rc==0) { DEBUG_OUT(1,(TEXT("Open Failed/r/n"))); } return rc; } /** * Function FOO_Close * * Description * brief Teardown context created by _Open */ BOOL FOO_Close(DWORD dwContext) { SETFNAME("FOO::Close"); DWORD rc = 0; FOO_DEV *pFoo; FOO_INSTANCE *pIns =(FOO_INSTANCE*)dwContext; DEBUG_OUT(1, (TEXT("%s: FOO driver close/r/n"), pszFname)); if((pIns==NULL)||(pIns->dwCookie!=FOO_INS_COOKIE)) { DEBUG_OUT(1, (TEXT("Close Foo device error/r/n"))); return rc; } pFoo = pIns->pDev; InterlockedDecrement(&pFoo->instance); //free memory LocalFree(pIns); rc = TRUE; return rc; } /** * Function FOO_IOControl * * Description * brief process IOCTL's */ BOOL FOO_IOControl(DWORD dwContext,DWORD dwIOControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned) { BOOL bResult = FALSE; char* pMap1; char* pMap2; COPY_STRUCT *pcs; FOO_INSTANCE *pIns = (FOO_INSTANCE*)dwContext; DEBUG_OUT(1, ( L"+FOO_IOControl(0x%x, 0x%x, 0x%x, %d, 0x%x, %d, 0x%x)/r/n", dwContext, dwIOControlCode, pInBuf, nInBufSize, pOutBuf, nOutBufSize, pBytesReturned )); if((pIns==NULL)||(pIns->dwCookie!=FOO_INS_COOKIE)) { DEBUG_OUT(1,(TEXT("Error:FOO_IOControl: Incorrect context parameter/r/n"))); return bResult; } switch(dwIOControlCode) { case IOCTL_FOO_COPYCMD: if((pInBuf==NULL)) { SetLastError(ERROR_INVALID_PARAMETER); break; } pcs = (COPY_STRUCT*)pInBuf; __try{ pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess()); pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess()); DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)/r/n"),pcs->pBuffer1,pcs->pBuffer2)); DEBUG_OUT(1, (TEXT("After map"))); DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)/r/n"),pMap1,pMap2)); memcpy(pMap2,pMap1,pcs->nLen); bResult = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER){ DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTL/r/n"))); break; } break; case IOCTL_FOO_LOCKBUS: break; case IOCTL_FOO_UNLOCKBUS: break; case IOCTL_FOO_XER: if((pInBuf==NULL)) { SetLastError(ERROR_INVALID_PARAMETER); break; } pcs = (COPY_STRUCT*)pInBuf; __try{ pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess()); pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess()); DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)/r/n"),pcs->pBuffer1,pcs->pBuffer2)); memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen); bResult = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER){ DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTL/r/n"))); break; } break; default: break; } return bResult; } /** * Function FOO_PowerDown * * Param: * * Returns: * * Description * brief clean up for suspend */ VOID FOO_PowerDown(DWORD dwContext) { } /** * Function FOO_PowerUp * * Param: * * Returns: * * Description * brief signal the driver that we are resuming */ VOID FOO_PowerUp(DWORD dwContext) { SETFNAME("FOO::PowerUp"); DEBUG_OUT(1, (TEXT("%s: remote driver resuming/r/n"), pszFname)); } /** * Function DllEntry * * brief Main DLL initialization and entry point */ BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID pvReserved) { SETFNAME("FOO::DllEntry"); switch(dwReason) { case DLL_PROCESS_ATTACH: DEBUGREGISTER((HMODULE)hInstDll); DEBUG_OUT(1, (TEXT("%s: DLL_PROCESS_ATTACH/r/n"), pszFname)); break; case DLL_PROCESS_DETACH: DEBUG_OUT(1, (TEXT("%s: DLL_PROCESS_DETACH/r/n"), pszFname)); break; } return TRUE; } 把代码贴在这里,一个是大家可以编译测试,第二是,有些地方我们要仔细讨论。编译driver吧,还不够,还需要写一个source文件,copy一个makefile文件,写一个def文件,export接口出来。编译出FOO.DLL.暂时不要编译debug版本的。以后讨论debug版本的一些调试。最后修改注册表加上: [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/FOO] "Prefix"="FOO" "Dll"="FOO.dll" "Index"=dword:1 "Order"=dword:18 修改你的bib文件把 foo.dll加上去。 现在启动系统可以看见foo.dll已经加载。可以使用下面的应用程序代码测试这个driver,使用evc编译。 #include #include #include #include "objbase.h" #include "initguid.h" #include "foo.h" //char data1[10]; int WinMain(void) { HANDLE hnd; COPY_STRUCT cs[1]; int i; //static char data1[10]; auto char data1[10]; auto char data2[10]; static char* p1,*p2; //cs.pBuffer1 = (char *)malloc(10); //cs.pBuffer2 = (char*)malloc(10); //cs.nLen = 10; p1 = (char *)LocalAlloc(LPTR,10); p2 = (char *)malloc(10); //cs[0].pBuffer1 = (char *)malloc(10); //cs[0].pBuffer2 = (char*)malloc(10); cs[0].pBuffer1 = &data1[0]; cs[0].pBuffer2 = &data2[0]; cs[0].nLen = 10; memset(cs[0].pBuffer1,'a',10); hnd = CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL); if(hnd==NULL) { printf("Open device falied!/n"); return; } DeviceIoControl(hnd,IOCTL_FOO_XER,&cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL); //for(i=0;i<9;i++) //{ //printf(" %c",*(cs.pBuffer2++)); //} printf("/n"); CloseHandle(hnd); // free(cs[0].pBuffer1); // free(cs[0].pBuffer2); } 可以通过evc的单步调试看结果。好了一切都完成了,我们来看看系统是怎么工作的吧,从应用程序开始, CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL); 会调用到 FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode) 而FOO_DEV_NAME名字定义在foo.h里面。 #define FOO_DEV_NAME L"Foo1:" 注意后面是 1 ,这个是和注册表的这一项匹配的 "Index"=dword:1 当调用CreateFile发生了什么,slot之间的转换,一系列系统操作后,调用到我们自己的driver函数FOO_Open,在这个函数里我们返回了一个句柄,它可以用来存储我们的自己driver的信息。在其它I/O操作中可以使用。 Driver什么时候加载的?在注册表里,device manager会一个个的加载,会调用到FOO_Init函数。这个函数返回一个指针,在调用FOO_Open又传回来了,这样我们就可以实现初始化一些自己driver的东西。 接着一个重要的函数, DeviceIoControl(hnd,IOCTL_FOO_XER,&cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL); 调用到 FOO_IOControl 走到这里 case IOCTL_FOO_XER: if((pInBuf==NULL)) { SetLastError(ERROR_INVALID_PARAMETER); break; } pcs = (COPY_STRUCT*)pInBuf; __try{ pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess()); pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess()); DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)/r/n"),pcs->pBuffer1,pcs->pBuffer2)); memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen); bResult = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER){ DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTL/r/n"))); break; } break; default: break; 这里又很多东西要研究, 从应用程序传来的参数有, control code,IOCTL_FOO_XER和一个重要的输入参数&cs[0],它是一个指针。cs 是一个结构体,定义在FOO.H typedef struct { char* pBuffer1; char* pBuffer2; int nLen; }COPY_STRUCT; 而且这个结构体里有两个指针。 DeviceIoControl 传过来的指针可以用吗?它包含的两个指针可以直接用吗? 按照PB连接帮助文档看, The operating system (OS ) manages pointers passed directly as parameters. Drivers must map all pointers contained in structures. DeviceIoControl buffers are often structures that contain data, some of which might be pointers. You can map a pointer contained in a structure by calling MapPtrToProcess, setting the first parameter to the pointer, and then setting the second parameter to GetCallerProcess. cs指针已经映射好了,但是它指向的结构里的指针我们需要自己使用MapPtrToProcess函数映射。这也就是: pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess()); pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess()); 的由来,可是后面的代码没有使用pMap1,pMap2。而是直接使用: memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen); 而且它还工作了,没有出现exception。很奇怪。我第一次在一个家伙的代码里看见这种情况,很吃惊,但是它工作的很好,是文档出错了?我们来分析一下,看看应用程序的代码: COPY_STRUCT cs[1]; auto char data1[10]; auto char data2[10]; cs结构和data1,data2数组都是自动变量,存放在堆栈里。假设这个应用程序被加载到0x18000000位置的slot里,那么他们的地址都是0x18XXXXXX。不熟悉wince memory architecture的可以看看资料,了解一下slot。当调用了 DeviceIoControl,按照文档的说法,cs指针得到了转换,因为从应用程序的进程转到了device.exe进程,而device进程又是当前的运行的进程,被映射到了slot0,系统负责转换cs指针。而cs包含的pBuffer1和pBuffer2是没有映射不能直接用的。事实上,我们传过来的指针根本就是不需要映射,因为他们都是0x18xxxxxx,在应用程序的slot里,所以只要device.exe有访问应用程序的权限,就可以了。而这个权限,系统已经帮我们设置好了。 那什么情况下要自己映射呢?如果应用程序在定义 data1和data2使用static关键字,或者使用LocalAlloc,HeapAlloc的时候,一定要自己映射cs里的指针。在应用程序里这样写: cs.pBuffer1 = (char *)malloc(10); cs.pBuffer2 = (char*)malloc(10); cs.nLen = 10; 如果不使用MapPtrToProcess完成映射,那就出现data abort exception. 为什么呢?因为这些变量都是在堆里分配的,而当应用程序运行时,被映射到slot0,堆的地址也就是处于slot的范围内,传递到device.exe后,device.exe被映射到了slot0,这个时候必须要将应用程序的指针映射回应用程序所在的slot。否则访问的是device.exe的空间,会发生不可知道的结果。 验证一下上面说的地址分配问题。 我们这样定义 COPY_STRUCT cs[1]; static char data1[10]; 堆里 auto char data2[10]; 栈里 这样赋值: cs[0].pBuffer1 = &data1[0]; cs[0].pBuffer2 = &data2[0]; cs[0].nLen = 10; 调试信息: cs[0].pBuffer1 = &data1[0]; 180112D0 ldr r2, [pc, #0xD0] 180112D4 str r2, [sp, #0x10] 读取&data1[0]使用的是PC作为基址,而此时的应用程序处于运行阶段映射到slot0,那么pc也就在0~01ffffff范围,我的调试结果是在0x000112D0+8,使用的是arm,流水线机制,当前指令地址+8才是pc值。 143: cs[0].pBuffer2 = &data2[0]; 180112D8 add r0, sp, #0x20 180112DC str r0, [sp, #0x14] 读取&data2[0]采用的是sp作为基址,sp在应用程序加载到slot的时候就确定了的。所以保持了在应用程序slot的值,处于0x18xxxxxx范围。 我们看到因为wince的slot机制,我们有时候需要映射,有时候不需要。所以wince文档说结构里的指针要映射。毕竟你不知道应用程序怎么写。当然,你可以根本不映射,只要把那个结构屏蔽调,写一个STATIC LIBRARY给用户使用,自己保证使用正确的地址分配就可以了。上面我说的那个家伙就是这么干的。 好了,接着调用: CloseHandle(hnd); 程序结束了,完成了一次简单的拷贝。 这个框架完成了,driver的基本接口设计,强调了内存指针的使用问题。但是相对于一个真正的driver,还缺少点东西,就是访问硬件的方法。下面继续讨论如何访问硬件。 原文地址 http://space.ee365.cn/?uid-2705-action-viewspace-itemid-183

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值