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