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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)138 _info(struct modinfo *modinfop)
139 {
140 return (mod_info(&grfans_modlinkage, modinfop));
141 }
142
143 /* ARGSUSED */
144 static int
grfans_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
grfans_do_attach(dev_info_t * dip)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, NULL) == 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, NULL) == 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
grfans_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
grfans_do_detach(dev_info_t * dip)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
grfans_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
grfans_open(dev_t * devp,int flags,int otyp,cred_t * credp)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
grfans_close(dev_t dev,int flags,int otyp,cred_t * credp)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
grfans_read(dev_t dev,struct uio * uiop,cred_t * cred_p)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
grfans_write(dev_t dev,struct uio * uiop,cred_t * cred_p)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
grfans_io(dev_t dev,struct uio * uiop,int rw)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