1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/stat.h> 29 #include <sys/file.h> 30 #include <sys/uio.h> 31 #include <sys/modctl.h> 32 #include <sys/open.h> 33 #include <sys/types.h> 34 #include <sys/kmem.h> 35 #include <sys/systm.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/conf.h> 39 #include <sys/mode.h> 40 #include <sys/policy.h> 41 42 #include <sys/grfans.h> 43 44 /* 45 * cb ops 46 */ 47 static int grfans_open(dev_t *, int, int, cred_t *); 48 static int grfans_close(dev_t, int, int, cred_t *); 49 static int grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p); 50 static int grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p); 51 static int grfans_io(dev_t dev, struct uio *uiop, int rw); 52 /* 53 * dev ops 54 */ 55 static int grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 56 void **result); 57 static int grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 58 static int grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 59 60 static struct cb_ops grfans_cbops = { 61 grfans_open, /* open */ 62 grfans_close, /* close */ 63 nodev, /* strategy */ 64 nodev, /* print */ 65 nodev, /* dump */ 66 grfans_read, /* read */ 67 grfans_write, /* write */ 68 nodev, /* ioctl */ 69 nodev, /* devmap */ 70 nodev, /* mmap */ 71 nodev, /* segmap */ 72 nochpoll, /* poll */ 73 ddi_prop_op, /* cb_prop_op */ 74 NULL, /* streamtab */ 75 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 76 CB_REV, /* rev */ 77 nodev, /* int (*cb_aread)() */ 78 nodev /* int (*cb_awrite)() */ 79 }; 80 81 static struct dev_ops grfans_ops = { 82 DEVO_REV, 83 0, 84 grfans_info, 85 nulldev, 86 nulldev, 87 grfans_attach, 88 grfans_detach, 89 nodev, 90 &grfans_cbops, 91 NULL 92 }; 93 94 static struct modldrv grfans_modldrv = { 95 &mod_driverops, /* type of module - driver */ 96 "grfans device driver", 97 &grfans_ops, 98 }; 99 100 static struct modlinkage grfans_modlinkage = { 101 MODREV_1, 102 &grfans_modldrv, 103 0 104 }; 105 106 static void *grfans_soft_statep; 107 static int grfans_debug = 0; 108 109 int 110 _init(void) 111 { 112 int error; 113 114 error = mod_install(&grfans_modlinkage); 115 if (error == 0) { 116 (void) ddi_soft_state_init(&grfans_soft_statep, 117 sizeof (struct grfans_unit), 1); 118 } 119 120 return (error); 121 } 122 123 int 124 _fini(void) 125 { 126 int error; 127 128 error = mod_remove(&grfans_modlinkage); 129 if (error == 0) { 130 ddi_soft_state_fini(&grfans_soft_statep); 131 } 132 133 return (error); 134 } 135 136 int 137 _info(struct modinfo *modinfop) 138 { 139 return (mod_info(&grfans_modlinkage, modinfop)); 140 } 141 142 /* ARGSUSED */ 143 static int 144 grfans_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 145 { 146 dev_t dev; 147 int instance; 148 149 if (infocmd == DDI_INFO_DEVT2INSTANCE) { 150 dev = (dev_t)arg; 151 instance = MINOR_TO_DEVINST(dev); 152 *result = (void *)(uintptr_t)instance; 153 return (DDI_SUCCESS); 154 } 155 return (DDI_FAILURE); 156 } 157 158 static int 159 grfans_do_attach(dev_info_t *dip) 160 { 161 struct grfans_unit *unitp; 162 int instance; 163 ddi_device_acc_attr_t attr; 164 int nregs; 165 char name[32]; 166 167 instance = ddi_get_instance(dip); 168 169 if (ddi_soft_state_zalloc(grfans_soft_statep, instance) != 0) { 170 cmn_err(CE_WARN, "%s%d failed to zalloc softstate", 171 ddi_get_name(dip), instance); 172 173 return (DDI_FAILURE); 174 } 175 176 if (grfans_debug) { 177 printf("attached instance number %d\n", instance); 178 } 179 180 unitp = ddi_get_soft_state(grfans_soft_statep, instance); 181 if (unitp == NULL) 182 return (DDI_FAILURE); 183 184 (void) snprintf(name, sizeof (name), "%s%d", ddi_driver_name(dip), 185 instance); 186 187 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 188 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 189 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 190 191 if (grfans_debug) { 192 printf("number of registers is %d\n", 193 ddi_dev_nregs(dip, &nregs)); 194 } 195 196 if (ddi_regs_map_setup(dip, 0, 197 (caddr_t *)&unitp->cpufan_reg, 198 3, 1, &attr, &unitp->cpufan_rhandle) != DDI_SUCCESS) { 199 cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset " 200 "0", name); 201 ddi_soft_state_free(grfans_soft_statep, instance); 202 return (DDI_FAILURE); 203 } 204 205 if (ddi_regs_map_setup(dip, 1, 206 (caddr_t *)&unitp->sysfan_reg, 207 0, 1, &attr, &unitp->sysfan_rhandle) != DDI_SUCCESS) { 208 cmn_err(CE_WARN, "%s ddi_regs_map_setup failed for regset " 209 "1", name); 210 ddi_regs_map_free(&unitp->cpufan_rhandle); 211 ddi_soft_state_free(grfans_soft_statep, instance); 212 return (DDI_FAILURE); 213 } 214 215 if (ddi_create_minor_node(dip, "cpu_fan", S_IFCHR, 216 DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(CPU_FAN_CHANNEL), 217 FANS_NODE_TYPE, NULL) == DDI_FAILURE) { 218 cmn_err(CE_WARN, "%s ddi_create_minor_node failed" 219 " for cpu fan", name); 220 ddi_regs_map_free(&unitp->cpufan_rhandle); 221 ddi_regs_map_free(&unitp->sysfan_rhandle); 222 ddi_soft_state_free(grfans_soft_statep, instance); 223 ddi_remove_minor_node(dip, NULL); 224 225 return (DDI_FAILURE); 226 } 227 228 if (ddi_create_minor_node(dip, "sys_fan", S_IFCHR, 229 DEVINST_TO_MINOR(instance) | CHANNEL_TO_MINOR(SYSTEM_FAN_CHANNEL), 230 FANS_NODE_TYPE, NULL) == DDI_FAILURE) { 231 cmn_err(CE_WARN, "%s ddi_create_minor_node failed" 232 " for system fan", name); 233 ddi_regs_map_free(&unitp->cpufan_rhandle); 234 ddi_regs_map_free(&unitp->sysfan_rhandle); 235 ddi_soft_state_free(grfans_soft_statep, instance); 236 ddi_remove_minor_node(dip, NULL); 237 238 return (DDI_FAILURE); 239 } 240 241 mutex_init(&unitp->mutex, NULL, MUTEX_DRIVER, NULL); 242 243 return (DDI_SUCCESS); 244 } 245 246 static int 247 grfans_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 248 { 249 switch (cmd) { 250 case DDI_ATTACH: 251 return (grfans_do_attach(dip)); 252 253 case DDI_RESUME: 254 return (DDI_SUCCESS); 255 256 default: 257 return (DDI_FAILURE); 258 } 259 } 260 261 static int 262 grfans_do_detach(dev_info_t *dip) 263 { 264 struct grfans_unit *unitp; 265 int instance; 266 267 instance = ddi_get_instance(dip); 268 unitp = ddi_get_soft_state(grfans_soft_statep, instance); 269 ddi_remove_minor_node(dip, NULL); 270 271 ddi_regs_map_free(&unitp->cpufan_rhandle); 272 ddi_regs_map_free(&unitp->sysfan_rhandle); 273 274 mutex_destroy(&unitp->mutex); 275 276 ddi_soft_state_free(grfans_soft_statep, instance); 277 278 return (DDI_SUCCESS); 279 } 280 281 static int 282 grfans_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 283 { 284 switch (cmd) { 285 case DDI_DETACH: 286 return (grfans_do_detach(dip)); 287 288 case DDI_SUSPEND: 289 return (DDI_SUCCESS); 290 291 default: 292 return (DDI_FAILURE); 293 } 294 } 295 296 /*ARGSUSED*/ 297 static int 298 grfans_open(dev_t *devp, int flags, int otyp, cred_t *credp) 299 { 300 struct grfans_unit *unitp; 301 int err = 0; 302 int instance = MINOR_TO_DEVINST(*devp); 303 int channel; 304 305 /* 306 * must be privileged to access this device 307 */ 308 if (secpolicy_sys_config(credp, B_FALSE) != 0) 309 return (EPERM); 310 311 if (instance < 0) { 312 cmn_err(CE_WARN, "grfan: instance less than 0: %d\n", 313 instance); 314 315 return (ENXIO); 316 } 317 318 unitp = ddi_get_soft_state(grfans_soft_statep, instance); 319 if (unitp == NULL) { 320 cmn_err(CE_WARN, "grfan: no soft state for instance %d\n", 321 instance); 322 323 return (ENXIO); 324 } 325 326 if (otyp != OTYP_CHR) 327 return (EINVAL); 328 329 channel = MINOR_TO_CHANNEL(getminor(*devp)); 330 331 mutex_enter(&unitp->mutex); 332 333 if (flags & FEXCL) { 334 if (unitp->oflag[channel] != 0) 335 err = EBUSY; 336 else 337 unitp->oflag[channel] = FEXCL; 338 } else { 339 if (unitp->oflag[channel] == FEXCL) 340 err = EBUSY; 341 else 342 unitp->oflag[channel] = (uint16_t)FOPEN; 343 } 344 345 mutex_exit(&unitp->mutex); 346 347 return (err); 348 } 349 350 /*ARGSUSED*/ 351 static int 352 grfans_close(dev_t dev, int flags, int otyp, cred_t *credp) 353 { 354 struct grfans_unit *unitp; 355 int instance = MINOR_TO_DEVINST(dev); 356 int channel; 357 358 if (instance < 0) 359 return (ENXIO); 360 361 unitp = ddi_get_soft_state(grfans_soft_statep, instance); 362 if (unitp == NULL) 363 return (ENXIO); 364 365 channel = MINOR_TO_CHANNEL(getminor(dev)); 366 367 unitp->oflag[channel] = 0; 368 369 return (DDI_SUCCESS); 370 } 371 372 /*ARGSUSED*/ 373 static int 374 grfans_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 375 { 376 return (grfans_io(dev, uiop, B_READ)); 377 } 378 379 /*ARGSUSED*/ 380 static int 381 grfans_write(dev_t dev, struct uio *uiop, cred_t *cred_p) 382 { 383 return (grfans_io(dev, uiop, B_WRITE)); 384 } 385 386 static int 387 grfans_io(dev_t dev, struct uio *uiop, int rw) 388 { 389 struct grfans_unit *unitp; 390 int instance = MINOR_TO_DEVINST(getminor(dev)); 391 int ret = 0; 392 size_t len = uiop->uio_resid; 393 int8_t out_value, req_value, reg_value; 394 caddr_t outputaddr; 395 396 if (instance < 0) 397 return (ENXIO); 398 399 if (len == 0) 400 return (0); 401 402 unitp = ddi_get_soft_state(grfans_soft_statep, instance); 403 404 if (unitp == NULL) 405 return (ENXIO); 406 407 if (MINOR_TO_CHANNEL(getminor(dev)) == CPU_FAN_CHANNEL) 408 outputaddr = &unitp->cpufan_output; 409 else 410 outputaddr = &unitp->sysfan_output; 411 412 if (rw == B_READ) { 413 if (*outputaddr == UNKNOWN_OUT) 414 return (EIO); 415 return (uiomove(outputaddr, 1, UIO_READ, uiop)); 416 } 417 418 /* 419 * rw == B_WRITE. 420 */ 421 if ((ret = uiomove(&req_value, sizeof (req_value), UIO_WRITE, 422 uiop)) == 0) { 423 if (MINOR_TO_CHANNEL(dev) == CPU_FAN_CHANNEL) { 424 /* 425 * Check bounds for cpu fan 426 */ 427 if (req_value == 0) { 428 reg_value = CPU_FAN_0; 429 out_value = 0; 430 } else if (req_value <= 25) { 431 reg_value = CPU_FAN_25; 432 out_value = 25; 433 } else if (req_value <= 50) { 434 reg_value = CPU_FAN_50; 435 out_value = 50; 436 } else if (req_value <= 75) { 437 reg_value = CPU_FAN_75; 438 out_value = 75; 439 } else if (req_value <= 100) { 440 reg_value = CPU_FAN_100; 441 out_value = 100; 442 } else 443 ret = EINVAL; 444 445 if (ret != EINVAL) { 446 uint8_t reg; 447 448 *outputaddr = out_value; 449 450 reg = ddi_get8(unitp->cpufan_rhandle, 451 unitp->cpufan_reg); 452 reg = (reg & ~CPU_FAN_MASK) | reg_value; 453 ddi_put8(unitp->cpufan_rhandle, 454 unitp->cpufan_reg, reg); 455 (void) ddi_get8(unitp->cpufan_rhandle, 456 unitp->cpufan_reg); 457 458 if (grfans_debug) { 459 printf("set output to %d at addr %p\n", 460 out_value, 461 unitp->cpufan_reg); 462 } 463 } 464 } else { 465 if (req_value == 0) { 466 reg_value = SYS_FAN_OFF; 467 out_value = 0; 468 } else if (req_value > 0) { 469 reg_value = SYS_FAN_ON; 470 out_value = 100; 471 } else { 472 ret = EINVAL; 473 } 474 475 if (ret != EINVAL) { 476 *outputaddr = out_value; 477 478 ddi_put8(unitp->sysfan_rhandle, 479 unitp->sysfan_reg, 480 reg_value); 481 (void) ddi_get8(unitp->sysfan_rhandle, 482 unitp->sysfan_reg); 483 if (grfans_debug) { 484 printf("set SYSFAN output to %d at " 485 "addr %p\n", out_value, 486 unitp->sysfan_reg); 487 } 488 } 489 } 490 } else { 491 ret = EFAULT; 492 } 493 494 return (ret); 495 } 496