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 * av1394 driver
29 */
30
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/cred.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/stat.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39
40 #include <sys/1394/targets/av1394/av1394_impl.h>
41
42 /* DDI/DKI entry points */
43 static int av1394_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
44 static int av1394_attach(dev_info_t *, ddi_attach_cmd_t);
45 static int av1394_detach(dev_info_t *, ddi_detach_cmd_t);
46 static int av1394_open(dev_t *, int, int, cred_t *);
47 static int av1394_close(dev_t, int, int, cred_t *);
48 static int av1394_read(dev_t, struct uio *, cred_t *);
49 static int av1394_write(dev_t, struct uio *, cred_t *);
50 static int av1394_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
51 static int av1394_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
52 size_t *, uint_t);
53 static int av1394_poll(dev_t, short, int, short *, struct pollhead **);
54
55 /* configuration routines */
56 static void av1394_cleanup(av1394_inst_t *, int);
57 static int av1394_t1394_attach(av1394_inst_t *, dev_info_t *);
58 static void av1394_t1394_detach(av1394_inst_t *);
59 static int av1394_add_events(av1394_inst_t *);
60 static void av1394_remove_events(av1394_inst_t *);
61
62 /* CPR */
63 static int av1394_cpr_suspend(av1394_inst_t *);
64 static int av1394_cpr_resume(av1394_inst_t *);
65
66 /* callbacks */
67 static void av1394_bus_reset(dev_info_t *, ddi_eventcookie_t, void *,
68 void *);
69 static void av1394_disconnect(dev_info_t *, ddi_eventcookie_t, void *,
70 void *);
71 static void av1394_reconnect(dev_info_t *, ddi_eventcookie_t, void *,
72 void *);
73
74 extern struct mod_ops mod_driverops;
75
76 struct cb_ops av1394_cb_ops = {
77 av1394_open, /* open */
78 av1394_close, /* close */
79 nulldev, /* strategy */
80 nulldev, /* print */
81 nulldev, /* dump */
82 av1394_read, /* read */
83 av1394_write, /* write */
84 av1394_ioctl, /* ioctl */
85 av1394_devmap, /* devmap */
86 nulldev, /* mmap */
87 nulldev, /* segmap */
88 av1394_poll, /* poll */
89 ddi_prop_op, /* cb_prop_op */
90 NULL, /* streamtab */
91 D_MP | D_NEW | D_HOTPLUG | D_DEVMAP
92 };
93
94 static struct dev_ops av1394_ops = {
95 DEVO_REV, /* devo_rev */
96 0, /* refcnt */
97 av1394_getinfo, /* getinfo */
98 nulldev, /* identify */
99 nulldev, /* probe */
100 av1394_attach, /* attach */
101 av1394_detach, /* detach */
102 nodev, /* reset */
103 &av1394_cb_ops, /* driver operations */
104 NULL, /* bus operations */
105 NULL, /* power */
106 ddi_quiesce_not_supported, /* devo_quiesce */
107 };
108
109 static struct modldrv av1394_modldrv = {
110 &mod_driverops,
111 "IEEE 1394 AV driver",
112 &av1394_ops
113 };
114
115 static struct modlinkage av1394_modlinkage = {
116 MODREV_1,
117 &av1394_modldrv,
118 NULL,
119 };
120
121 static void *av1394_statep;
122
123 #define AV1394_INST2STATE(inst) (ddi_get_soft_state(av1394_statep, inst))
124 #define AV1394_DEV2STATE(dev) \
125 (ddi_get_soft_state(av1394_statep, AV1394_DEV2INST(dev)))
126
127 /*
128 *
129 * --- DDI/DKI entry points
130 *
131 */
132 int
_init(void)133 _init(void)
134 {
135 int error;
136
137 error = ddi_soft_state_init(&av1394_statep, sizeof (av1394_inst_t), 1);
138 if (error != 0) {
139 return (error);
140 }
141
142 if ((error = mod_install(&av1394_modlinkage)) != 0) {
143 ddi_soft_state_fini(&av1394_statep);
144 }
145
146 return (error);
147 }
148
149 int
_fini(void)150 _fini(void)
151 {
152 int error;
153
154 if ((error = mod_remove(&av1394_modlinkage)) == 0) {
155 ddi_soft_state_fini(&av1394_statep);
156 }
157
158 return (error);
159 }
160
161 int
_info(struct modinfo * modinfop)162 _info(struct modinfo *modinfop)
163 {
164 return (mod_info(&av1394_modlinkage, modinfop));
165 }
166
167 /*
168 * attach
169 */
170 static int
av1394_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)171 av1394_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
172 {
173 int instance = ddi_get_instance(dip);
174 av1394_inst_t *avp;
175
176 switch (cmd) {
177 case DDI_ATTACH:
178 break;
179 case DDI_RESUME:
180 if ((avp = AV1394_INST2STATE(instance)) == NULL) {
181 return (DDI_FAILURE);
182 }
183 return (av1394_cpr_resume(avp));
184 default:
185 return (DDI_FAILURE);
186 }
187
188 if (ddi_soft_state_zalloc(av1394_statep, instance) != 0) {
189 return (DDI_FAILURE);
190 }
191 avp = AV1394_INST2STATE(instance);
192
193 if (av1394_t1394_attach(avp, dip) != DDI_SUCCESS) {
194 av1394_cleanup(avp, 1);
195 return (DDI_FAILURE);
196 }
197
198 mutex_init(&avp->av_mutex, NULL, MUTEX_DRIVER,
199 avp->av_attachinfo.iblock_cookie);
200
201 #ifndef __lock_lint
202 avp->av_dip = dip;
203 avp->av_instance = instance;
204 #endif
205
206 if (av1394_add_events(avp) != DDI_SUCCESS) {
207 av1394_cleanup(avp, 2);
208 return (DDI_FAILURE);
209 }
210
211 if (av1394_isoch_attach(avp) != DDI_SUCCESS) {
212 av1394_cleanup(avp, 3);
213 return (DDI_FAILURE);
214 }
215
216 if (av1394_async_attach(avp) != DDI_SUCCESS) {
217 av1394_cleanup(avp, 4);
218 return (DDI_FAILURE);
219 }
220
221 #ifndef __lock_lint
222 avp->av_dev_state = AV1394_DEV_ONLINE;
223 #endif
224
225 ddi_report_dev(dip);
226
227 return (DDI_SUCCESS);
228 }
229
230 static int
av1394_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)231 av1394_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
232 {
233 int instance = ddi_get_instance(dip);
234 av1394_inst_t *avp;
235
236 if ((avp = AV1394_INST2STATE(instance)) == NULL) {
237 return (DDI_FAILURE);
238 }
239
240 switch (cmd) {
241 case DDI_DETACH:
242 av1394_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
243 return (DDI_SUCCESS);
244 case DDI_SUSPEND:
245 return (av1394_cpr_suspend(avp));
246 default:
247 return (DDI_FAILURE);
248 }
249 }
250
251 /*ARGSUSED*/
252 static int
av1394_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)253 av1394_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
254 void **result)
255 {
256 dev_t dev = (dev_t)arg;
257 av1394_inst_t *avp;
258 int rval = DDI_FAILURE;
259
260 switch (infocmd) {
261 case DDI_INFO_DEVT2DEVINFO:
262 if ((avp = AV1394_DEV2STATE(dev)) != NULL) {
263 *result = avp->av_dip;
264 rval = DDI_SUCCESS;
265 } else {
266 *result = NULL;
267 }
268 break;
269 case DDI_INFO_DEVT2INSTANCE:
270 *result = (void *)(uintptr_t)AV1394_DEV2INST(dev);
271 rval = DDI_SUCCESS;
272 break;
273 }
274
275 return (rval);
276 }
277
278 /*ARGSUSED*/
279 static int
av1394_open(dev_t * dev,int flag,int otyp,cred_t * cr)280 av1394_open(dev_t *dev, int flag, int otyp, cred_t *cr)
281 {
282 av1394_inst_t *avp = AV1394_DEV2STATE(*dev);
283 int ret = ENXIO;
284
285 if (avp != NULL) {
286 if (AV1394_DEV_IS_ISOCH(*dev)) {
287 ret = 0;
288 } else if (AV1394_DEV_IS_ASYNC(*dev)) {
289 ret = av1394_async_open(avp, flag);
290 }
291 }
292 return (ret);
293 }
294
295 /*ARGSUSED*/
296 static int
av1394_close(dev_t dev,int flag,int otyp,cred_t * cr)297 av1394_close(dev_t dev, int flag, int otyp, cred_t *cr)
298 {
299 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
300 int ret = ENXIO;
301
302 if (avp != NULL) {
303 if (AV1394_DEV_IS_ISOCH(dev)) {
304 ret = av1394_isoch_close(avp, flag);
305 } else if (AV1394_DEV_IS_ASYNC(dev)) {
306 ret = av1394_async_close(avp, flag);
307 }
308 }
309 return (ret);
310 }
311
312 /*ARGSUSED*/
313 static int
av1394_read(dev_t dev,struct uio * uiop,cred_t * cr)314 av1394_read(dev_t dev, struct uio *uiop, cred_t *cr)
315 {
316 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
317 int ret = ENXIO;
318
319 if (avp != NULL) {
320 if (AV1394_DEV_IS_ISOCH(dev)) {
321 ret = av1394_isoch_read(avp, uiop);
322 } else if (AV1394_DEV_IS_ASYNC(dev)) {
323 ret = av1394_async_read(avp, uiop);
324 }
325 }
326 return (ret);
327 }
328
329 /*ARGSUSED*/
330 static int
av1394_write(dev_t dev,struct uio * uiop,cred_t * cr)331 av1394_write(dev_t dev, struct uio *uiop, cred_t *cr)
332 {
333 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
334 int ret = ENXIO;
335
336 if (avp != NULL) {
337 if (AV1394_DEV_IS_ISOCH(dev)) {
338 ret = av1394_isoch_write(avp, uiop);
339 } else if (AV1394_DEV_IS_ASYNC(dev)) {
340 ret = av1394_async_write(avp, uiop);
341 }
342 }
343 return (ret);
344 }
345
346 /*ARGSUSED*/
347 static int
av1394_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rvalp)348 av1394_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp)
349 {
350 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
351 int ret = ENXIO;
352
353 if (avp != NULL) {
354 if (AV1394_DEV_IS_ISOCH(dev)) {
355 ret = av1394_isoch_ioctl(avp, cmd, arg, mode, rvalp);
356 } else if (AV1394_DEV_IS_ASYNC(dev)) {
357 ret = av1394_async_ioctl(avp, cmd, arg, mode, rvalp);
358 }
359 }
360 return (ret);
361 }
362
363 /*ARGSUSED*/
364 static int
av1394_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)365 av1394_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
366 size_t *maplen, uint_t model)
367 {
368 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
369 int ret = ENXIO;
370
371 if ((avp != NULL) && (AV1394_DEV_IS_ISOCH(dev))) {
372 ret = av1394_isoch_devmap(avp, dhp, off, len, maplen, model);
373 }
374 return (ret);
375 }
376
377 static int
av1394_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)378 av1394_poll(dev_t dev, short events, int anyyet, short *reventsp,
379 struct pollhead **phpp)
380 {
381 av1394_inst_t *avp = AV1394_DEV2STATE(dev);
382 int ret = ENXIO;
383
384 if ((avp != NULL) && AV1394_DEV_IS_ASYNC(dev)) {
385 ret = av1394_async_poll(avp, events, anyyet, reventsp, phpp);
386 }
387 return (ret);
388 }
389
390
391 /*
392 *
393 * --- configuration routines
394 *
395 * av1394_cleanup()
396 * Cleanup after attach
397 */
398 static void
av1394_cleanup(av1394_inst_t * avp,int level)399 av1394_cleanup(av1394_inst_t *avp, int level)
400 {
401 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
402
403 switch (level) {
404 default:
405 av1394_async_detach(avp);
406 /* FALLTHRU */
407 case 4:
408 av1394_isoch_detach(avp);
409 /* FALLTHRU */
410 case 3:
411 av1394_remove_events(avp);
412 /* FALLTHRU */
413 case 2:
414 av1394_t1394_detach(avp);
415 mutex_destroy(&avp->av_mutex);
416 /* FALLTHRU */
417 case 1:
418 ddi_soft_state_free(av1394_statep, avp->av_instance);
419 }
420 }
421
422 static int
av1394_t1394_attach(av1394_inst_t * avp,dev_info_t * dip)423 av1394_t1394_attach(av1394_inst_t *avp, dev_info_t *dip)
424 {
425 int ret;
426
427 ret = t1394_attach(dip, T1394_VERSION_V1, 0, &avp->av_attachinfo,
428 &avp->av_t1394_hdl);
429
430 return (ret);
431 }
432
433 static void
av1394_t1394_detach(av1394_inst_t * avp)434 av1394_t1394_detach(av1394_inst_t *avp)
435 {
436 (void) t1394_detach(&avp->av_t1394_hdl, 0);
437 }
438
439 static int
av1394_add_events(av1394_inst_t * avp)440 av1394_add_events(av1394_inst_t *avp)
441 {
442 ddi_eventcookie_t br_evc, rem_evc, ins_evc;
443
444 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_BUS_RESET_EVENT,
445 &br_evc) != DDI_SUCCESS) {
446 return (DDI_FAILURE);
447 }
448 if (ddi_add_event_handler(avp->av_dip, br_evc, av1394_bus_reset,
449 avp, &avp->av_reset_cb) != DDI_SUCCESS) {
450 return (DDI_FAILURE);
451 }
452
453 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_REMOVE_EVENT,
454 &rem_evc) != DDI_SUCCESS) {
455 (void) ddi_remove_event_handler(avp->av_reset_cb);
456 return (DDI_FAILURE);
457 }
458 if (ddi_add_event_handler(avp->av_dip, rem_evc, av1394_disconnect,
459 avp, &avp->av_remove_cb) != DDI_SUCCESS) {
460 (void) ddi_remove_event_handler(avp->av_reset_cb);
461 return (DDI_FAILURE);
462 }
463
464 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_INSERT_EVENT,
465 &ins_evc) != DDI_SUCCESS) {
466 (void) ddi_remove_event_handler(avp->av_remove_cb);
467 (void) ddi_remove_event_handler(avp->av_reset_cb);
468 return (DDI_FAILURE);
469 }
470 if (ddi_add_event_handler(avp->av_dip, ins_evc, av1394_reconnect,
471 avp, &avp->av_insert_cb) != DDI_SUCCESS) {
472 (void) ddi_remove_event_handler(avp->av_remove_cb);
473 (void) ddi_remove_event_handler(avp->av_reset_cb);
474 return (DDI_FAILURE);
475 }
476
477 return (DDI_SUCCESS);
478 }
479
480 static void
av1394_remove_events(av1394_inst_t * avp)481 av1394_remove_events(av1394_inst_t *avp)
482 {
483 ddi_eventcookie_t evc;
484
485 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_INSERT_EVENT,
486 &evc) == DDI_SUCCESS) {
487 (void) ddi_remove_event_handler(avp->av_insert_cb);
488 }
489
490 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_REMOVE_EVENT,
491 &evc) == DDI_SUCCESS) {
492 (void) ddi_remove_event_handler(avp->av_remove_cb);
493 }
494
495 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_BUS_RESET_EVENT,
496 &evc) == DDI_SUCCESS) {
497 (void) ddi_remove_event_handler(avp->av_reset_cb);
498 }
499 }
500
501 /*
502 *
503 * --- CPR
504 *
505 */
506 static int
av1394_cpr_suspend(av1394_inst_t * avp)507 av1394_cpr_suspend(av1394_inst_t *avp)
508 {
509 int ret;
510
511 ret = av1394_isoch_cpr_suspend(avp);
512
513 if (ret == DDI_SUCCESS) {
514 mutex_enter(&avp->av_mutex);
515 avp->av_prev_dev_state = avp->av_dev_state;
516 avp->av_dev_state = AV1394_DEV_SUSPENDED;
517 mutex_exit(&avp->av_mutex);
518 }
519
520 return (ret);
521 }
522
523 /*
524 * CPR resume should always succeed
525 */
526 static int
av1394_cpr_resume(av1394_inst_t * avp)527 av1394_cpr_resume(av1394_inst_t *avp)
528 {
529 mutex_enter(&avp->av_mutex);
530 avp->av_dev_state = avp->av_prev_dev_state;
531 mutex_exit(&avp->av_mutex);
532
533 (void) av1394_async_cpr_resume(avp);
534
535 return (DDI_SUCCESS);
536 }
537
538 /*
539 *
540 * --- callbacks
541 *
542 */
543 /*ARGSUSED*/
544 static void
av1394_bus_reset(dev_info_t * dip,ddi_eventcookie_t evc,void * arg,void * data)545 av1394_bus_reset(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data)
546 {
547 av1394_inst_t *avp = arg;
548
549 if (avp == NULL) {
550 return;
551 }
552
553 mutex_enter(&avp->av_mutex);
554 avp->av_attachinfo.localinfo = *(t1394_localinfo_t *)data;
555 mutex_exit(&avp->av_mutex);
556
557 av1394_async_bus_reset(avp);
558 av1394_cmp_bus_reset(avp);
559 }
560
561 /*ARGSUSED*/
562 static void
av1394_disconnect(dev_info_t * dip,ddi_eventcookie_t evc,void * arg,void * data)563 av1394_disconnect(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data)
564 {
565 av1394_inst_t *avp = arg;
566
567 if (avp == NULL) {
568 return;
569 }
570
571 mutex_enter(&avp->av_mutex);
572 avp->av_dev_state = AV1394_DEV_DISCONNECTED;
573 mutex_exit(&avp->av_mutex);
574 }
575
576 /*ARGSUSED*/
577 static void
av1394_reconnect(dev_info_t * dip,ddi_eventcookie_t evc,void * arg,void * data)578 av1394_reconnect(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data)
579 {
580 av1394_inst_t *avp = arg;
581
582 if (avp == NULL) {
583 return;
584 }
585
586 mutex_enter(&avp->av_mutex);
587 avp->av_dev_state = AV1394_DEV_ONLINE;
588 avp->av_attachinfo.localinfo = *(t1394_localinfo_t *)data;
589 mutex_exit(&avp->av_mutex);
590
591 av1394_async_reconnect(avp);
592 }
593