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