WinDBG 做 APCI debug 后续

之前介绍过如何在RS1 之后的 Windows使用WinDBG做ASL 的Debug。最近我在 RS3 上再次实验发现有如下错误:

6: kd> !amli find _ptsAMLI_DBGERR: failed to read NameSpace root object

该做的动作都做了,但是仍然无法调试。查找资料在【参考1】看到这个问题的可能原因如下

  1. Checked Acpi.sys和Acpi.pdb文件和debuggee版本不符导致的。
  2. WinDbg没有load Acpi符号文件,只要.reload即可。

首先检查 Acpi.sys 和系统的版本,匹配无误。接下来就是没有 Load 起来 acpi.pdb的问题了。

运行 .reload 看到下面的信息:

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pdb – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pd_ – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\file.ptr – path not found

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/acpi.pdb

 

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/acpi.pd_

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/file.ptr

 

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  RESULT: 0x80190194

DBGHELP: acpi.pdb – file not found

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ACPI.sys –

DBGHELP: ACPI – export symbols

 

确实无法找到能和我给系统装入的 acpi.sys 匹配的 ACPI.pdb。接下来尝试使用.reload /f 强制更新

结果是仍然无法找到:

SYMSRV:  BYINDEX: 0x3B

C:\ProgramData\Dbg\sym

acpi.pdb

63383A79DFA1FA1BCAF8F9BE8ADA117E1

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pdb – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pd_ – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\file.ptr – path not found

SYMSRV:  RESULT: 0x80070003

SYMSRV:  BYINDEX: 0x3C

https://msdl.microsoft.com/download/symbols

acpi.pdb

63383A79DFA1FA1BCAF8F9BE8ADA117E1

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pdb – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\acpi.pd_ – path not found

SYMSRV:  UNC: C:\ProgramData\Dbg\sym\acpi.pdb\63383A79DFA1FA1BCAF8F9BE8ADA117E1\file.ptr – path not found

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/acpi.pdb

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/acpi.pd_

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  HTTPGET: /download/symbols/acpi.pdb/63383A79DFA1FA1BCAF8F9BE8ADA117E1/file.ptr

 

SYMSRV:  HttpQueryInfo: 80190194 – HTTP_STATUS_NOT_FOUND

SYMSRV:  RESULT: 0x80190194

DBGHELP: acpi.pdb – file not found

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ACPI.sys –

DBGHELP: ACPI – export symbols

 

在另外一篇文章【参考2】中,提到了另外的检查方法,使用这个方法检查 ACPI 模块的情况,结果相同,还是没有找到 ACPI.SYS对应的 PDB:

1: kd> !lmi acpiLoaded Module Info: [acpi]          Module: ACPI   Base Address: fffff80605c50000     Image Name: ACPI.sys   Machine Type: 34404 (X64)     Time Stamp: 64683d0c (This is a reproducible build file hash, not a true timestamp)           Size: 124000       CheckSum: c515bCharacteristics: 22  Debug Data Dirs: Type  Size     VA  Pointer             CODEVIEW    21, 7a69c,   7909c RSDS – GUID: {63383A79-DFA1-FA1B-CAF8-F9BE8ADA117E}               Age: 1, Pdb: acpi.pdb                 POGO   208, 7a6c0,   790c0 [Data not mapped]                REPRO     0,     0,       0  [Debug data not mapped]     Image Type: MEMORY   – Image read successfully from loaded memory.    Symbol Type: EXPORT   – PDB not found    Load Report: export symbols

 

至此,问题很明确:无法找到 acpi.pdb所以无法调试。再回去提供 checked 版本的ACPI.SYS的包中查找,其中提供了 ACPI.PDB。因此,就是说如果我将这个指定给 ACPI.SYS应该就可以继续调试了。直接将这个文件COPY到工作机的Software目录下。

 

检查当前 symbol 的路径

1: kd> .sympath Symbol search path is: srv*Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols

 

将software 目录添加到symbol 的path下【参考3】

 

1: kd> .sympath+ C:\software\DBGHELP: Symbol Search Path: cache*;SRV*https://msdl.microsoft.com/download/symbols;c:\software\SYMSRV:  BYINDEX: 0x11B         C:\ProgramData\Dbg\sym         ntkrnlmp.pdb         83DB42404EFD4AB6AFB6FA864B700CB31SYMSRV:  PATH: C:\ProgramData\Dbg\sym\ntkrnlmp.pdb\83DB42404EFD4AB6AFB6FA864B700CB31\ntkrnlmp.pdbSYMSRV:  RESULT: 0x00000000DBGHELP: C:\ProgramData\Dbg\sym\ntkrnlmp.pdb\83DB42404EFD4AB6AFB6FA864B700CB31\ntkrnlmp.pdb cached to C:\ProgramData\Dbg\sym\ntkrnlmp.pdb\83DB42404EFD4AB6AFB6FA864B700CB31\ntkrnlmp.pdb DBGHELP: nt – public symbols          C:\ProgramData\Dbg\sym\ntkrnlmp.pdb\83DB42404EFD4AB6AFB6FA864B700CB31\ntkrnlmp.pdbSymbol search path is: srv*;C:\software\Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols;c:\software\ ************* Path validation summary **************Response                         Time (ms)     LocationDeferred                                       srv*OK                                             C:\software\

执行一次 .reload指令,再检查acpi

1: kd> !lmi acpi

Loaded Module Info: [acpi]

Module: ACPI

Base Address: fffff80605c50000

Image Name: ACPI.sys

Machine Type: 34404 (X64)

Time Stamp: 64683d0c (This is a reproducible build file hash, not a true timestamp)

Size: 124000

CheckSum: c515b

Characteristics: 22

Debug Data Dirs: Type  Size     VA  Pointer

CODEVIEW    21, 7a69c,   7909c RSDS – GUID: {63383A79-DFA1-FA1B-CAF8-F9BE8ADA117E}

Age: 1, Pdb: acpi.pdb

POGO   208, 7a6c0,   790c0 [Data not mapped]

REPRO     0,     0,       0  [Debug data not mapped]   

Symbol Type: DEFERRED – No error – symbol load deferred   

Load Report: no symbols loaded

 

再次使用命令调试 acpi 就可以正常的工作了

 

1: kd> !amli dl

0:01:46.655 [ffffca82185a62c0] FinishedContext       Context=ffffca821f847010 rc=8004

QTh=0 QCt=0 QFg=00000000

 

0:01:46.655 [ffffca82185a62c0] QueueWorkItem

QTh=0 QCt=0 QFg=00000000 rc=8004

总结:出现这样的问题应该是我的Win10版本比较特殊,无法在微软公共Server上找到对应的 ACPI.SYS Symbol 所以导致的解析错误。手工加载对应的 PDB 文件即可。

参考:

  1. http://www.xuebuyuan.com/611480.html
  2. http://blog.csdn.net/whatday/article/details/7100292
  3. https://www.2cto.com/kf/201611/562340.html

 

说说 Arduino 101的 PWM频率

之前介绍过 Arduino Uno 的 PWM 频率问题,最近因为需要研究了一下 Arduino 101 的 PWM 频率问题。经过一番研究,发现在Arduino15\packages\Intel-Test@arduino.cn\hardware\arc32\2.0.0\cores\arduino\wiring_analog.c 中提供了analogWriteFrequency 函数。

/*
 * brief Set the frequency of analogWrite parameters.
 *
 * param res
 */
extern void analogWriteFrequency(uint8_t pin, uint32_t freq);

 

从参数上来看,只要在 freq 指定一个频率,就可以输出这个频率的 PWM信号。
首先,测试一下 PWM 的默认频率:

void setup() {
  // initialize digital pin 13 as an output.
  pinMode(6, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  analogWrite(6,127);
}

 

pwma

可以看到,对于 Pin6 默认的PWM 频率是 980Hz
接下来,试试 64KHz:

void setup() {
  // initialize digital pin 13 as an output.
  pinMode(6, OUTPUT);
  analogWriteFrequency(6, 64000);
}

 

pwmb

频率为 63.747Khz。
继续用这样的方法,升高频率到 250K,可以看到频率是 246.18Khz。

pwmc

继续升高到1Mhz
pwmd

实验程序,最高可以发出 16Mhz的频率。
然后就是区分度的问题,这个词是我创造的,我想表达的是 pwm升高1之后是否有正常的变化。比如:我们PWM 写入8位,就是分成256份。一份对应 1/256=0.39%。就是说,正常情况下,anglogWrite(PinX,1) 和anglogWrite(PinX,2)在占空比上应该有 0.39%的差别。份数越少,区分度越差。

Step to UEFI (146)Grayoutif ,Suppressif和Setup联动的探索

Grayoutif和Suppressif是比较常用的控制命令。Grayoutif的作用是设定某一个 item 跟随另一个 item 的变化而被设置为灰色无法修改【参考1】。Suppressif的作用是设定某一个 item 跟随另一个 item 的变化而被自动隐藏【参考2】。除了在使用的时候特别需要注意和 XXX_END 配对之外,这两个选项都不会影响被设置的Item的设定值,比如,选项隐藏之前是 Enabled 的,那么虽然隐藏掉了, 实际取值还是 Enabled。上面的结论可以通过设置之后进入 Shell 使用 dmpstore 命令查看对应的变量来验证。

此外有些时候,我们需要设定当选中一个选项后,另外的Item取值跟着变化(我记得有一个名词叫做“联动”)。下面就在 NT32 的环境中编写代码实现这个需求。
实验的环境是UDK2017。在 Setup -> Device Manager -> Browser Testcase Engine 下面有一个 Item “My one-of prompt #1”,我们在上面添加更多的选项来进行实验。

ss1

首先找到“My one-of prompt #1” Item,再其中加入一个选项“Disable Checkbox”,然后再声明这个为 INTERACTIVE

    //
    // Define oneof (EFI_IFR_ONE_OF)
    //
    oneof name = MyOneOf,                                // Define reference name for Question
      varid   = MyIfrNVData.SuppressGrayOutSomething,    // Use "DataStructure.Member" to reference Buffer Storage
      questionid = 0x2018,
      prompt  = STRING_TOKEN(STR_ONE_OF_PROMPT),
      help    = STRING_TOKEN(STR_ONE_OF_HELP),
      flags  = INTERACTIVE,
      //
      // Define an option (EFI_IFR_ONE_OF_OPTION)
      //
      option text = STRING_TOKEN(STR_ONE_OF_TEXT4), value = 0x0, flags = 0;
      option text = STRING_TOKEN(STR_ONE_OF_TEXT5), value = 0x1, flags = 0;
      //
      // DEFAULT indicate this option will be marked with EFI_IFR_OPTION_DEFAULT
      //
      option text = STRING_TOKEN(STR_ONE_OF_TEXT6), value = 0x2, flags = DEFAULT;
      option text = STRING_TOKEN(STR_ONE_OF_TEXT7), value = 0x3, flags = DEFAULT;      
endoneof;

 

处理的代码在 \MdeModulePkg\Universal\DriverSampleDxe\DriverSample.c 中
EFI_BROWSER_ACTION_CHANGING 处理的是之前的选项,比如,打开 Item 之后,你从第三项选择为第一项,那么Configuration->SuppressGrayOutSomething 的值是0;
EFI_BROWSER_ACTION_CHANGED处理的是当前的选项,比如,打开 Item 之后,你从第三项选择为第一项,那么Configuration->SuppressGrayOutSomething 的值是2;
例如,我们从 Enable Checkbox 切换为 Disable Checkboxss5

Debug 信息会有如下输出:

ss3

代码如下:

  case EFI_BROWSER_ACTION_CHANGED:
    switch (QuestionId) {
     //LABZ_Start
     case 0x2018:
      {
      //
      // Set initial vlaue of dynamic created oneof Question in Form Browser
      //
      Configuration = AllocateZeroPool (sizeof (DRIVER_SAMPLE_CONFIGURATION));
      ASSERT (Configuration != NULL);
      if (HiiGetBrowserData (&gDriverSampleFormSetGuid, VariableName, sizeof (DRIVER_SAMPLE_CONFIGURATION), (UINT8 *) Configuration)) {
                DEBUG ((EFI_D_ERROR, "Changed to[%d]\n",Configuration->SuppressGrayOutSomething));
                if (Configuration->SuppressGrayOutSomething == 2) {
                        Configuration->ChooseToActivateNuclearWeaponry=TRUE;
                }
                if (Configuration->SuppressGrayOutSomething == 3) {
                        Configuration->ChooseToActivateNuclearWeaponry=FALSE;
                }
        //
        // Update uncommitted data of Browser
        //
        HiiSetBrowserData (
          &gDriverSampleFormSetGuid,
          VariableName,
          sizeof (DRIVER_SAMPLE_CONFIGURATION),
          (UINT8 *) Configuration,
          NULL
          );
              
      }
       FreePool (Configuration);
      }
    break;             
    //LABZ_End

设置这个选项为 Disable 和 Enable 后,下面的 CheckBox 会跟着发生变化.

ss4

ss5

通过这样的功能,可以设计出方便用户使用的功能。比如,通过一个选项关闭多个影响OS 下 BIOS 刷写的选项,这样就免得用户一个个进行查找和设置。因为实验可以在NT32的模拟环境下方便的进行,有兴趣的朋友可以直接试试。

参考:

1. http://wiki.phoenix.com/wiki/index.php/EFI_IFR_SUPPRESS_IF
EFI IFR SUPPRESS IF
Creates a group of statements or questions which are conditionally invisible.

Prototype
#define EFI_IFR_SUPPRESS_IF_OP 0x0a
typedef struct _EFI_IFR_SUPPRESS_IF {
EFI_IFR_OP_HEADER Header;
} EFI_IFR_SUPRESS_IF;
Members
Member Description
Header The byte sequence that defines the type of opcode as well as the length of the opcode being defined. Header.OpCode = EFI_IFR_SUPPRESS_IF_OP.
Description
The suppress tag causes the nested objects to be hidden from the user if the expression appearing as the first nested object evaluates to TRUE. If the expression consists of more than a single opcode, then the first opcode in the expression must have the Scope bit set and the expression must end with EFI_IFR_END.

This display form is maintained until the scope for this opcode is closed.

2. http://wiki.phoenix.com/wiki/index.php/EFI_IFR_GRAY_OUT_IF
EFI IFR GRAY OUT IF
Creates a group of statements or questions which are conditionally grayed-out.

Prototype
#define EFI_IFR_GRAY_OUT_IF_OP 0x19
typedef struct _EFI_IFR_GRAY_OUT_IF {
EFI_IFR_OP_HEADER Header;
} EFI_IFR_GRAY_OUT_IF;
Members
Member Description
Header The byte sequence that defines the type of opcode as well as the length of the opcode being defined. Header.OpCode = EFI_IFR_GRAY_OUT_IF_OP.
Description
All nested statements or questions will be grayed out (not selectable and visually distinct) if the expression appearing as the first nested object evaluates to TRUE. If the expression consists of more than a single opcode, then the first opcode in the expression must have the Scope bit set and the expression must end with EFI_IFR_END.

Different browsers may support this option to varying degrees. For example, HTML has no similar construct so it may not support this facility.

示波器测量串口电压

测试的是串口输出上的 TX Pin,

1. 测试下面这个USB转串口公头模块(FTDI),测试Pin3

uart1

结果如下,特别注意,单位是 5V/Div, 出现了负电压

uart12

2. 再测试下面这个模块(FTDI),上面有一个拨动开关可以选择 切换5V和3.3V
uart13

2.1 先测试一下 3.3,结果如下

uart14

2.2 我们再测试一下 5V 项
uart15

从上面可以看到,当我们谈论串口的时候,电压有可能是从-12v到5v。特别是当我们看到标准的串口头时,一定要多留心一下他的电压,贸然的连接很可能导致设备的悲剧。

Step to UEFI (145)Crypto 实现的 SHA256

之前我们介绍过在UEFI 下实现MD5【参考1】和SHA-1 【参考2】的方法,这次介绍一下如何计算 SHA256 。同样的, SHA256也是一种 HASH 算法。
与之前不同,这次使用的是前面提到的CryptoPkg,在正常编译完成上述 Package后,会生成一个:CryptRuntimeDxe的EFI文件,我们需要做的是先在Shell 下加载这个 Driver。

sha2561

加载Driver之后,就可以使用EFI_RUNTIME_CRYPT_PROTOCOL :

///
/// Runtime Cryptographic Protocol Structure.
///
typedef struct {
  EFI_RUNTIME_CRYPT_SHA256_GET_CONTEXT_SIZE  Sha256GetContextSize;
  EFI_RUNTIME_CRYPT_SHA256_INIT              Sha256Init;
  EFI_RUNTIME_CRYPT_SHA256_UPDATE            Sha256Update;
  EFI_RUNTIME_CRYPT_SHA256_FINAL             Sha256Final;
  EFI_RUNTIME_CRYPT_RSA_NEW                  RsaNew;
  EFI_RUNTIME_CRYPT_RSA_FREE                 RsaFree;
  EFI_RUNTIME_CRYPT_RSA_SET_KEY              RsaSetKey;
  EFI_RUNTIME_CRYPT_RSA_PKCS1_VERIFY         RsaPkcs1Verify;
} EFI_RUNTIME_CRYPT_PROTOCOL;

 

在 AppPkg 中编写代码如下:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <stdlib.h>

#include "RuntimeCrypt.h"

//
// Max Known Digest Size is SHA512 Output (64 bytes) by far
//
#define MAX_DIGEST_SIZE    64

//
// Message string for digest validation
//
CHAR8 *HashData = "www.lab-z.com";

extern EFI_BOOT_SERVICES         *gBS;

///
/// Runtime Cryptographic Protocol GUID.
///
EFI_GUID  gEfiRuntimeCryptProtocolGuid =
                {0xe1475e0c, 0x1746, 0x4802, 
                        { 0x86, 0x2e, 0x1, 0x1c, 0x2c, 0x2d, 0x9d, 0x86 }};
int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
        EFI_RUNTIME_CRYPT_PROTOCOL  *mCryptProtocol = NULL;
        EFI_STATUS                  Status;
        UINT8                       Digest[MAX_DIGEST_SIZE];      
        UINTN    CtxSize;
        VOID     *HashCtx;
        UINTN    DataSize; 
        UINTN    Index;
        
        DataSize = AsciiStrLen (HashData);        
        //
        // Pointer to the runtime cryptographic protocol.
        //
        Status = gBS->LocateProtocol(
                        &gEfiRuntimeCryptProtocolGuid, 
                        NULL, 
                        (VOID **) &mCryptProtocol);
        if (EFI_ERROR(Status)) {
           Print(L"Can't find the runtime cryptographic protocol\n");
           return Status;
        }
        
        Print (L"- SHA256: \n");

        //
        // SHA256 Digest Validation
        //
        ZeroMem (Digest, MAX_DIGEST_SIZE);
        CtxSize = mCryptProtocol->Sha256GetContextSize ();
        HashCtx = AllocatePool (CtxSize);

        Print (L"Init... \n");
        Status  = mCryptProtocol->Sha256Init (HashCtx);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        Print (L"Update... \n");
        Status  = mCryptProtocol->Sha256Update (HashCtx, HashData, DataSize);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        Print (L"Finalize... \n");
        Status  = mCryptProtocol->Sha256Final (HashCtx, Digest);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        for (Index=0;Index<SHA256_DIGEST_SIZE;Index++) {
                Print (L"%2X  ",Digest[Index]);
        }
        Print (L"\n");
        FreePool (HashCtx);

        return EFI_SUCCESS;
}

 

上面的程序中,我们计算的是 “www.lab-z.com”的SHA256值,结果如下:

sha2562

我们可以使用【参考3】提供的在线工具进行计算,结果如下:

sha2563

可以看到,这个Protocol工作正常,能够准确的计算出 SHA256的值。

本文提到的完整的代码下载:
SHA256

参考:
1. 计算MD5 http://www.lab-z.com/uefimd5/
2. SHA-1的实现http://www.lab-z.com/sha1/
3. Hash在线计算、md5计算、sha1计算、sha256计算、sha512计算 https://1024tools.com/hash

Leonrado获得当前串口速率

最近在研究 Loenrado 的USB,在\arduino-1.8.4\hardware\arduino\avr\cores\arduino\CDC.cpp 中发现有趣的代码:

			// We check DTR state to determine if host port is open (bit 0 of lineState).
			if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
			{
#if MAGIC_KEY_POS != (RAMEND-1)
				// Backup ram value if its not a newer bootloader.
				// This should avoid memory corruption at least a bit, not fully
				if (magic_key_pos != (RAMEND-1)) {
					*(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
				}
#endif
				// Store boot key
				*(uint16_t *)magic_key_pos = MAGIC_KEY;
				wdt_enable(WDTO_120MS);
			}

 

这段代码是用来实现 Leonrado 刷新的:通过设置当前USB 传输速度为 1200,然后让板子重启进入Bootloader响应刷写指令而无需使用板载的 Reset按钮(未来会做更具体的分析)。
_usbLineInfo.dwDTERate 这里就是当前设定的USB 通讯速度,因此我们在这里添加代码根据当前速度设定LED On Off。加入的代码段如下:

#if MAGIC_KEY_POS != (RAMEND-1)
			// For future boards save the key in the inproblematic RAMEND
			// Which is reserved for the main() return value (which will never return)
			if (_updatedLUFAbootloader) {
				// horray, we got a new bootloader!
				magic_key_pos = (RAMEND-1);
			}
#endif
//LABZ_DEBUG_START
if (300 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,HIGH);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,HIGH);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (2400 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,HIGH);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (4800 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,HIGH);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (9600 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,HIGH);
        digitalWrite(7,LOW);
}
if (19200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,HIGH);
}
//LABZ_DEBUG_ENd
			// We check DTR state to determine if host port is open (bit 0 of lineState).
			if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
			{
#if MAGIC_KEY_POS != (RAMEND-1)
				// Backup ram value if its not a newer bootloader.
				// This should avoid memory corruption at least a bit, not fully
				if (magic_key_pos != (RAMEND-1)) {
					*(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
				}
#endif
				// Store boot key
				*(uint16_t *)magic_key_pos = MAGIC_KEY;
				wdt_enable(WDTO_120MS);
			}

 

这样,当设定不同速度时, D2-D7 上的LED会分别亮起来。

编写一个简单的 Arduino代码

void setup() {
  // put your setup code here, to run once:
        pinMode(2,OUTPUT);
        pinMode(3,OUTPUT);
        pinMode(4,OUTPUT);
        pinMode(5,OUTPUT);
        pinMode(6,OUTPUT);
        pinMode(7,OUTPUT);

        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}

void loop() {
  // put your main code here, to run repeatedly:

}

 

比如当前的IDE 的 Serial Monitor 设定为 4800, D5上面的LED会亮

arsp

推荐 Windows 下读取 SMBIOS的工具

最近发现一个能够在 Windows下读取 SMBIOS 的代码,有需要的朋友可以参考一下。
我在 UEFI 版本的 Windows 10 X64 下测试成功,编译器为 VS2015。

smbios

具体原理是使用: GetSystemFirmwareTable 来获得系统的SMBIOS信息。其中还有SMBIOS结构体的解析代码。有需要的朋友可以参考一下。
具体代码来自:https://github.com/KunYi/DumpSMBIOS
这里可以下载源程序

DumpSMBIOS-master

此外,还放了一个我编译的 X64 Release EXE版本,有需要的朋友可以试试,

DumpSMBIOS

最后,特别感谢作者KunYi Chen。

USB Usage AC Pan

最近我在查看一款鼠标的 HID 描述符时,遇到一个定义 AC Pan 搞不清楚意思:

ac1

直接看 USB HID协议上面解释也比较简单,看不懂

ac2

鼠标是最普通的款式,上面有三个按键,左右已经滚轮下面的一个按键,此外就只有滚轮了。当然我也没有找到发出 AC Pan 的方法。

想来想去可以使用 Leonardo 来进行验证,把这个值发出来看看系统有什么变化,也能了解功能。

为此,特地修改 Arduino 鼠标的描述符,在  \libraries\Mouse\src\Mouse.cpp 中加入 AC Pan。

 

static const uint8_t _hidReportDescriptor[] PROGMEM = {



  //  Mouse

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 54

    0x09, 0x02,                    // USAGE (Mouse)

    0xa1, 0x01,                    // COLLECTION (Application)

    0x09, 0x01,                    //   USAGE (Pointer)

    0xa1, 0x00,                    //   COLLECTION (Physical)

    0x85, 0x01,                    //     REPORT_ID (1)

    0x05, 0x09,                    //     USAGE_PAGE (Button)

    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)

    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)

    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)

    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)

    0x95, 0x03,                    //     REPORT_COUNT (3)

    0x75, 0x01,                    //     REPORT_SIZE (1)

    0x81, 0x02,                    //     INPUT (Data,Var,Abs)

    0x95, 0x01,                    //     REPORT_COUNT (1)

    0x75, 0x05,                    //     REPORT_SIZE (5)

    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)

    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)

    0x09, 0x30,                    //     USAGE (X)

    0x09, 0x31,                    //     USAGE (Y)

    0x09, 0x38,                    //     USAGE (Wheel)

    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)

    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)

    0x75, 0x08,                    //     REPORT_SIZE (8)

    0x95, 0x03,                    //     REPORT_COUNT (3)

    0x81, 0x06,                    //     INPUT (Data,Var,Rel)

    //LABZ_DEBUG_Start

     0x05, 0x0c,        //         USAGE_PAGE (Consumer Devices)

     0x0a, 0x38, 0x02,  //         USAGE (AC Pan)

     0x15, 0x81,        //         LOGICAL_MINIMUM (-127)

     0x25, 0x7f,        //         LOGICAL_MAXIMUM (127)

     0x75, 0x08,        //         REPORT_SIZE (8)

     0x95, 0x01,        //         REPORT_COUNT (1)   

     0x81, 0x06,        //         INPUT (Data,Var,Rel)

    //LABZ_DEBUG_End

    0xc0,                          //   END_COLLECTION

    0xc0,                          // END_COLLECTION

};

 

当然,做了上面的修改之后,每次发出的消息不再是4个Bytes(Button,X,Y,Wheel),而多了一个。相应的下面的函数也要进行修改,多发出一个 Pan值。

void Mouse_::move(signed char x, signed char y, signed char wheel,signed char pan)

{

               uint8_t m[5];

               m[0] = _buttons;

               m[1] = x;

               m[2] = y;

               m[3] = wheel;

        m[4] = pan;

               HID().SendReport(1,m,5);

}

 

 

修改好库之后,再编写一个测试代码,使用 Pin 7 8 拉低来发出 Pan -1 和 +1

 

#include <Mouse.h>



// set pin numbers for the five buttons:

const int upButton = 2;

const int downButton = 3;

const int leftButton = 4;

const int rightButton = 5;

const int mouseButton = 6;

const int pan1 = 7;

const int pan2 = 8;



int range = 5;              // output range of X or Y movement; affects movement speed

int responseDelay = 10;     // response delay of the mouse, in ms





void setup() {

  // initialize the buttons' inputs:

  pinMode(upButton, INPUT_PULLUP);

  pinMode(downButton, INPUT_PULLUP);

  pinMode(leftButton, INPUT_PULLUP);

  pinMode(rightButton, INPUT_PULLUP);

  pinMode(mouseButton, INPUT_PULLUP);

  pinMode(pan1, INPUT_PULLUP);

  pinMode(pan2, INPUT_PULLUP);

  // initialize mouse control:

  Mouse.begin();

}



void loop() {

  // read the buttons:

  int upState = digitalRead(upButton);

  int downState = digitalRead(downButton);

  int rightState = digitalRead(rightButton);

  int leftState = digitalRead(leftButton);

  int clickState = digitalRead(mouseButton);



  // calculate the movement distance based on the button states:

  int  xDistance = (leftState - rightState) * range;

  int  yDistance = (upState - downState) * range;



  // if X or Y is non-zero, move:

  if ((xDistance != 0) || (yDistance != 0)) {

    Mouse.move(xDistance, yDistance, 0,0);

  }



  if(digitalRead(pan1)==LOW) {

    Mouse.move(xDistance, yDistance, 0,1);

    Mouse.move(0, 0, 0,0);

    }

  if(digitalRead(pan2)==LOW) {

    Mouse.move(xDistance, yDistance, 0,-1);

    Mouse.move(0, 0, 0,0);

    }



  // if the mouse button is pressed:

  if (clickState == HIGH) {

    // if the mouse is not pressed, press it:

    if (!Mouse.isPressed(MOUSE_LEFT)) {

      Mouse.press(MOUSE_LEFT);

    }

  }

  // else the mouse button is not pressed:

  else {

    // if the mouse is pressed, release it:

    if (Mouse.isPressed(MOUSE_LEFT)) {

      Mouse.release(MOUSE_LEFT);

    }

  }



  // a delay so the mouse doesn't move too fast:

  delay(responseDelay);

}

 

最终的实验表明, AC Pan 是类似水平方向的滚轮,在使用 Excel 这样的软件经常需要水平方向的滚动用这个功能会很方便。

修改后的完整库下载:

Mouse

 

Step to UEFI (144)CryptoPkg 的使用

UDK2017 提供了一个加密解密库,在\UDK2017\CryptoPkg下面,本文介绍如何配置让这个 Package 能够使用。

crypto

具体配置方法可以在\UDK2017\CryptoPkg\Library\OpensslLib\OpenSSL-HOWTO.txt文件中看到,简单的说就是要去https://www.openssl.org/source/ 下载一套 OpenSll 的Source Code,加在目录中。虽然稳重说明要最新的版本,但是根据我的实验最新版本编译不过(有文件找不到,应该是不同的版本之间架构存在比较大的差别导致的)。经过实验,https://www.openssl.org/source/snapshot/ 下面的openssl-1.1.0-stable-SNAP-20180129.tar.gz 是可以使用的。
安装方法是:解压下载的文件,然后将全部内容解压到UDK2017\CryptoPkg\Library\OpensslLib 目录下的 Openssl 目录中:

crypto2

之后就可以按照正常编译 Package的方法进行编译
build -a X64 -p CryptoPkg\CryptoPkg.dsc
在这个Package中自带了一个测试的 Application,这是一个简单的自检程序,编译之后可以在Nt32环境下运行,结果如下:

crypto3

运行这个 Application 能够表明 CryptPkg 工作正常,对于实现 UEFI 下面加密解密有兴趣的朋友可以更深入的进行研究。

Arduino 控制USB设备(9) FTDI串口

最近又开始玩 USB Host 模块,尝试 FTDI 的 USB 转串口。在实验之前,你需要确定手上的是 FTDI 的芯片,在设备管理器中可以简单的判断:

fdti

在 USB Host 库中,给出了一个 FTDI 的例子,这个例子实现的是 LoopBack 的功能:

#include <cdcftdi.h>
#include <usbhub.h>

#include "pgmstrings.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

class FTDIAsync : public FTDIAsyncOper
{
public:
    uint8_t OnInit(FTDI *pftdi);
};

uint8_t FTDIAsync::OnInit(FTDI *pftdi)
{
    uint8_t rcode = 0;

    rcode = pftdi->SetBaudRate(115200);

    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetBaudRate"), rcode);
        return rcode;
    }
    rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL);

    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetFlowControl"), rcode);

    return rcode;
}

USB              Usb;
//USBHub         Hub(&Usb);
FTDIAsync        FtdiAsync;
FTDI             Ftdi(&Usb, &FtdiAsync);

uint32_t next_time;

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  next_time = millis() + 5000;
}

void loop()
{
    Usb.Task();

    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )
    {
        uint8_t  rcode;
        char strbuf[] = "DEADBEEF";
        //char strbuf[] = "The quick brown fox jumps over the lazy dog";
        //char strbuf[] = "This string contains 61 character to demonstrate FTDI buffers"; //add one symbol to it to see some garbage
        Serial.print(".");

        rcode = Ftdi.SndData(strlen(strbuf), (uint8_t*)strbuf);

	if (rcode)
            ErrorMessage<uint8_t>(PSTR("SndData"), rcode);

        delay(50);

        uint8_t  buf[64];

        for (uint8_t i=0; i<64; i++)
            buf[i] = 0;

        uint16_t rcvd = 64;
        rcode = Ftdi.RcvData(&rcvd, buf);

        if (rcode && rcode != hrNAK)
            ErrorMessage<uint8_t>(PSTR("Ret"), rcode);

        // The device reserves the first two bytes of data
        //   to contain the current values of the modem and line status registers.
        if (rcvd > 2)
            Serial.print((char*)(buf+2));

        delay(10);
    }
}

 

使用的时候,需要将模块的 RX 和 TX 接到一起:

ftdt

运行结果:

ftdiloop

接下来试试实现接收,将2个USB 串口接在一起,RX/TX交叉,GND也要接在一起

ft3

最好先在 PC 上确定连接正确能够正常收发:

ftdi1

接收的代码如下:

#include <cdcftdi.h>
#include <usbhub.h>

#include "pgmstrings.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

class FTDIAsync : public FTDIAsyncOper
{
public:
    uint8_t OnInit(FTDI *pftdi);
};

uint8_t FTDIAsync::OnInit(FTDI *pftdi)
{
    uint8_t rcode = 0;

    rcode = pftdi->SetBaudRate(115200);

    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetBaudRate"), rcode);
        return rcode;
    }
    rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL);

    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetFlowControl"), rcode);

    return rcode;
}

USB              Usb;
//USBHub         Hub(&Usb);
FTDIAsync        FtdiAsync;
FTDI             Ftdi(&Usb, &FtdiAsync);

uint32_t next_time;

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  next_time = millis() + 5000;
}

void loop()
{
    Usb.Task();

    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )
    {
        uint8_t  rcode;
        uint8_t  buf[64];

        for (uint8_t i=0; i<64; i++)
            buf[i] = 0;

        uint16_t rcvd = 64;
        rcode = Ftdi.RcvData(&rcvd, buf);

        if (rcode && rcode != hrNAK)
            ErrorMessage<uint8_t>(PSTR("Ret"), rcode);

        if (rcvd>2) {
          for (int Index=0;Index<rcvd;Index++) {
              Serial.print(buf[Index],HEX);
              Serial.print(" ");
          }
        }  
    }
}

 

最后,从PC端可以发送,在 Arduino 端可以正常解析出来

fdt3