 f912f1bdb6
			
		
	
	
		f912f1bdb6
		
	
	
	
	
		
			
			Add missing copyright and license notice. Also add a short description of the device. Signed-off-by: Klaus Jensen <k.jensen@samsung.com> Message-ID: <20230823-i2c-echo-fixes-v1-1-ccc05a6028f0@samsung.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
		
			
				
	
	
		
			167 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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);
 |