1 /* $NetBSD: sysmon_envsys.c,v 1.9 2005/12/11 12:23:56 christos 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.9 2005/12/11 12:23:56 christos 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 SYSMON_ENVSYS_LOCK(); 182 error = (*sme->sme_gtredata)(sme, tred); 183 SYSMON_ENVSYS_UNLOCK(); 184 } 185 tred->sensor = oidx; 186 sysmon_envsys_release(sme); 187 break; 188 } 189 190 case ENVSYS_STREINFO: 191 { 192 struct envsys_basic_info *binfo = (void *) data; 193 194 sme = sysmon_envsys_find(binfo->sensor); 195 if (sme == NULL) { 196 binfo->validflags = 0; 197 break; 198 } 199 oidx = binfo->sensor; 200 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 201 if (binfo->sensor < sme->sme_nsensors) { 202 SYSMON_ENVSYS_LOCK(); 203 error = (*sme->sme_streinfo)(sme, binfo); 204 SYSMON_ENVSYS_UNLOCK(); 205 } else 206 binfo->validflags = 0; 207 binfo->sensor = oidx; 208 sysmon_envsys_release(sme); 209 break; 210 } 211 212 case ENVSYS_GTREINFO: 213 { 214 struct envsys_basic_info *binfo = (void *) data; 215 216 binfo->validflags = 0; 217 218 sme = sysmon_envsys_find(binfo->sensor); 219 if (sme == NULL) 220 break; 221 oidx = binfo->sensor; 222 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 223 if (binfo->sensor < sme->sme_nsensors) 224 *binfo = sme->sme_sensor_info[binfo->sensor]; 225 binfo->sensor = oidx; 226 sysmon_envsys_release(sme); 227 break; 228 } 229 230 default: 231 error = ENOTTY; 232 } 233 234 return (error); 235 } 236 237 /* 238 * sysmon_envsys_register: 239 * 240 * Register an ENVSYS device. 241 */ 242 int 243 sysmon_envsys_register(struct sysmon_envsys *sme) 244 { 245 int error = 0; 246 247 KASSERT((sme->sme_flags & (SME_FLAG_BUSY | SME_FLAG_WANTED)) == 0); 248 simple_lock(&sysmon_envsys_list_slock); 249 250 if (sme->sme_envsys_version != SYSMON_ENVSYS_VERSION) { 251 error = EINVAL; 252 goto out; 253 } 254 255 sme->sme_fsensor = sysmon_envsys_next_sensor_index; 256 sysmon_envsys_next_sensor_index += sme->sme_nsensors; 257 LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list); 258 259 out: 260 simple_unlock(&sysmon_envsys_list_slock); 261 return (error); 262 } 263 264 /* 265 * sysmon_envsys_unregister: 266 * 267 * Unregister an ENVSYS device. 268 */ 269 void 270 sysmon_envsys_unregister(struct sysmon_envsys *sme) 271 { 272 273 simple_lock(&sysmon_envsys_list_slock); 274 while (sme->sme_flags & SME_FLAG_BUSY) { 275 sme->sme_flags |= SME_FLAG_WANTED; 276 ltsleep(sme, PWAIT, "smeunreg", 0, &sysmon_envsys_list_slock); 277 } 278 LIST_REMOVE(sme, sme_list); 279 simple_unlock(&sysmon_envsys_list_slock); 280 } 281 282 /* 283 * sysmon_envsys_find: 284 * 285 * Find an ENVSYS device. 286 * the found device should be sysmon_envsys_release'ed by the caller. 287 */ 288 struct sysmon_envsys * 289 sysmon_envsys_find(u_int idx) 290 { 291 struct sysmon_envsys *sme; 292 293 simple_lock(&sysmon_envsys_list_slock); 294 again: 295 for (sme = LIST_FIRST(&sysmon_envsys_list); sme != NULL; 296 sme = LIST_NEXT(sme, sme_list)) { 297 if (idx >= sme->sme_fsensor && 298 idx < (sme->sme_fsensor + sme->sme_nsensors)) { 299 if (sme->sme_flags & SME_FLAG_BUSY) { 300 sme->sme_flags |= SME_FLAG_WANTED; 301 ltsleep(sme, PWAIT, "smefind", 0, 302 &sysmon_envsys_list_slock); 303 goto again; 304 } 305 sme->sme_flags |= SME_FLAG_BUSY; 306 break; 307 } 308 } 309 310 simple_unlock(&sysmon_envsys_list_slock); 311 return sme; 312 } 313 314 /* 315 * sysmon_envsys_release: 316 * 317 * Release an ENVSYS device. 318 */ 319 /* ARGSUSED */ 320 void 321 sysmon_envsys_release(struct sysmon_envsys *sme) 322 { 323 324 KASSERT(sme->sme_flags & SME_FLAG_BUSY); 325 326 simple_lock(&sysmon_envsys_list_slock); 327 if (sme->sme_flags & SME_FLAG_WANTED) 328 wakeup(sme); 329 sme->sme_flags &= ~(SME_FLAG_BUSY | SME_FLAG_WANTED); 330 simple_unlock(&sysmon_envsys_list_slock); 331 } 332