编写Windows CE.net的usb驱动程序教程

时间:2011-09-03

  Windows CE.NET 是微软推出的功能强大的嵌入式操作系统,国内采用此操作系统的厂商已经很多了,本文就以windows ce.net为例,简单介绍一下如何开发windows ce.net下的USB驱动程序。
  Windows CE.NET 的USB系统软件分为两层: USB Client设备驱动程序和底层的Windows CE实现的函数层。USB设备驱动程序主要负责利用系统提供的底层接口配置设备,和设备进行通讯。底层的函数提本身又由两部分组成,通用串行总线驱动程序(USBD)模块和较低的主控制器驱动程序(HCD)模块。HCD负责层的处理,USBD模块实现较高的USBD函数接口。USB设备驱动主要利用 USBD接口函数和他们的外围设备打交道。
  USB设备驱动程序主要和USBD打交道,所以我们必须详细的了解USBD提供的函数。

  主要的传输函数有:
  abourttransferissuecontroltransfer
  closetransfer issueinterruptransfer
  getisochresultissueisochtransfer
  gettransferstatus istransfercomplete
  issuebulktransfer issuevendortransfer
  

    主要的用于打开和关闭usbd和usb设备之间的通信通道的函数有:
    abortpipetransfersclosepipe
    isdefaultpipehalted ispipehalted 
    openpiperesetdefaultpipe
    resetpipe

  相应的打包函数接口有:
  getframelengthgetframenumberreleaseframelengthcontrol
  setframelengthtakeframelengthcontrol

  取得设置设备配置函数:
  clearfeature setdescriptor
  getdescriptorsetfeature
  getinterface setinterface
  getstatussyncframe

  与usb进行交互的实现方法相关的多任务函数:
  findinterfaceregisterclientdeviceid
  getdeviceinforegisterclientsettings
  getusbdversion registernotificationroutine
  loadgenericinterfacedriver translatestringdescr
  openclientregisterkeyunregisternotificationroutine


  常见的Windows CE.NET下USB的设备驱动程序的编写有以下几种方法:
  ● 流式接口函数这种驱动程序主要呈现流式函数接口,主要输出XXX_Init,XXX_Deinit,XXX_Open,XXX_Close,XXX_Open,XXX_Close,XXX_Read,XXX_Write,XXX_Seek, XXX_IOControl,XXX_PowerUp,XXX_PowerDown等流式接口,注意上述的几个接口一定都要输出,另外XXX必须为三个字符,否则会出错。但是此类的驱动程序不是通过设备管理接口来加载的,所以必须手工的调用RegisterDevice()和 DeregisterDevice()函数来加载和卸载驱动程序。用户可以将此类的设备作为标准的文件来操作,只要调用相应的文件操作就可以和驱动程序打交道。
  ● 使用现有的Window CE.NET的应用程序接口此类设备主要是利用Windows CE.NET中已经有了现成的函数接口,例如USB Mass Storage Disk,它主要利用现有的Windows CE.Net中已经有的可安装文件系统接口,呈现给系统可用的文件系统,对于用户来讲,它是透明的,用户仅仅感觉在操作一个文件夹。
  ● 创建指定到特定的USBD的用户指定的API这种方法在USBD呈现设备时不需要任何限制,主要是特制的提供API给用户,一般不太常见。
  USB设备驱动程序必须输出的函数有:
  ● USBDeviecAttach当USB设备连接到计算机上时,USBD模块就会调用此函数,这个函数主要用于初始化USB设备,取得USB设备信息,配置USB设备,并且申请必需的资源。
  ● USBInstallDriver主要用于创建一个驱动程序加载所需的注册表信息,例如读写超时,设备名称等。
  ● USBUninstallDriver主要用于释放驱动程序所占用的资源,以及删除USBInstallDriver函数创建的注册表等。
  上述的三个函数接口是所有的USB驱动程序必须提供的,缺一不可。
  另外比较重要的是USB设备驱动程序的注册表配置,一般的USB设备驱动程序的注册表配置HKEY_LOCAL_MACHINE\Drivers\USB \LoadClients下,每个驱动程序的子键都有Group1_ID\Group2_ID\Group3_ID\DriverName格式,如果注册表信息与USB设备信息符合,USBD就会加载此驱动程序。否则设备的子键应该由供应商,设备类和协议信息通过下划线组成。
  例如你有个PDA设备,它具有一个USB接口,它的供应厂商ID假设为0x0888,设备ID为0x0999,没有使用特殊的协议,那么它的加载注册表应该写为:
  [HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\2184_2457\Default\Default\PDA] "DLL"="pdausb.dll"需要注意的是注册表构成都是十进制数值来标识的,注意一下十进制和十六进制的转换。
  有了这些基本信息,就可以编写USB设备了。
  首先,必须输出USBD要求调用的三个函数,首先到设备插入到USB端口时,USBD会调用USBDeviceAttach()函数,相应的代码如下:

  extern c bool
  usbdeviceattach(
  usb_handle hdevice, // usb设备句柄
  lpcusb_funcs lpusbfuncs, // usbdi的函数集合
  lpcusb_interface lpinterface, // 设备接口描述信息
  lpcwstr szuniquedriverid, // 设备id描述字符串。
  lpbool facceptcontrol, // 返回true,标识我们可以控制此设备, 反之表示不能控制
  dword dwunused)
  {
  *facceptcontrol = false;
  // 我们的鼠标设备有特定的描述信息,要检测是否是我们的设备。
  if (lpinterface == null)
  return false;
  // 打印相关的usb设备接口描述信息。
  debugmsg(zone_init,(text(usbmouse: deviceattach, if %u, #ep:%u, class:%u, sub:%u,prot:%urn), lpinterface->descriptor.binterfacenumber,lpinterface->descriptor.bnumendpoints, lpinterface->descriptor.binterfaceclass,lpinterface->descriptor.binterfacesubclass,lpinterface->descriptor.binterfaceprotocol));
  // 初试数据usb鼠标类,产生一个接受usb鼠标数据的线程
  cmouse * pmouse = new cmouse(hdevice, lpusbfuncs, lpinterface);
  if (pmouse == null)
  return false;
  if (!pmouse->initialize())
  {
  delete pmouse;
  return false;
  }

  // 注册一个监控usb设备事件的回调函数,用于监控usb设备是否已经拔掉。
  (*lpusbfuncs->lpregisternotificationroutine)(hdevice,
  usbdevicenotifications, pmouse);
  *facceptcontrol = true;
  return true;
  }
  第二个函数是 usbinstalldriver()函数,
  一些基本定义如下:
  const wchar gcszregisterclientdriverid[] = lregisterclientdriverid;
  const wchar gcszregisterclientsettings[] = lregisterclientsettings;
  const wchar gcszunregisterclientdriverid[] = lunregisterclientdriverid;
  const wchar gcszunregisterclientsettings[] = lunregisterclientsettings;
  const wchar gcszmousedriverid[] = lgeneric_sample_mouse_driver;
  函数接口如下:
  extern c bool
  usbinstalldriver(
  lpcwstr szdriverlibfile) // @parm [in] - contains client driver dll name
  {
  bool fret = false;
  hinstance hinst = loadlibrary(lusbd.dll);
  // 注册usb设备信息
  if(hinst)
  {
  lpregister_client_driver_id pregisterid = (lpregister_client_driver_id)
  getprocaddress(hinst, gcszregisterclientdriverid);
  lpregister_client_settings pregistersettings =
  (lpregister_client_settings) getprocaddress(hinst,
  gcszregisterclientsettings);
  if(pregisterid && pregistersettings)
  {
  usb_driver_settings driversettings;
  driversettings.dwcount = sizeof(driversettings);
  // 设置我们的特定的信息。
  driversettings.dwvendorid = usb_no_info;
  driversettings.dwproductid = usb_no_info;
  driversettings.dwreleasenumber = usb_no_info;
  driversettings.dwdeviceclass = usb_no_info;
  driversettings.dwdevicesubclass = usb_no_info;
  driversettings.dwdeviceprotocol = usb_no_info;
  driversettings.dwinterfaceclass = 0x03; // hid
  driversettings.dwinterfacesubclass = 0x01; // boot device
  driversettings.dwinterfaceprotocol = 0x02; // mouse
  fret = (*pregisterid)(gcszmousedriverid);
  if(fret)
  {
  fret = (*pregistersettings)(szdriverlibfile,
  gcszmousedriverid, null, &driversettings);
  if(!fret)
  {
  //bugbug unregister the client drivers id
  }
  }
  }
  else
  {
  retailmsg(1,(text(!usbmouse: error getting usbd function pointersrn)));
  }
  freelibrary(hinst);
  }
  return fret;
  }

  上述代码主要用于产生USB设备驱动程序需要的注册表信息,要注意的是:USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()与RegisterClientSettings来注册相应的设备信息。
  另外一个函数是usbuninstalldriver()函数,具体代码如下:
  extern c bool
  usbuninstalldriver()
  {
  bool fret = false;
  hinstance hinst = loadlibrary(lusbd.dll);
  if(hinst)
  {
  lpun_register_client_driver_id punregisterid =
  (lpun_register_client_driver_id)
  getprocaddress(hinst, gcszunregisterclientdriverid);
  lpun_register_client_settings punregistersettings =
  (lpun_register_client_settings) getprocaddress(hinst,
  gcszunregisterclientsettings);
  if(punregistersettings)
  {
  usb_driver_settings driversettings;
  driversettings.dwcount = sizeof(driversettings);
  // 必须填入与注册时相同的信息。
  driversettings.dwvendorid = usb_no_info;
  driversettings.dwproductid = usb_no_info;
  driversettings.dwreleasenumber = usb_no_info;
  driversettings.dwdeviceclass = usb_no_info;
  driversettings.dwdevicesubclass = usb_no_info;
  driversettings.dwdeviceprotocol = usb_no_info;
  driversettings.dwinterfaceclass = 0x03; // hid
  driversettings.dwinterfacesubclass = 0x01; // boot device
  driversettings.dwinterfaceprotocol = 0x02; // mouse
  fret = (*punregistersettings)(gcszmousedriverid, null,
  &driversettings);
  }
  if(punregisterid)
  {
  bool frettemp = (*punregisterid)(gcszmousedriverid);
  fret = fret ? frettemp : fret;
  }
  freelibrary(hinst);
  }
  return fret;
  }

  此函数主要用于删除USBInstallDriver()时创建的注册表信息,同样的它使用自己的函数接口UnRegisterClientDriverID()和UnRegisterClientSettings()来做相应的处理。


    另外一个需要处理的注册的监控通知函数usbdevicenotifications():
  extern c bool usbdevicenotifications(lpvoid lpvnotifyparameter, dword dwcode,
  lpdword * dwinfo1, lpdword * dwinfo2, lpdword * dwinfo3,
  lpdword * dwinfo4)
  {
  cmouse * pmouse = (cmouse *)lpvnotifyparameter;
  switch(dwcode)
  {
  case usb_close_device:
  //删除相关的资源。
  delete pmouse;
  return true;
  }
  return false;
  }
  usb鼠标的类的定义如下:
  class cmouse
  {
  public:
  cmouse::cmouse(usb_handle hdevice, lpcusb_funcs lpusbfuncs,
  lpcusb_interface lpinterface);
  ~cmouse();
  bool initialize();
  private:
  // 传输完毕调用的回调函数
  static dword callback mousetransfercompletestub(lpvoid lpvnotifyparameter);
  // 中断处理函数
  static ulong callback cmouse::mousethreadstub(pvoid context);
  dword mousetransfercomplete();
  dword mousethread();
  bool submitinterrupt();
  bool handleinterrupt();
  bool m_fclosing;
  bool m_freadyformouseevents;
  handle m_hevent;
  handle m_hthread;
  usb_handle m_hdevice;
  usb_pipe m_hinterruptpipe;
  usb_transfer m_hinterrupttransfer;
  lpcusb_funcs m_lpusbfuncs;
  lpcusb_interface m_pinterface;
  bool m_fprevbutton1;
  bool m_fprevbutton2;
  bool m_fprevbutton3;
  // 数据接受缓冲区。
  byte m_pbdatabuffer[8];
  };
  具体实现如下:
  // 构造函数,初始化时调用
  cmouse::cmouse(usb_handle hdevice, lpcusb_funcs lpusbfuncs,
  lpcusb_interface lpinterface)
  {
  m_fclosing = false;
  m_freadyformouseevents = false;
  m_hevent = null;
  m_hthread = null;
  m_hdevice = hdevice;
  m_hinterruptpipe = null;
  m_hinterrupttransfer = null;
  m_lpusbfuncs = lpusbfuncs;
  m_pinterface = lpinterface;
  m_fprevbutton1 = false;
  m_fprevbutton2 = false;
  m_fprevbutton3 = false;
  memset(m_pbdatabuffer, 0, sizeof(m_pbdatabuffer));
  }
  // 析构函数,用于清除申请的资源。
  cmouse::~cmouse()
  {
  // 通知系统去关闭相关的函数接口。
  m_fclosing = true;
  // wake up the connection thread again and give it time to die.
  if (m_hevent != null)
  {
  // 通知关闭数据接受线程。
  setevent(m_hevent);
  if (m_hthread != null)
  {
  dword dwwaitreturn;
  dwwaitreturn = waitforsingleobject(m_hthread, 1000);
  if (dwwaitreturn != wait_object_0)
  {
  terminatethread(m_hthread, dword(-1));
  }
  closehandle(m_hthread);
  m_hthread = null;
  }
  closehandle(m_hevent);
  m_hevent = null;
  }
  if(m_hinterrupttransfer)
  (*m_lpusbfuncs->lpclosetransfer)(m_hinterrupttransfer);
  if(m_hinterruptpipe)
  (*m_lpusbfuncs->lpclosepipe)(m_hinterruptpipe);
  }
  // 初始化usb鼠标驱动程序
  bool cmouse::initialize()
  {
  lpcusb_device lpdeviceinfo = (*m_lpusbfuncs->lpgetdeviceinfo)(m_hdevice);
  // 检测配置:usb鼠标应该只有一个中断管道
  if ((m_pinterface->lpendpoints[0].descriptor.bmattributes & usb_endpoint_type_mask) != usb_endpoint_type_interrupt)
  {
  retailmsg(1,(text(!usbmouse: ep 0 wrong type (%u)!rn),
  m_pinterface->lpendpoints[0].descriptor.bmattributes));
  return false;
  }
  debugmsg(zone_init,(text(usbmouse: ep 0:maxpacket: %u, interval: %urn),
  m_pinterface->lpendpoints[0].descriptor.wmaxpacketsize,
  m_pinterface->lpendpoints[0].descriptor.binterval));
  m_hinterruptpipe = (*m_lpusbfuncs->lpopenpipe)(m_hdevice,
  &m_pinterface->lpendpoints[0].descriptor);
  if (m_hinterruptpipe == null) {
  retailmsg(1,(text(mouse: error opening interrupt pipern)));
  return (false);
  }
  m_hevent = createevent(null, false, false, null);
  if (m_hevent == null)
  {
  retailmsg(1,(text(usbmouse: error on createevent for connect eventrn)));
  return(false);
  }
  // 创建数据接受线程
  m_hthread = createthread(0, 0, mousethreadstub, this, 0, null);
  if (m_hthread == null)
  {
  retailmsg(1,(text(usbmouse: error on createthreadrn)));
  return(false);
  }
  return(true);
  }
  // 从usb鼠标设备中读出数据,产生相应的鼠标事件。
  bool cmouse::submitinterrupt()
  {
  if(m_hinterrupttransfer)
  (*m_lpusbfuncs->lpclosetransfer)(m_hinterrupttransfer);
  // 从usb鼠标pipe中读数据
  m_hinterrupttransfer = (*m_lpusbfuncs->lpissueinterrupttransfer)
  (m_hinterruptpipe, mousetransfercompletestub, this,
  usb_in_transfer | usb_short_transfer_ok, // 表示读数据
  min(m_pinterface->lpendpoints[0].descriptor.wmaxpacketsize,
  sizeof(m_pbdatabuffer)),
  m_pbdatabuffer,
  null);
  if (m_hinterrupttransfer == null)
  {
  debugmsg(zone_error,(l !usbmouse: error in issueinterrupttransferrn));
  return false;
  }
  else
  {
  debugmsg(zone_transfer,(lusbmouse::submitinterrupt,transfer:0x%xrn,
  m_hinterrupttransfer));
  }
  return true;
  }
  // 处理鼠标中断传输的数据
  bool cmouse::handleinterrupt()
  {
  dword dwerror;
  dword dwbytes;
  dword dwflags = 0;
  int dx = (signed char)m_pbdatabuffer[1];
  int dy = (signed char)m_pbdatabuffer[2];
  bool fbutton1 = m_pbdatabuffer[0] & 0x01 ? true : false;
  bool fbutton2 = m_pbdatabuffer[0] & 0x02 ? true : false;
  bool fbutton3 = m_pbdatabuffer[0] & 0x04 ? true : false;
  if (!(*m_lpusbfuncs->lpgettransferstatus)(m_hinterrupttransfer, &dwbytes,&dwerror))
  {
  debugmsg(zone_error,(text(!usbmouse: error in gettransferstatus(0x%x)rn),
  m_hinterrupttransfer));
  return false;
  }
  else
  {
  debugmsg(zone_transfer,(text(usbmouse::handleinterrupt, htransfer 0x%x complete (%u bytes, error:%x)rn),
  m_hinterrupttransfer,dwbytes,dwerror));
  }
  if (!submitinterrupt())
  return false;
  if(dwerror != usb_no_error)
  {
  debugmsg(zone_error,(text(!usbmouse: error 0x%x in interrupt transferrn),dwerror));
  return true;
  }
  if(dwbytes < 3)
  {
  debugmsg(zone_error,(text(!usbmouse: invalid byte cnt %u from interrupt transferrn),dwbytes));
  return true;
  }
  if(dx || dy)
  dwflags |= mouseeventf_move;
  if(fbutton1 != m_fprevbutton1)
  {
  if(fbutton1)
  dwflags |= mouseeventf_leftdown;
  else
  dwflags |= mouseeventf_leftup;
  }
  if(fbutton2 != m_fprevbutton2)
  {
  if(fbutton2)
  dwflags |= mouseeventf_rightdown;
  else
  dwflags |= mouseeventf_rightup;
  }
  if(fbutton3 != m_fprevbutton3)
  {
  if(fbutton3)
  dwflags |= mouseeventf_middledown;
  else
  dwflags |= mouseeventf_middleup;
  }
  m_fprevbutton1 = fbutton1;
  m_fprevbutton2 = fbutton2;
  m_fprevbutton3 = fbutton3;
  debugmsg(zone_events,
  (text(usbmouse event: dx:%d, dy:%d, dwflags:0x%x (b1:%u, b2:%u, b3:%u)rn),
  dx,dy,dwflags,fbutton1,fbutton2,fbutton3));
  // 通知系统产生鼠标事件
  if (m_freadyformouseevents)
  mouse_event(dwflags, dx, dy, 0, 0);
  else
  m_freadyformouseevents = isapiready(sh_wmgr);
  return true;
  }
  dword callback cmouse::mousetransfercompletestub(lpvoid lpvnotifyparameter)
  {
  cmouse * pmouse = (cmouse *)lpvnotifyparameter;
  return(pmouse->mousetransfercomplete());
  }
  // 数据传输完毕回调函数
  dword cmouse::mousetransfercomplete()
  {
  if (m_hevent)
  setevent(m_hevent);
  return 0;
  }
  ulong callback cmouse::mousethreadstub(pvoid context)
  {
  cmouse * pmouse = (cmouse *)context;
  return(pmouse->mousethread());
  }
  // usb鼠标线
  dword cmouse::mousethread()
  {
  debugmsg(zone_init,(text(usbmouse: worker thread startedrn)));
  setthreadpriority(getcurrentthread(), thread_priority_highest);
  if (submitinterrupt())
  {
  while (!m_fclosing)
  {
  waitforsingleobject(m_hevent, infinite);
  if (m_fclosing)
  break;
  if ((*m_lpusbfuncs->lpistransfercomplete)(m_hinterrupttransfer))
  {
  if (!handleinterrupt())
  break;
  }
  else
  {
  retailmsg(1,(text(!usbmouse: event signalled, but transfer not completern)));
  // the only time this should happen is if we get an error on the transfer
  assert(m_fclosing || (m_hinterrupttransfer == null));
  break;
  }
  }
  }
  retailmsg(1,(text(usbmouse: worker thread exitingrn)));
  return(0);
  }

  其实USB的驱动程序编写就这么简单,类似的其他设备,比如打印机设备,就有Bulk OUT PIPE,需Bulk传输,那就要了解一下IssueBulkTransfer()应用。如果是开发USB Mass Storage Disk的驱动,就要了解更多的协议,如Bulk-Only Transport协议等。
  微软的Windows CE.NET的Platform Build中已经带有USB Printer和USB Mass Storage Disk的驱动的源代码了,好好研究一下,你一定会受益非浅的。



  
上一篇:D/A转换器设计
下一篇:WinCE 5.0边练边学(5)(6)

免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

相关技术资料