qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

pcspk.c (6871B)


      1 /*
      2  * QEMU PC speaker emulation
      3  *
      4  * Copyright (c) 2006 Joachim Henke
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 
     25 #include "qemu/osdep.h"
     26 #include "hw/isa/isa.h"
     27 #include "hw/audio/soundhw.h"
     28 #include "audio/audio.h"
     29 #include "qemu/module.h"
     30 #include "qemu/timer.h"
     31 #include "qemu/error-report.h"
     32 #include "hw/timer/i8254.h"
     33 #include "migration/vmstate.h"
     34 #include "hw/audio/pcspk.h"
     35 #include "qapi/error.h"
     36 #include "qom/object.h"
     37 
     38 #define PCSPK_BUF_LEN 1792
     39 #define PCSPK_SAMPLE_RATE 32000
     40 #define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
     41 #define PCSPK_MIN_COUNT DIV_ROUND_UP(PIT_FREQ, PCSPK_MAX_FREQ)
     42 
     43 OBJECT_DECLARE_SIMPLE_TYPE(PCSpkState, PC_SPEAKER)
     44 
     45 struct PCSpkState {
     46     ISADevice parent_obj;
     47 
     48     MemoryRegion ioport;
     49     uint32_t iobase;
     50     uint8_t sample_buf[PCSPK_BUF_LEN];
     51     QEMUSoundCard card;
     52     SWVoiceOut *voice;
     53     void *pit;
     54     unsigned int pit_count;
     55     unsigned int samples;
     56     unsigned int play_pos;
     57     uint8_t data_on;
     58     uint8_t dummy_refresh_clock;
     59     bool migrate;
     60 };
     61 
     62 static const char *s_spk = "pcspk";
     63 static PCSpkState *pcspk_state;
     64 
     65 static inline void generate_samples(PCSpkState *s)
     66 {
     67     unsigned int i;
     68 
     69     if (s->pit_count) {
     70         const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
     71         const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
     72 
     73         /* multiple of wavelength for gapless looping */
     74         s->samples = (QEMU_ALIGN_DOWN(PCSPK_BUF_LEN * PIT_FREQ, m) / (PIT_FREQ >> 1) + 1) >> 1;
     75         for (i = 0; i < s->samples; ++i)
     76             s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
     77     } else {
     78         s->samples = PCSPK_BUF_LEN;
     79         for (i = 0; i < PCSPK_BUF_LEN; ++i)
     80             s->sample_buf[i] = 128; /* silence */
     81     }
     82 }
     83 
     84 static void pcspk_callback(void *opaque, int free)
     85 {
     86     PCSpkState *s = opaque;
     87     PITChannelInfo ch;
     88     unsigned int n;
     89 
     90     pit_get_channel_info(s->pit, 2, &ch);
     91 
     92     if (ch.mode != 3) {
     93         return;
     94     }
     95 
     96     n = ch.initial_count;
     97     /* avoid frequencies that are not reproducible with sample rate */
     98     if (n < PCSPK_MIN_COUNT)
     99         n = 0;
    100 
    101     if (s->pit_count != n) {
    102         s->pit_count = n;
    103         s->play_pos = 0;
    104         generate_samples(s);
    105     }
    106 
    107     while (free > 0) {
    108         n = MIN(s->samples - s->play_pos, (unsigned int)free);
    109         n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
    110         if (!n)
    111             break;
    112         s->play_pos = (s->play_pos + n) % s->samples;
    113         free -= n;
    114     }
    115 }
    116 
    117 static int pcspk_audio_init(PCSpkState *s)
    118 {
    119     struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0};
    120 
    121     if (s->voice) {
    122         /* already initialized */
    123         return 0;
    124     }
    125 
    126     AUD_register_card(s_spk, &s->card);
    127 
    128     s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
    129     if (!s->voice) {
    130         AUD_log(s_spk, "Could not open voice\n");
    131         return -1;
    132     }
    133 
    134     return 0;
    135 }
    136 
    137 static uint64_t pcspk_io_read(void *opaque, hwaddr addr,
    138                               unsigned size)
    139 {
    140     PCSpkState *s = opaque;
    141     PITChannelInfo ch;
    142 
    143     pit_get_channel_info(s->pit, 2, &ch);
    144 
    145     s->dummy_refresh_clock ^= (1 << 4);
    146 
    147     return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock |
    148        (ch.out << 5);
    149 }
    150 
    151 static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val,
    152                            unsigned size)
    153 {
    154     PCSpkState *s = opaque;
    155     const int gate = val & 1;
    156 
    157     s->data_on = (val >> 1) & 1;
    158     pit_set_gate(s->pit, 2, gate);
    159     if (s->voice) {
    160         if (gate) /* restart */
    161             s->play_pos = 0;
    162         AUD_set_active_out(s->voice, gate & s->data_on);
    163     }
    164 }
    165 
    166 static const MemoryRegionOps pcspk_io_ops = {
    167     .read = pcspk_io_read,
    168     .write = pcspk_io_write,
    169     .impl = {
    170         .min_access_size = 1,
    171         .max_access_size = 1,
    172     },
    173 };
    174 
    175 static void pcspk_initfn(Object *obj)
    176 {
    177     PCSpkState *s = PC_SPEAKER(obj);
    178 
    179     memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "pcspk", 1);
    180 
    181     object_property_add_link(obj, "pit", TYPE_PIT_COMMON,
    182                              (Object **)&s->pit,
    183                              qdev_prop_allow_set_link_before_realize,
    184                              0);
    185 }
    186 
    187 static void pcspk_realizefn(DeviceState *dev, Error **errp)
    188 {
    189     ISADevice *isadev = ISA_DEVICE(dev);
    190     PCSpkState *s = PC_SPEAKER(dev);
    191 
    192     isa_register_ioport(isadev, &s->ioport, s->iobase);
    193 
    194     if (s->card.state) {
    195         pcspk_audio_init(s);
    196     }
    197 
    198     pcspk_state = s;
    199 }
    200 
    201 static bool migrate_needed(void *opaque)
    202 {
    203     PCSpkState *s = opaque;
    204 
    205     return s->migrate;
    206 }
    207 
    208 static const VMStateDescription vmstate_spk = {
    209     .name = "pcspk",
    210     .version_id = 1,
    211     .minimum_version_id = 1,
    212     .needed = migrate_needed,
    213     .fields      = (VMStateField[]) {
    214         VMSTATE_UINT8(data_on, PCSpkState),
    215         VMSTATE_UINT8(dummy_refresh_clock, PCSpkState),
    216         VMSTATE_END_OF_LIST()
    217     }
    218 };
    219 
    220 static Property pcspk_properties[] = {
    221     DEFINE_AUDIO_PROPERTIES(PCSpkState, card),
    222     DEFINE_PROP_UINT32("iobase", PCSpkState, iobase,  0x61),
    223     DEFINE_PROP_BOOL("migrate", PCSpkState, migrate,  true),
    224     DEFINE_PROP_END_OF_LIST(),
    225 };
    226 
    227 static void pcspk_class_initfn(ObjectClass *klass, void *data)
    228 {
    229     DeviceClass *dc = DEVICE_CLASS(klass);
    230 
    231     dc->realize = pcspk_realizefn;
    232     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
    233     dc->vmsd = &vmstate_spk;
    234     device_class_set_props(dc, pcspk_properties);
    235     /* Reason: realize sets global pcspk_state */
    236     /* Reason: pit object link */
    237     dc->user_creatable = false;
    238 }
    239 
    240 static const TypeInfo pcspk_info = {
    241     .name           = TYPE_PC_SPEAKER,
    242     .parent         = TYPE_ISA_DEVICE,
    243     .instance_size  = sizeof(PCSpkState),
    244     .instance_init  = pcspk_initfn,
    245     .class_init     = pcspk_class_initfn,
    246 };
    247 
    248 static void pcspk_register(void)
    249 {
    250     type_register_static(&pcspk_info);
    251 }
    252 type_init(pcspk_register)