X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fhardware%2Fkorad-kaxxxxp%2Fapi.c;h=d04152af55bfb18de2851b8039a34fc97bd02784;hb=d32120c4c30650c30720d04eaf88dcf7b76a1e9f;hp=366aaabe49f728eb24873424b0f00428ee9843e0;hpb=551024f26f62cdea8eef1b787fbef31e1b58c466;p=libsigrok.git diff --git a/src/hardware/korad-kaxxxxp/api.c b/src/hardware/korad-kaxxxxp/api.c index 366aaabe..d04152af 100644 --- a/src/hardware/korad-kaxxxxp/api.c +++ b/src/hardware/korad-kaxxxxp/api.c @@ -19,6 +19,9 @@ */ #include + +#include + #include "protocol.h" static const uint32_t scanopts[] = { @@ -54,34 +57,23 @@ static const double amps_5[] = { 0, 5.1, 0.001, }; static const struct korad_kaxxxxp_model models[] = { /* Vendor, model name, ID reply, channels, voltage, current, quirks. */ - {"Korad", "KA3005P", "KORADKA3005PV2.0", - 1, volts_30, amps_5, KORAD_QUIRK_ID_TRAILING}, - /* Some KA3005P have extra bytes after the ID text. */ - {"Korad", "KA3005P", "KORADKA3005PV2.0\x01", 1, volts_30, amps_5, 0}, - {"Korad", "KA3005P", "KORADKA3005PV2.0\xBC", 1, volts_30, amps_5, 0}, - {"Korad", "KA3005P", "KORAD KA3005P V4.2", 1, volts_30, amps_5, 0}, - {"Korad", "KA3005P", "KORAD KA3005P V5.5", 1, volts_30, amps_5, 0}, - {"Korad", "KD3005P", "KORAD KD3005P V2.0", 1, volts_30, amps_5, 0}, - {"Korad", "KD3005P", "KORADKD3005PV2.0", 1, volts_30, amps_5, 0}, - {"Korad", "KD3005P", "KORADKD3005PV2.1", 1, volts_30, amps_5, 0}, - {"Korad", "KD3005P", "KORAD KD3005P V4.1", 1, volts_30, amps_5, 0}, - {"Korad", "KD3005P", "KORAD KD3005P V6.8", 1, volts_30, amps_5, 0}, - {"Korad", "KD6005P", "KORAD KD6005P V2.2", 1, volts_60, amps_5, 0}, - {"RND", "KA3005P", "RND 320-KA3005P V5.5", 1, volts_30, amps_5, 0}, - {"RND", "KD3005P", "RND 320-KD3005P V4.2", 1, volts_30, amps_5, 0}, - {"RND", "KA3005P", "RND 320-KA3005P V2.0", 1, volts_30, amps_5, 0}, - {"Stamos Soldering", "S-LS-31", "S-LS-31 V2.0", - 1, volts_30, amps_5, KORAD_QUIRK_ID_NO_VENDOR}, - {"Tenma", "72-2535", "TENMA 72-2535 V2.1", 1, volts_30, amps_3, 0}, - {"Tenma", "72-2540", "TENMA72-2540V2.0", 1, volts_30, amps_5, 0}, - {"Tenma", "72-2540", "TENMA 72-2540 V2.1", 1, volts_30, amps_5, 0}, - {"Tenma", "72-2540", "TENMA 72-2540 V5.2", 1, volts_30, amps_5, 0}, - {"Tenma", "72-2550", "TENMA72-2550V2.0", 1, volts_60, amps_3, 0}, - {"Tenma", "72-2710", "TENMA 72-2710 V6.6", 1, volts_30, amps_5, 0}, - {"Velleman", "LABPS3005D", "VELLEMANLABPS3005DV2.0", - 1, volts_30, amps_5, KORAD_QUIRK_LABPS_OVP_EN}, - {"Velleman", "PS3005D", "VELLEMANPS3005DV2.0", - 1, volts_30, amps_5, 0}, + {"Korad", "KA3005P", "", 1, volts_30, amps_5, + KORAD_QUIRK_ID_TRAILING}, + {"Korad", "KD3005P", "", 1, volts_30, amps_5, 0}, + {"Korad", "KD6005P", "", 1, volts_60, amps_5, 0}, + {"RND", "KA3005P", "RND 320-KA3005P", 1, volts_30, amps_5, + KORAD_QUIRK_ID_OPT_VERSION}, + {"RND", "KD3005P", "RND 320-KD3005P", 1, volts_30, amps_5, + KORAD_QUIRK_ID_OPT_VERSION}, + {"Stamos Soldering", "S-LS-31", "", 1, volts_30, amps_5, + KORAD_QUIRK_ID_NO_VENDOR}, + {"Tenma", "72-2535", "", 1, volts_30, amps_3, 0}, + {"Tenma", "72-2540", "", 1, volts_30, amps_5, 0}, + {"Tenma", "72-2550", "", 1, volts_60, amps_3, 0}, + {"Tenma", "72-2710", "", 1, volts_30, amps_5, 0}, + {"Velleman", "LABPS3005D", "", 1, volts_30, amps_5, + KORAD_QUIRK_LABPS_OVP_EN}, + {"Velleman", "PS3005D", "", 1, volts_30, amps_5, 0}, ALL_ZERO }; @@ -101,8 +93,106 @@ static const size_t id_text_buffer_size = 48; static gboolean model_matches(const struct korad_kaxxxxp_model *model, const char *id_text) { - /* TODO Implement more versatile ID response text checks. */ - return g_strcmp0(model->id, id_text) == 0; + gboolean matches; + gboolean opt_version, skip_vendor, accept_trail; + const char *want; + + if (!model) + return FALSE; + + /* + * When the models[] entry contains a specific response text, + * then expect to see this very text in literal form. This + * lets the driver map weird and untypical responses to a + * specific set of display texts for vendor and model names. + * Accept an optionally trailing version if models[] says so. + */ + if (model->id && model->id[0]) { + opt_version = model->quirks & KORAD_QUIRK_ID_OPT_VERSION; + if (!opt_version) { + matches = g_strcmp0(id_text, model->id) == 0; + if (!matches) + return FALSE; + sr_dbg("Matches expected ID text: '%s'.", model->id); + return TRUE; + } + matches = g_str_has_prefix(id_text, model->id); + if (!matches) + return FALSE; + id_text += strlen(model->id); + while (isspace((int)*id_text)) + id_text++; + if (*id_text == 'V') { + id_text++; + while (*id_text == '.' || isdigit((int)*id_text)) + id_text++; + while (isspace((int)*id_text)) + id_text++; + } + if (*id_text) + return FALSE; + sr_dbg("Matches expected ID text [vers]: '%s'.", model->id); + return TRUE; + } + + /* + * A more generic approach which covers most devices: Check + * for the very vendor and model names which also are shown + * to users (the display texts). Weakened to match responses + * more widely: Case insensitive checks, optional whitespace + * in responses, optional version details. Optional trailing + * garbage. Optional omission of the vendor name. Shall match + * all the devices which were individually listed in earlier + * implementations of the driver, and shall also match firmware + * versions that were not listed before. + */ + skip_vendor = model->quirks & KORAD_QUIRK_ID_NO_VENDOR; + accept_trail = model->quirks & KORAD_QUIRK_ID_TRAILING; + if (!skip_vendor) { + want = model->vendor; + matches = g_ascii_strncasecmp(id_text, want, strlen(want)) == 0; + if (!matches) + return FALSE; + id_text += strlen(want); + while (isspace((int)*id_text)) + id_text++; + } + want = model->name; + matches = g_ascii_strncasecmp(id_text, want, strlen(want)) == 0; + if (!matches) + return FALSE; + id_text += strlen(want); + while (isspace((int)*id_text)) + id_text++; + if (*id_text == 'V') { + /* TODO Isolate and (also) return version details? */ + id_text++; + while (*id_text == '.' || isdigit((int)*id_text)) + id_text++; + while (isspace((int)*id_text)) + id_text++; + } + if (accept_trail) { + /* + * TODO Determine how many non-printables to accept here + * and how strict to check for "known" garbage variants. + */ + switch (*id_text) { + case '\x01': + case '\xbc': + id_text++; + break; + case '\x00': + /* EMPTY */ + break; + default: + return FALSE; + } + } + if (*id_text) + return FALSE; + sr_dbg("Matches generic '[vendor] model [vers] [trail]' pattern."); + return TRUE; } static GSList *scan(struct sr_dev_driver *di, GSList *options) @@ -182,16 +272,16 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) } model = NULL; - for (i = 0; models[i].id; i++) { + for (i = 0; models[i].name; i++) { if (!model_matches(&models[i], reply)) continue; model = &models[i]; break; } if (!model && force_detect) { - sr_warn("Found model ID '%s' is unknown, trying '%s' spec.", + sr_warn("Found unknown model ID '%s', trying '%s' spec.", reply, force_detect); - for (i = 0; models[i].id; i++) { + for (i = 0; models[i].name; i++) { if (!model_matches(&models[i], force_detect)) continue; sr_info("Found replacement, using it instead."); @@ -200,11 +290,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) } } if (!model) { - sr_err("Unknown model ID '%s' detected, aborting.", reply); + sr_err("Found unknown model ID '%s', aborting.", reply); return NULL; } - sr_dbg("Found: %s %s (idx %zu, ID '%s').", model->vendor, model->name, - model - &models[0], model->id); + sr_dbg("Found: %s %s (idx %zu).", model->vendor, model->name, + model - &models[0]); sdi = g_malloc0(sizeof(struct sr_dev_inst)); sdi->status = SR_ST_INACTIVE;