1 /*- 2 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include "opt_acpi.h" 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/bus.h> 34 #include <sys/ioccom.h> 35 #include <sys/sysctl.h> 36 37 #include "acpi.h" 38 #include <dev/acpica/acpivar.h> 39 #include <dev/acpica/acpiio.h> 40 41 MALLOC_DEFINE(M_ACPIBATT, "acpibatt", "ACPI generic battery data"); 42 43 struct acpi_batteries { 44 TAILQ_ENTRY(acpi_batteries) link; 45 struct acpi_battdesc battdesc; 46 }; 47 48 static TAILQ_HEAD(,acpi_batteries) acpi_batteries; 49 static int acpi_batteries_initted = 0; 50 static int acpi_batteries_units = 0; 51 static int acpi_battery_info_expire = 5; 52 static struct acpi_battinfo acpi_battery_battinfo; 53 54 int 55 acpi_battery_get_units(void) 56 { 57 return (acpi_batteries_units); 58 } 59 60 int 61 acpi_battery_get_battdesc(int logical_unit, struct acpi_battdesc *battdesc) 62 { 63 struct acpi_batteries *bp; 64 int i; 65 66 if (logical_unit < 0 || logical_unit >= acpi_batteries_units) 67 return (ENXIO); 68 69 i = 0; 70 TAILQ_FOREACH(bp, &acpi_batteries, link) { 71 if (logical_unit == i) { 72 battdesc->type = bp->battdesc.type; 73 battdesc->phys_unit = bp->battdesc.phys_unit; 74 return (0); 75 } 76 i++; 77 } 78 79 return (ENXIO); 80 } 81 82 int 83 acpi_battery_get_battinfo(int unit, struct acpi_battinfo *battinfo) 84 { 85 struct acpi_battdesc battdesc; 86 int error; 87 88 error = 0; 89 if (unit == -1) { 90 error = acpi_cmbat_get_battinfo(-1, battinfo); 91 goto out; 92 } else { 93 error = acpi_battery_get_battdesc(unit, &battdesc); 94 if (error != 0) 95 goto out; 96 97 switch (battdesc.type) { 98 case ACPI_BATT_TYPE_CMBAT: 99 error = acpi_cmbat_get_battinfo(battdesc.phys_unit, battinfo); 100 break; 101 default: 102 error = ENXIO; 103 break; 104 } 105 } 106 107 out: 108 return (error); 109 } 110 111 int 112 acpi_battery_get_info_expire(void) 113 { 114 return (acpi_battery_info_expire); 115 } 116 117 static int 118 acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) 119 { 120 union acpi_battery_ioctl_arg *ioctl_arg; 121 int error, logical_unit; 122 123 ioctl_arg = (union acpi_battery_ioctl_arg *)addr; 124 error = 0; 125 126 /* 127 * No security check required: information retrieval only. If 128 * new functions are added here, a check might be required. 129 */ 130 switch (cmd) { 131 case ACPIIO_BATT_GET_UNITS: 132 *(int *)addr = acpi_battery_get_units(); 133 break; 134 case ACPIIO_BATT_GET_BATTDESC: 135 logical_unit = ioctl_arg->unit; 136 error = acpi_battery_get_battdesc(logical_unit, &ioctl_arg->battdesc); 137 break; 138 case ACPIIO_BATT_GET_BATTINFO: 139 logical_unit = ioctl_arg->unit; 140 error = acpi_battery_get_battinfo(logical_unit, &ioctl_arg->battinfo); 141 break; 142 default: 143 error = EINVAL; 144 break; 145 } 146 147 return (error); 148 } 149 150 static int 151 acpi_battery_sysctl(SYSCTL_HANDLER_ARGS) 152 { 153 int val, error; 154 155 acpi_battery_get_battinfo(-1, &acpi_battery_battinfo); 156 val = *(u_int *)oidp->oid_arg1; 157 error = sysctl_handle_int(oidp, &val, 0, req); 158 return (error); 159 } 160 161 static int 162 acpi_battery_init(void) 163 { 164 struct acpi_softc *sc; 165 device_t dev; 166 int error; 167 168 dev = devclass_get_device(devclass_find("acpi"), 0); 169 if (dev == NULL) 170 return (ENXIO); 171 sc = device_get_softc(dev); 172 if (sc == NULL) 173 return (ENXIO); 174 175 error = 0; 176 TAILQ_INIT(&acpi_batteries); 177 acpi_batteries_initted = 1; 178 179 error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl, 180 NULL); 181 if (error != 0) 182 return (error); 183 error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTDESC, acpi_battery_ioctl, 184 NULL); 185 if (error != 0) 186 return (error); 187 error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl, 188 NULL); 189 if (error != 0) 190 return (error); 191 192 sysctl_ctx_init(&sc->acpi_battery_sysctl_ctx); 193 sc->acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_battery_sysctl_ctx, 194 SYSCTL_CHILDREN(sc->acpi_sysctl_tree), 195 OID_AUTO, "battery", CTLFLAG_RD, 0, ""); 196 SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx, 197 SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), 198 OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD, 199 &acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I", ""); 200 SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx, 201 SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), 202 OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD, 203 &acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I", ""); 204 SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx, 205 SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), 206 OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RD, 207 &acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I", ""); 208 SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx, 209 SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), 210 OID_AUTO, "units", CTLFLAG_RD, &acpi_batteries_units, 0, ""); 211 SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx, 212 SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), 213 OID_AUTO, "info_expire", CTLFLAG_RD | CTLFLAG_RW, 214 &acpi_battery_info_expire, 0, ""); 215 216 return (error); 217 } 218 219 int 220 acpi_battery_register(int type, int phys_unit) 221 { 222 struct acpi_batteries *bp; 223 int error; 224 225 error = 0; 226 bp = malloc(sizeof(*bp), M_ACPIBATT, M_NOWAIT); 227 if (bp == NULL) 228 return (ENOMEM); 229 230 bp->battdesc.type = type; 231 bp->battdesc.phys_unit = phys_unit; 232 if (acpi_batteries_initted == 0) { 233 if ((error = acpi_battery_init()) != 0) { 234 free(bp, M_ACPIBATT); 235 return (error); 236 } 237 } 238 239 TAILQ_INSERT_TAIL(&acpi_batteries, bp, link); 240 acpi_batteries_units++; 241 242 return (0); 243 } 244 245 int 246 acpi_battery_remove(int type, int phys_unit) 247 { 248 struct acpi_batteries *bp, *tmp; 249 250 TAILQ_FOREACH_SAFE(bp, &acpi_batteries, link, tmp) { 251 if (bp->battdesc.type == type && bp->battdesc.phys_unit == phys_unit) { 252 TAILQ_REMOVE(&acpi_batteries, bp, link); 253 acpi_batteries_units--; 254 free(bp, M_ACPIBATT); 255 return (0); 256 } 257 } 258 return (ENOENT); 259 } 260