1 /* $NetBSD: sysmon_envsys.c,v 1.11 2006/07/08 20:26:06 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.11 2006/07/08 20:26:06 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 struct sysmon_envsys *sysmon_envsys_find(u_int); 79 void sysmon_envsys_release(struct sysmon_envsys *); 80 81 /* 82 * sysmonopen_envsys: 83 * 84 * Open the system monitor device. 85 */ 86 int 87 sysmonopen_envsys(dev_t dev, int flag, int mode, struct lwp *l) 88 { 89 simple_lock(&sysmon_envsys_initialized_slock); 90 if (sysmon_envsys_initialized == 0) { 91 lockinit(&sysmon_envsys_lock, PWAIT|PCATCH, "smenv", 0, 0); 92 sysmon_envsys_initialized = 1; 93 } 94 simple_unlock(&sysmon_envsys_initialized_slock); 95 96 return (0); 97 } 98 99 /* 100 * sysmonclose_envsys: 101 * 102 * Close the system monitor device. 103 */ 104 int 105 sysmonclose_envsys(dev_t dev, int flag, int mode, struct lwp *l) 106 { 107 108 /* Nothing to do */ 109 return (0); 110 } 111 112 /* 113 * sysmonioctl_envsys: 114 * 115 * Perform an envsys control request. 116 */ 117 int 118 sysmonioctl_envsys(dev_t dev, u_long cmd, caddr_t data, int flag, 119 struct lwp *l) 120 { 121 struct sysmon_envsys *sme; 122 int error = 0; 123 u_int oidx; 124 125 switch (cmd) { 126 /* 127 * For ENVSYS commands, we translate the absolute sensor index 128 * to a device-relative sensor index. 129 */ 130 case ENVSYS_VERSION: 131 *(int32_t *)data = SYSMON_ENVSYS_VERSION; 132 break; 133 134 case ENVSYS_GRANGE: 135 { 136 struct envsys_range *rng = (void *) data; 137 int i; 138 139 140 /* Return empty range unless we find something better */ 141 rng->low = 1; 142 rng->high = 0; 143 144 if (rng->units == -1) { 145 rng->low = 0; 146 rng->high = sysmon_envsys_next_sensor_index; 147 break; 148 } 149 150 sme = sysmon_envsys_find(0); /* XXX */ 151 if (sme == NULL) { 152 /* Return empty range for `no sensors'. */ 153 break; 154 } 155 for (i = 0; 156 sme->sme_ranges[i].low <= sme->sme_ranges[i].high; 157 i++) { 158 if (sme->sme_ranges[i].units == rng->units) { 159 *rng = sme->sme_ranges[i]; 160 break; 161 } 162 } 163 sysmon_envsys_release(sme); 164 break; 165 } 166 167 case ENVSYS_GTREDATA: 168 { 169 struct envsys_tre_data *tred = (void *) data; 170 171 tred->validflags = 0; 172 173 sme = sysmon_envsys_find(tred->sensor); 174 if (sme == NULL) 175 break; 176 oidx = tred->sensor; 177 tred->sensor = SME_SENSOR_IDX(sme, tred->sensor); 178 if (tred->sensor < sme->sme_nsensors 179 && sme->sme_gtredata != NULL) { 180 SYSMON_ENVSYS_LOCK(); 181 error = (*sme->sme_gtredata)(sme, tred); 182 SYSMON_ENVSYS_UNLOCK(); 183 } 184 tred->sensor = oidx; 185 sysmon_envsys_release(sme); 186 break; 187 } 188 189 case ENVSYS_STREINFO: 190 { 191 struct envsys_basic_info *binfo = (void *) data; 192 193 sme = sysmon_envsys_find(binfo->sensor); 194 if (sme == NULL) { 195 binfo->validflags = 0; 196 break; 197 } 198 oidx = binfo->sensor; 199 binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor); 200 if (binfo->sensor < sme->sme_nsensors 201 && sme->sme_streinfo != NULL) { 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