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 #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 27 #include <sys/modctl.h> /* for modldrv */ 28 #include <sys/open.h> /* for open params. */ 29 #include <sys/types.h> 30 #include <sys/kmem.h> 31 #include <sys/sunddi.h> 32 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 33 #include <sys/ddi.h> 34 #include <sys/file.h> 35 #include <sys/note.h> 36 37 #include <sys/i2c/clients/pcf8591.h> 38 #include <sys/i2c/clients/pcf8591_impl.h> 39 40 static void *pcf8591soft_statep; 41 42 static uint8_t ipmode = PCF8591_4SINGLE; 43 static int32_t current_value = 0; 44 static int current_set_flag = 0; 45 46 static int pcf8591_do_attach(dev_info_t *); 47 static int pcf8591_do_detach(dev_info_t *); 48 static int pcf8591_do_resume(void); 49 static int pcf8591_do_suspend(void); 50 51 /* 52 * cb ops (only need ioctl) 53 */ 54 static int pcf8591_open(dev_t *, int, int, cred_t *); 55 static int pcf8591_close(dev_t, int, int, cred_t *); 56 static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 57 58 static struct cb_ops pcf8591_cbops = { 59 pcf8591_open, /* open */ 60 pcf8591_close, /* close */ 61 nodev, /* strategy */ 62 nodev, /* print */ 63 nodev, /* dump */ 64 nodev, /* read */ 65 nodev, /* write */ 66 pcf8591_ioctl, /* ioctl */ 67 nodev, /* devmap */ 68 nodev, /* mmap */ 69 nodev, /* segmap */ 70 nochpoll, /* poll */ 71 ddi_prop_op, /* cb_prop_op */ 72 NULL, /* streamtab */ 73 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 74 CB_REV, /* rev */ 75 nodev, /* int (*cb_aread)() */ 76 nodev /* int (*cb_awrite)() */ 77 }; 78 79 /* 80 * dev ops 81 */ 82 static int pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 83 void **result); 84 static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 85 static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 86 87 static struct dev_ops pcf8591_ops = { 88 DEVO_REV, 89 0, 90 pcf8591_info, 91 nulldev, 92 nulldev, 93 pcf8591_attach, 94 pcf8591_detach, 95 nodev, 96 &pcf8591_cbops, 97 NULL, 98 NULL, 99 ddi_quiesce_not_needed, /* quiesce */ 100 }; 101 102 extern struct mod_ops mod_driverops; 103 104 static struct modldrv pcf8591_modldrv = { 105 &mod_driverops, /* type of module - driver */ 106 "PCF8591 i2c device driver", 107 &pcf8591_ops 108 }; 109 110 static struct modlinkage pcf8591_modlinkage = { 111 MODREV_1, 112 &pcf8591_modldrv, 113 0 114 }; 115 116 117 int 118 _init(void) 119 { 120 int error; 121 122 error = mod_install(&pcf8591_modlinkage); 123 if (!error) 124 (void) ddi_soft_state_init(&pcf8591soft_statep, 125 sizeof (struct pcf8591_unit), 1); 126 return (error); 127 } 128 129 int 130 _fini(void) 131 { 132 int error; 133 134 error = mod_remove(&pcf8591_modlinkage); 135 if (!error) 136 ddi_soft_state_fini(&pcf8591soft_statep); 137 138 return (error); 139 } 140 141 int 142 _info(struct modinfo *modinfop) 143 { 144 return (mod_info(&pcf8591_modlinkage, modinfop)); 145 } 146 147 static int 148 pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp) 149 { 150 _NOTE(ARGUNUSED(credp)) 151 152 struct pcf8591_unit *unitp; 153 int instance; 154 int error = 0; 155 156 instance = MINOR_TO_INST(getminor(*devp)); 157 158 if (instance < 0) { 159 return (ENXIO); 160 } 161 162 unitp = (struct pcf8591_unit *) 163 ddi_get_soft_state(pcf8591soft_statep, instance); 164 165 if (unitp == NULL) { 166 return (ENXIO); 167 } 168 169 if (otyp != OTYP_CHR) { 170 return (EINVAL); 171 } 172 173 mutex_enter(&unitp->pcf8591_mutex); 174 175 if (flags & FEXCL) { 176 if (unitp->pcf8591_oflag != 0) { 177 error = EBUSY; 178 } else { 179 unitp->pcf8591_oflag = FEXCL; 180 } 181 } else { 182 if (unitp->pcf8591_oflag == FEXCL) { 183 error = EBUSY; 184 } else { 185 unitp->pcf8591_oflag = FOPEN; 186 } 187 } 188 189 mutex_exit(&unitp->pcf8591_mutex); 190 191 return (error); 192 } 193 194 static int 195 pcf8591_close(dev_t dev, int flags, int otyp, cred_t *credp) 196 { 197 _NOTE(ARGUNUSED(flags, otyp, credp)) 198 199 struct pcf8591_unit *unitp; 200 int instance; 201 202 instance = MINOR_TO_INST(getminor(dev)); 203 204 if (instance < 0) { 205 return (ENXIO); 206 } 207 208 unitp = (struct pcf8591_unit *) 209 ddi_get_soft_state(pcf8591soft_statep, instance); 210 211 if (unitp == NULL) { 212 return (ENXIO); 213 } 214 215 mutex_enter(&unitp->pcf8591_mutex); 216 217 unitp->pcf8591_oflag = 0; 218 219 mutex_exit(&unitp->pcf8591_mutex); 220 return (DDI_SUCCESS); 221 } 222 223 static int 224 pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 225 cred_t *credp, int *rvalp) 226 { 227 _NOTE(ARGUNUSED(credp, rvalp)) 228 229 struct pcf8591_unit *unitp; 230 int err = 0; 231 i2c_transfer_t *i2c_tran_pointer; 232 int port = MINOR_TO_PORT(getminor(dev)); 233 int instance = MINOR_TO_INST(getminor(dev)); 234 int autoincr = 0; 235 uchar_t control, reg; 236 int32_t value; 237 uint8_t uvalue; 238 239 if (arg == NULL) { 240 D2CMN_ERR((CE_WARN, "PCF8591: ioctl: arg passed in to ioctl " 241 "= NULL\n")); 242 err = EINVAL; 243 return (err); 244 } 245 unitp = (struct pcf8591_unit *) 246 ddi_get_soft_state(pcf8591soft_statep, instance); 247 248 if (unitp == NULL) { 249 cmn_err(CE_WARN, "PCF8591: ioctl: unitp not filled\n"); 250 return (ENOMEM); 251 } 252 253 mutex_enter(&unitp->pcf8591_mutex); 254 255 D1CMN_ERR((CE_NOTE, "%s: ioctl: port = %d instance = %d\n", 256 unitp->pcf8591_name, port, instance)); 257 258 switch (cmd) { 259 case I2C_GET_INPUT: 260 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer, 261 1, 2, I2C_SLEEP); 262 if (i2c_tran_pointer == NULL) { 263 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 264 "i2c_tran_pointer not allocated\n", 265 unitp->pcf8591_name)); 266 err = ENOMEM; 267 break; 268 } 269 reg = (uchar_t)port; 270 if ((reg == 0x02) && (ipmode == PCF8591_2DIFF)) { 271 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 272 "cannot use port 2 when ipmode is " 273 "0x03\n", unitp->pcf8591_name)); 274 err = EIO; 275 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 276 break; 277 } 278 279 if ((reg == 0x03) && (ipmode != PCF8591_4SINGLE)) { 280 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT " 281 "cannot use port 3 when ipmode is not " 282 "0x00\n", unitp->pcf8591_name)); 283 err = EIO; 284 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 285 break; 286 } 287 control = ((0 << PCF8591_ANALOG_OUTPUT_SHIFT) | 288 (ipmode << PCF8591_ANALOG_INPUT_SHIFT) | 289 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg); 290 291 i2c_tran_pointer->i2c_flags = I2C_WR_RD; 292 i2c_tran_pointer->i2c_wbuf[0] = control; 293 294 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer); 295 if (err) { 296 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT" 297 " i2c_transfer routine\n", 298 unitp->pcf8591_name)); 299 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 300 break; 301 } 302 i2c_tran_pointer->i2c_rbuf[0] = i2c_tran_pointer->i2c_rbuf[1]; 303 value = i2c_tran_pointer->i2c_rbuf[0]; 304 D1CMN_ERR((CE_NOTE, "%s: Back from transfer result is %x\n", 305 unitp->pcf8591_name, value)); 306 if (ddi_copyout((caddr_t)&value, 307 (caddr_t)arg, 308 sizeof (int32_t), mode) != DDI_SUCCESS) { 309 D2CMN_ERR((CE_WARN, "%s: Failed I2C_GET_INPUT" 310 " ddi_copyout routine\n", 311 unitp->pcf8591_name)); 312 err = EFAULT; 313 } 314 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 315 break; 316 317 case I2C_SET_OUTPUT: 318 reg = (uchar_t)port; 319 if (ipmode != PCF8591_4SINGLE) { 320 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT " 321 "cannot set output when ipmode is not " 322 "0x00\n", unitp->pcf8591_name)); 323 err = EIO; 324 break; 325 } 326 327 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer, 328 2, 0, I2C_SLEEP); 329 if (i2c_tran_pointer == NULL) { 330 D2CMN_ERR((CE_WARN, "%s: Failed in " 331 "I2C_SET_OUTPUT " 332 "i2c_tran_pointer not allocated\n", 333 unitp->pcf8591_name)); 334 err = ENOMEM; 335 break; 336 } 337 if (ddi_copyin((caddr_t)arg, (caddr_t)&value, 338 sizeof (int32_t), mode) != DDI_SUCCESS) { 339 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT" 340 " ddi_copyout routine\n", 341 unitp->pcf8591_name)); 342 err = EFAULT; 343 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 344 break; 345 } 346 control = ((1 << PCF8591_ANALOG_OUTPUT_SHIFT) | 347 (0 << PCF8591_ANALOG_INPUT_SHIFT) | 348 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg); 349 350 i2c_tran_pointer->i2c_flags = I2C_WR; 351 i2c_tran_pointer->i2c_wbuf[0] = control; 352 i2c_tran_pointer->i2c_wbuf[1] = (uchar_t)value; 353 354 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer); 355 if (!err) { 356 current_value = value; 357 current_set_flag = 1; 358 } 359 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer); 360 break; 361 362 case I2C_GET_OUTPUT: 363 if (current_set_flag == 0) { 364 err = EIO; 365 break; 366 } else { 367 if (ddi_copyout((caddr_t)(uintptr_t)current_value, 368 (caddr_t)arg, sizeof (int32_t), mode) 369 != DDI_SUCCESS) { 370 D2CMN_ERR((CE_WARN, "%s: Failed in " 371 "I2C_GET_OUTPUT ddi_copyout routine\n", 372 unitp->pcf8591_name)); 373 err = EFAULT; 374 break; 375 } 376 } 377 break; 378 379 case PCF8591_SET_IPMODE: 380 if (ddi_copyin((caddr_t)arg, (caddr_t)&uvalue, 381 sizeof (uint_t), mode) != DDI_SUCCESS) { 382 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE" 383 " ddi_copyout routine\n", 384 unitp->pcf8591_name)); 385 err = EFAULT; 386 break; 387 } 388 389 if (uvalue > 0x03) { 390 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE" 391 " value is not a valid mode\n", 392 unitp->pcf8591_name)); 393 err = EIO; 394 break; 395 } 396 397 ipmode = uvalue; 398 break; 399 400 default: 401 D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x\n", 402 unitp->pcf8591_name, cmd)); 403 err = EINVAL; 404 } 405 406 mutex_exit(&unitp->pcf8591_mutex); 407 return (err); 408 409 } 410 411 /* ARGSUSED */ 412 static int 413 pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 414 { 415 dev_t dev; 416 int instance; 417 418 if (infocmd == DDI_INFO_DEVT2INSTANCE) { 419 dev = (dev_t)arg; 420 instance = MINOR_TO_INST(getminor(dev)); 421 *result = (void *)(uintptr_t)instance; 422 return (DDI_SUCCESS); 423 } 424 return (DDI_FAILURE); 425 } 426 427 static int 428 pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 429 { 430 switch (cmd) { 431 case DDI_ATTACH: 432 return (pcf8591_do_attach(dip)); 433 case DDI_RESUME: 434 return (pcf8591_do_resume()); 435 default: 436 return (DDI_FAILURE); 437 } 438 } 439 440 static int 441 pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 442 { 443 switch (cmd) { 444 case DDI_DETACH: 445 return (pcf8591_do_detach(dip)); 446 case DDI_SUSPEND: 447 return (pcf8591_do_suspend()); 448 default: 449 return (DDI_FAILURE); 450 } 451 } 452 453 static int 454 pcf8591_do_attach(dev_info_t *dip) 455 { 456 struct pcf8591_unit *unitp; 457 int instance; 458 char name[MAXNAMELEN]; 459 minor_t minor_number; 460 int i; 461 462 instance = ddi_get_instance(dip); 463 464 if (ddi_soft_state_zalloc(pcf8591soft_statep, instance) != 0) { 465 cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n", 466 ddi_get_name(dip), instance); 467 return (DDI_FAILURE); 468 } 469 470 unitp = ddi_get_soft_state(pcf8591soft_statep, instance); 471 472 if (unitp == NULL) { 473 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 474 ddi_get_name(dip), instance); 475 return (ENOMEM); 476 } 477 478 (void) snprintf(unitp->pcf8591_name, sizeof (unitp->pcf8591_name), 479 "%s%d", ddi_node_name(dip), instance); 480 481 for (i = 0; i < 4; i++) { 482 (void) sprintf(name, "port_%d", i); 483 484 minor_number = INST_TO_MINOR(instance) | 485 PORT_TO_MINOR(I2C_PORT(i)); 486 487 if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number, 488 "ddi_i2c:adio", NULL) == DDI_FAILURE) { 489 cmn_err(CE_WARN, "%s ddi_create_minor_node failed for " 490 "%s\n", unitp->pcf8591_name, name); 491 ddi_soft_state_free(pcf8591soft_statep, instance); 492 493 return (DDI_FAILURE); 494 } 495 } 496 497 if (i2c_client_register(dip, &unitp->pcf8591_hdl) != I2C_SUCCESS) { 498 ddi_remove_minor_node(dip, NULL); 499 ddi_soft_state_free(pcf8591soft_statep, instance); 500 501 return (DDI_FAILURE); 502 } 503 504 mutex_init(&unitp->pcf8591_mutex, NULL, MUTEX_DRIVER, NULL); 505 506 return (DDI_SUCCESS); 507 } 508 509 static int 510 pcf8591_do_resume() 511 { 512 int ret = DDI_SUCCESS; 513 514 return (ret); 515 } 516 517 static int 518 pcf8591_do_suspend() 519 { 520 int ret = DDI_SUCCESS; 521 522 return (ret); 523 } 524 525 static int 526 pcf8591_do_detach(dev_info_t *dip) 527 { 528 struct pcf8591_unit *unitp; 529 int instance; 530 531 instance = ddi_get_instance(dip); 532 533 unitp = ddi_get_soft_state(pcf8591soft_statep, instance); 534 535 if (unitp == NULL) { 536 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 537 ddi_get_name(dip), instance); 538 return (ENOMEM); 539 } 540 541 i2c_client_unregister(unitp->pcf8591_hdl); 542 543 ddi_remove_minor_node(dip, NULL); 544 545 mutex_destroy(&unitp->pcf8591_mutex); 546 547 ddi_soft_state_free(pcf8591soft_statep, instance); 548 549 return (DDI_SUCCESS); 550 } 551