#include "Python.h"
#include "structmember.h"
#include "hw/hw.h"
#include "sysemu.h"
#include "devtree.h"
#include "qemu-char.h"
#include "display_state.h"
#include "hw/gui.h"
#include "hw/fb_render_engine.h"
#include "qemu-timer.h"
static void qemu_py_die(void)
{
PyErr_Print();
exit(1);
}
#define qemu_py_assert(x) do { if (!(x)) { qemu_py_die(); } } while (0)
static void qemu_py_load_module(const char *name)
{
PyObject *module;
module = PyImport_ImportModule(name);
if (!module)
qemu_py_die();
}
static int64_t qemu_py_int64_from_pynum(PyObject *ob)
{
if (PyInt_Check(ob))
return PyInt_AsLong(ob);
return PyLong_AsLongLong(ob);
}
static uint64_t qemu_py_uint64_from_pynum(PyObject *ob)
{
if (PyInt_Check(ob))
return PyInt_AsUnsignedLongMask(ob);
return PyLong_AsUnsignedLongLongMask(ob);
}
typedef struct {
PyObject_HEAD
PyObject *size;
PyObject *readl;
PyObject *writel;
} qemu_py_ioregion;
static void qemu_py_ioregion_dealloc(qemu_py_ioregion *self)
{
Py_CLEAR(self->size);
Py_CLEAR(self->readl);
Py_CLEAR(self->writel);
self->ob_type->tp_free((PyObject*)self);
}
static int qemu_py_ioregion_init(qemu_py_ioregion *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"size", "readl", "writel", NULL};
PyObject *obsize;
PyObject *readl;
PyObject *writel;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist,
&obsize, &readl, &writel))
return -1;
Py_INCREF(obsize);
self->size = obsize;
Py_INCREF(readl);
self->readl = readl;
Py_INCREF(writel);
self->writel = writel;
return 0;
}
static PyMemberDef qemu_py_ioregion_members[] = {
{"size", T_OBJECT, offsetof(qemu_py_ioregion, size), 0,
"size"},
{"readl", T_OBJECT, offsetof(qemu_py_ioregion, readl), 0,
"32-bit read"},
{"writel", T_OBJECT, offsetof(qemu_py_ioregion, writel), 0,
"32-bit write"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_ioregionType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.ioregion", /* tp_name */
sizeof(qemu_py_ioregion), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_ioregion_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU IORegion", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
qemu_py_ioregion_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_ioregion_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
QEMUFile *f;
} qemu_py_file;
static void qemu_py_file_dealloc(qemu_py_file *self)
{
self->f = NULL;
self->ob_type->tp_free((PyObject*)self);
}
static int qemu_py_file_init(qemu_py_file *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"f", NULL};
PyObject *obdata;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obdata))
return -1;
self->f = PyCObject_AsVoidPtr(obdata);
if (PyErr_Occurred())
return -1;
return 0;
}
static PyObject *qemu_py_file_get_u32(qemu_py_file *self, PyObject *args)
{
uint32_t val;
if (self->f) {
val = qemu_get_be32(self->f);
return PyLong_FromUnsignedLong(val);
}
Py_RETURN_NONE;
}
static PyObject *qemu_py_file_put_u32(qemu_py_file *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"value", NULL};
PyObject *obval;
uint32_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obval))
return NULL;
val = qemu_py_uint64_from_pynum(obval);
if (PyErr_Occurred())
return NULL;
if (self->f)
qemu_put_be32(self->f, val);
Py_RETURN_NONE;
}
static PyObject *qemu_py_file_get_u64(qemu_py_file *self, PyObject *args)
{
uint64_t val;
if (self->f) {
val = qemu_get_be64(self->f);
return PyLong_FromUnsignedLongLong(val);
}
Py_RETURN_NONE;
}
static PyObject *qemu_py_file_get_s64(qemu_py_file *self, PyObject *args)
{
int64_t val;
if (self->f) {
val = qemu_get_be64(self->f);
return PyLong_FromLongLong(val);
}
Py_RETURN_NONE;
}
static PyObject *qemu_py_file_put_u64(qemu_py_file *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"value", NULL};
PyObject *obval;
uint64_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obval))
return NULL;
val = qemu_py_uint64_from_pynum(obval);
if (PyErr_Occurred())
return NULL;
if (self->f)
qemu_put_be64(self->f, val);
Py_RETURN_NONE;
}
static PyObject *qemu_py_file_put_s64(qemu_py_file *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"value", NULL};
PyObject *obval;
int64_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obval))
return NULL;
val = qemu_py_int64_from_pynum(obval);
if (PyErr_Occurred())
return NULL;
if (self->f)
qemu_put_be64(self->f, val);
Py_RETURN_NONE;
}
static PyMethodDef qemu_py_file_methods[] = {
{"get_u32", (PyCFunction)qemu_py_file_get_u32, METH_NOARGS,
"Read unsigned 32-bit value"},
{"put_u32", (PyCFunction)qemu_py_file_put_u32, METH_VARARGS|METH_KEYWORDS,
"Write unsigned 32-bit value"},
{"get_u64", (PyCFunction)qemu_py_file_get_u64, METH_NOARGS,
"Read unsigned 64-bit value"},
{"put_u64", (PyCFunction)qemu_py_file_put_u64, METH_VARARGS|METH_KEYWORDS,
"Write unsigned 64-bit value"},
{"get_s64", (PyCFunction)qemu_py_file_get_s64, METH_NOARGS,
"Read signed 64-bit value"},
{"put_s64", (PyCFunction)qemu_py_file_put_s64, METH_VARARGS|METH_KEYWORDS,
"Write signed 64-bit value"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_fileType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.file", /* tp_name */
sizeof(qemu_py_file), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_file_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU File Object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
qemu_py_file_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_file_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
ptimer_state *ptimer;
} qemu_py_ptimer;
static void qemu_py_ptimer_dealloc(qemu_py_ptimer *self)
{
self->ptimer = NULL;
self->ob_type->tp_free((PyObject*)self);
}
static void qemu_py_ptimer_tick(void *opaque)
{
PyObject *fn = opaque;
PyObject *obval;
obval = PyObject_CallFunctionObjArgs(fn, NULL);
qemu_py_assert(obval);
Py_DECREF(obval);
}
static int qemu_py_ptimer_init(qemu_py_ptimer *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"tick", "freq", NULL};
PyObject *tick;
PyObject *obfreq = NULL;
uint64_t freq;
QEMUBH *bh;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
&tick, &obfreq))
return -1;
if (!PyCallable_Check(tick)) {
PyErr_SetString(PyExc_TypeError, "tick handler must be callable");
return -1;
}
if (obfreq) {
freq = qemu_py_uint64_from_pynum(obfreq);
if (PyErr_Occurred())
return -1;
} else {
freq = 0;
}
Py_INCREF(tick);
bh = qemu_bh_new(qemu_py_ptimer_tick, tick);
self->ptimer = ptimer_init(bh);
if (freq)
ptimer_set_freq(self->ptimer, freq);
return 0;
}
static PyObject *qemu_py_ptimer_run(qemu_py_ptimer *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"oneshot", NULL};
int oneshot;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &oneshot))
return NULL;
ptimer_run(self->ptimer, oneshot != 0);
Py_RETURN_NONE;
}
static PyObject *qemu_py_ptimer_stop(qemu_py_ptimer *self, PyObject *args)
{
ptimer_stop(self->ptimer);
Py_RETURN_NONE;
}
static PyObject *qemu_py_ptimer_set_limit(qemu_py_ptimer *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"limit", "reload", NULL};
PyObject *oblimit;
uint64_t limit;
int reload;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, &oblimit,
&reload))
return NULL;
limit = qemu_py_uint64_from_pynum(oblimit);
if (PyErr_Occurred())
return NULL;
ptimer_set_limit(self->ptimer, limit, reload != 0);
Py_RETURN_NONE;
}
static PyObject *qemu_py_ptimer_get(qemu_py_ptimer *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"f", NULL};
qemu_py_file *f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
&qemu_py_fileType, &f))
return NULL;
qemu_get_ptimer(f->f, self->ptimer);
Py_RETURN_NONE;
}
static PyObject *qemu_py_ptimer_put(qemu_py_ptimer *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"f", NULL};
qemu_py_file *f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
&qemu_py_fileType, &f))
return NULL;
qemu_put_ptimer(f->f, self->ptimer);
Py_RETURN_NONE;
}
static PyObject *qemu_py_ptimer_get_count(qemu_py_ptimer *self, void *closure)
{
return PyLong_FromUnsignedLongLong(ptimer_get_count(self->ptimer));
}
static int qemu_py_ptimer_set_count(qemu_py_ptimer *self, PyObject *obval,
void *closure)
{
uint64_t val;
if (obval == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete ptimer count");
return -1;
}
val = qemu_py_uint64_from_pynum(obval);
if (PyErr_Occurred())
return -1;
ptimer_set_count(self->ptimer, val);
return 0;
}
static PyGetSetDef qemu_py_ptimer_getseters [] = {
{"count", (getter)qemu_py_ptimer_get_count,
(setter)qemu_py_ptimer_set_count,
"current counter value", NULL},
{NULL} /* Sentinel */
};
static PyMethodDef qemu_py_ptimer_methods[] = {
{"run", (PyCFunction)qemu_py_ptimer_run, METH_VARARGS|METH_KEYWORDS,
"Start timer"},
{"stop", (PyCFunction)qemu_py_ptimer_stop, METH_NOARGS,
"Stop timer"},
{"set_limit", (PyCFunction)qemu_py_ptimer_set_limit,
METH_VARARGS|METH_KEYWORDS,
"Set reload value"},
{"get", (PyCFunction)qemu_py_ptimer_get, METH_VARARGS|METH_KEYWORDS,
"Restore state"},
{"put", (PyCFunction)qemu_py_ptimer_put, METH_VARARGS|METH_KEYWORDS,
"Save state"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_ptimerType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.ptimer", /* tp_name */
sizeof(qemu_py_ptimer), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_ptimer_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU Periodic Timer", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
qemu_py_ptimer_methods, /* tp_methods */
0, /* tp_members */
qemu_py_ptimer_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_ptimer_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
render_data *rdata;
} qemu_py_palette;
static void qemu_py_palette_dealloc(qemu_py_palette *self)
{
self->rdata = NULL;
self->ob_type->tp_free((PyObject*)self);
}
static int qemu_py_palette_init(qemu_py_palette *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"rdata", NULL};
PyObject *obdata;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obdata))
return -1;
self->rdata = PyCObject_AsVoidPtr(obdata);
if (PyErr_Occurred())
return -1;
return 0;
}
static Py_ssize_t qemu_py_palette_len(PyObject *o)
{
return 256;
}
static PyObject *qemu_py_palette_getitem(PyObject *o, Py_ssize_t i)
{
qemu_py_palette *self = (qemu_py_palette *)o;
if (i < 0 || i >= 256) {
PyErr_SetString(PyExc_ValueError, "Palette index must be 0-255");
return NULL;
}
return PyLong_FromUnsignedLong(get_palette_value(self->rdata, i));
}
static int qemu_py_palette_setitem(PyObject *o, Py_ssize_t i, PyObject *obval)
{
qemu_py_palette *self = (qemu_py_palette *)o;
uint32_t val;
if (i < 0 || i >= 256) {
PyErr_SetString(PyExc_ValueError, "Palette index must be 0-255");
return -1;
}
val = PyLong_AsUnsignedLongMask(obval);
if (PyErr_Occurred())
return -1;
set_palette_value(self->rdata, i, val);
return 0;
}
static PySequenceMethods qemu_py_palette_seq = {
.sq_length = qemu_py_palette_len,
.sq_item = qemu_py_palette_getitem,
.sq_ass_item = qemu_py_palette_setitem
};
static PyTypeObject qemu_py_paletteType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.palette", /* tp_name */
sizeof(qemu_py_palette), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_palette_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&qemu_py_palette_seq, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU Framebuffer Palette", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_palette_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
render_data *rdata;
DisplayState *ds;
PyObject *update_cb;
PyObject *palette;
int need_update;
} qemu_py_render;
static void qemu_py_render_dealloc(qemu_py_render *self)
{
if (self->rdata)
destroy_render_data(self->rdata);
self->rdata = NULL;
Py_CLEAR(self->palette);
self->ob_type->tp_free((PyObject*)self);
}
static void qemu_py_update_display(void *opaque)
{
qemu_py_render *self = opaque;
PyObject *obval;
PyObject *fn;
int do_update;
if (nographic || ds_get_bits_per_pixel(self->ds) == 0)
return;
fn = self->update_cb;
if (fn && PyCallable_Check(fn)) {
obval = PyObject_CallFunctionObjArgs(fn, NULL);
qemu_py_assert(obval);
do_update = PyInt_AsLong(obval);
qemu_py_assert(!PyErr_Occurred());
Py_DECREF(obval);
if (!do_update)
return;
}
render(self->ds, self->rdata, self->need_update);
self->need_update = 0;
}
static void qemu_py_invalidate_display(void *opaque)
{
qemu_py_render *self = opaque;
self->need_update = 1;
}
static int qemu_py_render_init(qemu_py_render *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"name", "width", "height", NULL};
int width;
int height;
char *name;
PyObject *ob;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", kwlist,
&name, &width, &height))
return -1;
self->ds = gui_get_graphic_console(name,
qemu_py_update_display,
qemu_py_invalidate_display,
NULL, self);
if (!self->ds) {
PyErr_SetString(PyExc_AssertionError, "Display creation failed");
return -1;
}
if (width && height) {
if (!gui_resize_vt(self->ds, width, height)) {
/* can't resize */
width = height = 0;
}
}
if (!width)
width = ds_get_width(self->ds);
if (!height)
height = ds_get_height(self->ds);
self->rdata = create_render_data();
set_cols(self->rdata, width);
set_rows(self->rdata, height);
ob = PyCObject_FromVoidPtr(self->rdata, NULL);
if (!ob)
return -1;
self->palette =
PyObject_CallFunctionObjArgs((PyObject *)&qemu_py_paletteType,
ob, NULL);
if (!self->palette)
return -1;
Py_DECREF(ob);
return 0;
}
static PyObject *qemu_py_render_get(qemu_py_render *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"f", NULL};
qemu_py_file *f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
&qemu_py_fileType, &f))
return NULL;
qemu_get_render_data(f->f, self->rdata);
Py_RETURN_NONE;
}
static PyObject *qemu_py_render_put(qemu_py_render *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"f", NULL};
qemu_py_file *f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist,
&qemu_py_fileType, &f))
return NULL;
qemu_put_render_data(f->f, self->rdata);
Py_RETURN_NONE;
}
static PyMemberDef qemu_py_render_members[] = {
{"update", T_OBJECT, offsetof(qemu_py_render, update_cb), 0,
"Display update callback"},
{"palette", T_OBJECT, offsetof(qemu_py_render, palette), 0,
"palette"},
{NULL} /* Sentinel */
};
enum {
QEMU_PY_FB_BASE,
QEMU_PY_FB_WIDTH,
QEMU_PY_FB_HEIGHT,
QEMU_PY_FB_ORIENTATION,
QEMU_PY_FB_BLANK,
QEMU_PY_FB_BPP,
QEMU_PY_FB_COLOR_ORDER,
QEMU_PY_FB_BYTE_ORDER,
QEMU_PY_FB_PIXEL_ORDER,
QEMU_PY_FB_ROW_PITCH
};
static PyObject *
qemu_py_render_getattr(qemu_py_render *self, void *closure)
{
/* FIXME: should be target_phys_addr_t? */
uint32_t val;
render_data *rdata = self->rdata;
switch ((size_t)closure) {
case QEMU_PY_FB_BASE:
val = get_fb_base_in_target(rdata);
break;
case QEMU_PY_FB_WIDTH:
val = get_cols(rdata);
break;
case QEMU_PY_FB_HEIGHT:
val = get_rows(rdata);
break;
case QEMU_PY_FB_ORIENTATION:
val = get_orientation(rdata);
break;
case QEMU_PY_FB_BLANK:
val = get_blank_mode(rdata);
break;
case QEMU_PY_FB_BPP:
switch (get_src_bpp(rdata)) {
case BPP_SRC_1: val = 1; break;
case BPP_SRC_2: val = 2; break;
case BPP_SRC_4: val = 4; break;
case BPP_SRC_8: val = 8; break;
case BPP_SRC_15: val = 15; break;
case BPP_SRC_16: val = 16; break;
case BPP_SRC_24: val = 24; break;
case BPP_SRC_32: val = 32; break;
default: val = 0; break;
}
break;
case QEMU_PY_FB_COLOR_ORDER:
val = get_color_order(rdata);
break;
case QEMU_PY_FB_BYTE_ORDER:
val = get_byte_order(rdata);
break;
case QEMU_PY_FB_PIXEL_ORDER:
val = get_pixel_order(rdata);
break;
case QEMU_PY_FB_ROW_PITCH:
val = get_row_pitch(rdata);
break;
default:
val = -1;
}
return PyLong_FromUnsignedLong(val);
}
static int
qemu_py_render_setattr(qemu_py_render *self, PyObject *obval, void *closure)
{
/* FIXME: should be target_phys_addr_t? */
uint32_t val;
render_data *rdata = self->rdata;
if (obval == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete attribute");
return -1;
}
val = PyInt_AsUnsignedLongMask(obval);
if (PyErr_Occurred())
return -1;
switch ((size_t)closure) {
case QEMU_PY_FB_BASE:
set_fb_base_from_target(rdata, val);
break;
case QEMU_PY_FB_WIDTH:
set_cols(rdata, val);
break;
case QEMU_PY_FB_HEIGHT:
set_rows(rdata, val);
break;
case QEMU_PY_FB_ORIENTATION:
set_orientation(rdata, val);
break;
case QEMU_PY_FB_BLANK:
set_blank_mode(rdata, val);
break;
case QEMU_PY_FB_BPP:
switch (val) {
case 1: val = BPP_SRC_1; break;
case 2: val = BPP_SRC_2; break;
case 4: val = BPP_SRC_4; break;
case 8: val = BPP_SRC_8; break;
case 15: val = BPP_SRC_15; break;
case 16: val = BPP_SRC_16; break;
case 24: val = BPP_SRC_24; break;
case 32: val = BPP_SRC_32; break;
default: val = get_src_bpp(rdata); break;
}
set_src_bpp(rdata, val);
break;
case QEMU_PY_FB_COLOR_ORDER:
set_color_order(rdata, val);
break;
case QEMU_PY_FB_BYTE_ORDER:
set_byte_order(rdata, val);
break;
case QEMU_PY_FB_PIXEL_ORDER:
set_pixel_order(rdata, val);
break;
case QEMU_PY_FB_ROW_PITCH:
set_row_pitch(rdata, val);
break;
default:
val = -1;
break;
}
return 0;
}
static PyGetSetDef qemu_py_render_getseters [] = {
{"base", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"base address", (void *)(size_t)QEMU_PY_FB_BASE},
{"width", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"screen width", (void *)(size_t)QEMU_PY_FB_WIDTH},
{"height", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"screen height", (void *)(size_t)QEMU_PY_FB_HEIGHT},
{"orientation", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"screen orientation", (void *)(size_t)QEMU_PY_FB_ORIENTATION},
{"blank", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"blanking mode", (void *)(size_t)QEMU_PY_FB_BLANK},
{"bpp", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"color depth", (void *)(size_t)QEMU_PY_FB_BPP},
{"color_order", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"color order", (void *)(size_t)QEMU_PY_FB_COLOR_ORDER},
{"byte_order", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"byte order", (void *)(size_t)QEMU_PY_FB_BYTE_ORDER},
{"pixel_order", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"pixel packing order", (void *)(size_t)QEMU_PY_FB_PIXEL_ORDER},
{"row_pitch", (getter)qemu_py_render_getattr,
(setter)qemu_py_render_setattr,
"row pitch", (void *)(size_t)QEMU_PY_FB_ROW_PITCH},
{NULL} /* Sentinel */
};
static PyMethodDef qemu_py_render_methods[] = {
{"get", (PyCFunction)qemu_py_render_get, METH_VARARGS|METH_KEYWORDS,
"Restore state"},
{"put", (PyCFunction)qemu_py_render_put, METH_VARARGS|METH_KEYWORDS,
"Save state"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_renderType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.render", /* tp_name */
sizeof(qemu_py_render), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_render_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU Framebuffer Render Engine", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
qemu_py_render_methods, /* tp_methods */
qemu_py_render_members, /* tp_members */
qemu_py_render_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_render_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
CharDriverState *chr;
PyObject *can_receive_cb;
PyObject *receive_cb;
PyObject *event_cb;
} qemu_py_chardev;
static void qemu_py_chardev_dealloc(qemu_py_chardev *self)
{
Py_CLEAR(self->can_receive_cb);
Py_CLEAR(self->receive_cb);
Py_CLEAR(self->event_cb);
self->ob_type->tp_free((PyObject*)self);
}
static int qemu_py_chardev_can_receive(void *opaque)
{
qemu_py_chardev *self = opaque;
PyObject *obval;
int val;
if (!self->can_receive_cb)
return 0;
obval = PyObject_CallFunctionObjArgs(self->can_receive_cb, NULL);
qemu_py_assert(obval);
val = PyInt_AsLong(obval);
qemu_py_assert(!PyErr_Occurred());
Py_DECREF(obval);
return val;
}
static void qemu_py_chardev_receive(void *opaque, const uint8_t *buf, int size)
{
qemu_py_chardev *self = opaque;
PyObject *obval;
PyObject *obbuf;
if (!self->receive_cb)
return;
obbuf = PyBuffer_FromMemory((void *)buf, size);
qemu_py_assert(obbuf);
obval = PyObject_CallFunctionObjArgs(self->receive_cb, obbuf, NULL);
Py_DECREF(obbuf);
qemu_py_assert(obval);
Py_DECREF(obval);
}
static void qemu_py_chardev_event(void *opaque, int event)
{
qemu_py_chardev *self = opaque;
PyObject *obval;
PyObject *obevent;
if (!self->event_cb)
return;
obevent = PyInt_FromLong(event);
qemu_py_assert(obevent);
obval = PyObject_CallFunctionObjArgs(self->event_cb, obevent, NULL);
Py_DECREF(obevent);
qemu_py_assert(obval);
Py_DECREF(obval);
}
static int qemu_py_chardev_init(qemu_py_chardev *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"chardev", NULL};
PyObject *obchr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obchr))
return -1;
self->chr = PyCObject_AsVoidPtr(obchr);
if (PyErr_Occurred())
return -1;
if (self->chr) {
qemu_chr_add_handlers(self->chr, qemu_py_chardev_can_receive,
qemu_py_chardev_receive,
qemu_py_chardev_event, self);
}
return 0;
}
static PyObject *qemu_py_chardev_handle_connect(qemu_py_chardev *self,
PyObject *args)
{
if (!self->chr)
Py_RETURN_NONE;
qemu_chr_connect(self->chr);
Py_RETURN_NONE;
}
static PyObject *qemu_py_chardev_set_handlers(qemu_py_chardev *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"can_receive", "receive", "event", NULL};
PyObject *obcan_receive;
PyObject *obreceive;
PyObject *obevent = Py_None;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist,
&obcan_receive, &obreceive, &obevent))
return NULL;
if (!self->chr)
Py_RETURN_NONE;
Py_CLEAR(self->can_receive_cb);
Py_CLEAR(self->receive_cb);
Py_CLEAR(self->event_cb);
if (!PyCallable_Check(obcan_receive)) {
PyErr_SetString(PyExc_TypeError,
"can_receive handler must be callable");
return NULL;
}
self->can_receive_cb = obcan_receive;
Py_INCREF(obcan_receive);
if (!PyCallable_Check(obreceive)) {
PyErr_SetString(PyExc_TypeError, "receive handler must be callable");
return NULL;
}
self->receive_cb = obreceive;
Py_INCREF(obreceive);
if (obevent != Py_None) {
if (!PyCallable_Check(obreceive)) {
PyErr_SetString(PyExc_TypeError, "event handler must be callable");
return NULL;
}
self->event_cb = obevent;
Py_INCREF(obevent);
}
Py_RETURN_NONE;
}
static PyObject *qemu_py_chardev_write(qemu_py_chardev *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"data", NULL};
PyObject *obdata;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obdata))
return NULL;
if (!self->chr)
Py_RETURN_NONE;
if (PyString_Check(obdata)) {
char *data;
Py_ssize_t len;
if (PyString_AsStringAndSize(obdata, &data, &len) < 0)
return NULL;
qemu_chr_write(self->chr, (uint8_t *)data, len);
} else {
uint8_t ch;
ch = PyInt_AsLong(obdata);
if (PyErr_Occurred())
return NULL;
qemu_chr_write(self->chr, &ch, 1);
}
Py_RETURN_NONE;
}
static PyMemberDef qemu_py_chardev_members[] = {
{NULL} /* Sentinel */
};
static PyMethodDef qemu_py_chardev_methods[] = {
{"handle_connect", (PyCFunction)qemu_py_chardev_handle_connect,
METH_NOARGS,
"Handle character device connect if required"},
{"set_handlers", (PyCFunction)qemu_py_chardev_set_handlers,
METH_VARARGS|METH_KEYWORDS,
"Set event handlers"},
{"write", (PyCFunction)qemu_py_chardev_write, METH_VARARGS|METH_KEYWORDS,
"Write a byte or string"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_chardevType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.chardev", /* tp_name */
sizeof(qemu_py_chardev), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_chardev_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"QEMU Character Device", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
qemu_py_chardev_methods, /* tp_methods */
qemu_py_chardev_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_chardev_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct {
PyObject_HEAD
QEMUDevice *qdev;
} qemu_py_devclass;
static void qemu_py_devclass_dealloc(qemu_py_devclass *self)
{
self->ob_type->tp_free((PyObject*)self);
}
static int qemu_py_devclass_init(qemu_py_devclass *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"qdev", NULL};
PyObject *obqdev;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obqdev))
return -1;
self->qdev = PyCObject_AsVoidPtr(obqdev);
if (PyErr_Occurred())
return -1;
return 0;
}
/* FIXME: Turn ths into an actual IRQ object? */
static PyObject *qemu_py_set_irq_level(qemu_py_devclass *self, PyObject *args)
{
int ok;
int irq_num;
int level;
ok = PyArg_ParseTuple(args, "ii", &irq_num, &level);
if (!ok)
return NULL;
if (!self->qdev) {
PyErr_SetString(PyExc_ValueError, "device not initialized");
return NULL;
}
qdev_set_irq_level(self->qdev, irq_num, level);
Py_RETURN_NONE;
}
/* PyLong_AsUnsignedLongLongMask doesn't seem to work on Int objects.
This wrapper automatically picks the right routine. */
static target_phys_addr_t qemu_py_physaddr_from_pynum(PyObject *ob)
{
if (PyInt_Check(ob))
return PyInt_AsUnsignedLongMask(ob);
return PyLong_AsUnsignedLongLongMask(ob);
}
static PyObject *qemu_py_dma_readb(qemu_py_chardev *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"addr", NULL};
PyObject *obaddr;
target_phys_addr_t addr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obaddr))
return NULL;
addr = qemu_py_physaddr_from_pynum(obaddr);
if (PyErr_Occurred())
return NULL;
return PyInt_FromLong(ldub_phys(addr));
}
static PyObject *qemu_py_dma_writeb(qemu_py_chardev *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"addr", "value", NULL};
PyObject *obaddr;
PyObject *obval;
target_phys_addr_t addr;
uint8_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist,
&obaddr, &obval))
return NULL;
addr = qemu_py_physaddr_from_pynum(obaddr);
if (PyErr_Occurred())
return NULL;
val = PyInt_AsLong(obval);
if (PyErr_Occurred())
return NULL;
stb_phys(addr, val);
Py_RETURN_NONE;
}
static PyObject *qemu_py_dma_readl(qemu_py_chardev *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"addr", NULL};
PyObject *obaddr;
target_phys_addr_t addr;
uint8_t buf[4];
uint32_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obaddr))
return NULL;
addr = qemu_py_physaddr_from_pynum(obaddr);
if (PyErr_Occurred())
return NULL;
if (addr & 3) {
cpu_physical_memory_read(addr, buf, 4);
val = ldl_p(buf);
} else {
val = ldl_phys(addr);
}
return PyLong_FromUnsignedLong(val);
}
static PyObject *qemu_py_dma_writel(qemu_py_chardev *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"addr", "value", NULL};
PyObject *obaddr;
PyObject *obval;
target_phys_addr_t addr;
uint8_t buf[4];
uint32_t val;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist,
&obaddr, &obval))
return NULL;
addr = qemu_py_physaddr_from_pynum(obaddr);
if (PyErr_Occurred())
return NULL;
val = PyInt_AsLong(obval);
if (PyErr_Occurred())
return NULL;
if (addr & 3) {
stl_p(buf, val);
cpu_physical_memory_write(addr, buf, 4);
} else {
stl_phys(addr, val);
}
Py_RETURN_NONE;
}
static void qemu_py_set_irq_input(void *opaque, int irq, int level)
{
PyObject *fn = opaque;
PyObject *obirq;
PyObject *oblevel;
PyObject *obval;
obirq = PyInt_FromLong(irq);
qemu_py_assert(obirq);
oblevel = level ? Py_True : Py_False;
Py_INCREF(oblevel);
obval = PyObject_CallFunctionObjArgs(fn, obirq, oblevel, NULL);
qemu_py_assert(obval);
Py_DECREF(obval);
Py_DECREF(obirq);
Py_DECREF(oblevel);
}
static PyObject *qemu_py_create_interrupts(qemu_py_devclass *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"callback", "count", NULL};
PyObject *obcallback;
int count;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist,
&obcallback, &count))
return NULL;
if (!PyCallable_Check(obcallback)) {
PyErr_SetString(PyExc_TypeError, "irq handler must be callable");
return NULL;
}
Py_INCREF(obcallback);
qdev_create_interrupts(self->qdev, qemu_py_set_irq_input, obcallback,
count);
Py_RETURN_NONE;
}
static PyObject *qemu_py_dummy_loadsave(qemu_py_devclass *self, PyObject *args)
{
Py_RETURN_NONE;
}
static PyMemberDef qemu_py_devclass_members[] = {
#if 0
{"irqs", T_INT, offsetof(qemu_py_devclass, num_irqs), 0,
"Number of irqs"},
#endif
{NULL} /* Sentinel */
};
static PyMethodDef qemu_py_devclass_methods[] = {
{"set_irq_level", (PyCFunction)qemu_py_set_irq_level, METH_VARARGS,
"Set IRQ state"},
{"create_interrupts", (PyCFunction)qemu_py_create_interrupts,
METH_VARARGS|METH_KEYWORDS,
"Create IRQ inputs"},
{"dma_readb", (PyCFunction)qemu_py_dma_readb, METH_VARARGS|METH_KEYWORDS,
"Read a byte from system memory"},
{"dma_writeb", (PyCFunction)qemu_py_dma_writeb, METH_VARARGS|METH_KEYWORDS,
"Write a byte to system memory"},
{"dma_readl", (PyCFunction)qemu_py_dma_readl, METH_VARARGS|METH_KEYWORDS,
"Read a 32-bit word from system memory"},
{"dma_writel", (PyCFunction)qemu_py_dma_writel, METH_VARARGS|METH_KEYWORDS,
"Write a 32-bit word to system memory"},
{"load", (PyCFunction)qemu_py_dummy_loadsave, METH_VARARGS,
"load snapshot state"},
{"save", (PyCFunction)qemu_py_dummy_loadsave, METH_VARARGS,
"save snapshot state"},
{NULL} /* Sentinel */
};
static PyTypeObject qemu_py_devclassType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"qemu.devclass", /* tp_name */
sizeof(qemu_py_devclass), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)qemu_py_devclass_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
"QEMU device class objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
qemu_py_devclass_methods, /* tp_methods */
qemu_py_devclass_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)qemu_py_devclass_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
typedef struct
{
PyObject *dev;
int n;
PyObject *region;
} qemu_py_callback_info;
static uint32_t qemu_py_read(void *opaque, target_phys_addr_t offset)
{
qemu_py_callback_info *info = opaque;
PyObject *r;
PyObject *fn;
PyObject *oboffset;
PyObject *obval;
uint32_t val;
r = info->region;
qemu_py_assert(r);
fn = PyObject_GetAttrString(r, "readl");
qemu_py_assert(fn);
oboffset = PyInt_FromLong(offset);
obval = PyObject_CallFunctionObjArgs(fn, info->dev, oboffset, NULL);
Py_DECREF(oboffset);
Py_DECREF(fn);
qemu_py_assert(obval);
val = PyLong_AsUnsignedLongMask(obval);
qemu_py_assert(!PyErr_Occurred());
Py_DECREF(obval);
return val;
}
static void qemu_py_write(void *opaque, target_phys_addr_t offset,
uint32_t value)
{
qemu_py_callback_info *info = opaque;
PyObject *r;
PyObject *fn;
PyObject *oboffset;
PyObject *obval;
r = info->region;
qemu_py_assert(r);
fn = PyObject_GetAttrString(r, "writel");
qemu_py_assert(fn);
oboffset = PyInt_FromLong(offset);
obval = PyLong_FromUnsignedLong(value);
PyObject_CallFunctionObjArgs(fn, info->dev, oboffset, obval, NULL);
Py_DECREF(oboffset);
Py_DECREF(obval);
Py_DECREF(fn);
qemu_py_assert(!PyErr_Occurred());
}
static CPUReadMemoryFunc *qemu_py_readfn[] = {
qemu_py_read,
qemu_py_read,
qemu_py_read
};
static CPUWriteMemoryFunc *qemu_py_writefn[] = {
qemu_py_write,
qemu_py_write,
qemu_py_write
};
static void qemu_py_dev_create(QEMUDevice *dev)
{
PyObject *ob;
PyObject *devclass;
PyObject *regions;
PyObject *obqdev;
qemu_py_devclass *self;
qemu_py_callback_info *callbacks;
Py_ssize_t pos;
PyObject *properties;
PyObject *key;
PyObject *value;
int num_regions;
int i;
devclass = qdev_get_class_opaque(dev);
obqdev = PyCObject_FromVoidPtr(dev, NULL);
ob = PyObject_CallFunctionObjArgs(devclass, obqdev, NULL);
qemu_py_assert(ob);
Py_DECREF(obqdev);
self = (qemu_py_devclass *)ob;
self->qdev = dev;
qdev_set_opaque(dev, ob);
value = PyString_FromString(qdev_get_name(dev));
PyObject_SetAttrString(ob, "name", value);
regions = PyObject_GetAttrString(devclass, "regions");
qemu_py_assert(regions);
num_regions = PyList_Size(regions);
callbacks = qemu_mallocz(num_regions * sizeof(callbacks[0]));
for (i = 0; i < num_regions; i++) {
callbacks[i].dev = ob;
callbacks[i].n = i;
callbacks[i].region = PyList_GetItem(regions, i);
Py_INCREF(callbacks[i].region);
qdev_set_region_opaque(dev, i, &callbacks[i]);
}
Py_DECREF(regions);
properties = PyObject_GetAttrString(devclass, "properties");
qemu_py_assert(properties);
pos = 0;
while (PyDict_Next(properties, &pos, &key, &value)) {
char *key_name = PyString_AsString(key);
qemu_py_assert(key_name);
if (strcmp(key_name, "chardev") == 0) {
CharDriverState *chr;
PyObject *obchr;
chr = qdev_get_chardev(dev);
obchr = PyCObject_FromVoidPtr(chr, NULL);
qemu_py_assert(obchr);
value =
PyObject_CallFunctionObjArgs((PyObject *)&qemu_py_chardevType,
obchr, NULL);
qemu_py_assert(value);
Py_DECREF(obchr);
} else if (PyLong_Check(value) || PyInt_Check(value)) {
long intval;
intval = qdev_get_property_int(dev, key_name);
value = PyInt_FromLong(intval);
} else {
const char *strval;
strval = qdev_get_property_string(dev, key_name);
value = PyString_FromString(strval);
}
if (PyDict_SetItem(properties, key, value) < 0) {
Py_DECREF(value);
}
}
Py_DECREF(properties);
PyObject_CallMethod(ob, "create", NULL);
qemu_py_assert(!PyErr_Occurred());
}
static PyObject *qemu_py_create_file(QEMUFile *f)
{
PyObject *obfile;
PyObject *obarg;
obarg = PyCObject_FromVoidPtr(f, NULL);
if (!obarg)
return NULL;
obfile = PyObject_CallFunctionObjArgs((PyObject *)&qemu_py_fileType,
obarg, NULL);
Py_DECREF(obarg);
return obfile;
}
static void qemu_py_save(QEMUFile *f, void *opaque)
{
PyObject *self = opaque;
PyObject *obfile;
PyObject *obresult;
obfile = qemu_py_create_file(f);
qemu_py_assert(obfile);
obresult = PyObject_CallMethod(self, "save", "O", obfile);
qemu_py_assert(obresult);
Py_DECREF(obfile);
Py_DECREF(obresult);
}
static int qemu_py_load(QEMUFile *f, void *opaque, int version_id)
{
PyObject *self = opaque;
PyObject *obfile;
PyObject *obresult;
if (version_id != 1)
return -EINVAL;
obfile = qemu_py_create_file(f);
qemu_py_assert(obfile);
obresult = PyObject_CallMethod(self, "load", "O", obfile);
/* TODO: Graceful error handling. */
qemu_py_assert(obresult);
Py_DECREF(obfile);
Py_DECREF(obresult);
return 0;
}
static PyObject *qemu_py_register_device(PyObject *self, PyObject *args)
{
int ok;
QEMUDeviceClass *dc;
PyObject *devclass;
PyObject *regions;
PyObject *attr;
PyObject *properties;
Py_ssize_t pos;
PyObject *key;
PyObject *value;
int i;
char *name;
int num_irqs;
ok = PyArg_ParseTuple(args, "O", &devclass);
if (!ok)
return NULL;
if (!PyType_Check(devclass)
|| !PyObject_IsSubclass(devclass, (PyObject *)&qemu_py_devclassType)) {
PyErr_SetString(PyExc_TypeError, "Expected qemu.devclass derivative");
return NULL;
}
Py_INCREF(devclass);
attr = PyObject_GetAttrString(devclass, "irqs");
if (!attr)
return NULL;
num_irqs = PyInt_AsLong(attr);
if (PyErr_Occurred())
return NULL;
Py_DECREF(attr);
attr = PyObject_GetAttrString(devclass, "name");
if (!attr)
return NULL;
name = PyString_AsString(attr);
if (PyErr_Occurred())
return NULL;
dc = qdev_new(name, qemu_py_dev_create, num_irqs);
Py_DECREF(attr);
qdev_add_class_opaque(dc, devclass);
regions = PyObject_GetAttrString(devclass, "regions");
if (!regions)
return NULL;
if (!PyList_Check(regions)) {
PyErr_SetString(PyExc_TypeError, "devclass.regions must be a list");
return NULL;
}
for (i = 0; i < PyList_Size(regions); i++) {
target_phys_addr_t size;
PyObject *r;
r = PyList_GetItem(regions, i);
if (!r)
return NULL;
attr = PyObject_GetAttrString(r, "size");
size = PyInt_AsLong(attr);
if (PyErr_Occurred())
return NULL;
qdev_add_registers(dc, qemu_py_readfn, qemu_py_writefn, size);
Py_DECREF(attr);
}
Py_DECREF(regions);
properties = PyObject_GetAttrString(devclass, "properties");
if (!properties)
return NULL;
if (!PyDict_Check(properties)) {
Py_DECREF(properties);
PyErr_SetString(PyExc_TypeError,
"qemu.devclass.properties must be a dict");
return NULL;
}
pos = 0;
while (PyDict_Next(properties, &pos, &key, &value)) {
char *key_name = PyString_AsString(key);
if (!key_name)
goto bad_property;
if (strcmp(key_name, "chardev") == 0) {
qdev_add_chardev(dc);
} else if (PyString_Check(value)) {
qdev_add_property_string(dc, key_name, PyString_AsString(value));
} else if (PyLong_Check(value) || PyInt_Check(value)) {
long intval;
intval = PyLong_AsLong(value);
if (PyErr_Occurred())
goto bad_property;
qdev_add_property_int(dc, key_name, PyLong_AsLong(value));
} else {
PyErr_SetString(PyExc_TypeError,
"qemu.devclass.properties value must be int or string");
bad_property:
Py_DECREF(properties);
return NULL;
}
}
Py_DECREF(properties);
/* TODO: Implement savevm versioning. */
qdev_add_savevm(dc, 1, qemu_py_save, qemu_py_load);
Py_RETURN_NONE;
}
static PyObject *qemu_py_get_clock(PyObject *self, PyObject *args)
{
return PyLong_FromUnsignedLongLong((uint64_t)qemu_get_clock(vm_clock));
}
static PyObject *qemu_py_start_time(PyObject *self, PyObject *args)
{
struct tm tm;
uint64_t t;
qemu_get_timedate(&tm, 0);
t = mktimegm(&tm);
return PyLong_FromUnsignedLongLong(t);
}
static void qemu_py_keyboard_event(void *opaque, int keycode)
{
PyObject *fn = opaque;
PyObject *obval;
PyObject *obkey;
obkey = PyInt_FromLong(keycode);
obval = PyObject_CallFunctionObjArgs(fn, obkey, NULL);
qemu_py_assert(obval);
Py_DECREF(obkey);
Py_DECREF(obval);
}
static PyObject *qemu_py_register_keyboard(PyObject *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"handler", NULL};
PyObject *fn;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fn))
return NULL;
if (!PyCallable_Check(fn)) {
PyErr_SetString(PyExc_TypeError, "keyboard handler must be callable");
return NULL;
}
Py_INCREF(fn);
gui_register_dev_key_callback(qemu_py_keyboard_event, fn);
Py_RETURN_NONE;
}
static void qemu_py_mouse_event(void *opaque, int dx, int dy, int dz,
int buttons)
{
PyObject *fn = opaque;
PyObject *obval;
PyObject *obx;
PyObject *oby;
PyObject *obz;
PyObject *obbuttons;
obx = PyInt_FromLong(dx);
oby = PyInt_FromLong(dy);
obz = PyInt_FromLong(dz);
obbuttons = PyInt_FromLong(buttons);
obval = PyObject_CallFunctionObjArgs(fn, obx, oby, obz, obbuttons, NULL);
qemu_py_assert(obval);
Py_DECREF(obx);
Py_DECREF(oby);
Py_DECREF(obz);
Py_DECREF(obbuttons);
Py_DECREF(obval);
}
static PyObject *qemu_py_register_mouse(PyObject *self, PyObject *args,
PyObject *kwds)
{
static char *kwlist[] = {"handler", "absolute", NULL};
PyObject *fn;
int absolute;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, &fn, &absolute))
return NULL;
if (!PyCallable_Check(fn)) {
PyErr_SetString(PyExc_TypeError, "mouse handler must be callable");
return NULL;
}
Py_INCREF(fn);
gui_register_mouse_event_handler(qemu_py_mouse_event, fn, absolute, "dev");
Py_RETURN_NONE;
}
static PyMethodDef qemu_py_methods[] =
{
{"register_device", qemu_py_register_device, METH_VARARGS,
"Register new device class"},
{"get_clock", qemu_py_get_clock, METH_NOARGS,
"Get current virtual time"},
{"start_time", qemu_py_start_time, METH_NOARGS,
"Get VM start time (seconds sice epoch)"},
{"register_keyboard", (PyCFunction)qemu_py_register_keyboard,
METH_VARARGS|METH_KEYWORDS,
"Register keyboard event handler"},
{"register_mouse", (PyCFunction)qemu_py_register_mouse,
METH_VARARGS|METH_KEYWORDS,
"Register mouse event handler"},
{NULL}
};
static void qemu_py_add_class(PyObject *module, const char *name,
PyTypeObject *type)
{
if (!type->tp_new)
type->tp_new = PyType_GenericNew;
if (PyType_Ready(type) < 0)
qemu_py_die();
Py_INCREF(type);
PyModule_AddObject(module, name, (PyObject *)type);
}
static void qemu_py_init_interface(void)
{
PyObject *m;
m = Py_InitModule("qemu", qemu_py_methods);
/* FIXME: Add default attributes to qemu_py_devclass. */
qemu_py_add_class(m, "devclass", &qemu_py_devclassType);
qemu_py_add_class(m, "ioregion", &qemu_py_ioregionType);
qemu_py_add_class(m, "chardev", &qemu_py_chardevType);
qemu_py_add_class(m, "render", &qemu_py_renderType);
qemu_py_add_class(m, "palette", &qemu_py_paletteType);
qemu_py_add_class(m, "ptimer", &qemu_py_ptimerType);
qemu_py_add_class(m, "file", &qemu_py_fileType);
}
#define PLUGIN_INIT_SCRIPT "import sys\nsys.path.insert(0, \"%s/plugins\")"
void qemu_python_init(char *argv0)
{
char *buf;
Py_SetProgramName(argv0);
Py_Initialize();
/* Nasty hack to look for plugin modules. */
buf = qemu_mallocz(strlen(bios_dir) + strlen(PLUGIN_INIT_SCRIPT));
sprintf(buf, PLUGIN_INIT_SCRIPT, bios_dir);
#ifdef _WIN32
{
char *p;
/* Windows path separators (backslash) are interpreted as escape
characters in a python string. Fortunately python also accepts
unix path separators (forward slash), so use those instead. */
for (p = buf; *p; p++) {
if (*p == '\\')
*p = '/';
}
}
#endif
PyRun_SimpleString(buf);
qemu_free(buf);
qemu_py_init_interface();
qemu_py_load_module("qemu_arm_plugins");
}