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