|  | /* | 
|  | * Example I2C device using asynchronous I2C send. | 
|  | * | 
|  | * Copyright (C) 2023 Samsung Electronics Co., Ltd. All Rights Reserved. | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2.  See | 
|  | * the COPYING file in the top-level directory. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/timer.h" | 
|  | #include "qemu/main-loop.h" | 
|  | #include "block/aio.h" | 
|  | #include "hw/i2c/i2c.h" | 
|  |  | 
|  | #define TYPE_I2C_ECHO "i2c-echo" | 
|  | OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) | 
|  |  | 
|  | enum i2c_echo_state { | 
|  | I2C_ECHO_STATE_IDLE, | 
|  | I2C_ECHO_STATE_START_SEND, | 
|  | I2C_ECHO_STATE_ACK, | 
|  | }; | 
|  |  | 
|  | typedef struct I2CEchoState { | 
|  | I2CSlave parent_obj; | 
|  |  | 
|  | I2CBus *bus; | 
|  |  | 
|  | enum i2c_echo_state state; | 
|  | QEMUBH *bh; | 
|  |  | 
|  | unsigned int pos; | 
|  | uint8_t data[3]; | 
|  | } I2CEchoState; | 
|  |  | 
|  | static void i2c_echo_bh(void *opaque) | 
|  | { | 
|  | I2CEchoState *state = opaque; | 
|  |  | 
|  | switch (state->state) { | 
|  | case I2C_ECHO_STATE_IDLE: | 
|  | return; | 
|  |  | 
|  | case I2C_ECHO_STATE_START_SEND: | 
|  | if (i2c_start_send_async(state->bus, state->data[0])) { | 
|  | goto release_bus; | 
|  | } | 
|  |  | 
|  | state->pos++; | 
|  | state->state = I2C_ECHO_STATE_ACK; | 
|  | return; | 
|  |  | 
|  | case I2C_ECHO_STATE_ACK: | 
|  | if (state->pos > 2) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (i2c_send_async(state->bus, state->data[state->pos++])) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  |  | 
|  | i2c_end_transfer(state->bus); | 
|  | release_bus: | 
|  | i2c_bus_release(state->bus); | 
|  |  | 
|  | state->state = I2C_ECHO_STATE_IDLE; | 
|  | } | 
|  |  | 
|  | static int i2c_echo_event(I2CSlave *s, enum i2c_event event) | 
|  | { | 
|  | I2CEchoState *state = I2C_ECHO(s); | 
|  |  | 
|  | switch (event) { | 
|  | case I2C_START_RECV: | 
|  | state->pos = 0; | 
|  |  | 
|  | break; | 
|  |  | 
|  | case I2C_START_SEND: | 
|  | state->pos = 0; | 
|  |  | 
|  | break; | 
|  |  | 
|  | case I2C_FINISH: | 
|  | state->pos = 0; | 
|  | state->state = I2C_ECHO_STATE_START_SEND; | 
|  | i2c_bus_master(state->bus, state->bh); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case I2C_NACK: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uint8_t i2c_echo_recv(I2CSlave *s) | 
|  | { | 
|  | I2CEchoState *state = I2C_ECHO(s); | 
|  |  | 
|  | if (state->pos > 2) { | 
|  | return 0xff; | 
|  | } | 
|  |  | 
|  | return state->data[state->pos++]; | 
|  | } | 
|  |  | 
|  | static int i2c_echo_send(I2CSlave *s, uint8_t data) | 
|  | { | 
|  | I2CEchoState *state = I2C_ECHO(s); | 
|  |  | 
|  | if (state->pos > 2) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | state->data[state->pos++] = data; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void i2c_echo_realize(DeviceState *dev, Error **errp) | 
|  | { | 
|  | I2CEchoState *state = I2C_ECHO(dev); | 
|  | BusState *bus = qdev_get_parent_bus(dev); | 
|  |  | 
|  | state->bus = I2C_BUS(bus); | 
|  | state->bh = qemu_bh_new(i2c_echo_bh, state); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void i2c_echo_class_init(ObjectClass *oc, void *data) | 
|  | { | 
|  | I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); | 
|  | DeviceClass *dc = DEVICE_CLASS(oc); | 
|  |  | 
|  | dc->realize = i2c_echo_realize; | 
|  |  | 
|  | sc->event = i2c_echo_event; | 
|  | sc->recv = i2c_echo_recv; | 
|  | sc->send = i2c_echo_send; | 
|  | } | 
|  |  | 
|  | static const TypeInfo i2c_echo = { | 
|  | .name = TYPE_I2C_ECHO, | 
|  | .parent = TYPE_I2C_SLAVE, | 
|  | .instance_size = sizeof(I2CEchoState), | 
|  | .class_init = i2c_echo_class_init, | 
|  | }; | 
|  |  | 
|  | static void register_types(void) | 
|  | { | 
|  | type_register_static(&i2c_echo); | 
|  | } | 
|  |  | 
|  | type_init(register_types); |