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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)138 _info(struct modinfo *modinfop)
139 {
140 return (mod_info(&pcf8574_modlinkage, modinfop));
141 }
142
143 static int
pcf8574_open(dev_t * devp,int flags,int otyp,cred_t * credp)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
pcf8574_close(dev_t dev,int flags,int otyp,cred_t * credp)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
pcf8574_get(struct pcf8574_unit * unitp,uchar_t * byte)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
pcf8574_set(struct pcf8574_unit * unitp,uchar_t byte)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
pcf8574_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
pcf8574_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
pcf8574_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
pcf8574_do_attach(dev_info_t * dip)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
pcf8574_do_resume()535 pcf8574_do_resume()
536 {
537 int ret = DDI_SUCCESS;
538
539 return (ret);
540 }
541
542 static int
pcf8574_do_suspend()543 pcf8574_do_suspend()
544 {
545 int ret = DDI_SUCCESS;
546
547 return (ret);
548 }
549
550 static int
pcf8574_do_detach(dev_info_t * dip)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