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 i2c_transfer_t *i2c_tran_pointer; 223 int err = I2C_SUCCESS; 224 225 D1CMN_ERR((CE_WARN, "Entered the pcf8574_get routine\n")); 226 227 (void) i2c_transfer_alloc(unitp->pcf8574_hdl, &i2c_tran_pointer, 228 0, 1, I2C_SLEEP); 229 if (i2c_tran_pointer == NULL) { 230 D2CMN_ERR((CE_WARN, "%s: Failed in pcf8574_get " 231 "i2c_tran_pointer not allocated\n", 232 unitp->pcf8574_name)); 233 return (ENOMEM); 234 } 235 236 i2c_tran_pointer->i2c_flags = I2C_RD; 237 err = i2c_transfer(unitp->pcf8574_hdl, i2c_tran_pointer); 238 if (err) { 239 D2CMN_ERR((CE_WARN, "%s: Failed in the i2c_transfer routine\n", 240 unitp->pcf8574_name)); 241 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 242 return (err); 243 } 244 245 D1CMN_ERR((CE_WARN, "Back from a transfer value is %x\n", 246 i2c_tran_pointer->i2c_rbuf[0])); 247 *byte = i2c_tran_pointer->i2c_rbuf[0]; 248 249 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 250 return (err); 251 } 252 253 static int 254 pcf8574_set(struct pcf8574_unit *unitp, uchar_t byte) { 255 i2c_transfer_t *i2c_tran_pointer; 256 int err = I2C_SUCCESS; 257 258 (void) i2c_transfer_alloc(unitp->pcf8574_hdl, &i2c_tran_pointer, 259 1, 0, I2C_SLEEP); 260 if (i2c_tran_pointer == NULL) { 261 D2CMN_ERR((CE_WARN, "%s: Failed in pcf8574_set " 262 "i2c_tran_pointer not allocated\n", 263 unitp->pcf8574_name)); 264 return (ENOMEM); 265 } 266 267 i2c_tran_pointer->i2c_flags = I2C_WR; 268 i2c_tran_pointer->i2c_wbuf[0] = byte; 269 D1CMN_ERR((CE_NOTE, "%s: contains %x\n", unitp->pcf8574_name, 270 i2c_tran_pointer->i2c_wbuf[0])); 271 272 err = i2c_transfer(unitp->pcf8574_hdl, i2c_tran_pointer); 273 if (err) { 274 D2CMN_ERR((CE_WARN, "%s: Failed in the pcf8574_set" 275 " i2c_transfer routine\n", 276 unitp->pcf8574_name)); 277 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 278 return (err); 279 } 280 i2c_transfer_free(unitp->pcf8574_hdl, i2c_tran_pointer); 281 return (err); 282 } 283 284 static int 285 pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 286 cred_t *credp, int *rvalp) 287 { 288 _NOTE(ARGUNUSED(credp, rvalp)) 289 290 struct pcf8574_unit *unitp; 291 int instance; 292 int err = 0; 293 i2c_bit_t ioctl_bit; 294 i2c_port_t ioctl_port; 295 uchar_t byte; 296 297 if (arg == NULL) { 298 D2CMN_ERR((CE_WARN, "PCF8574: ioctl: arg passed in to ioctl " 299 "= NULL\n")); 300 err = EINVAL; 301 return (err); 302 } 303 304 instance = getminor(dev); 305 unitp = (struct pcf8574_unit *) 306 ddi_get_soft_state(pcf8574soft_statep, instance); 307 if (unitp == NULL) { 308 cmn_err(CE_WARN, "PCF8574: ioctl: unitp not filled\n"); 309 return (ENOMEM); 310 } 311 312 mutex_enter(&unitp->pcf8574_mutex); 313 314 switch (cmd) { 315 case I2C_GET_PORT: 316 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_port, 317 sizeof (i2c_port_t), mode) != DDI_SUCCESS) { 318 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_GET_PORT" 319 " ddi_copyin routine\n", 320 unitp->pcf8574_name)); 321 err = EFAULT; 322 break; 323 } 324 325 err = pcf8574_get(unitp, &byte); 326 if (err != I2C_SUCCESS) { 327 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_GET_PORT" 328 " pcf8574_get routine\n", 329 unitp->pcf8574_name)); 330 break; 331 } 332 333 ioctl_port.value = byte; 334 if (ddi_copyout((caddr_t)&ioctl_port, (caddr_t)arg, 335 sizeof (i2c_port_t), mode) != DDI_SUCCESS) { 336 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_PORT " 337 "ddi_copyout routine\n", 338 unitp->pcf8574_name)); 339 err = EFAULT; 340 } 341 342 D1CMN_ERR((CE_NOTE, "%s: contains %x\n", unitp->pcf8574_name, 343 byte)); 344 break; 345 346 case I2C_SET_PORT: 347 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_port, 348 sizeof (uint8_t), mode) != DDI_SUCCESS) { 349 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_SET_PORT" 350 "ddi_cpoyin routine\n", 351 unitp->pcf8574_name)); 352 err = EFAULT; 353 break; 354 } 355 356 err = pcf8574_set(unitp, ioctl_port.value); 357 if (err != I2C_SUCCESS) { 358 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_SET_PORT" 359 " pcf8574_set routine\n", 360 unitp->pcf8574_name)); 361 break; 362 } 363 break; 364 365 case I2C_GET_BIT: 366 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_bit, 367 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 368 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_GET_BIT" 369 " ddi_copyin routine\n", 370 unitp->pcf8574_name)); 371 err = EFAULT; 372 break; 373 } 374 375 if (ioctl_bit.bit_num > 7) { 376 D2CMN_ERR((CE_WARN, "%s: In I2C_GET_BIT bit num" 377 " was not between 0 and 7\n", 378 unitp->pcf8574_name)); 379 err = EIO; 380 break; 381 } 382 383 err = pcf8574_get(unitp, &byte); 384 if (err != I2C_SUCCESS) { 385 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_GET_BIT" 386 " pcf8574_get routine\n", 387 unitp->pcf8574_name)); 388 break; 389 } 390 391 D1CMN_ERR((CE_NOTE, "%s: byte returned from device is %x\n", 392 unitp->pcf8574_name, byte)); 393 ioctl_bit.bit_value = (boolean_t)PCF8574_BIT_READ_MASK(byte, 394 ioctl_bit.bit_num); 395 D1CMN_ERR((CE_NOTE, "%s: byte now contains %x\n", 396 unitp->pcf8574_name, byte)); 397 398 if (ddi_copyout((caddr_t)&ioctl_bit, (caddr_t)arg, 399 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 400 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_BIT" 401 " ddi_copyout routine\n", 402 unitp->pcf8574_name)); 403 err = EFAULT; 404 } 405 break; 406 407 case I2C_SET_BIT: 408 if (ddi_copyin((caddr_t)arg, (caddr_t)&ioctl_bit, 409 sizeof (i2c_bit_t), mode) != DDI_SUCCESS) { 410 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_BIT" 411 " ddi_copyin routine\n", 412 unitp->pcf8574_name)); 413 err = EFAULT; 414 break; 415 } 416 417 if (ioctl_bit.bit_num > 7) { 418 D2CMN_ERR((CE_WARN, "%s: I2C_SET_BIT: bit_num sent" 419 " in was not between 0 and 7", 420 unitp->pcf8574_name)); 421 err = EIO; 422 break; 423 } 424 425 err = pcf8574_get(unitp, &byte); 426 if (err != I2C_SUCCESS) { 427 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_SET_BIT" 428 " pcf8574_get routine\n", 429 unitp->pcf8574_name)); 430 break; 431 } 432 433 D1CMN_ERR((CE_NOTE, "%s: byte returned from device is %x\n", 434 unitp->pcf8574_name, byte)); 435 byte = PCF8574_BIT_WRITE_MASK(byte, ioctl_bit.bit_num, 436 ioctl_bit.bit_value); 437 D1CMN_ERR((CE_NOTE, "%s: byte after shifting is %x\n", 438 unitp->pcf8574_name, byte)); 439 440 err = pcf8574_set(unitp, byte); 441 if (err != I2C_SUCCESS) { 442 D2CMN_ERR((CE_WARN, "%s: Failed in the I2C_SET_BIT" 443 " pcf8574_set routine\n", 444 unitp->pcf8574_name)); 445 break; 446 } 447 break; 448 449 default: 450 D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x\n", 451 unitp->pcf8574_name, cmd)); 452 err = EINVAL; 453 } 454 455 mutex_exit(&unitp->pcf8574_mutex); 456 return (err); 457 } 458 459 static int 460 pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 461 { 462 switch (cmd) { 463 case DDI_ATTACH: 464 return (pcf8574_do_attach(dip)); 465 case DDI_RESUME: 466 return (pcf8574_do_resume()); 467 default: 468 return (DDI_FAILURE); 469 } 470 } 471 472 static int 473 pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 474 { 475 switch (cmd) { 476 case DDI_DETACH: 477 return (pcf8574_do_detach(dip)); 478 case DDI_SUSPEND: 479 return (pcf8574_do_suspend()); 480 default: 481 return (DDI_FAILURE); 482 } 483 } 484 485 static int 486 pcf8574_do_attach(dev_info_t *dip) 487 { 488 struct pcf8574_unit *unitp; 489 int instance; 490 491 instance = ddi_get_instance(dip); 492 493 if (ddi_soft_state_zalloc(pcf8574soft_statep, instance) != 0) { 494 cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n", 495 ddi_get_name(dip), instance); 496 return (DDI_FAILURE); 497 } 498 499 unitp = ddi_get_soft_state(pcf8574soft_statep, instance); 500 501 if (unitp == NULL) { 502 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 503 ddi_get_name(dip), instance); 504 return (ENOMEM); 505 } 506 507 (void) snprintf(unitp->pcf8574_name, sizeof (unitp->pcf8574_name), 508 "%s%d", ddi_node_name(dip), instance); 509 510 511 if (ddi_create_minor_node(dip, "pcf8574", S_IFCHR, instance, 512 "ddi_i2c:ioexp", NULL) == DDI_FAILURE) { 513 cmn_err(CE_WARN, "%s ddi_create_minor_node failed for " 514 "%s\n", unitp->pcf8574_name, "pcf8574"); 515 ddi_soft_state_free(pcf8574soft_statep, instance); 516 517 return (DDI_FAILURE); 518 } 519 520 if (i2c_client_register(dip, &unitp->pcf8574_hdl) != I2C_SUCCESS) { 521 ddi_remove_minor_node(dip, NULL); 522 ddi_soft_state_free(pcf8574soft_statep, instance); 523 524 return (DDI_FAILURE); 525 } 526 527 mutex_init(&unitp->pcf8574_mutex, NULL, MUTEX_DRIVER, NULL); 528 529 return (DDI_SUCCESS); 530 } 531 532 static int 533 pcf8574_do_resume() 534 { 535 int ret = DDI_SUCCESS; 536 537 return (ret); 538 } 539 540 static int 541 pcf8574_do_suspend() 542 { 543 int ret = DDI_SUCCESS; 544 545 return (ret); 546 } 547 548 static int 549 pcf8574_do_detach(dev_info_t *dip) 550 { 551 struct pcf8574_unit *unitp; 552 int instance; 553 554 instance = ddi_get_instance(dip); 555 556 unitp = ddi_get_soft_state(pcf8574soft_statep, instance); 557 558 if (unitp == NULL) { 559 cmn_err(CE_WARN, "%s%d: unitp not filled\n", 560 ddi_get_name(dip), instance); 561 return (ENOMEM); 562 } 563 564 i2c_client_unregister(unitp->pcf8574_hdl); 565 566 ddi_remove_minor_node(dip, NULL); 567 568 mutex_destroy(&unitp->pcf8574_mutex); 569 570 ddi_soft_state_free(pcf8574soft_statep, instance); 571 572 return (DDI_SUCCESS); 573 574 } 575