1 /* 2 * qdev GPIO helpers 3 * 4 * Copyright (c) 2009 CodeSourcery 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "hw/qdev-core.h" 22 #include "hw/irq.h" 23 #include "qapi/error.h" 24 25 static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, 26 const char *name) 27 { 28 NamedGPIOList *ngl; 29 30 QLIST_FOREACH(ngl, &dev->gpios, node) { 31 /* NULL is a valid and matchable name. */ 32 if (g_strcmp0(name, ngl->name) == 0) { 33 return ngl; 34 } 35 } 36 37 ngl = g_malloc0(sizeof(*ngl)); 38 ngl->name = g_strdup(name); 39 QLIST_INSERT_HEAD(&dev->gpios, ngl, node); 40 return ngl; 41 } 42 43 void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, 44 qemu_irq_handler handler, 45 void *opaque, 46 const char *name, int n) 47 { 48 int i; 49 NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); 50 51 assert(gpio_list->num_out == 0 || !name); 52 gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler, 53 opaque, n); 54 55 if (!name) { 56 name = "unnamed-gpio-in"; 57 } 58 for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) { 59 gchar *propname = g_strdup_printf("%s[%u]", name, i); 60 61 object_property_add_child(OBJECT(dev), propname, 62 OBJECT(gpio_list->in[i])); 63 g_free(propname); 64 } 65 66 gpio_list->num_in += n; 67 } 68 69 void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) 70 { 71 qdev_init_gpio_in_named(dev, handler, NULL, n); 72 } 73 74 void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, 75 const char *name, int n) 76 { 77 int i; 78 NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); 79 80 assert(gpio_list->num_in == 0 || !name); 81 82 if (!name) { 83 name = "unnamed-gpio-out"; 84 } 85 memset(pins, 0, sizeof(*pins) * n); 86 for (i = 0; i < n; ++i) { 87 gchar *propname = g_strdup_printf("%s[%u]", name, 88 gpio_list->num_out + i); 89 90 object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, 91 (Object **)&pins[i], 92 object_property_allow_set_link, 93 OBJ_PROP_LINK_STRONG); 94 g_free(propname); 95 } 96 gpio_list->num_out += n; 97 } 98 99 void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) 100 { 101 qdev_init_gpio_out_named(dev, pins, NULL, n); 102 } 103 104 qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n) 105 { 106 NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); 107 108 assert(n >= 0 && n < gpio_list->num_in); 109 return gpio_list->in[n]; 110 } 111 112 qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) 113 { 114 return qdev_get_gpio_in_named(dev, NULL, n); 115 } 116 117 void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, 118 qemu_irq input_pin) 119 { 120 char *propname = g_strdup_printf("%s[%d]", 121 name ? name : "unnamed-gpio-out", n); 122 if (input_pin && !OBJECT(input_pin)->parent) { 123 /* We need a name for object_property_set_link to work */ 124 object_property_add_child(container_get(qdev_get_machine(), 125 "/unattached"), 126 "non-qdev-gpio[*]", OBJECT(input_pin)); 127 } 128 object_property_set_link(OBJECT(dev), propname, 129 OBJECT(input_pin), &error_abort); 130 g_free(propname); 131 } 132 133 qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) 134 { 135 g_autofree char *propname = g_strdup_printf("%s[%d]", 136 name ? name : "unnamed-gpio-out", n); 137 138 qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, 139 NULL); 140 141 return ret; 142 } 143 144 /* disconnect a GPIO output, returning the disconnected input (if any) */ 145 146 static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, 147 const char *name, int n) 148 { 149 char *propname = g_strdup_printf("%s[%d]", 150 name ? name : "unnamed-gpio-out", n); 151 152 qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, 153 NULL); 154 if (ret) { 155 object_property_set_link(OBJECT(dev), propname, NULL, NULL); 156 } 157 g_free(propname); 158 return ret; 159 } 160 161 qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, 162 const char *name, int n) 163 { 164 qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n); 165 qdev_connect_gpio_out_named(dev, name, n, icpt); 166 return disconnected; 167 } 168 169 void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq input_pin) 170 { 171 qdev_connect_gpio_out_named(dev, NULL, n, input_pin); 172 } 173 174 void qdev_pass_gpios(DeviceState *dev, DeviceState *container, 175 const char *name) 176 { 177 int i; 178 NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name); 179 180 for (i = 0; i < ngl->num_in; i++) { 181 const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in"; 182 char *propname = g_strdup_printf("%s[%d]", nm, i); 183 184 object_property_add_alias(OBJECT(container), propname, 185 OBJECT(dev), propname); 186 g_free(propname); 187 } 188 for (i = 0; i < ngl->num_out; i++) { 189 const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out"; 190 char *propname = g_strdup_printf("%s[%d]", nm, i); 191 192 object_property_add_alias(OBJECT(container), propname, 193 OBJECT(dev), propname); 194 g_free(propname); 195 } 196 QLIST_REMOVE(ngl, node); 197 QLIST_INSERT_HEAD(&container->gpios, ngl, node); 198 } 199