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/pcf8574_impl.h> 38 39 static void *pcf8574soft_statep; 40 41 static int pcf8574_do_attach(dev_info_t *); 42 static int pcf8574_do_detach(dev_info_t *); 43 static int pcf8574_do_resume(void); 44 static int pcf8574_do_suspend(void); 45 static int pcf8574_get(struct pcf8574_unit *, uchar_t *); 46 static int pcf8574_set(struct pcf8574_unit *, uchar_t); 47 48 /* 49 * cb ops (only need ioctl) 50 */ 51 static int pcf8574_open(dev_t *, int, int, cred_t *); 52 static int pcf8574_close(dev_t, int, int, cred_t *); 53 static int pcf8574_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 54 55 static struct cb_ops pcf8574_cbops = { 56 pcf8574_open, /* open */ 57 pcf8574_close, /* close */ 58 nodev, /* strategy */ 59 nodev, /* print */ 60 nodev, /* dump */ 61 nodev, /* read */ 62 nodev, /* write */ 63 pcf8574_ioctl, /* ioctl */ 64 nodev, /* devmap */ 65 nodev, /* mmap */ 66 nodev, /* segmap */ 67 nochpoll, /* poll */ 68 ddi_prop_op, /* cb_prop_op */ 69 NULL, /* streamtab */ 70 D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 71 CB_REV, /* rev */ 72 nodev, /* int (*cb_aread)() */ 73 nodev /* int (*cb_awrite)() */ 74 }; 75 76 /* 77 * dev ops 78 */ 79 static int pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 80 static int pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 81 82 static struct dev_ops pcf8574_ops = { 83 DEVO_REV, 84 0, 85 ddi_getinfo_1to1, 86 nulldev, 87 nulldev, 88 pcf8574_attach, 89 pcf8574_detach, 90 nodev, 91 &pcf8574_cbops, 92 NULL, /* bus_ops */ 93 NULL, /* power */ 94 ddi_quiesce_not_needed, /* quiesce */ 95 }; 96 97 extern struct mod_ops mod_driverops; 98 99 static struct modldrv pcf8574_modldrv = { 100 &mod_driverops, /* type of module - driver */ 101 "PCF8574 i2c device driver", 102 &pcf8574_ops 103 }; 104 105 static struct modlinkage pcf8574_modlinkage = { 106 MODREV_1, 107 &pcf8574_modldrv, 108 0 109 }; 110 111 112 int 113 _init(void) 114 { 115 int error; 116 117 error = mod_install(&pcf8574_modlinkage); 118 119 if (!error) 120 (void) ddi_soft_state_init(&pcf8574soft_statep, 121 sizeof (struct pcf8574_unit), 1); 122 return (error); 123 } 124 125 int 126 _fini(void) 127 { 128 int error; 129 130 error = mod_remove(&pcf8574_modlinkage); 131 if (!error) 132 ddi_soft_state_fini(&pcf8574soft_statep); 133 134 return (error); 135 } 136 137 int 138 _info(struct modinfo *modinfop) 139 { 140 return (mod_info(&pcf8574_modlinkage, modinfop)); 141 } 142 143 static int 144 pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp) 145 { 146 _NOTE(ARGUNUSED(credp)) 147 148 struct pcf8574_unit *unitp; 149 int instance; 150 int error = 0; 151 152 D1CMN_ERR((CE_WARN, "Opening the PCF8574 device\n")); 153 154 instance = getminor(*devp); 155 156 if (instance < 0) { 157 return (ENXIO); 158 } 159 160 unitp = (struct pcf8574_unit *) 161 ddi_get_soft_state(pcf8574soft_statep, instance); 162 163 if (unitp == NULL) { 164 return (ENXIO); 165 } 166 167 if (otyp != OTYP_CHR) { 168 return (EINVAL); 169 } 170 171 mutex_enter(&unitp->pcf8574_mutex); 172 173 if (flags & FEXCL) { 174 if (unitp->pcf8574_oflag != 0) { 175 error = EBUSY; 176 } else { 177 unitp->pcf8574_oflag = FEXCL; 178 } 179 } else { 180 if (unitp->pcf8574_oflag == FEXCL) { 181 error = EBUSY; 182 } else { 183 unitp->pcf8574_oflag = FOPEN; 184 } 185 } 186 187 mutex_exit(&unitp->pcf8574_mutex); 188 189 return (error); 190 } 191 192 static int 193 pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp) 194 { 195 _NOTE(ARGUNUSED(flags, otyp, credp)) 196 197 struct pcf8574_unit *unitp; 198 int instance; 199 200 instance = getminor(dev); 201 202 if (instance < 0) { 203 return (ENXIO); 204 } 205 unitp = (struct pcf8574_unit *) 206 ddi_get_soft_state(pcf8574soft_statep, instance); 207 208 if (unitp == NULL) { 209 return (ENXIO); 210 } 211 212 mutex_enter(&unitp->pcf8574_mutex); 213 214 unitp->pcf8574_oflag = 0; 215 216 mutex_exit(&unitp->pcf8574_mutex); 217 return (DDI_SUCCESS); 218 } 219 220 static int 221 pcf8574_get(struct pcf8574_unit *unitp, uchar_t *byte) 222 { 223 i2c_transfer_t *i2c_tran_pointer; 224 int err = I2C_SUCCESS; 225 226 D1CMN_ERR((CE_WARN, "Entered the pcf8574_get routine\n")); 227 228 (void) i2c_transfer_alloc(unitp->pcf8574_hdl, &i2c_tran_pointer, 229 0, 1, I2C_SLEEP); 230 if (i2c_tran_pointer == NULL) { 231 D2CMN_ERR((CE_WARN, "%s: Failed in pcf8574_get " 232 "i2c_tran_pointer not allocated\n", 233 unitp->pcf8574_name)); 234 return (ENOMEM); 235 } 236 237 i2c_tran_pointer->i2c_flags = I2C_RD; 238 err = i2c_transfer(unitp->pcf8574_hdl, i2c_tran_pointer); 239 if (err) { 240 D2CMN_ERR((CE_WARN, "%s: Failed in the i2c_transfer routine\n", 241 unitp->pcf8574_name)); 242 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 243 return (err); 244 } 245 246 D1CMN_ERR((CE_WARN, "Back from a transfer value is %x\n", 247 i2c_tran_pointer->i2c_rbuf[0])); 248 *byte = i2c_tran_pointer->i2c_rbuf[0]; 249 250 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 251 return (err); 252 } 253 254 static int 255 pcf8574_set(struct pcf8574_unit *unitp, uchar_t byte) 256 { 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 == (intptr_t)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", 0) == 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