283 lines
11 KiB
C
283 lines
11 KiB
C
|
/* getvaluetest - check that the bitness/endianness dependent conversions
|
||
|
* of representation of numeric types between wire protocols and different
|
||
|
* CPU computations produce expected results.
|
||
|
*
|
||
|
* See also:
|
||
|
* https://github.com/networkupstools/nut/pull/1055
|
||
|
* https://github.com/networkupstools/nut/pull/1040
|
||
|
* https://github.com/networkupstools/nut/pull/1024
|
||
|
* https://github.com/networkupstools/nut/issues/1023
|
||
|
*
|
||
|
* Copyright (C)
|
||
|
* 2021 Nick Briggs <nicholas.h.briggs@gmail.com>
|
||
|
* 2022 Jim Klimov <jimklimov+nut@gmail.com>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "hidtypes.h"
|
||
|
#include "usb-common.h"
|
||
|
#include "common.h"
|
||
|
|
||
|
void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue);
|
||
|
|
||
|
static void Usage(char *name) {
|
||
|
printf("%s [<buf> <offset> <size> <min> <max> <expect>]\n", name);
|
||
|
printf(" <buf> - string of hex digit pairs, space separated\n");
|
||
|
printf(" <offset> - offset of the report item value in bits, typically 0..31\n");
|
||
|
printf(" <size> - size of the report item value in bits: typically 1..32\n");
|
||
|
printf(" <min> - logical minimum value for report item\n");
|
||
|
printf(" <max> - logical maximum value for report item\n");
|
||
|
printf(" <expect> - expected value\n");
|
||
|
printf("\n");
|
||
|
printf("%s \"0c 64 11 0d\" 8 16 0 65535 3345\n", name);
|
||
|
printf("\nIf no arguments are given a builtin set of tests are run.\n");
|
||
|
}
|
||
|
|
||
|
static void PrintBufAndData(uint8_t *buf, size_t bufSize, HIDData_t *pData) {
|
||
|
size_t i;
|
||
|
|
||
|
printf("buf \"");
|
||
|
for (i = 0; i < bufSize - 1; i++) {
|
||
|
printf("%02x ", buf[i]);
|
||
|
}
|
||
|
printf("%02x\"", buf[bufSize - 1]);
|
||
|
printf(" offset %u size %u logmin %ld (0x%lx) logmax %ld (0x%lx)",
|
||
|
pData->Offset, pData->Size, pData->LogMin, pData->LogMin, pData->LogMax, pData->LogMax);
|
||
|
}
|
||
|
|
||
|
static int RunBuiltInTests(char *argv[]) {
|
||
|
NUT_UNUSED_VARIABLE(argv);
|
||
|
|
||
|
int exitStatus = 0;
|
||
|
size_t i;
|
||
|
char *next;
|
||
|
uint8_t reportBuf[64];
|
||
|
size_t bufSize;
|
||
|
HIDData_t data;
|
||
|
long value;
|
||
|
|
||
|
static struct {
|
||
|
char *buf; /* item data, starts with report id byte, then remaining report bytes */
|
||
|
uint8_t Offset; /* item offset in bits, typically 0..31 */
|
||
|
uint8_t Size; /* item size in bits, typically 1..32 */
|
||
|
long LogMin, LogMax; /* logical minimum and maximum values */
|
||
|
long expectedValue; /* the expected result of decoding the value in the buffer */
|
||
|
} testData[] = {
|
||
|
{.buf = "00 ff ff ff ff", .Offset = 0, .Size = 32, .LogMin = -1, .LogMax = 2147483647, .expectedValue = -1},
|
||
|
{.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = -1, .LogMax = 127, .expectedValue = -1},
|
||
|
{.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 127, .expectedValue = 127},
|
||
|
{.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 255},
|
||
|
{.buf = "33 00 0a 08 80", .Offset = 0, .Size = 32, .LogMin = 0, .LogMax = 65535, .expectedValue = 2560},
|
||
|
{.buf = "00 00 08 00 00", .Offset = 0, .Size = 32, .LogMin = 0, .LogMax = 65535, .expectedValue = 2048},
|
||
|
{.buf = "06 00 00 08", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 0},
|
||
|
{.buf = "06 00 00 08", .Offset = 8, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 0},
|
||
|
{.buf = "06 00 00 08", .Offset = 16, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 8},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 0, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 1, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 2, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 1},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 3, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 1},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 4, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 5, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 6, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 7, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 8, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 9, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0},
|
||
|
{.buf = "16 0c 00 00 00", .Offset = 10, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}
|
||
|
};
|
||
|
|
||
|
for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++) {
|
||
|
next = testData[i].buf;
|
||
|
for (bufSize = 0; *next != 0; bufSize++) {
|
||
|
reportBuf[bufSize] = (uint8_t) strtol(next, (char **)&next, 16);
|
||
|
}
|
||
|
memset((void *)&data, 0, sizeof(data));
|
||
|
data.Offset = testData[i].Offset;
|
||
|
data.Size = testData[i].Size;
|
||
|
data.LogMin = testData[i].LogMin;
|
||
|
data.LogMax = testData[i].LogMax;
|
||
|
|
||
|
GetValue(reportBuf, &data, &value);
|
||
|
|
||
|
printf("Test #%zd ", i + 1);
|
||
|
PrintBufAndData(reportBuf, bufSize, &data);
|
||
|
if (value == testData[i].expectedValue) {
|
||
|
printf(" value %ld PASS\n", value);
|
||
|
} else {
|
||
|
printf(" value %ld FAIL expected %ld\n", value, testData[i].expectedValue);
|
||
|
exitStatus = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Emulate rdlen calculations in libusb{0,1}.c or
|
||
|
* langid calculations in nutdrv_qx.c; in these
|
||
|
* cases we take two bytes (cast from usb_ctrl_char
|
||
|
* type, may be signed depending on used API version)
|
||
|
* from the protocol buffer, and build a platform
|
||
|
* dependent representation of a two-byte word.
|
||
|
*/
|
||
|
usb_ctrl_char bufC[2];
|
||
|
signed char bufS[2];
|
||
|
unsigned char bufU[2];
|
||
|
int rdlen;
|
||
|
|
||
|
/* Example from issue https://github.com/networkupstools/nut/issues/1261
|
||
|
* where resulting length 0x01a9 should be "425" but ended up "-87" */
|
||
|
bufC[0] = (usb_ctrl_char)0xa9;
|
||
|
bufC[1] = (usb_ctrl_char)0x01;
|
||
|
|
||
|
bufS[0] = (signed char)0xa9;
|
||
|
bufS[1] = (signed char)0x01;
|
||
|
|
||
|
bufU[0] = (unsigned char)0xa9;
|
||
|
bufU[1] = (unsigned char)0x01;
|
||
|
|
||
|
/* Check different conversion methods and hope current build CPU,
|
||
|
* C implementation etc. do not mess up bit-shifting vs rotation,
|
||
|
* zeroing high bits, int type width extension et al. If something
|
||
|
* is mismatched below, the production NUT code may need adaptations
|
||
|
* for that platform to count stuff correctly!
|
||
|
*/
|
||
|
printf("\nTesting bit-shifting approaches used in codebase to get 2-byte int lengths from wire bytes:\n");
|
||
|
printf("(Expected correct value is '425', incorrect '-87' or other)\n");
|
||
|
|
||
|
#define REPORT_VERDICT(expected) { if (expected) { printf(" - PASS\n"); } else { printf(" - FAIL\n"); exitStatus = 1; } }
|
||
|
|
||
|
rdlen = bufC[0] | (bufC[1] << 8);
|
||
|
printf(" * reference: no casting, usb_ctrl_char :\t%d\t(depends on libusb API built against)", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425 || rdlen == -87)
|
||
|
|
||
|
rdlen = bufS[0] | (bufS[1] << 8);
|
||
|
printf(" * reference: no casting, signed char :\t%d\t(expected '-87' here)", rdlen);
|
||
|
REPORT_VERDICT (rdlen == -87)
|
||
|
|
||
|
rdlen = bufU[0] | (bufU[1] << 8);
|
||
|
printf(" * reference: no casting, unsigned char :\t%d\t(expected '425')", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
|
||
|
rdlen = (uint8_t)bufC[0] | ((uint8_t)bufC[1] << 8);
|
||
|
printf(" * uint8_t casting, usb_ctrl_char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = (uint8_t)bufS[0] | ((uint8_t)bufS[1] << 8);
|
||
|
printf(" * uint8_t casting, signed char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = (uint8_t)bufU[0] | ((uint8_t)bufU[1] << 8);
|
||
|
printf(" * uint8_t casting, unsigned char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
|
||
|
rdlen = ((uint8_t)bufC[0]) | (((uint8_t)bufC[1]) << 8);
|
||
|
printf(" * uint8_t casting with parentheses, usb_ctrl_char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = ((uint8_t)bufS[0]) | (((uint8_t)bufS[1]) << 8);
|
||
|
printf(" * uint8_t casting with parentheses, signed char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = ((uint8_t)bufU[0]) | (((uint8_t)bufU[1]) << 8);
|
||
|
printf(" * uint8_t casting with parentheses, unsigned char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
|
||
|
rdlen = ((uint16_t)(bufC[0]) & 0x00FF) | (((uint16_t)(bufC[1]) & 0x00FF) << 8);
|
||
|
printf(" * uint16_t casting with 8-bit mask, usb_ctrl_char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = ((uint16_t)(bufS[0]) & 0x00FF) | (((uint16_t)(bufS[1]) & 0x00FF) << 8);
|
||
|
printf(" * uint16_t casting with 8-bit mask, signed char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = ((uint16_t)(bufU[0]) & 0x00FF) | (((uint16_t)(bufU[1]) & 0x00FF) << 8);
|
||
|
printf(" * uint16_t casting with 8-bit mask, unsigned char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
|
||
|
rdlen = 256 * (uint8_t)(bufC[1]) + (uint8_t)(bufC[0]);
|
||
|
printf(" * uint8_t casting with multiplication, usb_ctrl_char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = 256 * (uint8_t)(bufS[1]) + (uint8_t)(bufS[0]);
|
||
|
printf(" * uint8_t casting with multiplication, signed char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
rdlen = 256 * (uint8_t)(bufU[1]) + (uint8_t)(bufU[0]);
|
||
|
printf(" * uint8_t casting with multiplication, unsigned char :\t%d", rdlen);
|
||
|
REPORT_VERDICT (rdlen == 425)
|
||
|
|
||
|
|
||
|
return (exitStatus);
|
||
|
}
|
||
|
|
||
|
static int RunCommandLineTest(char *argv[]) {
|
||
|
uint8_t reportBuf[64];
|
||
|
size_t bufSize;
|
||
|
char *start, *end;
|
||
|
HIDData_t data;
|
||
|
long value, expectedValue;
|
||
|
|
||
|
start = argv[1];
|
||
|
end = NULL;
|
||
|
for (bufSize = 0; *start != 0; bufSize++) {
|
||
|
reportBuf[bufSize] = (uint8_t) strtol(start, (char **)&end, 16);
|
||
|
if (start == end) break;
|
||
|
start = end;
|
||
|
}
|
||
|
memset((void *)&data, 0, sizeof(data));
|
||
|
data.Offset = (uint8_t) atoi(argv[2]);
|
||
|
data.Size = (uint8_t) atoi(argv[3]);
|
||
|
data.LogMin = strtol(argv[4], 0, 0);
|
||
|
data.LogMax = strtol(argv[5], 0, 0);
|
||
|
expectedValue = strtol(argv[6], 0, 0);
|
||
|
|
||
|
GetValue(reportBuf, &data, &value);
|
||
|
|
||
|
printf("Test #0 ");
|
||
|
PrintBufAndData(reportBuf, bufSize, &data);
|
||
|
if (value == expectedValue) {
|
||
|
printf(" value %ld PASS\n", value);
|
||
|
return (0);
|
||
|
} else {
|
||
|
printf(" value %ld FAIL expected %ld\n", value, expectedValue);
|
||
|
return (1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[]) {
|
||
|
int status;
|
||
|
|
||
|
switch (argc) {
|
||
|
case 1:
|
||
|
status = RunBuiltInTests(argv);
|
||
|
break;
|
||
|
case 7:
|
||
|
status = RunCommandLineTest(argv);
|
||
|
break;
|
||
|
default:
|
||
|
Usage(argv[0]);
|
||
|
status = 2;
|
||
|
}
|
||
|
return(status);
|
||
|
}
|