static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value_ns) { struct timer_state *s = (struct timer_state *)opaque; int64_t alarm_tks, now_tks; switch(offset) { case TIMER_ALARM_LOW: s->alarm_low_ns = value_ns; alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32); now_tks = qemu_get_clock(vm_clock); if (alarm_tks <= now_tks) { goldfish_device_set_irq(&s->dev, 0, 1); } else { qemu_mod_timer(s->timer, alarm_tks); s->armed = 1; } break; case TIMER_ALARM_HIGH: s->alarm_high_ns = value_ns; break; case TIMER_CLEAR_ALARM: qemu_del_timer(s->timer); s->armed = 0; /* fall through */ case TIMER_CLEAR_INTERRUPT: goldfish_device_set_irq(&s->dev, 0, 0); break; default: cpu_abort (cpu_single_env, "goldfish_timer_write: Bad offset %x\n", offset); } }
static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value) { struct timer_state *s = (struct timer_state *)opaque; int64_t alarm, now; switch(offset) { case TIMER_ALARM_LOW: s->alarm_low = value; alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, ticks_per_sec, 1000000000); now = qemu_get_clock(vm_clock); if (alarm <= now) { goldfish_device_set_irq(&s->dev, 0, 1); } else { qemu_mod_timer(s->timer, alarm); s->armed = 1; } break; case TIMER_ALARM_HIGH: s->alarm_high = value; //printf("alarm_high %d\n", s->alarm_high); break; case TIMER_CLEAR_ALARM: qemu_del_timer(s->timer); s->armed = 0; /* fall through */ case TIMER_CLEAR_INTERRUPT: goldfish_device_set_irq(&s->dev, 0, 0); break; default: cpu_abort (cpu_single_env, "goldfish_timer_write: Bad offset %x\n", offset); } }
static void goldfish_nfc_process_ctrl(struct nfc_state* s) { int res; if (s->ctrl == CTRL_INTR_ACK) { s->status ^= STATUS_INTR; goldfish_device_set_irq(&s->dev, 0, 0); } else if (s->ctrl == CTRL_RESP_RCV) { s->status &= ~(STATUS_NCI_RESP|STATUS_HCI_RESP); } else if (s->ctrl == CTRL_NTFN_RCV) { s->status ^= STATUS_NCI_NTFN; } else if (s->ctrl == CTRL_DATA_RCV) { s->status ^= STATUS_NCI_DATA; } else if (s->ctrl == CTRL_NCI_CMND_SNT) { if (s->status&(STATUS_NCI_RESP|STATUS_HCI_RESP)) return; /* previous response still loaded, do nothing */ s->status |= STATUS_NCI_CMND; memset(s->resp, 0, sizeof(s->resp)); res = nfc_process_nci_msg((const union nci_packet*)s->cmnd, &s->nfc, (union nci_packet*)s->resp); s->status &= ~STATUS_NCI_CMND; s->status |= STATUS_NCI_RESP * !!res; s->status |= STATUS_INTR; goldfish_device_set_irq(&s->dev, 0, 1); } else if (s->ctrl == CTRL_HCI_CMND_SNT) { if (s->status&(STATUS_NCI_RESP|STATUS_HCI_RESP)) return; /* previous response still loaded, do nothing */ s->status |= STATUS_HCI_CMND; memset(s->resp, 0, sizeof(s->resp)); res = nfc_process_hci_cmd((const union hci_packet*)s->cmnd, &s->nfc, (union hci_answer*)s->resp); s->status &= ~STATUS_HCI_CMND; s->status |= STATUS_HCI_RESP * !!res; s->status |= STATUS_INTR; goldfish_device_set_irq(&s->dev, 0, 1); } }
static void goldfish_switch_write(void *opaque, target_phys_addr_t offset, uint32_t value) { struct switch_state *s = (struct switch_state *)opaque; switch(offset) { case SW_NAME_PTR: safe_memory_rw_debug(cpu_single_env, value, (void*)s->name, strlen(s->name), 1); break; case SW_STATE: if(s->writefn) { uint32_t new_state; new_state = s->writefn(s->writeopaque, value); if(new_state != s->state) { goldfish_switch_set_state(s, new_state); } } else cpu_abort (cpu_single_env, "goldfish_switch_write: write to SW_STATE on input\n"); break; case SW_INT_ENABLE: value &= 1; if(s->state_changed && s->int_enable != value) goldfish_device_set_irq(&s->dev, 0, value); s->int_enable = value; break; default: cpu_abort (cpu_single_env, "goldfish_switch_write: Bad offset %x\n", offset); } }
static void goldfish_timer_tick(void *opaque) { struct timer_state *s = (struct timer_state *)opaque; s->armed = 0; goldfish_device_set_irq(&s->dev, 0, 1); }
void goldfish_battery_set_prop(int ac, int property, int value) { int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED); if (!battery_state || !battery_state->hw_has_battery) return; if (ac) { switch (property) { case POWER_SUPPLY_PROP_ONLINE: battery_state->ac_online = value; break; } } else { switch (property) { case POWER_SUPPLY_PROP_STATUS: battery_state->status = value; break; case POWER_SUPPLY_PROP_HEALTH: battery_state->health = value; break; case POWER_SUPPLY_PROP_PRESENT: battery_state->present = value; break; case POWER_SUPPLY_PROP_CAPACITY: battery_state->capacity = value; break; } } if (new_status != battery_state->int_status) { battery_state->int_status |= new_status; goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable)); } }
void goldfish_battery_set_prop(void *opaque, int ac, int property, int value) { int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED); GoldfishBatteryDevice *s = (GoldfishBatteryDevice *)opaque; if (ac) { switch (property) { case POWER_SUPPLY_PROP_ONLINE: s->ac_online = value; break; } } else { switch (property) { case POWER_SUPPLY_PROP_STATUS: s->status = value; break; case POWER_SUPPLY_PROP_HEALTH: s->health = value; break; case POWER_SUPPLY_PROP_PRESENT: s->present = value; break; case POWER_SUPPLY_PROP_CAPACITY: s->capacity = value; break; } } if (new_status != s->int_status) { s->int_status |= new_status; goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable)); } }
static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset) { uint32_t ret; GoldfishBatteryDevice *s = (GoldfishBatteryDevice *)opaque; switch(offset) { case BATTERY_INT_STATUS: // return current buffer status flags ret = s->int_status & s->int_enable; if (ret) { goldfish_device_set_irq(&s->dev, 0, 0); s->int_status = 0; } return ret; case BATTERY_INT_ENABLE: return s->int_enable; case BATTERY_AC_ONLINE: return s->ac_online; case BATTERY_STATUS: return s->status; case BATTERY_HEALTH: return s->health; case BATTERY_PRESENT: return s->present; case BATTERY_CAPACITY: return s->capacity; default: cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset); return 0; } }
static uint32_t goldfish_switch_read(void *opaque, target_phys_addr_t offset) { struct switch_state *s = (struct switch_state *)opaque; switch (offset) { case SW_NAME_LEN: return strlen(s->name); case SW_FLAGS: return s->writefn ? SW_FLAGS_OUTPUT : 0; case SW_STATE: return s->state; case SW_INT_STATUS: if(s->state_changed && s->int_enable) { s->state_changed = 0; goldfish_device_set_irq(&s->dev, 0, 0); return 1; } return 0; default: cpu_abort (cpu_single_env, "goldfish_switch_read: Bad offset %x\n", offset); return 0; } }
void goldfish_switch_set_state(void *opaque, uint32_t state) { struct switch_state *s = opaque; s->state_changed = 1; s->state = state; if(s->int_enable) goldfish_device_set_irq(&s->dev, 0, 1); }
static void goldfish_rfkill_update_irq(struct rfkill_state *s) { /* Pull down IRQ line if no active interrupt source. */ int level = (s->int_mask & s->inta) ? 1 : 0; D("irq level: %d\n", level); goldfish_device_set_irq(&s->dev, 0, level); }
static void tty_receive(void *opaque, const uint8_t *buf, int size) { struct tty_state *s = opaque; memcpy(s->data + s->data_count, buf, size); s->data_count += size; if(s->data_count > 0 && s->ready) goldfish_device_set_irq(&s->dev, 0, 1); }
static void goldfish_bus_op_init(struct bus_state *s) { struct goldfish_device *dev = first_device; while(dev) { dev->reported_state = 0; dev = dev->next; } s->current = NULL; goldfish_device_set_irq(&s->dev, 0, first_device != NULL); }
static void goldfish_tty_update_irq(struct tty_state *s) { static int current_level = 0; int level; level = (s_tty.int_mask & s_tty.int_active) ? 1 : 0; if (level != current_level) { current_level = level; goldfish_device_set_irq(&s->dev, 0, level); } }
/* I/O read */ static uint32_t pipe_dev_read(void *opaque, hwaddr offset) { PipeDevice *dev = (PipeDevice *)opaque; switch (offset) { case PIPE_REG_STATUS: DR("%s: REG_STATUS status=%d (0x%x)", __FUNCTION__, dev->status, dev->status); return dev->status; case PIPE_REG_CHANNEL: if (dev->signaled_pipes != NULL) { Pipe* pipe = dev->signaled_pipes; DR("%s: channel=0x%llx wanted=%d", __FUNCTION__, (unsigned long long)pipe->channel, pipe->wanted); dev->wakes = pipe->wanted; pipe->wanted = 0; dev->signaled_pipes = pipe->next_waked; pipe->next_waked = NULL; if (dev->signaled_pipes == NULL) { goldfish_device_set_irq(&dev->dev, 0, 0); DD("%s: lowering IRQ", __FUNCTION__); } return (uint32_t)(pipe->channel & 0xFFFFFFFFUL); } DR("%s: no signaled channels", __FUNCTION__); return 0; case PIPE_REG_CHANNEL_HIGH: if (dev->signaled_pipes != NULL) { Pipe* pipe = dev->signaled_pipes; DR("%s: channel_high=0x%llx wanted=%d", __FUNCTION__, (unsigned long long)pipe->channel, pipe->wanted); return (uint32_t)(pipe->channel >> 32); } DR("%s: no signaled channels", __FUNCTION__); return 0; case PIPE_REG_WAKES: DR("%s: wakes %d", __FUNCTION__, dev->wakes); return dev->wakes; case PIPE_REG_PARAMS_ADDR_HIGH: return (uint32_t)(dev->params_addr >> 32); case PIPE_REG_PARAMS_ADDR_LOW: return (uint32_t)(dev->params_addr & 0xFFFFFFFFUL); default: D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset); }
static uint32_t goldfish_bus_read(void *opaque, hwaddr offset) { struct bus_state *s = (struct bus_state *)opaque; switch (offset) { case PDEV_BUS_OP: if(s->current) { s->current->reported_state = 1; s->current = s->current->next; } else { s->current = first_device; } while(s->current && s->current->reported_state == 1) s->current = s->current->next; if(s->current) return PDEV_BUS_OP_ADD_DEV; else { goldfish_device_set_irq(&s->dev, 0, 0); return PDEV_BUS_OP_DONE; } case PDEV_BUS_NAME_LEN: return s->current ? strlen(s->current->name) : 0; case PDEV_BUS_ID: return s->current ? s->current->id : 0; case PDEV_BUS_IO_BASE: return s->current ? s->current->base : 0; case PDEV_BUS_IO_SIZE: return s->current ? s->current->size : 0; case PDEV_BUS_IRQ: return s->current ? s->current->irq : 0; case PDEV_BUS_IRQ_COUNT: return s->current ? s->current->irq_count : 0; default: cpu_abort(cpu_single_env, "goldfish_bus_read: Bad offset %" HWADDR_PRIx "\n", offset); return 0; } }
void goldfish_pipe_wake( void* hwpipe, unsigned flags ) { Pipe* pipe = hwpipe; Pipe** lookup; PipeDevice* dev = pipe->device; DD("%s: channel=0x%x flags=%d", __FUNCTION__, pipe->channel, flags); lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe); if (!*lookup) { pipe->next_waked = dev->signaled_pipes; dev->signaled_pipes = pipe; } pipe->wanted |= (unsigned)flags; goldfish_device_set_irq(&dev->dev, 0, 1); DD("%s: raising IRQ", __FUNCTION__); }
static void goldfish_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value) { struct rtc_state *s = (struct rtc_state *)opaque; int64_t alarm; switch(offset) { case 0x8: s->alarm_low = value; alarm = s->alarm_low | (int64_t)s->alarm_high << 32; //printf("next alarm at %lld, tps %lld\n", alarm, ticks_per_sec); //qemu_mod_timer(s->timer, alarm); break; case 0xc: s->alarm_high = value; //printf("alarm_high %d\n", s->alarm_high); break; case 0x10: goldfish_device_set_irq(&s->dev, 0, 0); break; default: cpu_abort (cpu_single_env, "goldfish_rtc_write: Bad offset %x\n", offset); } }
static uint32_t goldfish_sensor_read(void *opaque, target_phys_addr_t offset) { uint32_t ret; struct goldfish_sensor_state *s = opaque; offset -= s->dev.base; switch(offset) { case INT_ENABLE: // return current buffer status flags ret = s->int_status & s->int_enable; if (ret) { goldfish_device_set_irq(&s->dev, 0, 0); s->int_status = 0; } return ret; case ACCEL_X: return s->accel_x; case ACCEL_Y: return s->accel_y; case ACCEL_Z: return s->accel_z; case COMPASS_X: return s->compass_x; case COMPASS_Y: return s->compass_y; case COMPASS_Z: return s->compass_z; case ORIENT_X: return s->orient_x; case ORIENT_Y: return s->orient_y; case ORIENT_Z: return s->orient_z; default: cpu_abort (cpu_single_env, "goldfish_sensor_read: Bad offset %x\n", offset); return 0; } }
static int goldfish_timer_load(QEMUFile* f, void* opaque, int version_id) { struct timer_state* s = opaque; if (version_id != GOLDFISH_TIMER_SAVE_VERSION) return -1; s->now = qemu_get_be64(f); s->armed = qemu_get_byte(f); if (s->armed) { int64_t now = qemu_get_clock(vm_clock); int64_t diff = qemu_get_be64(f); int64_t alarm = now + diff; if (alarm <= now) { goldfish_device_set_irq(&s->dev, 0, 1); s->armed = 0; } else { qemu_mod_timer(s->timer, alarm); } } return 0; }
int goldfish_nfc_send_ntf(ssize_t (*create)(void*, struct nfc_device*, size_t, union nci_packet*), void* data) { struct nfc_state* s; size_t maxlen; ssize_t res; assert(create); s = &_nfc_states[0]; maxlen = MIN(sizeof(s->ntfn), MAX_NCI_PAYLOAD_LENGTH); memset(s->ntfn, 0, maxlen); res = create(data, &s->nfc, maxlen, (union nci_packet*)s->ntfn); if (res < 0) return -1; s->status |= STATUS_NCI_NTFN * !!res; s->status |= STATUS_INTR; goldfish_device_set_irq(&s->dev, 0, 1); return 0; }
void goldfish_sensor_set_prop(int sensor, int x,int y,int z) { switch (sensor) { case 0: sensor_state->accel_x=x; sensor_state->accel_y=y; sensor_state->accel_z=z; break; case 1: sensor_state->compass_x=x; sensor_state->compass_y=y; sensor_state->compass_z=z; break; case 2: sensor_state->orient_x=x; sensor_state->orient_y=y; sensor_state->orient_z=z; break; } sensor_state->int_status |= (1<<sensor); goldfish_device_set_irq(&sensor_state->dev, 0, (sensor_state->int_status & sensor_state->int_enable)); }
static int goldfish_timer_load(QEMUFile* f, void* opaque, int version_id) { struct timer_state* s = opaque; if (version_id != GOLDFISH_TIMER_SAVE_VERSION) return -1; s->now_ns = qemu_get_sbe64(f); /* using qemu_get_be64 (without 's') causes faulty code generation in the compiler, dropping the 32 most significant bits */ s->armed = qemu_get_byte(f); if (s->armed) { int64_t now_tks = qemu_get_clock(vm_clock); int64_t diff_tks = qemu_get_be64(f); int64_t alarm_tks = now_tks + diff_tks; if (alarm_tks <= now_tks) { goldfish_device_set_irq(&s->dev, 0, 1); s->armed = 0; } else { qemu_mod_timer(s->timer, alarm_tks); } } return 0; }
static void goldfish_tty_write(void *opaque, hwaddr offset, uint32_t value) { struct tty_state *s = (struct tty_state *)opaque; //printf("goldfish_tty_read %x %x %x\n", offset, value, size); switch(offset) { case TTY_PUT_CHAR: { uint8_t ch = value; if(s->cs) qemu_chr_write(s->cs, &ch, 1); } break; case TTY_CMD: switch(value) { case TTY_CMD_INT_DISABLE: if(s->ready) { if(s->data_count > 0) goldfish_device_set_irq(&s->dev, 0, 0); s->ready = 0; } break; case TTY_CMD_INT_ENABLE: if(!s->ready) { if(s->data_count > 0) goldfish_device_set_irq(&s->dev, 0, 1); s->ready = 1; } break; case TTY_CMD_WRITE_BUFFER: if(s->cs) { int len; hwaddr buf; buf = s->ptr; len = s->ptr_len; while (len) { char temp[64]; int to_write = sizeof(temp); if (to_write > len) to_write = len; safe_memory_rw_debug(cpu_single_env, buf, (uint8_t*)temp, to_write, 0); qemu_chr_write(s->cs, (const uint8_t*)temp, to_write); buf += to_write; len -= to_write; } //printf("goldfish_tty_write: got %d bytes from %x\n", s->ptr_len, s->ptr); } break; case TTY_CMD_READ_BUFFER: if(s->ptr_len > s->data_count) cpu_abort (cpu_single_env, "goldfish_tty_write: reading more data than available %d %d\n", s->ptr_len, s->data_count); safe_memory_rw_debug(cpu_single_env,s->ptr, s->data, s->ptr_len,1); //printf("goldfish_tty_write: read %d bytes to %x\n", s->ptr_len, s->ptr); if(s->data_count > s->ptr_len) memmove(s->data, s->data + s->ptr_len, s->data_count - s->ptr_len); s->data_count -= s->ptr_len; if(s->data_count == 0 && s->ready) goldfish_device_set_irq(&s->dev, 0, 0); break; default: cpu_abort (cpu_single_env, "goldfish_tty_write: Bad command %x\n", value); }; break; case TTY_DATA_PTR: s->ptr = value; break; case TTY_DATA_LEN: s->ptr_len = value; break; default: cpu_abort (cpu_single_env, "goldfish_tty_write: Bad offset %x\n", offset); } }