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