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/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/note.h> 40 #include <sys/i2c/misc/i2c_svc.h> 41 #include <sys/i2c/clients/tda8444_impl.h> 42 43 /* 44 * cb ops 45 */ 46 static int tda8444_open(dev_t *, int, int, cred_t *); 47 static int tda8444_close(dev_t, int, int, cred_t *); 48 static int tda8444_read(dev_t dev, struct uio *uiop, cred_t *cred_p); 49 static int tda8444_write(dev_t dev, struct uio *uiop, cred_t *cred_p); 50 static int tda8444_io(dev_t dev, struct uio *uiop, int rw); 51 /* 52 * dev ops 53 */ 54 static int tda8444_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 55 void **result); 56 static int tda8444_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 57 static int tda8444_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 58 59 static struct cb_ops tda8444_cbops = { 60 tda8444_open, /* open */ 61 tda8444_close, /* close */ 62 nodev, /* strategy */ 63 nodev, /* print */ 64 nodev, /* dump */ 65 tda8444_read, /* read */ 66 tda8444_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 tda8444_ops = { 81 DEVO_REV, 82 0, 83 tda8444_info, 84 nulldev, 85 nulldev, 86 tda8444_attach, 87 tda8444_detach, 88 nodev, 89 &tda8444_cbops, 90 NULL, 91 NULL, 92 ddi_quiesce_not_supported, /* devo_quiesce */ 93 }; 94 95 static struct modldrv tda8444_modldrv = { 96 &mod_driverops, /* type of module - driver */ 97 "tda8444 device driver", 98 &tda8444_ops, 99 }; 100 101 static struct modlinkage tda8444_modlinkage = { 102 MODREV_1, 103 &tda8444_modldrv, 104 0 105 }; 106 107 static void *tda8444_soft_statep; 108 static int tda8444_debug = 0; 109 110 int 111 _init(void) 112 { 113 int error; 114 115 error = mod_install(&tda8444_modlinkage); 116 if (error == 0) { 117 (void) ddi_soft_state_init(&tda8444_soft_statep, 118 sizeof (struct tda8444_unit), TDA8444_MAX_DACS); 119 } 120 121 return (error); 122 } 123 124 int 125 _fini(void) 126 { 127 int error; 128 129 error = mod_remove(&tda8444_modlinkage); 130 if (error == 0) { 131 ddi_soft_state_fini(&tda8444_soft_statep); 132 } 133 134 return (error); 135 } 136 137 int 138 _info(struct modinfo *modinfop) 139 { 140 return (mod_info(&tda8444_modlinkage, modinfop)); 141 } 142 143 /* ARGSUSED */ 144 static int 145 tda8444_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 = TDA8444_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 tda8444_do_resume(dev_info_t *dip) 161 { 162 int instance = ddi_get_instance(dip); 163 struct tda8444_unit *unitp; 164 int channel; 165 int ret = DDI_SUCCESS; 166 167 unitp = (struct tda8444_unit *) 168 ddi_get_soft_state(tda8444_soft_statep, instance); 169 170 if (unitp == NULL) { 171 172 return (ENXIO); 173 } 174 175 for (channel = 0; channel < TDA8444_CHANS; channel++) { 176 unitp->tda8444_transfer->i2c_wbuf[0] = TDA8444_REGBASE | 177 channel; 178 unitp->tda8444_transfer->i2c_wbuf[1] = 179 unitp->tda8444_output[channel]; 180 DPRINTF(RESUME, ("tda8444_resume: setting channel %d to %d", 181 channel, unitp->tda8444_output[channel])); 182 if (i2c_transfer(unitp->tda8444_hdl, 183 unitp->tda8444_transfer) != I2C_SUCCESS) { 184 ret = DDI_FAILURE; 185 } 186 } 187 188 mutex_enter(&unitp->tda8444_mutex); 189 unitp->tda8444_flags = 0; 190 cv_signal(&unitp->tda8444_cv); 191 mutex_exit(&unitp->tda8444_mutex); 192 193 return (ret); 194 } 195 196 static int 197 tda8444_do_attach(dev_info_t *dip) 198 { 199 struct tda8444_unit *unitp; 200 char name[MAXNAMELEN]; 201 int instance; 202 minor_t minor; 203 int i; 204 205 instance = ddi_get_instance(dip); 206 207 if (ddi_soft_state_zalloc(tda8444_soft_statep, instance) != 0) { 208 cmn_err(CE_WARN, "%s%d failed to zalloc softstate", 209 ddi_get_name(dip), instance); 210 211 return (DDI_FAILURE); 212 } 213 214 unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 215 216 if (unitp == NULL) { 217 return (DDI_FAILURE); 218 } 219 220 (void) snprintf(unitp->tda8444_name, sizeof (unitp->tda8444_name), 221 "%s%d", ddi_driver_name(dip), instance); 222 223 for (i = 0; i < TDA8444_CHANS; i++) { 224 (void) sprintf(name, "%d", i); 225 minor = TDA8444_CHANNEL_TO_MINOR(i) | 226 TDA8444_DEVINST_TO_MINOR(instance); 227 if (ddi_create_minor_node(dip, name, S_IFCHR, minor, 228 TDA8444_NODE_TYPE, NULL) == DDI_FAILURE) { 229 cmn_err(CE_WARN, "%s ddi_create_minor_node failed", 230 unitp->tda8444_name); 231 ddi_soft_state_free(tda8444_soft_statep, instance); 232 ddi_remove_minor_node(dip, NULL); 233 234 return (DDI_FAILURE); 235 } 236 unitp->tda8444_output[i] = TDA8444_UNKNOWN_OUT; 237 } 238 239 /* 240 * preallocate a single buffer for all writes 241 */ 242 if (i2c_transfer_alloc(unitp->tda8444_hdl, &unitp->tda8444_transfer, 243 2, 0, I2C_SLEEP) != I2C_SUCCESS) { 244 cmn_err(CE_WARN, "i2c_transfer_alloc failed"); 245 ddi_remove_minor_node(dip, NULL); 246 ddi_soft_state_free(tda8444_soft_statep, instance); 247 248 return (DDI_FAILURE); 249 } 250 unitp->tda8444_transfer->i2c_flags = I2C_WR; 251 unitp->tda8444_transfer->i2c_version = I2C_XFER_REV; 252 253 if (i2c_client_register(dip, &unitp->tda8444_hdl) != I2C_SUCCESS) { 254 ddi_remove_minor_node(dip, NULL); 255 cmn_err(CE_WARN, "i2c_client_register failed"); 256 ddi_soft_state_free(tda8444_soft_statep, instance); 257 i2c_transfer_free(unitp->tda8444_hdl, unitp->tda8444_transfer); 258 259 return (DDI_FAILURE); 260 } 261 262 mutex_init(&unitp->tda8444_mutex, NULL, MUTEX_DRIVER, NULL); 263 cv_init(&unitp->tda8444_cv, NULL, CV_DRIVER, NULL); 264 265 return (DDI_SUCCESS); 266 } 267 268 static int 269 tda8444_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 270 { 271 switch (cmd) { 272 case DDI_ATTACH: 273 274 return (tda8444_do_attach(dip)); 275 case DDI_RESUME: 276 277 return (tda8444_do_resume(dip)); 278 default: 279 280 return (DDI_FAILURE); 281 } 282 } 283 284 static int 285 tda8444_do_detach(dev_info_t *dip) 286 { 287 struct tda8444_unit *unitp; 288 int instance; 289 290 instance = ddi_get_instance(dip); 291 unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 292 293 i2c_transfer_free(unitp->tda8444_hdl, unitp->tda8444_transfer); 294 i2c_client_unregister(unitp->tda8444_hdl); 295 ddi_remove_minor_node(dip, NULL); 296 mutex_destroy(&unitp->tda8444_mutex); 297 cv_destroy(&unitp->tda8444_cv); 298 ddi_soft_state_free(tda8444_soft_statep, instance); 299 300 return (DDI_SUCCESS); 301 } 302 303 static int 304 tda8444_do_suspend(dev_info_t *dip) 305 { 306 struct tda8444_unit *unitp; 307 int instance; 308 309 instance = ddi_get_instance(dip); 310 unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 311 312 /* 313 * Set the busy flag so that future transactions block 314 * until resume. 315 */ 316 mutex_enter(&unitp->tda8444_mutex); 317 while (unitp->tda8444_flags == TDA8444_BUSY) { 318 if (cv_wait_sig(&unitp->tda8444_cv, 319 &unitp->tda8444_mutex) <= 0) { 320 mutex_exit(&unitp->tda8444_mutex); 321 322 return (DDI_FAILURE); 323 } 324 } 325 unitp->tda8444_flags = TDA8444_SUSPENDED; 326 mutex_exit(&unitp->tda8444_mutex); 327 return (DDI_SUCCESS); 328 } 329 330 static int 331 tda8444_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 332 { 333 switch (cmd) { 334 case DDI_DETACH: 335 336 return (tda8444_do_detach(dip)); 337 case DDI_SUSPEND: 338 339 return (tda8444_do_suspend(dip)); 340 default: 341 342 return (DDI_FAILURE); 343 } 344 } 345 346 static int 347 tda8444_open(dev_t *devp, int flags, int otyp, cred_t *credp) 348 { 349 _NOTE(ARGUNUSED(credp)) 350 struct tda8444_unit *unitp; 351 int err = 0; 352 int instance = TDA8444_MINOR_TO_DEVINST(*devp); 353 int channel = TDA8444_MINOR_TO_CHANNEL(*devp); 354 355 if (instance < 0) { 356 357 return (ENXIO); 358 } 359 360 unitp = (struct tda8444_unit *) 361 ddi_get_soft_state(tda8444_soft_statep, instance); 362 363 if (unitp == NULL) { 364 365 return (ENXIO); 366 } 367 368 if (otyp != OTYP_CHR) { 369 370 return (EINVAL); 371 } 372 373 mutex_enter(&unitp->tda8444_mutex); 374 375 if (flags & FEXCL) { 376 if (unitp->tda8444_oflag[channel] != 0) { 377 err = EBUSY; 378 } else { 379 unitp->tda8444_oflag[channel] = FEXCL; 380 } 381 } else { 382 if (unitp->tda8444_oflag[channel] == FEXCL) { 383 err = EBUSY; 384 } else { 385 unitp->tda8444_oflag[channel] = (uint16_t)FOPEN; 386 } 387 } 388 389 mutex_exit(&unitp->tda8444_mutex); 390 391 return (err); 392 } 393 394 static int 395 tda8444_close(dev_t dev, int flags, int otyp, cred_t *credp) 396 { 397 _NOTE(ARGUNUSED(flags, otyp, credp)) 398 struct tda8444_unit *unitp; 399 int instance = TDA8444_MINOR_TO_DEVINST(dev); 400 int channel = TDA8444_MINOR_TO_CHANNEL(dev); 401 402 if (instance < 0) { 403 404 return (ENXIO); 405 } 406 407 unitp = (struct tda8444_unit *) 408 ddi_get_soft_state(tda8444_soft_statep, instance); 409 410 if (unitp == NULL) { 411 412 return (ENXIO); 413 } 414 415 mutex_enter(&unitp->tda8444_mutex); 416 417 unitp->tda8444_oflag[channel] = 0; 418 419 mutex_exit(&unitp->tda8444_mutex); 420 421 return (DDI_SUCCESS); 422 } 423 424 static int 425 tda8444_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 426 { 427 _NOTE(ARGUNUSED(cred_p)) 428 return (tda8444_io(dev, uiop, B_READ)); 429 } 430 431 static int 432 tda8444_write(dev_t dev, struct uio *uiop, cred_t *cred_p) 433 { 434 _NOTE(ARGUNUSED(cred_p)) 435 return (tda8444_io(dev, uiop, B_WRITE)); 436 } 437 438 static int 439 tda8444_io(dev_t dev, struct uio *uiop, int rw) 440 { 441 struct tda8444_unit *unitp; 442 int instance = TDA8444_MINOR_TO_DEVINST(getminor(dev)); 443 int channel = TDA8444_MINOR_TO_CHANNEL(getminor(dev)); 444 int ret = 0; 445 size_t len = uiop->uio_resid; 446 int8_t out_value; 447 448 if (instance < 0) { 449 450 return (ENXIO); 451 } 452 453 if (len == 0) { 454 return (0); 455 } 456 457 unitp = (struct tda8444_unit *) 458 ddi_get_soft_state(tda8444_soft_statep, instance); 459 460 if (unitp == NULL) { 461 462 return (ENXIO); 463 } 464 465 if (rw == B_READ) { 466 if (unitp->tda8444_output[channel] != TDA8444_UNKNOWN_OUT) { 467 return (uiomove(&unitp->tda8444_output[channel], 1, 468 UIO_READ, uiop)); 469 } else { 470 return (EIO); 471 } 472 } 473 474 /* 475 * rw == B_WRITE. Make sure each write to a device is single 476 * threaded since we pre-allocate a single write buffer. This is not a 477 * bottleneck since concurrent writes would serialize at the 478 * transport level anyway. 479 */ 480 mutex_enter(&unitp->tda8444_mutex); 481 if (unitp->tda8444_flags == TDA8444_SUSPENDED) { 482 mutex_exit(&unitp->tda8444_mutex); 483 484 return (EAGAIN); 485 } 486 487 while (unitp->tda8444_flags == TDA8444_BUSY) { 488 if (cv_wait_sig(&unitp->tda8444_cv, 489 &unitp->tda8444_mutex) <= 0) { 490 mutex_exit(&unitp->tda8444_mutex); 491 492 return (EINTR); 493 } 494 } 495 unitp->tda8444_flags = TDA8444_BUSY; 496 mutex_exit(&unitp->tda8444_mutex); 497 498 unitp->tda8444_transfer->i2c_wbuf[0] = (TDA8444_REGBASE | channel); 499 if ((ret = uiomove(&out_value, sizeof (out_value), UIO_WRITE, 500 uiop)) == 0) { 501 502 /* 503 * Check bounds 504 */ 505 if ((out_value > TDA8444_MAX_OUT) || 506 (out_value < TDA8444_MIN_OUT)) { 507 ret = EINVAL; 508 } else { 509 unitp->tda8444_transfer->i2c_wbuf[1] = 510 (uchar_t)out_value; 511 DPRINTF(IO, ("setting channel %d to %d", channel, 512 unitp->tda8444_transfer->i2c_wbuf[1])); 513 514 if (i2c_transfer(unitp->tda8444_hdl, 515 unitp->tda8444_transfer) != I2C_SUCCESS) { 516 ret = EIO; 517 } else { 518 unitp->tda8444_output[channel] = out_value; 519 } 520 } 521 } else { 522 ret = EFAULT; 523 } 524 525 mutex_enter(&unitp->tda8444_mutex); 526 unitp->tda8444_flags = 0; 527 cv_signal(&unitp->tda8444_cv); 528 mutex_exit(&unitp->tda8444_mutex); 529 530 return (ret); 531 } 532