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 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <sys/conf.h> 30#include <sys/modctl.h> 31#include <sys/sunddi.h> 32#include <sys/callb.h> 33#include <sys/strlog.h> 34#include <sys/file.h> 35#include <sys/lom_io.h> 36#include <sys/ddi.h> 37#include <sys/time.h> 38 39#define LOMIOCALCTL_OLD _IOW('a', 4, ts_aldata_t) 40#define LOMIOCALSTATE_OLD _IOWR('a', 5, ts_aldata_t) 41 42struct tsalarm_softc { 43 dev_info_t *dip; 44 kmutex_t mutex; 45}; 46 47#define getsoftc(minor) \ 48 ((struct tsalarm_softc *)ddi_get_soft_state(statep, (minor))) 49/* 50 * Driver entry points 51 */ 52 53/* dev_ops and cb_ops entry point function declarations */ 54 55static int tsalarm_attach(dev_info_t *, ddi_attach_cmd_t); 56static int tsalarm_detach(dev_info_t *, ddi_detach_cmd_t); 57static int tsalarm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 58 59static int tsalarm_open(dev_t *, int, int, cred_t *); 60static int tsalarm_close(dev_t, int, int, cred_t *); 61static int tsalarm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 62 63static struct cb_ops tsalarm_cb_ops = { 64 tsalarm_open, /* open */ 65 tsalarm_close, /* close */ 66 nodev, /* strategy() */ 67 nodev, /* print() */ 68 nodev, /* dump() */ 69 nodev, /* read() */ 70 nodev, /* write() */ 71 tsalarm_ioctl, /* ioctl() */ 72 nodev, /* devmap() */ 73 nodev, /* mmap() */ 74 ddi_segmap, /* segmap() */ 75 nochpoll, /* poll() */ 76 ddi_prop_op, /* prop_op() */ 77 NULL, /* cb_str */ 78 D_NEW | D_MP /* cb_flag */ 79}; 80 81 82static struct dev_ops tsalarm_ops = { 83 DEVO_REV, 84 0, /* ref count */ 85 tsalarm_getinfo, /* getinfo() */ 86 nulldev, /* identify() */ 87 nulldev, /* probe() */ 88 tsalarm_attach, /* attach() */ 89 tsalarm_detach, /* detach */ 90 nodev, /* reset */ 91 &tsalarm_cb_ops, /* pointer to cb_ops structure */ 92 (struct bus_ops *)NULL, 93 nulldev, /* power() */ 94 ddi_quiesce_not_needed, /* quiesce() */ 95}; 96 97/* 98 * Loadable module support. 99 */ 100extern struct mod_ops mod_driverops; 101static void *statep; 102 103static struct modldrv modldrv = { 104 &mod_driverops, /* Type of module. This is a driver */ 105 "tsalarm control driver", /* Name of the module */ 106 &tsalarm_ops /* pointer to the dev_ops structure */ 107}; 108 109static struct modlinkage modlinkage = { 110 MODREV_1, 111 &modldrv, 112 NULL 113}; 114 115extern int rmclomv_alarm_get(int alarm_type, int *alarm_state); 116extern int rmclomv_alarm_set(int alarm_type, int new_state); 117 118int 119_init(void) 120{ 121 int e; 122 123 if (e = ddi_soft_state_init(&statep, 124 sizeof (struct tsalarm_softc), 1)) { 125 return (e); 126 } 127 128 if ((e = mod_install(&modlinkage)) != 0) { 129 ddi_soft_state_fini(&statep); 130 } 131 132 return (e); 133} 134 135 136int 137_fini(void) 138{ 139 int e; 140 141 if ((e = mod_remove(&modlinkage)) != 0) { 142 return (e); 143 } 144 145 ddi_soft_state_fini(&statep); 146 147 return (DDI_SUCCESS); 148} 149 150 151int 152_info(struct modinfo *modinfop) 153{ 154 return (mod_info(&modlinkage, modinfop)); 155} 156 157 158/* ARGSUSED */ 159static int 160tsalarm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 161{ 162 int inst = getminor((dev_t)arg); 163 int retval = DDI_SUCCESS; 164 struct tsalarm_softc *softc; 165 166 switch (cmd) { 167 168 case DDI_INFO_DEVT2DEVINFO: 169 if ((softc = getsoftc(inst)) == NULL) { 170 *result = (void *)NULL; 171 retval = DDI_FAILURE; 172 } else { 173 *result = (void *)softc->dip; 174 } 175 break; 176 177 case DDI_INFO_DEVT2INSTANCE: 178 *result = (void *)(uintptr_t)inst; 179 break; 180 181 default: 182 retval = DDI_FAILURE; 183 } 184 185 return (retval); 186} 187 188static int 189tsalarm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 190{ 191 192 int inst; 193 struct tsalarm_softc *softc = NULL; 194 195 switch (cmd) { 196 197 case DDI_ATTACH: 198 inst = ddi_get_instance(dip); 199 /* 200 * Allocate a soft state structure for this instance. 201 */ 202 if (ddi_soft_state_zalloc(statep, inst) != DDI_SUCCESS) 203 goto attach_failed; 204 205 softc = getsoftc(inst); 206 softc->dip = dip; 207 mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, NULL); 208 /* 209 * Create minor node. The minor device number, inst, has no 210 * meaning. The model number above, which will be added to 211 * the device's softc, is used to direct peculiar behavior. 212 */ 213 if (ddi_create_minor_node(dip, "lom", S_IFCHR, 0, 214 DDI_PSEUDO, NULL) == DDI_FAILURE) 215 goto attach_failed; 216 217 ddi_report_dev(dip); 218 return (DDI_SUCCESS); 219 220 case DDI_RESUME: 221 return (DDI_SUCCESS); 222 223 default: 224 return (DDI_FAILURE); 225 } 226 227attach_failed: 228 /* Free soft state, if allocated. remove minor node if added earlier */ 229 if (softc) { 230 mutex_destroy(&softc->mutex); 231 ddi_soft_state_free(statep, inst); 232 } 233 234 ddi_remove_minor_node(dip, NULL); 235 236 return (DDI_FAILURE); 237} 238 239static int 240tsalarm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 241{ 242 int inst; 243 struct tsalarm_softc *softc; 244 245 switch (cmd) { 246 247 case DDI_DETACH: 248 inst = ddi_get_instance(dip); 249 if ((softc = getsoftc(inst)) == NULL) 250 return (DDI_FAILURE); 251 /* 252 * Free the soft state and remove minor node added earlier. 253 */ 254 ddi_remove_minor_node(dip, NULL); 255 mutex_destroy(&softc->mutex); 256 ddi_soft_state_free(statep, inst); 257 return (DDI_SUCCESS); 258 259 case DDI_SUSPEND: 260 return (DDI_SUCCESS); 261 262 default: 263 return (DDI_FAILURE); 264 265 } 266} 267 268/* ARGSUSED */ 269static int 270tsalarm_open(dev_t *devp, int flag, int otyp, cred_t *credp) 271{ 272 int inst = getminor(*devp); 273 274 return (getsoftc(inst) == NULL ? ENXIO : 0); 275} 276 277 278/* ARGSUSED */ 279static int 280tsalarm_close(dev_t dev, int flag, int otyp, cred_t *credp) 281{ 282 int inst = getminor(dev); 283 284 return (getsoftc(inst) == NULL ? ENXIO : 0); 285} 286 287 288/* ARGSUSED */ 289static int 290tsalarm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 291 cred_t *credp, int *rvalp) 292{ 293 int inst = getminor(dev); 294 struct tsalarm_softc *softc; 295 int retval = 0; 296 ts_aldata_t ts_alinfo; 297 int alarm_type, alarm_state = 0; 298 299 if ((softc = getsoftc(inst)) == NULL) 300 return (ENXIO); 301 302 mutex_enter(&softc->mutex); 303 304 switch (cmd) { 305 306 case LOMIOCALSTATE: 307 case LOMIOCALSTATE_OLD: 308 { 309 if (ddi_copyin((caddr_t)arg, (caddr_t)&ts_alinfo, 310 sizeof (ts_aldata_t), mode) != 0) { 311 retval = EFAULT; 312 goto end; 313 } 314 315 alarm_type = ts_alinfo.alarm_no; 316 if ((alarm_type < ALARM_CRITICAL) || 317 (alarm_type > ALARM_USER)) { 318 retval = EINVAL; 319 goto end; 320 } 321 322 retval = rmclomv_alarm_get(alarm_type, &alarm_state); 323 324 if (retval != 0) 325 goto end; 326 327 if ((alarm_state != 0) && (alarm_state != 1)) { 328 retval = EIO; 329 goto end; 330 } 331 332 ts_alinfo.alarm_state = alarm_state; 333 if (ddi_copyout((caddr_t)&ts_alinfo, (caddr_t)arg, 334 sizeof (ts_aldata_t), mode) != 0) { 335 retval = EFAULT; 336 goto end; 337 } 338 339 } 340 break; 341 342 case LOMIOCALCTL: 343 case LOMIOCALCTL_OLD: 344 { 345 if (ddi_copyin((caddr_t)arg, (caddr_t)&ts_alinfo, 346 sizeof (ts_aldata_t), mode) != 0) { 347 retval = EFAULT; 348 goto end; 349 } 350 351 alarm_type = ts_alinfo.alarm_no; 352 alarm_state = ts_alinfo.alarm_state; 353 354 if ((alarm_type < ALARM_CRITICAL) || 355 (alarm_type > ALARM_USER)) { 356 retval = EINVAL; 357 goto end; 358 } 359 if ((alarm_state < ALARM_OFF) || 360 (alarm_state > ALARM_ON)) { 361 retval = EINVAL; 362 goto end; 363 } 364 365 retval = rmclomv_alarm_set(alarm_type, alarm_state); 366 } 367 break; 368 369 default: 370 retval = EINVAL; 371 break; 372 } 373 374end: 375 mutex_exit(&softc->mutex); 376 377 return (retval); 378} 379