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