157 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			157 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#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);
							 |