Home | History | Annotate | Download | only in goldfish
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 #include "migration/qemu-file.h"
     13 #include "sysemu/char.h"
     14 #include "hw/android/goldfish/device.h"
     15 #include "hw/android/goldfish/vmem.h"
     16 #include "hw/hw.h"
     17 
     18 enum {
     19     TTY_PUT_CHAR       = 0x00,
     20     TTY_BYTES_READY    = 0x04,
     21     TTY_CMD            = 0x08,
     22 
     23     TTY_DATA_PTR       = 0x10,
     24     TTY_DATA_LEN       = 0x14,
     25     TTY_DATA_PTR_HIGH  = 0x18,
     26 
     27     TTY_CMD_INT_DISABLE    = 0,
     28     TTY_CMD_INT_ENABLE     = 1,
     29     TTY_CMD_WRITE_BUFFER   = 2,
     30     TTY_CMD_READ_BUFFER    = 3,
     31 };
     32 
     33 struct tty_state {
     34     struct goldfish_device dev;
     35     CharDriverState *cs;
     36     uint64_t ptr;
     37     uint32_t ptr_len;
     38     uint32_t ready;
     39     uint8_t data[128];
     40     uint32_t data_count;
     41 };
     42 
     43 #define  GOLDFISH_TTY_SAVE_VERSION  2
     44 
     45 static void  goldfish_tty_save(QEMUFile*  f, void*  opaque)
     46 {
     47     struct tty_state*  s = opaque;
     48 
     49     qemu_put_be64( f, s->ptr );
     50     qemu_put_be32( f, s->ptr_len );
     51     qemu_put_byte( f, s->ready );
     52     qemu_put_byte( f, s->data_count );
     53     qemu_put_buffer( f, s->data, s->data_count );
     54 }
     55 
     56 static int  goldfish_tty_load(QEMUFile*  f, void*  opaque, int  version_id)
     57 {
     58     struct tty_state*  s = opaque;
     59 
     60     if ((version_id != GOLDFISH_TTY_SAVE_VERSION) &&
     61         (version_id != (GOLDFISH_TTY_SAVE_VERSION - 1))) {
     62         return -1;
     63     }
     64     if (version_id == (GOLDFISH_TTY_SAVE_VERSION - 1)) {
     65         s->ptr    = (uint64_t)qemu_get_be32(f);
     66     } else {
     67         s->ptr    = qemu_get_be64(f);
     68     }
     69     s->ptr_len    = qemu_get_be32(f);
     70     s->ready      = qemu_get_byte(f);
     71     s->data_count = qemu_get_byte(f);
     72     qemu_get_buffer(f, s->data, s->data_count);
     73 
     74     return 0;
     75 }
     76 
     77 static uint32_t goldfish_tty_read(void *opaque, hwaddr offset)
     78 {
     79     struct tty_state *s = (struct tty_state *)opaque;
     80 
     81     //printf("goldfish_tty_read %x %x\n", offset, size);
     82 
     83     switch (offset) {
     84         case TTY_BYTES_READY:
     85             return s->data_count;
     86     default:
     87         cpu_abort (cpu_single_env, "goldfish_tty_read: Bad offset %x\n", offset);
     88         return 0;
     89     }
     90 }
     91 
     92 static void goldfish_tty_write(void *opaque, hwaddr offset, uint32_t value)
     93 {
     94     struct tty_state *s = (struct tty_state *)opaque;
     95 
     96     //printf("goldfish_tty_write %x %x %x\n", offset, value, size);
     97 
     98     switch(offset) {
     99         case TTY_PUT_CHAR: {
    100             uint8_t ch = value;
    101             if(s->cs)
    102                 qemu_chr_write(s->cs, &ch, 1);
    103         } break;
    104 
    105         case TTY_CMD:
    106             switch(value) {
    107                 case TTY_CMD_INT_DISABLE:
    108                     if(s->ready) {
    109                         if(s->data_count > 0)
    110                             goldfish_device_set_irq(&s->dev, 0, 0);
    111                         s->ready = 0;
    112                     }
    113                     break;
    114 
    115                 case TTY_CMD_INT_ENABLE:
    116                     if(!s->ready) {
    117                         if(s->data_count > 0)
    118                             goldfish_device_set_irq(&s->dev, 0, 1);
    119                         s->ready = 1;
    120                     }
    121                     break;
    122 
    123                 case TTY_CMD_WRITE_BUFFER:
    124                     if(s->cs) {
    125                         int len;
    126                         target_ulong  buf;
    127 
    128                         buf = s->ptr;
    129                         len = s->ptr_len;
    130 
    131                         while (len) {
    132                             char   temp[64];
    133                             int    to_write = sizeof(temp);
    134                             if (to_write > len)
    135                                 to_write = len;
    136 
    137                             safe_memory_rw_debug(current_cpu, buf, (uint8_t*)temp, to_write, 0);
    138                             qemu_chr_write(s->cs, (const uint8_t*)temp, to_write);
    139                             buf += to_write;
    140                             len -= to_write;
    141                         }
    142                         //printf("goldfish_tty_write: got %d bytes from %llx\n", s->ptr_len, (unsigned long long)s->ptr);
    143                     }
    144                     break;
    145 
    146                 case TTY_CMD_READ_BUFFER:
    147                     if(s->ptr_len > s->data_count)
    148                         cpu_abort (cpu_single_env, "goldfish_tty_write: reading more data than available %d %d\n", s->ptr_len, s->data_count);
    149                     safe_memory_rw_debug(current_cpu, s->ptr, s->data, s->ptr_len,1);
    150                     //printf("goldfish_tty_write: read %d bytes to %llx\n", s->ptr_len, (unsigned long long)s->ptr);
    151                     if(s->data_count > s->ptr_len)
    152                         memmove(s->data, s->data + s->ptr_len, s->data_count - s->ptr_len);
    153                     s->data_count -= s->ptr_len;
    154                     if(s->data_count == 0 && s->ready)
    155                         goldfish_device_set_irq(&s->dev, 0, 0);
    156                     break;
    157 
    158                 default:
    159                     cpu_abort (cpu_single_env, "goldfish_tty_write: Bad command %x\n", value);
    160             };
    161             break;
    162 
    163         case TTY_DATA_PTR:
    164             uint64_set_low(&s->ptr, value);
    165             break;
    166 
    167         case TTY_DATA_PTR_HIGH:
    168             uint64_set_high(&s->ptr, value);
    169             break;
    170 
    171         case TTY_DATA_LEN:
    172             s->ptr_len = value;
    173             break;
    174 
    175         default:
    176             cpu_abort (cpu_single_env, "goldfish_tty_write: Bad offset %x\n", offset);
    177     }
    178 }
    179 
    180 static int tty_can_receive(void *opaque)
    181 {
    182     struct tty_state *s = opaque;
    183 
    184     return (sizeof(s->data) - s->data_count);
    185 }
    186 
    187 static void tty_receive(void *opaque, const uint8_t *buf, int size)
    188 {
    189     struct tty_state *s = opaque;
    190 
    191     memcpy(s->data + s->data_count, buf, size);
    192     s->data_count += size;
    193     if(s->data_count > 0 && s->ready)
    194         goldfish_device_set_irq(&s->dev, 0, 1);
    195 }
    196 
    197 static CPUReadMemoryFunc *goldfish_tty_readfn[] = {
    198     goldfish_tty_read,
    199     goldfish_tty_read,
    200     goldfish_tty_read
    201 };
    202 
    203 static CPUWriteMemoryFunc *goldfish_tty_writefn[] = {
    204     goldfish_tty_write,
    205     goldfish_tty_write,
    206     goldfish_tty_write
    207 };
    208 
    209 int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq)
    210 {
    211     int ret;
    212     struct tty_state *s;
    213     static int  instance_id = 0;
    214 
    215     s = g_malloc0(sizeof(*s));
    216     s->dev.name = "goldfish_tty";
    217     s->dev.id = id;
    218     s->dev.base = base;
    219     s->dev.size = 0x1000;
    220     s->dev.irq = irq;
    221     s->dev.irq_count = 1;
    222     s->cs = cs;
    223 
    224     if(cs) {
    225         qemu_chr_add_handlers(cs, tty_can_receive, tty_receive, NULL, s);
    226     }
    227 
    228     ret = goldfish_device_add(&s->dev, goldfish_tty_readfn, goldfish_tty_writefn, s);
    229     if(ret) {
    230         g_free(s);
    231     } else {
    232         register_savevm(NULL,
    233                         "goldfish_tty",
    234                         instance_id++,
    235                         GOLDFISH_TTY_SAVE_VERSION,
    236                         goldfish_tty_save,
    237                         goldfish_tty_load,
    238                         s);
    239     }
    240     return ret;
    241 }
    242 
    243