1 /* $NetBSD: sysmon_envsys.c,v 1.10 2006/03/20 03:23:35 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Zembu Labs, Inc. 5 * All rights reserved. 6 * 7 * Author: Jason R. Thorpe <thorpej@zembu.com> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Zembu Labs, Inc. 20 * 4. Neither the name of Zembu Labs nor the names of its employees may 21 * be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS 25 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- 26 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- 27 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /* 37 * Environmental sensor framework for sysmon. Hardware monitors 38 * such as the LM78 and VIA 82C686A (or even ACPI, eventually) can 39 * register themselves to provide backplane fan and temperature 40 * information, etc. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys.c,v 1.10 2006/03/20 03:23:35 lukem Exp $"); 45 46 #include <sys/param.h> 47 #include <sys/conf.h> 48 #include <sys/errno.h> 49 #include <sys/fcntl.h> 50 #include <sys/lock.h> 51 #include <sys/kernel.h> 52 #include <sys/systm.h> 53 #include <sys/proc.h> 54 55 #include <dev/sysmon/sysmonvar.h> 56 57 /* 58 * We run at ENVSYS version 1. 59 */ 60 #define SYSMON_ENVSYS_VERSION (1 * 1000) 61 62 struct lock sysmon_envsys_lock; 63 64 LIST_HEAD(, sysmon_envsys) sysmon_envsys_list = 65 LIST_HEAD_INITIALIZER(&sysmon_envsys_list); 66 struct simplelock sysmon_envsys_list_slock = SIMPLELOCK_INITIALIZER; 67 u_int sysmon_envsys_next_sensor_index; 68 69 int sysmon_envsys_initialized; 70 struct simplelock sysmon_envsys_initialized_slock = SIMPLELOCK_INITIALIZER; 71 72 #define SYSMON_ENVSYS_LOCK() \ 73 lockmgr(&sysmon_envsys_lock, LK_EXCLUSIVE, NULL) 74 75 #define SYSMON_ENVSYS_UNLOCK() \ 76 lockmgr(&sysmon_envsys_lock, LK_RELEASE, NULL) 77 78 int sysmonioctl_envsys(dev_t, u_long, caddr_t, int, struct lwp *); 79 80 struct sysmon_envsys *sysmon_envsys_find(u_int); 81 void sysmon_envsys_release(struct sysmon_envsys *); 82 83 /* 84 * sysmonopen_envsys: 85 * 86 * Open the system monitor device. 87 */ 88 int 89 sysmonopen_envsys(dev_t dev, int flag, int mode, struct lwp *l) 90 { 91 simple_lock(&sysmon_envsys_initialized_slock); 92 if (sysmon_envsys_initialized == 0) { 93 lockinit(&sysmon_envsys_lock, PWAIT|PCATCH, "smenv", 0, 0); 94 sysmon_envsys_initialized = 1; 95 } 96 simple_unlock(&sysmon_envsys_initialized_slock); 97 98 return (0); 99 } 100 101 /* 102 * sysmonclose_envsys: 103 * 104 * Close the system monitor device. 105 */ 106 int 107 sysmonclose_envsys(dev_t dev, int flag, int mode, struct lwp *l) 108 { 109 110 /* Nothing to do */ 111 return (0); 112 } 113 114 /* 115 * sysmonioctl_envsys: 116 * 117 * Perform an envsys control request. 118 */ 119 int 120 sysmonioctl_envsys(dev_t dev, u_long cmd, caddr_t data, int flag, 121 struct lwp *l) 122 { 123 struct sysmon_envsys *sme; 124 int error = 0; 125 u_int oidx; 126 127 switch (cmd) { 128 /* 129 * For ENVSYS commands, we translate the absolute sensor index 130 * to a device-relative sensor index. 131 */ 132 case ENVSYS_VERSION: 133 *(int32_t *)data = SYSMON_ENVSYS_VERSION; 134 break; 135 136 case ENVSYS_GRANGE: 137 { 138 struct envsys_range *rng = (void *) data; 139 int i; 140 141 142 /* Return empty range unless we find something better */ 143 rng->low = 1; 144 rng->high = 0; 145 146 if (rng->units == -1) { 147 rng->low = 0; 148 rng->high = sysmon_envsys_next_sensor_index; 149 break; 150 } 151 152 sme = sysmon_envsys_find(0); /* XXX */ 153 if (sme == NULL) { 154 /* Return empty range for `no sensors'. */ 155 break; 156 } 157 for (i = 0; 158 sme->sme_ranges[i].low <= sme->sme_ranges[i].high; 159 i++) { 160 if (sme->sme_ranges[i].units == rng->units) { 161 *rng = sme->sme_ranges[i]; 162 break; 163 } 164 } 165 sysmon_envsys_release(sme); 166 break; 167 } 168 169 case ENVSYS_GTREDATA: 170 { 171 struct envsys_tre_data *tred = (void *) data; 172 173 tred->validflags = 0; 174 175 sme = sysmon_envsys_find(tred->sensor); 176 if (sme == NULL) 177 break; 178 oidx = tred->sensor; 179 tred->sensor = SME_SENSOR_IDX(sme, tred->sensor); 180 if (tred->sensor < sme->sme_nsensors 181 && sme->sme_gtredata != NULL) { 182 SYSMON_ENVSYS_LOCK(); 183 error = (*sme->sme_gtredata)(sme, tred); 184 SYSMON_ENVSYS_UNLOCK(); 185 } 186 tred->sensor = oidx; 187 sysmon_envsys_release(sme); 188 break; 189 } 190 191 case ENVSYS_STREINFO: 192 { 193 struct envsys_basic_info *binfo = (void *) data; 194 195 sme = sysmon_envsys_find(binfo->sensor); 196 if (sme == NULL) { 197 binfo->validflags = 0; 198 break; 199 } 200 oidx = binfo->sensor; 201 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 202 if (binfo->sensor < sme->sme_nsensors 203 && sme->sme_streinfo != NULL) { 204 SYSMON_ENVSYS_LOCK(); 205 error = (*sme->sme_streinfo)(sme, binfo); 206 SYSMON_ENVSYS_UNLOCK(); 207 } else 208 binfo->validflags = 0; 209 binfo->sensor = oidx; 210 sysmon_envsys_release(sme); 211 break; 212 } 213 214 case ENVSYS_GTREINFO: 215 { 216 struct envsys_basic_info *binfo = (void *) data; 217 218 binfo->validflags = 0; 219 220 sme = sysmon_envsys_find(binfo->sensor); 221 if (sme == NULL) 222 break; 223 oidx = binfo->sensor; 224 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 225 if (binfo->sensor < sme->sme_nsensors) 226 *binfo = sme->sme_sensor_info[binfo->sensor]; 227 binfo->sensor = oidx; 228 sysmon_envsys_release(sme); 229 break; 230 } 231 232 default: 233 error = ENOTTY; 234 } 235 236 return (error); 237 } 238 239 /* 240 * sysmon_envsys_register: 241 * 242 * Register an ENVSYS device. 243 */ 244 int 245 sysmon_envsys_register(struct sysmon_envsys *sme) 246 { 247 int error = 0; 248 249 KASSERT((sme->sme_flags & (SME_FLAG_BUSY | SME_FLAG_WANTED)) == 0); 250 simple_lock(&sysmon_envsys_list_slock); 251 252 if (sme->sme_envsys_version != SYSMON_ENVSYS_VERSION) { 253 error = EINVAL; 254 goto out; 255 } 256 257 sme->sme_fsensor = sysmon_envsys_next_sensor_index; 258 sysmon_envsys_next_sensor_index += sme->sme_nsensors; 259 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); 260 261 out: 262 simple_unlock(&sysmon_envsys_list_slock); 263 return (error); 264 } 265 266 /* 267 * sysmon_envsys_unregister: 268 * 269 * Unregister an ENVSYS device. 270 */ 271 void 272 sysmon_envsys_unregister(struct sysmon_envsys *sme) 273 { 274 275 simple_lock(&sysmon_envsys_list_slock); 276 while (sme->sme_flags & SME_FLAG_BUSY) { 277 sme->sme_flags |= SME_FLAG_WANTED; 278 ltsleep(sme, PWAIT, "smeunreg", 0, &sysmon_envsys_list_slock); 279 } 280 LIST_REMOVE(sme, sme_list); 281 simple_unlock(&sysmon_envsys_list_slock); 282 } 283 284 /* 285 * sysmon_envsys_find: 286 * 287 * Find an ENVSYS device. 288 * the found device should be sysmon_envsys_release'ed by the caller. 289 */ 290 struct sysmon_envsys * 291 sysmon_envsys_find(u_int idx) 292 { 293 struct sysmon_envsys *sme; 294 295 simple_lock(&sysmon_envsys_list_slock); 296 again: 297 for (sme = LIST_FIRST(&sysmon_envsys_list); sme != NULL; 298 sme = LIST_NEXT(sme, sme_list)) { 299 if (idx >= sme->sme_fsensor && 300 idx < (sme->sme_fsensor + sme->sme_nsensors)) { 301 if (sme->sme_flags & SME_FLAG_BUSY) { 302 sme->sme_flags |= SME_FLAG_WANTED; 303 ltsleep(sme, PWAIT, "smefind", 0, 304 &sysmon_envsys_list_slock); 305 goto again; 306 } 307 sme->sme_flags |= SME_FLAG_BUSY; 308 break; 309 } 310 } 311 312 simple_unlock(&sysmon_envsys_list_slock); 313 return sme; 314 } 315 316 /* 317 * sysmon_envsys_release: 318 * 319 * Release an ENVSYS device. 320 */ 321 /* ARGSUSED */ 322 void 323 sysmon_envsys_release(struct sysmon_envsys *sme) 324 { 325 326 KASSERT(sme->sme_flags & SME_FLAG_BUSY); 327 328 simple_lock(&sysmon_envsys_list_slock); 329 if (sme->sme_flags & SME_FLAG_WANTED) 330 wakeup(sme); 331 sme->sme_flags &= ~(SME_FLAG_BUSY | SME_FLAG_WANTED); 332 simple_unlock(&sysmon_envsys_list_slock); 333 } 334