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