From patchwork Thu Mar 23 10:09:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Karol Nowak X-Patchwork-Id: 13185402 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 1BDF6C6FD1D for ; Thu, 23 Mar 2023 10:14:41 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pfHxY-00012k-U6; Thu, 23 Mar 2023 06:14:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pfHxW-00012H-1S for qemu-devel@nongnu.org; Thu, 23 Mar 2023 06:14:14 -0400 Received: from mail-db5eur02on2041.outbound.protection.outlook.com ([40.107.249.41] helo=EUR02-DB5-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pfHxS-0002O0-E0 for qemu-devel@nongnu.org; Thu, 23 Mar 2023 06:14:13 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=IXlHNlBLpaPesYq9lkROfj8xo5aocsrs8z3Kc+fWIUcqGFIVZVkRQdfyuKEEml2mYNoXpJabP6RxWGiX1F3wBOVP7ctK1Um1KM7Bj6eDP6pqHiko4JqKU1cfvNfwTkpwbgvLv7dCsEbJ15nOMpRlhzQE56ggQHTeyA7tOwGtgKkkSRIoS8mNH5XBkA6gp9dItkPg12G76g1A8wPPztMFonxRwozLgxXP8fyqwyPKMDYyWJlaD1UdaAvpnhvUkijxyvvK3LGe8ld/3/DC8dRwxEpOzkbhXL3dP7/Zv+nJj/ZQgTID+QoPJxLkRgK5SxQJVdl7pfPaaI5h/7NwuGiuzQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=fBAIfraWILHKxvY4LIqo/8S//xK0NHBtLtBjHY7vXT8=; b=YMs5E4WudLs57zgmt7rLa1P58LqWsRbvGsJC4KNuWhcN1U3yb9uR09DvNAZTC/wVlJDkcae2hSPSwX/Dl5lkQ+gjGGTl5ZopN2sb+E3GAi4TTJTUVnJcUiqtS/cY3byk98DgzB//RZgmJcMu5ebF58Z8bklVmGt9KtxUNKWYrJQFL5xFsUY/us9Z6pnPAu/g4N5nZogxzDDsTq9fnYfYTL9A9WkLD06sFRgAKbk58UhjJ8E+QxvRtblmwU0WTh027GOUHTJqDfYPbR1bVx7+/JTeUO+i4jSbCqxYQPCRMnK3xCrV5mZqMKrr3KiEvpjlMcqVI3nTmZqsOzemh4Hm/Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=spyro-soft.com; dmarc=pass action=none header.from=spyro-soft.com; dkim=pass header.d=spyro-soft.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spyro-soft.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=fBAIfraWILHKxvY4LIqo/8S//xK0NHBtLtBjHY7vXT8=; b=mVs9PhVebXcBErQkXifKS6GvhEujsE5vjWcBv0oF3fC03+qh6+3SsGWJQsdAsCh5fd0s+lVLqRnRfL6uuHBARUr0IJavQpm6yTjoZvEJgAjRNBIXfQpqgPp29XQK/OE2FMHGCf2sYHUdRqyTq4ohbydFcQlKQ6N1tRSM8ho5imUo865V93FGUM2kWQwB+n3TfyDxxNQyTCYwAn7qorvGGEpvrFKldQhQdQM/2Z3SYcOAgGKFMF5KRHQtsi6Ty5Sur5aFurP+/yG2AYooptp/3T4SmMxZZZ+MzS6V5K6SGh3a1d1/v8YEjrzgT2FsS0V0tZm0+W9FkTKak2AsQami0A== Received: from PAXP193MB1679.EURP193.PROD.OUTLOOK.COM (2603:10a6:102:1cd::15) by PR3P193MB0571.EURP193.PROD.OUTLOOK.COM (2603:10a6:102:24::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6178.38; Thu, 23 Mar 2023 10:09:02 +0000 Received: from PAXP193MB1679.EURP193.PROD.OUTLOOK.COM ([fe80::e1fe:74c1:9a18:2200]) by PAXP193MB1679.EURP193.PROD.OUTLOOK.COM ([fe80::e1fe:74c1:9a18:2200%9]) with mapi id 15.20.6178.038; Thu, 23 Mar 2023 10:09:02 +0000 From: Karol Nowak To: "qemu-devel@nongnu.org" CC: "philmd@linaro.org" , "clg@kaod.org" Subject: [RFC PATCH v1] hw/misc: add i2c slave device that passes i2c ops outside Thread-Topic: [RFC PATCH v1] hw/misc: add i2c slave device that passes i2c ops outside Thread-Index: AQHZXV7TZA/TT0nkrkqwOlsWBtSUpA== Date: Thu, 23 Mar 2023 10:09:02 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: msip_labels: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=spyro-soft.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: PAXP193MB1679:EE_|PR3P193MB0571:EE_ x-ms-office365-filtering-correlation-id: 7dc801cf-2024-46ad-0f9e-08db2b86a0cd x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: yPIMf3/1wqKMs1XBqXL3BA7fScS+XiH3y6GdhaVJgRs5CA0sKoMY0lo7JpSSim5MM9L0eGGYezCTqeuSdAGMH7RtahWajO5hNbwM0hK/C32sJW0Q5iwinDoqKSpkXHNpVBXeBRTW4FHafo5wZqFA2hkUMiByWfop4sySBCgiHuM758oJn/5msQtTuVmCE0A5Vr/1hi16DZMg7SnNIIgw6pocBtfCes3K2kYMUOQ2Rog9OACTaLxc4iZIVEDvrZnQxMUUm1rIcv637n4BgoyqdMQav0iCsjAmc1Y7FeWvO1O4iWWa+ZhECYLZ+cgjXAMuJw8AUqcmsNe+BHUYNJNvLyQGtM8nwc4K5bmr41atgiailzgEtx0od5Uk7Ae8VHf+9mDQSeWqDnKus83BjYLWLAbPAc6cygjn73ymP57y6KjytdXSZHDit+WQiYJC8gjhNGt8/BLdvIvTcKUI7uVr42NoEm8Tsb4NWSPZPV1ikVOPtjiHnE9rUbyz5mFeggZEansVw0qWQjJ63Hj7IXKNXdJWoWPE9EqTJLT4sdLM2gDnmuQyHI0cVXDJrcOv67SisXCrDFJxkPjw/i5kXZoykiRjJ8VQrvsOqoi2Zq2eCBS37g9xGHt1zY1K78hlSMgBBNKsW68LPJmjQwq8YbOteWqAJgsnE2KYqnpeMdxTkF4LbKeeDkk5YJK9pW1CLTomPmgVsFSgD8MmL+phvWbX3w== x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PAXP193MB1679.EURP193.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230025)(4636009)(39850400004)(366004)(346002)(376002)(396003)(136003)(451199018)(54906003)(19627405001)(41300700001)(86362001)(478600001)(8936002)(5660300002)(30864003)(33656002)(38070700005)(71200400001)(52536014)(66556008)(64756008)(66446008)(66476007)(7696005)(66946007)(6916009)(76116006)(91956017)(4326008)(8676002)(316002)(2906002)(122000001)(6506007)(38100700002)(26005)(9686003)(186003)(83380400001)(55016003)(66574015)(579004); DIR:OUT; SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?iso-8859-1?q?PR9+lf0sMnSbXrFu+//L6r7?= =?iso-8859-1?q?/A8Wnt1tdk4Y72V6XXNUd621nWTb6CvqvkdlKLZga2TuVE2R7x+6v5FS1hl9?= =?iso-8859-1?q?PhEgYdgk5onO7aQoBjJcL/CbN5gPMxnLxQae4sV/w1STrrWiTC3zusGnkIRj?= =?iso-8859-1?q?bW60r+x9CjfybcB793efAfAzi76AWADIhpY+oUXQV+NoysY9Rv1dswH6fRN5?= =?iso-8859-1?q?rInwjxlaWIq5tArkOouc/PJB+lPk+bz4x4UbBzQ5HzlgqF66LlmqaMGoDIqQ?= =?iso-8859-1?q?kWdnXZbajR+9x9Txm3bYHiZVchc51fqb5ftEOXcqB4n1ahD2m/7l+tcXaIKC?= =?iso-8859-1?q?rd9oPhXVIraca/ksx2tMoqtnEPhWG6Q+jQre52W+TmDkqawgttSlOHHK8e0b?= =?iso-8859-1?q?nQi6EGAmmlNqK8A/OrkqRxPpW1KwPo/hJUWuI3QdgsnLRFqtQkBt+jbgEm+w?= =?iso-8859-1?q?z28sirz0061DPdAkvASz+GwwmWnXGxRwCcaynv330d+vLZt4qFDeD7LAkPdy?= =?iso-8859-1?q?UIjAMik6W05Qx0il8Vfz6qWZq/fnRsKUJ9ccNc4vkltYP96vBGvleVS1g75l?= =?iso-8859-1?q?2Dpl7WI5f9fTOe/TOQvbZNrRY5OlHkqEKTy9jB4lS0hl5YkyfAr17KFsA0Ei?= =?iso-8859-1?q?xtgU2MF5/KfCp+vTQdWMC/HmULclDn66e4dXANFrXuFRGDqqfRjdq58lD5BM?= =?iso-8859-1?q?92tOoMPffANoDSIAA89NDes6RHtcygRx01VXDY+QyWi5j/pLoAEiCfWwDDPD?= =?iso-8859-1?q?48mlS1fqnwCFPySsJSO/JLqhIVSzoRcXt17EjWMXCWgYgrvcza6F2K8QejuS?= =?iso-8859-1?q?7f6xNoUS6OoWa7MnWlOKlu/CVMmFosV0kRvCdT3oe/Sprrm0vaw/SQoQ/7RW?= =?iso-8859-1?q?azaqsqdsHc4KO8BSZ0bWSBa46CV72YCu82hi6aB0vi9z1KKDn4Wg47Ap2rgj?= =?iso-8859-1?q?ygLc+pCDL5E9WHD3BZRAoeG2SIV10LjgVwvgxA2cAa3pvTm1o6YzRPyWNNt6?= =?iso-8859-1?q?eWoq/ZOFShzliIhAyKmu89h05Q26hQHtHbL8z2/zQUyNXCUnzTjYqjRdfyTZ?= =?iso-8859-1?q?KuKiwg0h29pSHNuBfNOzL8qJ45kzD0Oig+5WACZJe3jnNmjhF9ybWiphvaX8?= =?iso-8859-1?q?vh3nD2xDbXtf8YXdJq8VEwXUa7G79etkvjwZWVzj9xdrStSWdnSwjIRwpckn?= =?iso-8859-1?q?lixmVnWBmz+IddHrhVZg8ZzonZyzL31mmlFdLbCNG9JbCsN8vaSjjH5Rb96L?= =?iso-8859-1?q?OOLw/JnfpyCUX373m4cRSJbq38Trpy7q6IyHQ2rNYI11jwVo04fp58SJE9sP?= =?iso-8859-1?q?rySAJJGOAjkPA8XO8KksXiNu2WElPFRZa5yiXcEb975jgfYXv+Uf8C9J6sSh?= =?iso-8859-1?q?egWSDx+5LySWjcKa/hbfJhW+Drk77JsVS1RajXCxKvDWuOufkn9LzbURA0tk?= =?iso-8859-1?q?PLSaLbx3R+r9Kdf+DnV7xSEAPhwmLJX7gSyJWcCAXazLDaMCgQ4wC+gkL+Hd?= =?iso-8859-1?q?i1LRpuKG3dtWzOpoStDS715dD9cltE+alUjDYgylpHg7ZFR9Mdyhg6dL2IMh?= =?iso-8859-1?q?Tf6u9sv6jzl2lSm/lA7bRz2H9pfKSO0wazp6V//rPmL3wvwXbjH+fjMA=3D?= MIME-Version: 1.0 X-OriginatorOrg: spyro-soft.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: PAXP193MB1679.EURP193.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-Network-Message-Id: 7dc801cf-2024-46ad-0f9e-08db2b86a0cd X-MS-Exchange-CrossTenant-originalarrivaltime: 23 Mar 2023 10:09:02.6306 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: fbcab9a6-38ab-4bcd-ab3d-a468c8ee380c X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: xHQ0eAqXYjtIlvVU+W7whFGHaR8lEYVwY0lVKN0CFbvLoDkgwipgEXzuWOGuSLEN5KPkFRrEUrD6RKC3oT+wFQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PR3P193MB0571 Received-SPF: pass client-ip=40.107.249.41; envelope-from=knw@spyro-soft.com; helo=EUR02-DB5-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Hi, There is a feature I prepared which may be practical for some QEMU users. The feature provides a new I2C slave device that prepares a message depending what i2c-slave callback was called and sends it outside of QEMU through the character device to a client that receives that message, processes it and send back a response. Thanks to that feature, a user can emulate a logic of I2C device outside of QEMU. The message contains 3 bytes ended with CRLF: BBB\r\l Basically, the I2C slave does 4 steps in each i2c-slave callback: * encode * send * receive * decode I put more details in esp32_i2c_tcp_slave.c and also provided a demo client in python that uses TCP. The feature still needs some improvements, but the question is: * Do you find the feature useful? NOTE: The feature originally was prepared for espressif/qemu that's why there are references to esp32 Signed-off-by: Karol Nowak --- hw/misc/esp32_i2c_tcp_slave.c | 288 ++++++++++++++++++++++++++ include/hw/misc/esp32_i2c_tcp_slave.h | 19 ++ tests/i2c-tcp-demo/i2c-tcp-demo.py | 133 ++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 hw/misc/esp32_i2c_tcp_slave.c create mode 100644 include/hw/misc/esp32_i2c_tcp_slave.h create mode 100644 tests/i2c-tcp-demo/i2c-tcp-demo.py -- 2.34.1 Please consider the environment before printing this e-mail. diff --git a/hw/misc/esp32_i2c_tcp_slave.c b/hw/misc/esp32_i2c_tcp_slave.c new file mode 100644 index 0000000000..db3b6d366a --- /dev/null +++ b/hw/misc/esp32_i2c_tcp_slave.c @@ -0,0 +1,288 @@ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "hw/i2c/i2c.h" +#include "hw/irq.h" +#include "hw/misc/esp32_i2c_tcp_slave.h" +#include "qemu/module.h" + +#include "qapi/qmp/json-writer.h" +#include "chardev/char-fe.h" +#include "io/channel-socket.h" +#include "chardev/char-io.h" +#include "chardev/char-socket.h" +#include "qapi/error.h" + +/* + * Description: + * To allow to emulate a I2C slave device which is not supported by QEMU, + * a new I2C slave device was created that encapsulates I2C operations + * and passes them through a selected chardev to the host + * where a client resides that implements a logic of emulated device. + * + * + * Architecture: + * --------------------------- + * | QEMU | + * | | ----------------------- + * | ESP32 Firmware writes | | | + * | to I2C Slave | | I2C Slave Emulation | + * | | | | + * | -----------------------&---------&---- | + * | | I2C Slave at 0x7F & tcp & recv msg | + * | -----------------------&---------&---- process msg | + * | | | send respone | + * | | | | + * | | | | + * --------------------------- |---------------------- + * + * + * Syntax & protocol: + * QEMU I2C Slave sends a msg in following format: BBB\r\n + * where each 'B' represents a single byte 0-255 + * QEMU I2C Slave expects a respone message in the same format as fast as possible + * Example: + * req: 0x45 0x01 0x00 \r\n + * resp: 0x45 0x01 0x00 \r\n + * + * The format BBB\r\n + * first 'B' is a message type + * second 'B' is a data value + * third 'B' is an error value (not used at the moment) + * + * There are three types of message + * 'E' or 0x45 - Event: + * 'S' or 0x53 - Send: byte sent to emulated I2C Slave + * 'R' or 0x52 - Recv: byte to be received by I2C Master + * + * + * 'E' message + * second byte is an event type: + * 0x0: I2C_START_RECV + * 0x1: I2C_START_SEND + * 0x2: I2C_START_SEND_ASYNC + * 0x3: I2C_FINISH + * 0x4: I2C_NACK + * + * Example: + * 0x45 0x01 0x00 - start send + * 0x45 0x03 0x00 - finish + * + * In case of 'E' message, a response is the same as a request message + * + * 'S' message + * second byte is a byte transmitted from I2C Master to I2C slave device + * the byte to by processed by I2C Slave Device + * + * Example: + * 0x53 0x20 0x00 + * + * In case of 'S' message, a response is the same as a request message + * + * 'R' message + * the I2C Master expect a byte from the emulated i2c slave device + * A client has to modify the second byte of the request message + * and send it back as a response. + * + * Example: + * req: 0x52 0x00 0x00 + * resp: 0x52 0x11 0x00 + * + * + * Examples of Transmission: + * 1) i2cset -c 0x7F -r 0x20 0x11 0x22 0x33 0x44 0x55 + * req: 45 01 00 + * resp: 45 01 00 + * + * req: 53 20 00 + * resp: 53 20 00 + * + * req: 53 11 00 + * resp: 53 11 00 + * + * req: 53 22 00 + * resp: 53 22 00 + * + * req: 53 33 00 + * resp: 53 33 00 + * + * req: 53 44 00 + * resp: 53 44 00 + * + * req: 53 55 00 + * resp: 53 55 00 + * + * req: 45 03 00 + * resp: 45 03 00 + * + * 2) i2cget -c 0x7F -r 0x20 -l 0x03 + * req: 45 01 00 + * resp: 45 01 00 + * + * req: 53 20 00 + * resp: 53 20 00 + * + * req: 45 03 00 + * resp: 45 03 00 + * + * req: 45 00 00 + * resp: 45 00 00 + * + * req: 52 00 00 + * resp: 52 11 00 + * + * req: 52 00 00 + * resp: 52 22 00 + * + * req: 52 00 00 + * resp: 52 33 00 + * + * req: 45 03 00 + * resp: 45 03 00 + * + * + * To start i2c.socket server, set QEMU param: + * -chardev socket,port=16001,wait=no,host=localhost,server=on,ipv4=on,id=i2c.socket + * + * Simple demo I2C Slave Emulation in Python: + * tests/i2c-tcp-demo/i2c-tcp-demo.py + * + * Limitations: + * - there is no recv timeout which may lead to qemu hang + * + */ + +#define CHARDEV_NAME "i2c.socket" + +static Chardev *chardev; +static CharBackend char_backend; +static bool chardev_open; + +typedef struct { + uint8_t id; + uint8_t byte; + uint8_t err; +} packet; + +static int chr_can_receive(void *opaque) +{ + return CHR_READ_BUF_LEN; +} + +static void chr_event(void *opaque, QEMUChrEvent event) +{ + switch (event) { + case CHR_EVENT_OPENED: + qemu_log("connected\n"); + chardev_open = true; + break; + case CHR_EVENT_CLOSED: + qemu_log("disconnected\n"); + chardev_open = false; + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void send_packet(packet *p) +{ + static const char *PACKET_FMT = "%c%c%c\r\n"; + static char buff[32]; + + /* encode */ + int len = snprintf(buff, sizeof(buff), PACKET_FMT, p->id, p->byte, p->err); + + /* send */ + qemu_chr_fe_write_all(&char_backend, (uint8_t *)buff, len); + + /* receive */ + qemu_chr_fe_read_all(&char_backend, (uint8_t *)buff, len); + + /* decode */ + sscanf(buff, PACKET_FMT, &p->id, &p->byte, &p->err); +} + +static uint8_t slave_rx(I2CSlave *i2c) +{ + packet p = {.id = 'R', + .byte = 0, + .err = 0}; + + send_packet(&p); + + return p.byte; +} + +static int slave_tx(I2CSlave *i2c, uint8_t data) +{ + packet p = {.id = 'S', + .byte = data, + .err = 0}; + + send_packet(&p); + + return 0; +} + +static int slave_event(I2CSlave *i2c, enum i2c_event event) +{ + packet p = {.id = 'E', + .byte = event, + .err = 0}; + + send_packet(&p); + + return 0; +} + +static void slave_realize(DeviceState *dev, Error **errp) +{ +} + +static void slave_init(Object *obj) +{ + Error *err = NULL; + chardev = qemu_chr_find(CHARDEV_NAME); + if (!chardev) { + error_report("chardev '%s' not found", CHARDEV_NAME); + return; + } + + if (!qemu_chr_fe_init(&char_backend, chardev, &err)) { + error_report_err(err); + return; + } + + qemu_chr_fe_set_handlers(&char_backend, chr_can_receive, NULL, chr_event, + NULL, NULL, NULL, true); +} + +static void slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + dc->realize = slave_realize; + k->event = slave_event; + k->recv = slave_rx; + k->send = slave_tx; +} + +static const TypeInfo esp32_i2c_tcp_info = { + .name = TYPE_ESP32_I2C_TCP, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(ESP32_I2C_TCP_State), + .instance_init = slave_init, + .class_init = slave_class_init, +}; + +static void esp32_i2c_tcp_type_init(void) +{ + type_register_static(&esp32_i2c_tcp_info); +} + +type_init(esp32_i2c_tcp_type_init); diff --git a/include/hw/misc/esp32_i2c_tcp_slave.h b/include/hw/misc/esp32_i2c_tcp_slave.h new file mode 100644 index 0000000000..e36bac7ffe --- /dev/null +++ b/include/hw/misc/esp32_i2c_tcp_slave.h @@ -0,0 +1,19 @@ +/* + */ +#ifndef QEMU_ESP32_I2C_TCP_SLAVE_H +#define QEMU_ESP32_I2C_TCP_SLAVE_H + +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_ESP32_I2C_TCP "esp32_i2c_tcp" +OBJECT_DECLARE_SIMPLE_TYPE(ESP32_I2C_TCP_State, ESP32_I2C_TCP) + +/** + */ +struct ESP32_I2C_TCP_State { + /*< private >*/ + I2CSlave i2c; +}; + +#endif diff --git a/tests/i2c-tcp-demo/i2c-tcp-demo.py b/tests/i2c-tcp-demo/i2c-tcp-demo.py new file mode 100644 index 0000000000..d4bec457f3 --- /dev/null +++ b/tests/i2c-tcp-demo/i2c-tcp-demo.py @@ -0,0 +1,133 @@ +import json +from twisted.internet import task +from twisted.internet.defer import Deferred +from twisted.internet.protocol import ClientFactory +from twisted.protocols.basic import LineReceiver +from dataclasses import dataclass +from enum import Enum + +# i2cset -c 0x7F -r 0x20 0x11 0x22 0x33 0x44 0x55 +# i2cget -c 0x7F -r 0x20 -l 0x0A + +HOST = "localhost" +PORT = 16001 + + +class EVENT(Enum): + I2C_START_RECV = 0 + I2C_START_SEND = 1 + I2C_START_SEND_ASYNC = 2 + I2C_FINISH = 3 + I2C_NACK = 4 + + +@dataclass +class I2CSlave: + mem: bytearray = bytearray(256) + mem_addr: int = 0 + curr_addr: int = 0 + first_send: bool = True + recv_conuter: int = 0 + + +i2cslave = I2CSlave() + + +def dump_mem(): + print("Mem:") + bytes_per_row = 32 + rows = int(256 / bytes_per_row) + for i in range(0, rows): + begin = i*bytes_per_row + end = begin+bytes_per_row + prefix = hex(begin) + if i == 0: + prefix = "0x00" + print(prefix + ": " + i2cslave.mem[begin:end].hex(" ")) + + print("\n") + + +def event_handler(packet): + evt = EVENT(packet[1]) + print("Event handler: " + evt.name) + + if evt is EVENT.I2C_FINISH: + i2cslave.recv_conuter = 0 + i2cslave.first_send = True + dump_mem() + + return packet + + +def recv_handler(packet): + print("Recv handler: byte number " + str(i2cslave.recv_conuter) + + " from addr=" + hex(i2cslave.mem_addr) + + ", val=" + hex(i2cslave.mem[i2cslave.mem_addr])) + i2cslave.recv_conuter += 1 + resp = bytearray(packet) + resp[1] = i2cslave.mem[i2cslave.mem_addr] + i2cslave.mem_addr += 1 + if i2cslave.mem_addr == 256: + i2cslave.mem_addr = 0 + return bytes(resp) + + +def send_handler(packet): + print("Send handler: ", end='') + if i2cslave.first_send == True: + print("address byte: ", hex(packet[1])) + i2cslave.mem_addr = packet[1] + i2cslave.first_send = False + else: + print("data byte: ", hex(packet[1])) + i2cslave.mem[i2cslave.mem_addr] = packet[1] + i2cslave.mem_addr += 1 + if i2cslave.mem_addr == 256: + i2cslave.mem_addr = 0 + return packet + + +handlers = {'E': event_handler, 'R': recv_handler, 'S': send_handler} + + +class PacketReceiver(LineReceiver): + def __init__(self) -> None: + super().__init__() + + def connectionMade(self): + print("connected") + + def lineReceived(self, line): + # print(line.hex(" ")) + resp = line + if len(line) == 3: + resp = handlers[chr(line[0])](line) + + self.sendLine(resp) + + +class PacketReceiverFactory(ClientFactory): + protocol = PacketReceiver + + def __init__(self): + self.done = Deferred() + + def clientConnectionFailed(self, connector, reason): + print("connection failed:", reason.getErrorMessage()) + self.done.errback(reason) + + def clientConnectionLost(self, connector, reason): + print("connection lost:", reason.getErrorMessage()) + self.done.callback(None) + + +def main(reactor): + dump_mem() + factory = PacketReceiverFactory() + reactor.connectTCP(HOST, PORT, factory) + return factory.done + + +if __name__ == "__main__": + task.react(main)