]> arthur.barton.de Git - netatalk.git/blob - sys/solaris/tpi.c
Remove bdb env on exit
[netatalk.git] / sys / solaris / tpi.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif /* HAVE_CONFIG_H */
4 #include <sys/types.h>
5 #include <sys/kmem.h>
6 #include <sys/conf.h>
7 #include <sys/stream.h>
8 #include <sys/devops.h>
9 #include <sys/modctl.h>
10 #include <sys/ddi.h>
11 #include <sys/stat.h>
12 #include <sys/sockio.h>
13 #include <sys/socket.h>
14 #include <sys/tihdr.h>
15 #include <sys/tiuser.h>
16 #include <sys/timod.h>
17 #include <sys/sunddi.h>
18 #include <sys/ethernet.h>
19 #include <net/if.h>
20 #include <net/route.h>
21 #include <errno.h>
22
23 #include <netatalk/endian.h>
24 #include <netatalk/at.h>
25 #include <netatalk/ddp.h>
26
27 #include "ioc.h"
28 #include "if.h"
29 #include "sock.h"
30 #include "rt.h"
31
32     static int
33 tpi_getinfo( dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp )
34 {
35     *resultp = NULL;
36     return( DDI_FAILURE );
37 }
38
39 /* Solaris 10 removed DDI_IDENTIFIED and replaced "identify" with "nulldev" */
40 #ifdef DDI_IDENTIFIED
41     static int
42 tpi_identify( dev_info_t *dip )
43 {
44     char *tmp;
45
46     /* don't use strcmp under Solaris 9, problem loading kernel module */
47     tmp = ddi_get_name( dip );
48     if ((tmp[0]== 'd') && (tmp[1]=='d') && (tmp[2]=='p') && tmp[3]==0) {
49         return( DDI_IDENTIFIED );
50     } else {
51         return( DDI_NOT_IDENTIFIED );
52     }
53 }
54 #endif /* DDI_IDENTIFIED */
55
56     static int
57 tpi_attach( dev_info_t *dip, ddi_attach_cmd_t cmd )
58 {
59     int         rc;
60
61     if ( cmd != DDI_ATTACH ) {
62         return( DDI_FAILURE );
63     }
64
65     if (( rc = ddi_create_minor_node( dip, "ddp", S_IFCHR, 0, DDI_PSEUDO,
66             CLONE_DEV )) != DDI_SUCCESS ) {
67         /* undo anything */
68     }
69     return( rc );
70 }
71
72     static int
73 tpi_detach( dev_info_t *dip, ddi_detach_cmd_t cmd )
74 {
75     if ( cmd != DDI_DETACH ) {
76         return( DDI_FAILURE );
77     }
78
79     ddi_remove_minor_node( dip, "ddp" );
80
81     return( DDI_SUCCESS );
82 }
83
84     static int
85 tpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
86 {
87     static minor_t      minor = 1;
88
89     if ( sflag != CLONEOPEN ) {
90         return( EINVAL );
91     }
92     if (( q->q_ptr = (void *)sock_alloc( q )) == NULL ) {
93         return( ENOMEM );
94     }
95
96     *dev = makedevice( getmajor( *dev ), minor++ );
97     qprocson( q );
98     return( 0 );
99 }
100
101     static int
102 tpi_close( queue_t *q, int oflag, cred_t *cred )
103 {
104     struct sock_data    *sd = (struct sock_data *)q->q_ptr;
105
106     qprocsoff( q );
107     sock_free( sd );
108     return( 0 );
109 }
110
111     static int
112 tpi_rput( queue_t *q,  mblk_t *m )
113 {
114     cmn_err( CE_NOTE, "tpi_rput dp_type = 0x%X\n", m->b_datap->db_type );
115     freemsg( m );
116     return( 0 );
117 }
118
119     void
120 t_bind_ack( queue_t *q, struct sockaddr_at *sat )
121 {
122     mblk_t              *m;
123     struct T_bind_ack   *t;
124
125     if (( m = allocb( sizeof( struct T_bind_ack ) +
126             sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
127         return;
128     }
129     m->b_wptr = m->b_rptr + sizeof( struct T_bind_ack );
130     m->b_datap->db_type = M_PCPROTO;
131
132     t = (struct T_bind_ack *)m->b_rptr;
133     t->PRIM_type = T_BIND_ACK;
134     t->ADDR_length = sizeof( struct sockaddr_at );
135     t->ADDR_offset = m->b_wptr - m->b_rptr;
136     t->CONIND_number = 0;
137
138     bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
139     m->b_wptr += sizeof( struct sockaddr_at );
140
141     qreply( q, m );
142     return;
143 }
144
145     void
146 t_ok_ack( queue_t *q, long prim )
147 {
148     mblk_t              *m;
149     struct T_ok_ack     *t;
150
151
152     if (( m = allocb( sizeof( struct T_ok_ack ), BPRI_HI )) == NULL ) {
153         return;
154     }
155     m->b_wptr = m->b_rptr + sizeof( struct T_ok_ack );
156     m->b_datap->db_type = M_PCPROTO;
157
158     t = (struct T_ok_ack *)m->b_rptr;
159     t->PRIM_type = T_OK_ACK;
160     t->CORRECT_prim = prim;
161     qreply( q, m );
162     return;
163 }
164
165     void
166 t_error_ack( queue_t *q, long prim, long terror, long uerror )
167 {
168     mblk_t              *m;
169     struct T_error_ack  *t;
170
171
172     if (( m = allocb( sizeof( struct T_error_ack ), BPRI_HI )) == NULL ) {
173         return;
174     }
175     m->b_wptr = m->b_rptr + sizeof( struct T_error_ack );
176     m->b_datap->db_type = M_PCPROTO;
177
178     t = (struct T_error_ack *)m->b_rptr;
179     t->PRIM_type = T_ERROR_ACK;
180     t->ERROR_prim = prim;
181     t->TLI_error = terror;
182     t->UNIX_error = uerror;
183     qreply( q, m );
184     return;
185 }
186
187     void
188 t_info_ack( queue_t *q, long state )
189 {
190     mblk_t              *m;
191     struct T_info_ack   *t;
192
193
194     if (( m = allocb( sizeof( struct T_info_ack ), BPRI_HI )) == NULL ) {
195         return;
196     }
197     m->b_wptr = m->b_rptr + sizeof( struct T_info_ack );
198     m->b_datap->db_type = M_PCPROTO;
199
200     t = (struct T_info_ack *)m->b_rptr;
201     t->PRIM_type = T_INFO_ACK;
202     t->TSDU_size = 586;
203     t->ETSDU_size = -2;
204     t->CDATA_size = -2;
205     t->DDATA_size = -2;
206     t->ADDR_size = sizeof( struct sockaddr_at );
207     t->OPT_size = 64;
208     t->TIDU_size = 1024;
209     t->SERV_type = T_CLTS;
210     t->CURRENT_state = state;
211     t->PROVIDER_flag = 0;
212     qreply( q, m );
213     return;
214 }
215
216     void
217 t_unitdata_ind( queue_t *q, mblk_t *m0, struct sockaddr_at *sat )
218 {
219     mblk_t                      *m;
220     struct T_unitdata_ind       *t;
221
222     if (( m = allocb( sizeof( struct T_unitdata_ind ) +
223             sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
224         return;
225     }
226     m->b_wptr = m->b_rptr + sizeof( struct T_unitdata_ind );
227     m->b_datap->db_type = M_PROTO;
228
229     t = (struct T_unitdata_ind *)m->b_rptr;
230     t->PRIM_type = T_UNITDATA_IND;
231     t->SRC_length = sizeof( struct sockaddr_at );
232     t->SRC_offset = m->b_wptr - m->b_rptr;
233     bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
234     m->b_wptr += sizeof( struct sockaddr_at );
235     t->OPT_length = 0;
236     t->OPT_offset = 0;
237     linkb( m, m0 );
238
239     qreply( q, m );
240     return;
241 }
242
243 struct ioc_state {
244     int         is_state;
245     int         is_count;
246     caddr_t     is_addr;
247 };
248
249     static int
250 tpi_wput( queue_t *q,  mblk_t *m )
251 {
252     struct sock_data    *sd = (struct sock_data *)RD(q)->q_ptr;
253     union T_primitives  *tl;
254     struct iocblk       *ioc;
255     struct copyresp     *cp;
256     struct ioc_state    *is;
257     struct ddpehdr      *deh;
258     mblk_t              *m0;
259     struct sockaddr_at  sat;
260     struct netbuf       nb;
261     struct rtentry      rt;
262     struct ifreq        ifr;
263     int                 err;
264
265     switch ( m->b_datap->db_type ) {
266     case M_PCPROTO :
267     case M_PROTO :
268         if ( m->b_wptr - m->b_rptr < sizeof( tl->type )) {
269             freemsg( m );
270             break;
271         }
272         tl = (union T_primitives *)m->b_rptr;
273         switch ( tl->type ) {
274         case T_INFO_REQ :
275             t_info_ack( q, sd->sd_state );
276             freemsg( m );
277             break;
278
279         case T_UNBIND_REQ :
280             if ( m->b_wptr - m->b_rptr < sizeof( struct T_unbind_req )) {
281                 freemsg( m );
282                 break;
283             }
284             if ( sd->sd_state != TS_IDLE ) {
285                 t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
286                 freemsg( m );
287                 break;
288             }
289             bzero( (caddr_t)&sd->sd_sat, sizeof( struct sockaddr_at ));
290             sd->sd_state = TS_UNBND;
291             t_ok_ack( q, T_UNBIND_REQ );
292             break;
293
294         case T_BIND_REQ :
295             if ( m->b_wptr - m->b_rptr < sizeof( struct T_bind_req )) {
296                 freemsg( m );
297                 break;
298             }
299             if ( sd->sd_state != TS_UNBND ) {
300                 t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
301                 freemsg( m );
302                 break;
303             }
304
305             if ( tl->bind_req.ADDR_length == 0 ) {
306                 bzero( (caddr_t)&sat, sizeof( struct sockaddr_at ));
307                 sat.sat_family = AF_APPLETALK;
308             } else {
309                 if ( tl->bind_req.ADDR_length != sizeof( struct sockaddr ) ||
310                         m->b_wptr - m->b_rptr <
311                         tl->bind_req.ADDR_offset + tl->bind_req.ADDR_length ) {
312                     cmn_err( CE_CONT, "tpi_wput T_BIND_REQ wierd\n" );
313                     freemsg( m );
314                     break;
315                 }
316                 sat = *(struct sockaddr_at *)(m->b_rptr +
317                         tl->bind_req.ADDR_offset );
318             }
319
320             if (( err = sock_bind( sd, &sat )) != 0 ) {
321                 t_error_ack( q, T_BIND_REQ, TSYSERR, err );
322             } else {
323                 /* seems like we must return the requested address */
324                 t_bind_ack( q, &sat );
325             }
326             freemsg( m );
327             break;
328
329         case T_UNITDATA_REQ :
330             if ( m->b_wptr - m->b_rptr < sizeof( struct T_unitdata_req )) {
331                 freemsg( m );
332                 break;
333             }
334             if ( sd->sd_state != TS_IDLE ) {
335                 cmn_err( CE_NOTE, "tpi_wput unitdata on unbound socket\n" );
336                 t_error_ack( q, T_UNITDATA_REQ, TOUTSTATE, 0 );
337                 freemsg( m );
338                 break;
339             }
340             if ( tl->unitdata_req.DEST_length != sizeof( struct sockaddr )) {
341                 cmn_err( CE_NOTE, "tpi_wput T_UNITDATA_REQ %d\n",
342                         tl->unitdata_req.DEST_length );
343                 freemsg( m );
344                 break;
345             }
346
347 #ifdef notdef
348             /*
349              * Sometimes, the socket layer gives us crap...  Sound like a bug?
350              */
351             if ( m->b_rptr + tl->unitdata_req.DEST_offset +
352                     tl->unitdata_req.DEST_length > m->b_wptr ) {
353 cmn_err( CE_CONT, "tpi_wput T_UNITDATA_REQ mblk size %X %X\n", m->b_rptr + tl->unitdata_req.DEST_offset + tl->unitdata_req.DEST_length, m->b_wptr );
354                 freemsg( m );
355                 break;
356             }
357 #endif /* notdef */
358
359             sat = *(struct sockaddr_at *)(m->b_rptr +
360                     tl->unitdata_req.DEST_offset );
361             if ( sat.sat_family != AF_APPLETALK ) {
362                 cmn_err( CE_CONT, "tpi_wput non-AppleTalk\n" );
363                 freemsg( m );
364                 break;
365             }
366
367             if ( m->b_wptr - m->b_rptr < sizeof( struct ddpehdr )) {
368                 cmn_err( CE_CONT, "tpi_wput m too short\n" );
369                 freemsg( m );
370                 break;
371             }
372             m->b_wptr = m->b_rptr + sizeof( struct ddpehdr );
373             m->b_datap->db_type = M_DATA;
374             deh = (struct ddpehdr *)m->b_rptr;
375             deh->deh_pad = 0;
376             deh->deh_hops = 0;
377             deh->deh_len = msgdsize( m );
378
379             deh->deh_dnet = sat.sat_addr.s_net;
380             deh->deh_dnode = sat.sat_addr.s_node;
381             deh->deh_dport = sat.sat_port;
382
383             deh->deh_snet = sd->sd_sat.sat_addr.s_net;
384             deh->deh_snode = sd->sd_sat.sat_addr.s_node;
385             deh->deh_sport = sd->sd_sat.sat_port;
386
387             deh->deh_sum = 0;                   /* XXX */
388             deh->deh_bytes = htonl( deh->deh_bytes );
389             return( if_route( if_withaddr( &sd->sd_sat ), m, &sat ));
390
391         default :
392             /* cmn_err( CE_NOTE, "tpi_wput M_PCPROTO 0x%X\n", tl->type ); */
393             t_error_ack( q, tl->type, TNOTSUPPORT, 0 );
394             freemsg( m );
395             break;
396         }
397         break;
398
399     case M_IOCTL :
400         if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
401             freemsg( m );
402             break;
403         }
404         ioc = (struct iocblk *)m->b_rptr;
405         if ( ioc->ioc_count != TRANSPARENT ) {
406             cmn_err( CE_CONT, "tpi_wput non-TRANSPARENT %X\n", ioc->ioc_cmd );
407             ioc_error_ack( q, m, EINVAL );
408             break;
409         }
410         if ( m->b_cont == NULL ) {
411             cmn_err( CE_CONT, "tpi_wput M_IOCTL no arg\n" );
412             ioc_error_ack( q, m, EINVAL );
413             break;
414         }
415
416         /* de-allocated after M_IOCDATA processing */
417         if (( m0 = allocb( sizeof( struct ioc_state ), BPRI_HI )) == NULL ) {
418             cmn_err( CE_CONT, "tpi_wput m0 no mem\n" );
419             ioc_error_ack( q, m, EINVAL );
420             break;
421         }
422         m0->b_wptr = m->b_rptr + sizeof( struct ioc_state );
423         is = (struct ioc_state *)m0->b_rptr;
424
425         switch ( ioc->ioc_cmd ) {
426         case SIOCADDRT :
427         case SIOCDELRT :
428             if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
429                 ioc_error_ack( q, m, err );
430                 break;
431             }
432             is->is_state = M_COPYIN;
433             is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
434             ioc_copyin( q, m, m0, is->is_addr, sizeof( struct rtentry ));
435             break;
436
437         case SIOCADDMULTI :
438         case SIOCSIFADDR :
439             if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
440                 ioc_error_ack( q, m, err );
441                 break;
442             }
443
444         case SIOCGIFADDR :
445             is->is_state = M_COPYIN;
446             is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
447             ioc_copyin( q, m, m0, is->is_addr, sizeof( struct ifreq ));
448             break;
449
450         case TI_GETMYNAME :
451             is->is_state = M_COPYIN;
452             is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
453             ioc_copyin( q, m, m0, is->is_addr, sizeof( struct netbuf ));
454             break;
455
456         default :
457             ioc_error_ack( q, m, EINVAL );
458             break;
459         }
460         break;
461
462     case M_IOCDATA :
463         if ( m->b_wptr - m->b_rptr < sizeof( struct copyresp )) {
464             freemsg( m );
465             break;
466         }
467         cp = (struct copyresp *)m->b_rptr;
468         if ( cp->cp_rval != 0 ) {
469             cmn_err( CE_CONT, "tpi_wput IOCDATA failed %s\n", cp->cp_rval );
470             freemsg( m );
471             break;
472         }
473
474         if (( m0 = cp->cp_private ) == NULL ) {
475             cmn_err( CE_CONT, "tpi_wput IOCDATA no state\n" );
476             ioc_error_ack( q, m, EINVAL );
477             break;
478         }
479         if ( m0->b_wptr - m0->b_rptr < sizeof( struct ioc_state )) {
480             cmn_err( CE_CONT, "tpi_wput IOCDATA private too short\n" );
481             ioc_error_ack( q, m, EINVAL );
482             break;
483         }
484         is = (struct ioc_state *)m0->b_rptr;
485
486         switch ( cp->cp_cmd ) {
487         case TI_GETMYNAME :
488             switch ( is->is_state ) {
489             case M_COPYIN :
490                 if ( m->b_cont == NULL ) {
491                     cmn_err( CE_CONT, "tpi_wput TI_GETMYNAME COPYIN no arg\n" );
492                     ioc_error_ack( q, m, EINVAL );
493                     break;
494                 }
495                 nb = *(struct netbuf *)m->b_cont->b_rptr;
496                 nb.len = sizeof( struct sockaddr_at );
497                 /* copy out netbuf */
498                 is->is_state = M_COPYOUT;
499                 is->is_count = 1;
500                 ioc_copyout( q, m, m0, (caddr_t)&nb, is->is_addr,
501                         sizeof( struct netbuf ));
502                 is->is_addr = nb.buf;
503                 return( 0 );
504
505             case M_COPYOUT :
506                 switch ( is->is_count ) {
507                 case 1 :
508                     /* copy out address to nb.buf */
509                     is->is_state = M_COPYOUT;
510                     is->is_count = 2;
511                     ioc_copyout( q, m, m0, (caddr_t)&sd->sd_sat, is->is_addr,
512                             sizeof( struct sockaddr_at ));
513                     return( 0 );
514
515                 case 2 :
516                     ioc_ok_ack( q, m, 0 );
517                     break;
518
519                 default :
520                     cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME count %d\n",
521                             is->is_count );
522                     ioc_error_ack( q, m, EINVAL );
523                     break;
524                 }
525                 break;
526
527             default :
528                 cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME state %d\n",
529                         is->is_state );
530                 ioc_error_ack( q, m, EINVAL );
531                 break;
532             }
533             break;
534
535         case SIOCADDRT :        /* manipulate routing table */
536         case SIOCDELRT :
537             if (( err = drv_priv( cp->cp_cr )) != 0 ) {
538                 ioc_error_ack( q, m, err );
539                 break;
540             }
541             if ( is->is_state != M_COPYIN ) {
542                 cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad state\n" );
543                 freemsg( m );
544                 break;
545             }
546
547             rt = *(struct rtentry *)m->b_cont->b_rptr;
548
549             if ( cp->cp_cmd == SIOCADDRT ) {
550                 err = rt_add( (struct sockaddr_at *)&rt.rt_dst,
551                         (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
552             } else if ( cp->cp_cmd == SIOCDELRT ) {
553                 err = rt_del( (struct sockaddr_at *)&rt.rt_dst,
554                         (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
555             } else {
556                 cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad cmd\n" );
557                 freemsg( m );
558                 break;
559             }
560             if ( err != 0 ) {
561                 ioc_error_ack( q, m, err );
562             } else {
563                 ioc_ok_ack( q, m, 0 );
564             }
565             break;
566
567         /*
568          * These both require lower messages to be sent.
569          */
570         case SIOCADDMULTI :
571         case SIOCSIFADDR :
572             if (( err = drv_priv( cp->cp_cr )) != 0 ) {
573                 ioc_error_ack( q, m, err );
574                 break;
575             }
576             if ( is->is_state != M_COPYIN ) {
577                 cmn_err( CE_CONT, "tpi_wput SIOCSIFADDR bad state\n" );
578                 freemsg( m );
579                 break;
580             }
581
582             ifr = *(struct ifreq *)m->b_cont->b_rptr;
583
584             /* initiate command, pass q and m (current context to be saved */
585             if ( cp->cp_cmd == SIOCSIFADDR ) {
586                 err = if_setaddr( q, m, ifr.ifr_name,
587                         (struct sockaddr_at *)&ifr.ifr_addr );
588             } else {
589                 err = if_addmulti( q, m, ifr.ifr_name, &ifr.ifr_addr );
590             }
591             if ( err != 0 ) {
592                 ioc_error_ack( q, m, err );
593                 break;
594             }
595             break;
596
597         case SIOCGIFADDR :      /* get interface address */
598             switch ( is->is_state ) {
599             case M_COPYOUT :
600                 /* ack the original ioctl */
601                 ioc_ok_ack( q, m, 0 );
602                 break;
603
604             case M_COPYIN :
605                 if ( m->b_cont == NULL ) {
606                     cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR COPYIN no arg\n" );
607                     ioc_error_ack( q, m, EINVAL );
608                     break;
609                 }
610
611                 /* size??? */
612                 ifr = *(struct ifreq *)m->b_cont->b_rptr;
613                 if (( err = if_getaddr( ifr.ifr_name,
614                         (struct sockaddr_at *)&ifr.ifr_addr )) != 0 ) {
615                     ioc_error_ack( q, m, err );
616                 }
617                 is->is_state = M_COPYOUT;
618                 ioc_copyout( q, m, m0, (caddr_t)&ifr, is->is_addr,
619                         sizeof( struct ifreq ));
620                 return( 0 );    /* avoid freemsg( m0 ) below */
621
622             default :
623                 cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR bad state\n" );
624                 freemsg( m );
625                 break;
626             }
627             break;
628
629         default :
630             cmn_err( CE_NOTE, "tpi_wput M_IOCDATA 0x%X\n", cp->cp_cmd );
631             ioc_error_ack( q, m, EINVAL );
632             break;
633         }
634         freemsg( m0 );
635         break;
636
637     default :
638         cmn_err( CE_NOTE, "!tpi_wput dp_type = 0x%X\n", m->b_datap->db_type );
639         freemsg( m );
640         break;
641     }
642
643     return( 0 );
644 }
645
646 static struct module_info       tpi_info = {
647     0,                  /* XXX */
648     "ddp",
649     0,
650     1500,
651     3000,
652     64
653 };
654
655 static struct qinit             tpi_rinit = {
656     tpi_rput,                   /* qi_putp */
657     NULL,                       /* qi_srvp */
658     tpi_open,                   /* qi_qopen */
659     tpi_close,                  /* qi_qclose */
660     NULL,
661     &tpi_info,                  /* qi_minfo */
662     NULL,
663 };
664
665 static struct qinit             tpi_winit = {
666     tpi_wput,                   /* qi_putp */
667     NULL,
668     NULL,
669     NULL,
670     NULL,
671     &tpi_info,
672     NULL,
673 };
674
675 static struct streamtab         tpi_stream = {
676     &tpi_rinit,
677     &tpi_winit,
678     NULL,
679     NULL
680 };
681
682 static struct cb_ops            tpi_cbops = {
683     nulldev,            /* cb_open */
684     nulldev,            /* cb_close */
685     nodev,
686     nodev,
687     nodev,
688     nodev,
689     nodev,
690     nodev,
691     nodev,
692     nodev,
693     nodev,
694     nochpoll,
695     ddi_prop_op,
696     &tpi_stream,
697     D_NEW | D_MP | D_MTPERMOD,  /* cb_flag */
698     CB_REV,             /* cb_rev */
699     nodev,              /* cb_aread */
700     nodev,              /* cb_awrite */
701 };
702
703 static struct dev_ops           tpi_devops = {
704     DEVO_REV,
705     0,
706     tpi_getinfo,
707 #ifdef DDI_IDENTIFIED
708     tpi_identify,
709 #else
710     nulldev,
711 #endif
712     nulldev,
713     tpi_attach,
714     tpi_detach,
715     nodev,
716     &tpi_cbops,
717     (struct bus_ops *)NULL,
718     NULL,
719 };
720
721 /*
722  * DDP Streams device.  This device is opened by socket().
723  */
724 struct modldrv                  tpi_ldrv = {
725     &mod_driverops,
726     "DDP Streams device",
727     &tpi_devops,
728 };