From b30406873c81ebd922f805747a58aa4ffdce2ed5 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sat, 27 Apr 2019 12:16:13 +0300 Subject: [PATCH] sanei_usb: Add support for record testing mode --- backend/dll.c | 52 +++++++++++++++++++++------ include/sane/sanei_usb.h | 8 +++-- sanei/sanei_usb.c | 78 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 20 deletions(-) diff --git a/backend/dll.c b/backend/dll.c index bd0a7f8eb..926a08064 100644 --- a/backend/dll.c +++ b/backend/dll.c @@ -1180,7 +1180,7 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) dev_name = strchr (full_name, ':'); - int is_fakeusb = 0, is_fakeusbdev = 0; + int is_fakeusb = 0, is_fakeusbdev = 0, is_fakeusbout = 0; if (dev_name) { @@ -1188,6 +1188,8 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) dev_name - full_name == 7; is_fakeusbdev = strncmp(full_name, "fakeusbdev", dev_name - full_name) == 0 && dev_name - full_name == 10; + is_fakeusbout = strncmp(full_name, "fakeusbout", dev_name - full_name) == 0 && + dev_name - full_name == 10; } if (is_fakeusb || is_fakeusbdev) @@ -1204,18 +1206,46 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) return SANE_STATUS_ACCESS_DENIED; } } - else if (dev_name) - { - be_name = strndup(full_name, dev_name - full_name); - ++dev_name; /* skip colon */ - } else { - /* if no colon interpret full_name as the backend name; an empty - backend device name will cause us to open the first device of - that backend. */ - be_name = strdup(full_name); - dev_name = ""; + char* fakeusbout_path = NULL; + if (is_fakeusbout) + { + ++dev_name; // skip colon + + const char* path_end = strchr(dev_name, ':'); + if (path_end == NULL) + { + DBG (0, "%s: the device name does not contain path\n", __func__); + return SANE_STATUS_INVAL; + } + fakeusbout_path = strndup(dev_name, path_end - dev_name); + + full_name = path_end + 1; // skip colon + dev_name = strchr(full_name, ':'); + } + + if (dev_name) + { + be_name = strndup(full_name, dev_name - full_name); + ++dev_name; /* skip colon */ + } + else + { + /* if no colon interpret full_name as the backend name; an empty + backend device name will cause us to open the first device of + that backend. */ + be_name = strdup(full_name); + dev_name = ""; + } + + if (is_fakeusbout) + { + status = sanei_usb_testing_enable_record(fakeusbout_path, be_name); + free(fakeusbout_path); + if (status != SANE_STATUS_GOOD) + return status; + } } if (!be_name) diff --git a/include/sane/sanei_usb.h b/include/sane/sanei_usb.h index 2f2e87563..63820ba2a 100644 --- a/include/sane/sanei_usb.h +++ b/include/sane/sanei_usb.h @@ -205,10 +205,12 @@ extern SANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path, * * Initializes sanei_usb for recording communication with the scanner. This * function must be called before sanei_usb_init(). -* -* @param path Path to the XML data file. + * + * @param path Path to the XML data file. + * @param be_name The name of the backend to enable recording for. */ -extern SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path); +extern SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, + SANE_String_Const be_name); /** Returns backend name for testing. * diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index c0a130b10..f4a95eaa3 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -193,6 +193,7 @@ static sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled; static int testing_development_mode = 0; int testing_known_commands_input_failed = 0; unsigned testing_last_known_seq = 0; +SANE_String testing_record_backend = NULL; xmlNode* testing_append_commands_node = NULL; // XML file from which we read testing data @@ -516,10 +517,13 @@ void fail_test() { } -SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path) +SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, SANE_String_Const be_name) { - (void) path; - return SANE_STATUS_UNSUPPORTED; + testing_mode = sanei_usb_testing_mode_record; + testing_record_backend = strdup(be_name); + testing_xml_path = strdup(path); + + return SANE_STATUS_GOOD; } static xmlNode* sanei_xml_find_first_child_with_name(xmlNode* parent, @@ -985,7 +989,8 @@ static SANE_Status sanei_usb_testing_init() if (testing_mode == sanei_usb_testing_mode_record) { - return SANE_STATUS_UNSUPPORTED; + testing_xml_doc = xmlNewDoc((const xmlChar*)"1.0"); + return SANE_STATUS_GOOD; } if (device_number != 0) @@ -1137,8 +1142,13 @@ static SANE_Status sanei_usb_testing_init() static void sanei_usb_testing_exit() { - if (testing_development_mode) + if (testing_development_mode || testing_mode == sanei_usb_testing_mode_record) { + if (testing_mode == sanei_usb_testing_mode_record) + { + xmlAddNextSibling(testing_append_commands_node, xmlNewText((const xmlChar*)"\n ")); + free(testing_record_backend); + } xmlSaveFileEnc(testing_xml_path, testing_xml_doc, "UTF-8"); } xmlFreeDoc(testing_xml_doc); @@ -2087,6 +2097,62 @@ sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type) } } +static void sanei_usb_record_open(SANE_Int dn) +{ + xmlNode* e_root = xmlNewNode(NULL, (const xmlChar*) "device_capture"); + xmlDocSetRootElement(testing_xml_doc, e_root); + xmlNewProp(e_root, (const xmlChar*)"backend", (const xmlChar*) testing_record_backend); + + xmlNode* e_description = xmlNewChild(e_root, NULL, (const xmlChar*) "description", NULL); + sanei_xml_set_hex_attr(e_description, "id_vendor", devices[dn].vendor); + sanei_xml_set_hex_attr(e_description, "id_product", devices[dn].product); + + xmlNode* e_configurations = xmlNewChild(e_description, NULL, + (const xmlChar*) "configurations", NULL); + xmlNode* e_configuration = xmlNewChild(e_configurations, NULL, + (const xmlChar*) "configuration", NULL); + sanei_xml_set_uint_attr(e_configuration, "number", 1); + + xmlNode* e_interface = xmlNewChild(e_configuration, NULL, (const xmlChar*) "interface", NULL); + sanei_xml_set_uint_attr(e_interface, "number", devices[dn].interface_nr); + + struct endpoint_data_desc { + const char* transfer_type; + const char* direction; + SANE_Int ep_address; + }; + + struct endpoint_data_desc endpoints[8] = + { + { "BULK", "IN", devices[dn].bulk_in_ep }, + { "BULK", "OUT", devices[dn].bulk_out_ep }, + { "ISOCHRONOUS", "IN", devices[dn].iso_in_ep }, + { "ISOCHRONOUS", "OUT", devices[dn].iso_out_ep }, + { "INTERRUPT", "IN", devices[dn].int_in_ep }, + { "INTERRUPT", "OUT", devices[dn].int_out_ep }, + { "CONTROL", "IN", devices[dn].control_in_ep }, + { "CONTROL", "OUT", devices[dn].control_out_ep } + }; + + for (int i = 0; i < 8; ++i) + { + if (endpoints[i].ep_address) + { + xmlNode* e_endpoint = xmlNewChild(e_interface, NULL, (const xmlChar*)"endpoint", NULL); + xmlNewProp(e_endpoint, (const xmlChar*)"transfer_type", + (const xmlChar*) endpoints[i].transfer_type); + sanei_xml_set_uint_attr(e_endpoint, "number", endpoints[i].ep_address & 0x0f); + xmlNewProp(e_endpoint, (const xmlChar*)"direction", + (const xmlChar*) endpoints[i].direction); + sanei_xml_set_hex_attr(e_endpoint, "address", endpoints[i].ep_address); + } + } + xmlNode* e_transactions = xmlNewChild(e_root, NULL, (const xmlChar*)"transactions", NULL); + + // add an empty node so that we have something to append to + testing_append_commands_node = xmlAddChild(e_transactions, xmlNewText((const xmlChar*)""));; +} + SANE_Status sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) { @@ -2645,7 +2711,7 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) if (testing_mode == sanei_usb_testing_mode_record) { - // ZZTODO: record the USB state + sanei_usb_record_open(devcount); } devices[devcount].open = SANE_TRUE;