]> arthur.barton.de Git - netatalk.git/blob - sys/solaris/dlpi.c
e70b465ca8d8034f579a1131dbe78cd8db101c9e
[netatalk.git] / sys / solaris / dlpi.c
1 #include <sys/types.h>
2 #include <sys/kmem.h>
3 #include <sys/stream.h>
4 #include <sys/conf.h>
5 #include <sys/modctl.h>
6 #include <sys/cmn_err.h>
7 #include <sys/ddi.h>
8 #include <sys/socket.h>
9 #include <sys/sockio.h>
10 #include <sys/dlpi.h>
11 #include <sys/ethernet.h>
12 #include <sys/byteorder.h>
13 #include <sys/sunddi.h>
14 #include <net/if.h>
15 #include <errno.h>
16
17 #include <netatalk/phase2.h>
18 #include <netatalk/at.h>
19
20 #include "ioc.h"
21 #include "if.h"
22
23 u_char  at_multicastaddr[ ETHERADDRL ] = {
24     0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
25 };
26 u_char  at_org_code[ 3 ] = {
27     0x08, 0x00, 0x07,
28 };
29 u_char  aarp_org_code[ 3 ] = {
30     0x00, 0x00, 0x00,
31 };
32
33     static int
34 dlpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
35 {
36     struct atif_data    *aid;
37     int                 err = 0;
38
39     if (( err = drv_priv( cred )) != 0 ) {
40         return( err );
41     }
42     if (( aid = if_alloc( q )) == NULL ) {
43         return( ENOMEM );
44     }
45     q->q_ptr = (void *)aid;
46
47     qprocson( q );
48     return( err );
49 }
50
51     static int
52 dlpi_close( queue_t *q, int oflag, cred_t *cred )
53 {
54     struct atif_data    *aid = (struct atif_data *)q->q_ptr;
55
56     qprocsoff( q );
57     if_free( aid );
58     return( 0 );
59 }
60
61     static int
62 dl_bind_req( queue_t *q, ulong sap )
63 {
64     union DL_primitives *dl;
65     mblk_t              *m;
66
67     if (( m = allocb( DL_BIND_REQ_SIZE, BPRI_HI )) == NULL ) {
68         return( ENOMEM );
69     }
70     m->b_wptr = m->b_rptr + DL_BIND_REQ_SIZE;
71     m->b_datap->db_type = M_PROTO;
72
73     dl = (union DL_primitives *)m->b_rptr;
74     dl->dl_primitive = DL_BIND_REQ;
75     dl->bind_req.dl_sap = sap;
76     dl->bind_req.dl_max_conind = 0;
77     dl->bind_req.dl_service_mode = DL_CLDLS;
78     dl->bind_req.dl_conn_mgmt = 0;
79     dl->bind_req.dl_xidtest_flg = 0;    /* XXX */
80     putnext( q, m );
81     return( 0 );
82 }
83
84     static int
85 dl_attach_req( queue_t *q, ulong ppa )
86 {
87     union DL_primitives *dl;
88     mblk_t              *m;
89
90     if (( m = allocb( DL_ATTACH_REQ_SIZE, BPRI_HI )) == NULL ) {
91         return( ENOMEM );
92     }
93     m->b_wptr = m->b_rptr + DL_ATTACH_REQ_SIZE;
94     m->b_datap->db_type = M_PROTO;
95
96     dl = (union DL_primitives *)m->b_rptr;
97     dl->dl_primitive = DL_ATTACH_REQ;
98     dl->attach_req.dl_ppa = ppa;
99     putnext( q, m );
100     return( 0 );
101 }
102
103     int
104 dl_enabmulti_req( queue_t *q, caddr_t addr )
105 {
106     union DL_primitives *dl;
107     mblk_t              *m;
108
109     if (( m = allocb( DL_ENABMULTI_REQ_SIZE + ETHERADDRL, BPRI_HI )) == NULL ) {
110         return( ENOMEM );
111     }
112     m->b_wptr = m->b_rptr + DL_ENABMULTI_REQ_SIZE;
113     m->b_datap->db_type = M_PROTO;
114
115     dl = (union DL_primitives *)m->b_rptr;
116     dl->dl_primitive = DL_ENABMULTI_REQ;
117     dl->enabmulti_req.dl_addr_length = ETHERADDRL;
118     dl->enabmulti_req.dl_addr_offset = m->b_wptr - m->b_rptr;
119     bcopy( addr, m->b_wptr, ETHERADDRL );
120     m->b_wptr += ETHERADDRL;
121     putnext( q, m );
122     return( 0 );
123 }
124
125     int
126 dl_unitdata_req( queue_t *q, mblk_t *m0, ushort type, caddr_t addr )
127 {
128     union DL_primitives *dl;
129     struct llc          *llc;
130     mblk_t              *m1, *m;
131     ushort              len;
132
133     /* len = msgdsize( m0 ) + sizeof( struct llc ); */
134
135     if (( m1 = allocb( sizeof( struct llc ), BPRI_HI )) == NULL ) {
136         cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 1\n" );
137         return( ENOMEM );
138     }
139     m1->b_wptr = m1->b_rptr + sizeof( struct llc );
140     m1->b_datap->db_type = M_DATA;
141     llc = (struct llc *)m1->b_rptr;
142
143     llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
144     llc->llc_control = LLC_UI;
145     if ( type == ETHERTYPE_AARP ) {
146         bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
147     } else if ( type == ETHERTYPE_AT ) {
148         bcopy( at_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
149     } else {
150         cmn_err( CE_NOTE, "dl_unitdate_req type %X\n", type );
151         return( EINVAL );
152     }
153     llc->llc_ether_type = htons( type );
154     linkb( m1, m0 );
155
156     if (( m = allocb( DL_UNITDATA_REQ_SIZE + ETHERADDRL + sizeof( ushort ),
157                       BPRI_HI )) == NULL ) {
158         cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 2\n" );
159         return( ENOMEM );
160     }
161     m->b_wptr = m->b_rptr + DL_UNITDATA_REQ_SIZE;
162     m->b_datap->db_type = M_PROTO;
163     linkb( m, m1 );
164
165     dl = (union DL_primitives *)m->b_rptr;
166     dl->dl_primitive = DL_UNITDATA_REQ;
167     dl->unitdata_req.dl_dest_addr_length = ETHERADDRL + sizeof ( ushort );
168     dl->unitdata_req.dl_dest_addr_offset = m->b_wptr - m->b_rptr;
169
170     bcopy(addr, m->b_wptr, ETHERADDRL );
171     m->b_wptr += ETHERADDRL;
172     len = 0;
173     bcopy( &len, m->b_wptr, sizeof( ushort ));
174     m->b_wptr += sizeof( ushort );
175     putnext( q, m );
176     return( 0 );
177 }
178
179     static int
180 dlpi_rput( queue_t *q, mblk_t *m )
181 {
182     struct atif_data    *aid = (struct atif_data *)q->q_ptr;
183     union DL_primitives *dl;
184     mblk_t              *m0;
185     struct llc          *llc;
186
187     switch ( m->b_datap->db_type ) {
188     case M_IOCNAK :
189         putnext( q, m );
190         return( 0 );
191
192     case M_PCPROTO :
193     case M_PROTO :
194         if ( m->b_wptr - m->b_rptr < sizeof( dl->dl_primitive )) {
195             break;
196         }
197         dl = (union DL_primitives *)m->b_rptr;
198         switch ( dl->dl_primitive ) {
199         case DL_UNITDATA_IND :
200             if ( m->b_wptr - m->b_rptr < sizeof( DL_UNITDATA_IND_SIZE )) {
201                 break;
202             }
203             if (( m0 = unlinkb( m )) == NULL ) {
204                 break;
205             }
206             if ( m0->b_wptr - m0->b_rptr < sizeof( struct llc )) {
207                 freemsg( m0 );
208                 break;
209             }
210             llc = (struct llc *)m0->b_rptr;
211             if ( llc->llc_dsap != LLC_SNAP_LSAP ||
212                     llc->llc_ssap != LLC_SNAP_LSAP ||
213                     llc->llc_control != LLC_UI ) {
214                 freemsg( m0 );
215                 break;
216             }
217
218             if ( bcmp( llc->llc_org_code, at_org_code,
219                     sizeof( at_org_code )) == 0 &&
220                     ntohs( llc->llc_ether_type ) == ETHERTYPE_AT ) {
221                 adjmsg( m0, sizeof( struct llc ));
222                 ddp_rput( aid, m0 );
223             } else if ( bcmp( llc->llc_org_code, aarp_org_code,
224                     sizeof( aarp_org_code )) == 0 &&
225                     ntohs( llc->llc_ether_type ) == ETHERTYPE_AARP ) {
226                 adjmsg( m0, sizeof( struct llc ));
227                 aarp_rput( q, m0 );
228             } else {
229                 freemsg( m0 );
230             }
231             break;
232
233         case DL_OK_ACK :
234             if ( m->b_wptr - m->b_rptr < sizeof( DL_OK_ACK_SIZE )) {
235                 break;
236             }
237             switch ( dl->ok_ack.dl_correct_primitive ) {
238             case DL_ATTACH_REQ :
239                 if ( aid->aid_state != DL_ATTACH_PENDING ) {
240                     cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach state %d\n",
241                             aid->aid_state );
242                     break;
243                 }
244                 if ( aid->aid_c.c_type != IF_UNITSEL ) {
245                     cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach context %x\n",
246                             aid->aid_c.c_type );
247                     break;
248                 }
249
250                 if ( WR(q)->q_next == NULL || WR(q)->q_next->q_qinfo == NULL ||
251                         WR(q)->q_next->q_qinfo->qi_minfo == NULL ||
252                         WR(q)->q_next->q_qinfo->qi_minfo->mi_idname == NULL ) {
253                     cmn_err( CE_NOTE, "dlpi_rput can't get interface name\n" );
254                     break;
255                 }
256
257                 if_name( aid, WR(q)->q_next->q_qinfo->qi_minfo->mi_idname,
258                         aid->aid_c.c_u.u_unit.uu_ppa );
259
260                 aid->aid_state = DL_BIND_PENDING;
261
262 #ifdef i386
263                 /*
264                  * As of Solaris 7 (nice name), the i386 arch needs to be
265                  * bound as 0 to receive 802 frames.  However, in the same
266                  * OS, Sparcs must be bound as ETHERMTU (or at least not 0)
267                  * to receive the same frames.  A bug?  In the Solaris 7
268                  * (nice name) kernel?
269                  */
270                 dl_bind_req( WR( q ), 0 );
271 #else /* i386 */
272                 dl_bind_req( WR( q ), ETHERMTU );
273 #endif /* i386 */
274                 break;
275
276             case DL_ENABMULTI_REQ :
277                 if ( aid->aid_c.c_type != SIOCADDMULTI ) {
278                     cmn_err( CE_NOTE,
279                             "dlpi_rput DL_OK_ACK enabmulti context %x\n",
280                             aid->aid_c.c_type );
281                     break;
282                 }
283
284                 ioc_ok_ack( aid->aid_c.c_u.u_multi.um_q,
285                         aid->aid_c.c_u.u_multi.um_m, 0 );
286                 aid->aid_c.c_type = 0;
287                 aid->aid_c.c_u.u_multi.um_q = NULL;
288                 aid->aid_c.c_u.u_multi.um_m = 0;
289                 break;
290
291             default :
292                 cmn_err( CE_CONT, "!dlpi_rput DL_OK_ACK unhandled %d\n",
293                         dl->ok_ack.dl_correct_primitive );
294                 break;
295             }
296             break;
297
298         case DL_BIND_ACK :
299             if ( m->b_wptr - m->b_rptr < sizeof( DL_BIND_ACK_SIZE )) {
300                 break;
301             }
302             if ( aid->aid_state != DL_BIND_PENDING ) {
303                 break;
304             }
305             if ( aid->aid_c.c_type != IF_UNITSEL ) {
306                 break;
307             }
308             bcopy( m->b_rptr + dl->bind_ack.dl_addr_offset, aid->aid_hwaddr, 
309                     dl->bind_ack.dl_addr_length );
310             aid->aid_state = DL_IDLE;
311             ioc_ok_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, 0 );
312             aid->aid_c.c_type = 0;
313             aid->aid_c.c_u.u_unit.uu_m = NULL;
314             aid->aid_c.c_u.u_unit.uu_ppa = 0;
315             break;
316
317         case DL_ERROR_ACK :
318             if ( m->b_wptr - m->b_rptr < sizeof( DL_ERROR_ACK_SIZE )) {
319                 break;
320             }
321
322             switch ( aid->aid_c.c_type ) {
323             case IF_UNITSEL :
324                 if ( dl->error_ack.dl_errno == DL_SYSERR ) {
325                     ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m,
326                             dl->error_ack.dl_unix_errno );
327                 } else {
328                     cmn_err( CE_CONT, "dlpi_rput DL_ERROR_ACK 0x%x\n",
329                             dl->error_ack.dl_errno );
330                     ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, EINVAL );
331                 }
332                 aid->aid_c.c_type = 0;
333                 aid->aid_c.c_u.u_unit.uu_m = NULL;
334                 aid->aid_c.c_u.u_unit.uu_ppa = 0;
335                 break;
336
337             default :
338                 cmn_err( CE_NOTE, "dlpi_rput DL_ERROR_ACK unhandled %d %d %d\n",
339                         dl->error_ack.dl_error_primitive,
340                         dl->error_ack.dl_errno, dl->error_ack.dl_unix_errno );
341                 break;
342             }
343             break;
344
345         default :
346             cmn_err( CE_NOTE, "dlpi_rput M_PCPROTO 0x%x\n", dl->dl_primitive );
347             break;
348         }
349         break;
350
351     default :
352         cmn_err( CE_NOTE, "dlpi_rput 0x%X\n", m->b_datap->db_type );
353         break;
354     }
355
356     freemsg( m );
357     return( 0 );
358 }
359
360     static int
361 dlpi_wput( queue_t *q, mblk_t *m )
362 {
363     struct atif_data            *aid = (struct atif_data *)RD(q)->q_ptr;
364     struct iocblk               *ioc;
365     int                         rc;
366
367     switch ( m->b_datap->db_type ) {
368     case M_IOCTL :
369         if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
370             freemsg( m );
371             break;
372         }
373         ioc = (struct iocblk *)m->b_rptr;
374         switch ( ioc->ioc_cmd ) {
375         case IF_UNITSEL :
376             if ( ioc->ioc_count != TRANSPARENT ) {
377                 cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL non-TRANSPARENT\n" );
378                 ioc_error_ack( q, m, EINVAL );
379                 break;
380             }
381             if ( m->b_cont == NULL ) {
382                 cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL no arg\n" );
383                 ioc_error_ack( q, m, EINVAL );
384                 break;
385             }
386             if ( aid->aid_state != DL_UNATTACHED ) {
387                 cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL already attached\n" );
388                 ioc_error_ack( q, m, EINVAL );
389                 break;
390             }
391             if ( aid->aid_c.c_type != 0 ) {
392                 cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL context %x\n",
393                         aid->aid_c.c_type );
394                 ioc_error_ack( q, m, EINVAL );
395                 break;
396             }
397
398             aid->aid_state = DL_ATTACH_PENDING;
399             aid->aid_c.c_type = IF_UNITSEL;
400             aid->aid_c.c_u.u_unit.uu_m = m;
401             aid->aid_c.c_u.u_unit.uu_ppa = *(ulong *)m->b_cont->b_rptr;
402             if (( rc = dl_attach_req( q, aid->aid_c.c_u.u_unit.uu_ppa )) < 0 ) {
403                 ioc_error_ack( q, m, rc );
404                 break;
405             }
406             break;
407
408         default :
409             cmn_err( CE_NOTE, "dlpi_wput M_IOCTL 0x%X\n", ioc->ioc_cmd );
410             putnext( q, m );
411             break;
412         }
413         break;
414
415     default :
416         cmn_err( CE_NOTE, "dlpi_wput 0x%X\n", m->b_datap->db_type );
417         freemsg( m );
418         break;
419     }
420
421     return( 0 );
422 }
423
424 static struct module_info       dlpi_info = {
425     0,
426     "ddp",
427     0,
428     1500,
429     3000,
430     64
431 };
432
433 static struct qinit             dlpi_rinit = {
434     dlpi_rput,          /* qi_putp */
435     NULL,               /* qi_srvp */
436     dlpi_open,          /* qi_qopen */
437     dlpi_close,         /* qi_qclose */
438     NULL,
439     &dlpi_info,         /* qi_minfo */
440     NULL,
441 };
442
443 static struct qinit             dlpi_winit = {
444     dlpi_wput,          /* qi_putp */
445     NULL,               /* qi_srvp */
446     NULL,               /* qi_qopen */
447     NULL,               /* qi_qclose */
448     NULL,
449     &dlpi_info,         /* qi_minfo */
450     NULL,
451 };
452
453 static struct streamtab         dlpi_stream = {
454     &dlpi_rinit,
455     &dlpi_winit,
456     NULL,
457     NULL
458 };
459
460 static struct fmodsw            dlpi_fmodsw = {
461     "ddp",
462     &dlpi_stream,
463     D_NEW | D_MP | D_MTPERMOD
464 };
465
466 /*
467  * DDP Streams module.  This module is pushed on DLPI drivers by atalkd.
468  */
469 struct modlstrmod               dlpi_lstrmod = {
470     &mod_strmodops,
471     "DDP Streams module",
472     &dlpi_fmodsw,
473 };