1 /*
2 *
3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
6 *
7 *
8 * This Host ATM Research Platform ("HARP") file (the "Software") is
9 * made available by Network Computing Services, Inc. ("NetworkCS")
10 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * support of any kind.
12 *
13 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17 * In no event shall NetworkCS be responsible for any damages, including
18 * but not limited to consequential damages, arising from or relating to
19 * any use of the Software or related support.
20 *
21 * Copyright 1994-1998 Network Computing Services, Inc.
22 *
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
25 *
26 * @(#) $FreeBSD: releng/5.1/sys/netatm/atm_device.c 111888 2003-03-04 23:19:55Z jlemon $
27 *
28 */
29
30 /*
31 * Core ATM Services
32 * -----------------
33 *
34 * ATM device support functions
35 *
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/malloc.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/syslog.h>
46 #include <net/if.h>
47 #include <netatm/port.h>
48 #include <netatm/queue.h>
49 #include <netatm/atm.h>
50 #include <netatm/atm_sys.h>
51 #include <netatm/atm_sap.h>
52 #include <netatm/atm_cm.h>
53 #include <netatm/atm_if.h>
54 #include <netatm/atm_vc.h>
55 #include <netatm/atm_stack.h>
56 #include <netatm/atm_pcb.h>
57 #include <netatm/atm_var.h>
58
59 #ifndef lint
60 __RCSID("@(#) $FreeBSD: releng/5.1/sys/netatm/atm_device.c 111888 2003-03-04 23:19:55Z jlemon $");
61 #endif
62
63
64 /*
65 * Private structures for managing allocated kernel memory resources
66 *
67 * For each allocation of kernel memory, one Mem_ent will be used.
68 * The Mem_ent structures will be allocated in blocks inside of a
69 * Mem_blk structure.
70 */
71 #define MEM_NMEMENT 10 /* How many Mem_ent's in a Mem_blk */
72
73 struct mem_ent {
74 void *me_kaddr; /* Allocated memory address */
75 u_int me_ksize; /* Allocated memory length */
76 void *me_uaddr; /* Memory address returned to caller */
77 u_int me_flags; /* Flags (see below) */
78 };
79 typedef struct mem_ent Mem_ent;
80
81 /*
82 * Memory entry flags
83 */
84 #define MEF_NONCACHE 1 /* Memory is noncacheable */
85
86
87 struct mem_blk {
88 struct mem_blk *mb_next; /* Next block in chain */
89 Mem_ent mb_mement[MEM_NMEMENT]; /* Allocated memory entries */
90 };
91 typedef struct mem_blk Mem_blk;
92
93 static Mem_blk *atm_mem_head = NULL;
94
95 static struct t_atm_cause atm_dev_cause = {
96 T_ATM_ITU_CODING,
97 T_ATM_LOC_USER,
98 T_ATM_CAUSE_VPCI_VCI_ASSIGNMENT_FAILURE,
99 {0, 0, 0, 0}
100 };
101
102 extern struct ifqueue atm_intrq;
103
104 /*
105 * ATM Device Stack Instantiation
106 *
107 * Called at splnet.
108 *
109 * Arguments
110 * ssp pointer to array of stack definition pointers
111 * for connection
112 * ssp[0] points to upper layer's stack definition
113 * ssp[1] points to this layer's stack definition
114 * ssp[2] points to lower layer's stack definition
115 * cvcp pointer to connection vcc for this stack
116 *
117 * Returns
118 * 0 instantiation successful
119 * err instantiation failed - reason indicated
120 *
121 */
122 int
123 atm_dev_inst(ssp, cvcp)
124 struct stack_defn **ssp;
125 Atm_connvc *cvcp;
126 {
127 Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
128 Cmn_vcc *cvp;
129 int err;
130
131 /*
132 * Check to see if device has been initialized
133 */
134 if ((cup->cu_flags & CUF_INITED) == 0)
135 return ( EIO );
136
137 /*
138 * Validate lower SAP
139 */
140 /*
141 * Device driver is the lowest layer - no need to validate
142 */
143
144 /*
145 * Validate PVC vpi.vci
146 */
147 if (cvcp->cvc_attr.called.addr.address_format == T_ATM_PVC_ADDR) {
148 /*
149 * Look through existing circuits - return error if found
150 */
151 Atm_addr_pvc *pp;
152
153 pp = (Atm_addr_pvc *)cvcp->cvc_attr.called.addr.address;
154 if (atm_dev_vcc_find(cup, ATM_PVC_GET_VPI(pp),
155 ATM_PVC_GET_VCI(pp), 0))
156 return ( EADDRINUSE );
157 }
158
159 /*
160 * Validate our SAP type
161 */
162 switch ((*(ssp+1))->sd_sap) {
163 case SAP_CPCS_AAL3_4:
164 case SAP_CPCS_AAL5:
165 case SAP_ATM:
166 break;
167 default:
168 return (EINVAL);
169 }
170
171 /*
172 * Allocate a VCC control block
173 */
174 cvp = uma_zalloc(cup->cu_vcc_zone, M_WAITOK);
175 if (cvp == NULL)
176 return (ENOMEM);
177
178 cvp->cv_state = CVS_INST;
179 cvp->cv_toku = (*ssp)->sd_toku;
180 cvp->cv_upper = (*ssp)->sd_upper;
181 cvp->cv_connvc = cvcp;
182
183 /*
184 * Let device have a look at the connection request
185 */
186 err = (*cup->cu_instvcc)(cup, cvp);
187 if (err) {
188 uma_zfree(cup->cu_vcc_zone, cvp);
189 return (err);
190 }
191
192 /*
193 * Looks good so far, so link in device VCC
194 */
195 LINK2TAIL ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
196
197 /*
198 * Save my token
199 */
200 (*++ssp)->sd_toku = cvp;
201
202 /*
203 * Pass instantiation down the stack
204 */
205 /*
206 * No need - we're the lowest point.
207 */
208 /* err = (*(ssp + 1))->sd_inst(ssp, cvcp); */
209
210 /*
211 * Save the lower layer's interface info
212 */
213 /*
214 * No need - we're the lowest point
215 */
216 /* cvp->cv_lower = (*++ssp)->sd_lower; */
217 /* cvp->cv_tok1 = (*ssp)->sd_toku; */
218
219 return (0);
220 }
221
222
223 /*
224 * ATM Device Stack Command Handler
225 *
226 * Arguments
227 * cmd stack command code
228 * tok session token (Cmn_vcc)
229 * arg1 command specific argument
230 * arg2 command specific argument
231 *
232 * Returns
233 * none
234 *
235 */
236 /*ARGSUSED*/
237 void
238 atm_dev_lower(cmd, tok, arg1, arg2)
239 int cmd;
240 void *tok;
241 intptr_t arg1;
242 intptr_t arg2;
243 {
244 Cmn_vcc *cvp = (Cmn_vcc *)tok;
245 Atm_connvc *cvcp = cvp->cv_connvc;
246 Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
247 struct vccb *vcp;
248 u_int state;
249 int s;
250
251 switch ( cmd ) {
252
253 case CPCS_INIT:
254 /*
255 * Sanity check
256 */
257 if ( cvp->cv_state != CVS_INST ) {
258 log ( LOG_ERR,
259 "atm_dev_lower: INIT: tok=%p, state=%d\n",
260 tok, cvp->cv_state );
261 break;
262 }
263
264 vcp = cvp->cv_connvc->cvc_vcc;
265
266 /*
267 * Validate SVC vpi.vci
268 */
269 if ( vcp->vc_type & VCC_SVC ) {
270
271 if (atm_dev_vcc_find(cup, vcp->vc_vpi, vcp->vc_vci,
272 vcp->vc_type & (VCC_IN | VCC_OUT))
273 != cvp){
274 log ( LOG_ERR,
275 "atm_dev_lower: dup SVC (%d,%d) tok=%p\n",
276 vcp->vc_vpi, vcp->vc_vci, tok );
277 atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
278 break;
279 }
280 }
281
282 /*
283 * Tell the device to open the VCC
284 */
285 cvp->cv_state = CVS_INITED;
286 s = splimp();
287 if ((*cup->cu_openvcc)(cup, cvp)) {
288 atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
289 (void) splx(s);
290 break;
291 }
292 (void) splx(s);
293 break;
294
295 case CPCS_TERM: {
296 KBuffer *m, *prev, *next;
297 int *ip;
298
299 s = splimp();
300
301 /*
302 * Disconnect the VCC - ignore return code
303 */
304 if ((cvp->cv_state == CVS_INITED) ||
305 (cvp->cv_state == CVS_ACTIVE)) {
306 (void) (*cup->cu_closevcc)(cup, cvp);
307 }
308 cvp->cv_state = CVS_TERM;
309
310 /*
311 * Remove from interface list
312 */
313 UNLINK ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
314
315 /*
316 * Free any buffers from this VCC on the ATM interrupt queue
317 */
318 prev = NULL;
319 IF_LOCK(&atm_intrq);
320 for (m = atm_intrq.ifq_head; m; m = next) {
321 next = KB_QNEXT(m);
322
323 /*
324 * See if this entry is for the terminating VCC
325 */
326 KB_DATASTART(m, ip, int *);
327 ip++;
328 if (*ip == (intptr_t)cvp) {
329 /*
330 * Yep, so dequeue the entry
331 */
332 if (prev == NULL)
333 atm_intrq.ifq_head = next;
334 else
335 KB_QNEXT(prev) = next;
336
337 if (next == NULL)
338 atm_intrq.ifq_tail = prev;
339
340 atm_intrq.ifq_len--;
341
342 /*
343 * Free the unwanted buffers
344 */
345 KB_FREEALL(m);
346 } else {
347 prev = m;
348 }
349 }
350 IF_UNLOCK(&atm_intrq);
351 (void) splx(s);
352
353 /*
354 * Free VCC resources
355 */
356 uma_zfree(cup->cu_vcc_zone, cvp);
357 break;
358 }
359
360 case CPCS_UNITDATA_INV:
361
362 /*
363 * Sanity check
364 *
365 * Use temp state variable since we dont want to lock out
366 * interrupts, but initial VC activation interrupt may
367 * happen here, changing state somewhere in the middle.
368 */
369 state = cvp->cv_state;
370 if ((state != CVS_ACTIVE) &&
371 (state != CVS_INITED)) {
372 log ( LOG_ERR,
373 "atm_dev_lower: UNITDATA: tok=%p, state=%d\n",
374 tok, state );
375 KB_FREEALL((KBuffer *)arg1);
376 break;
377 }
378
379 /*
380 * Hand the data off to the device
381 */
382 (*cup->cu_output)(cup, cvp, (KBuffer *)arg1);
383
384 break;
385
386 case CPCS_UABORT_INV:
387 log ( LOG_ERR,
388 "atm_dev_lower: unimplemented stack cmd 0x%x, tok=%p\n",
389 cmd, tok );
390 break;
391
392 default:
393 log ( LOG_ERR,
394 "atm_dev_lower: unknown stack cmd 0x%x, tok=%p\n",
395 cmd, tok );
396
397 }
398
399 return;
400 }
401
402
403
404 /*
405 * Allocate kernel memory block
406 *
407 * This function will allocate a kernel memory block of the type specified
408 * in the flags parameter. The returned address will point to a memory
409 * block of the requested size and alignment. The memory block will also
410 * be zeroed. The alloc/free functions will manage/mask both the OS-specific
411 * kernel memory management requirements and the bookkeeping required to
412 * deal with data alignment issues.
413 *
414 * This function should not be called from interrupt level.
415 *
416 * Arguments:
417 * size size of memory block to allocate
418 * align data alignment requirement
419 * flags allocation flags (ATM_DEV_*)
420 *
421 * Returns:
422 * uaddr pointer to aligned memory block
423 * NULL unable to allocate memory
424 *
425 */
426 void *
427 atm_dev_alloc(size, align, flags)
428 u_int size;
429 u_int align;
430 u_int flags;
431 {
432 Mem_blk *mbp;
433 Mem_ent *mep;
434 u_int kalign, ksize;
435 int s, i;
436
437 s = splimp();
438
439 /*
440 * Find a free Mem_ent
441 */
442 mep = NULL;
443 for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
444 for (i = 0; i < MEM_NMEMENT; i++) {
445 if (mbp->mb_mement[i].me_uaddr == NULL) {
446 mep = &mbp->mb_mement[i];
447 break;
448 }
449 }
450 }
451
452 /*
453 * If there are no free Mem_ent's, then allocate a new Mem_blk
454 * and link it into the chain
455 */
456 if (mep == NULL) {
457 mbp = malloc(sizeof(Mem_blk), M_DEVBUF, M_NOWAIT|M_ZERO);
458 if (mbp == NULL) {
459 log(LOG_ERR, "atm_dev_alloc: Mem_blk failure\n");
460 (void) splx(s);
461 return (NULL);
462 }
463 mbp->mb_next = atm_mem_head;
464 atm_mem_head = mbp;
465 mep = mbp->mb_mement;
466 }
467
468 /*
469 * Now we need to get the kernel's allocation alignment minimum
470 *
471 * This is obviously very OS-specific stuff
472 */
473 kalign = MINALLOCSIZE;
474
475 /*
476 * Figure out how much memory we must allocate to satify the
477 * user's size and alignment needs
478 */
479 if (align <= kalign)
480 ksize = size;
481 else
482 ksize = size + align - kalign;
483
484 /*
485 * Finally, go get the memory
486 */
487 if (flags & ATM_DEV_NONCACHE) {
488 mep->me_kaddr = malloc(ksize, M_DEVBUF, M_NOWAIT);
489 } else {
490 mep->me_kaddr = malloc(ksize, M_DEVBUF, M_NOWAIT);
491 }
492
493 if (mep->me_kaddr == NULL) {
494 log(LOG_ERR, "atm_dev_alloc: %skernel memory unavailable\n",
495 (flags & ATM_DEV_NONCACHE) ? "non-cacheable " : "");
496 (void) splx(s);
497 return (NULL);
498 }
499
500 /*
501 * Calculate correct alignment address to pass back to user
502 */
503 mep->me_uaddr = (void *) roundup((uintptr_t)mep->me_kaddr, align);
504 mep->me_ksize = ksize;
505 mep->me_flags = flags;
506
507 /*
508 * Clear memory for user
509 */
510 bzero(mep->me_uaddr, size);
511
512 ATM_DEBUG4("atm_dev_alloc: size=%d, align=%d, flags=%d, uaddr=%p\n",
513 size, align, flags, mep->me_uaddr);
514
515 (void) splx(s);
516
517 return (mep->me_uaddr);
518 }
519
520
521 /*
522 * Free kernel memory block
523 *
524 * This function will free a kernel memory block previously allocated by
525 * the atm_dev_alloc function.
526 *
527 * This function should not be called from interrupt level.
528 *
529 * Arguments:
530 * uaddr pointer to allocated aligned memory block
531 *
532 * Returns:
533 * none
534 *
535 */
536 void
537 atm_dev_free(uaddr)
538 volatile void *uaddr;
539 {
540 Mem_blk *mbp;
541 Mem_ent *mep;
542 int s, i;
543
544 ATM_DEBUG1("atm_dev_free: uaddr=%p\n", uaddr);
545
546 s = splimp();
547
548 /*
549 * Protect ourselves...
550 */
551 if (uaddr == NULL)
552 panic("atm_dev_free: trying to free null address");
553
554 /*
555 * Find our associated entry
556 */
557 mep = NULL;
558 for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
559 for (i = 0; i < MEM_NMEMENT; i++) {
560 if (mbp->mb_mement[i].me_uaddr == uaddr) {
561 mep = &mbp->mb_mement[i];
562 break;
563 }
564 }
565 }
566
567 /*
568 * If we didn't find our entry, then unceremoniously let the caller
569 * know they screwed up (it certainly couldn't be a bug here...)
570 */
571 if (mep == NULL)
572 panic("atm_dev_free: trying to free unknown address");
573
574 /*
575 * Give the memory space back to the kernel
576 */
577 if (mep->me_flags & ATM_DEV_NONCACHE) {
578 free(mep->me_kaddr, M_DEVBUF);
579 } else {
580 free(mep->me_kaddr, M_DEVBUF);
581 }
582
583 /*
584 * Free our entry
585 */
586 mep->me_uaddr = NULL;
587
588 (void) splx(s);
589
590 return;
591 }
592
593 /*
594 * Compress buffer chain
595 *
596 * This function will compress a supplied buffer chain into a minimum number
597 * of kernel buffers. Typically, this function will be used because the
598 * number of buffers in an output buffer chain is too large for a device's
599 * DMA capabilities. This should only be called as a last resort, since
600 * all the data copying will surely kill any hopes of decent performance.
601 *
602 * Arguments:
603 * m pointer to source buffer chain
604 *
605 * Returns:
606 * n pointer to compressed buffer chain
607 *
608 */
609 KBuffer *
610 atm_dev_compress(m)
611 KBuffer *m;
612 {
613 KBuffer *n, *n0, **np;
614 int len, space;
615 caddr_t src, dst;
616
617 n = n0 = NULL;
618 np = &n0;
619 dst = NULL;
620 space = 0;
621
622 /*
623 * Copy each source buffer into compressed chain
624 */
625 while (m) {
626
627 if (space == 0) {
628
629 /*
630 * Allocate another buffer for compressed chain
631 */
632 KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA);
633 if (n) {
634 space = ATM_DEV_CMPR_LG;
635 } else {
636 KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT,
637 KB_T_DATA);
638 if (n) {
639 space = ATM_DEV_CMPR_SM;
640 } else {
641 /*
642 * Unable to get any new buffers, so
643 * just return the partially compressed
644 * chain and hope...
645 */
646 *np = m;
647 break;
648 }
649 }
650
651 KB_HEADSET(n, 0);
652 KB_LEN(n) = 0;
653 KB_BFRSTART(n, dst, caddr_t);
654
655 *np = n;
656 np = &KB_NEXT(n);
657 }
658
659 /*
660 * Copy what we can from source buffer
661 */
662 len = MIN(space, KB_LEN(m));
663 KB_DATASTART(m, src, caddr_t);
664 bcopy(src, dst, len);
665
666 /*
667 * Adjust for copied data
668 */
669 dst += len;
670 space -= len;
671
672 KB_HEADADJ(m, -len);
673 KB_TAILADJ(n, len);
674
675 /*
676 * If we've exhausted our current source buffer, free it
677 * and move to the next one
678 */
679 if (KB_LEN(m) == 0) {
680 KB_FREEONE(m, m);
681 }
682 }
683
684 return (n0);
685 }
686
687
688 /*
689 * Locate VCC entry
690 *
691 * This function will return the VCC entry for a specified interface and
692 * VPI/VCI value.
693 *
694 * Arguments:
695 * cup pointer to interface unit structure
696 * vpi VPI value
697 * vci VCI value
698 * type VCC type
699 *
700 * Returns:
701 * vcp pointer to located VCC entry matching
702 * NULL no VCC found
703 *
704 */
705 Cmn_vcc *
706 atm_dev_vcc_find(cup, vpi, vci, type)
707 Cmn_unit *cup;
708 u_int vpi;
709 u_int vci;
710 u_int type;
711 {
712 Cmn_vcc *cvp;
713 int s = splnet();
714
715 /*
716 * Go find VCC
717 *
718 * (Probably should stick in a hash table some time)
719 */
720 for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) {
721 struct vccb *vcp;
722
723 vcp = cvp->cv_connvc->cvc_vcc;
724 if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) &&
725 ((vcp->vc_type & type) == type))
726 break;
727 }
728
729 (void) splx(s);
730 return (cvp);
731 }
732
733
734 #ifdef notdef
735 /*
736 * Module unloading notification
737 *
738 * This function must be called just prior to unloading the module from
739 * memory. All allocated memory will be freed here and anything else that
740 * needs cleaning up.
741 *
742 * Arguments:
743 * none
744 *
745 * Returns:
746 * none
747 *
748 */
749 void
750 atm_unload()
751 {
752 Mem_blk *mbp;
753 Mem_ent *mep;
754 int s, i;
755
756 s = splimp();
757
758 /*
759 * Free up all of our memory management storage
760 */
761 while (mbp = atm_mem_head) {
762
763 /*
764 * Make sure users have freed up all of their memory
765 */
766 for (i = 0; i < MEM_NMEMENT; i++) {
767 if (mbp->mb_mement[i].me_uaddr != NULL) {
768 panic("atm_unload: unfreed memory");
769 }
770 }
771
772 atm_mem_head = mbp->mb_next;
773
774 /*
775 * Hand this block back to the kernel
776 */
777 free((caddr_t)mbp, M_DEVBUF);
778 }
779
780 (void) splx(s);
781
782 return;
783 }
784 #endif /* notdef */
785
786
787 /*
788 * Print a PDU
789 *
790 * Arguments:
791 * cup pointer to device unit
792 * cvp pointer to VCC control block
793 * m pointer to pdu buffer chain
794 * msg pointer to message string
795 *
796 * Returns:
797 * none
798 *
799 */
800 void
801 atm_dev_pdu_print(cup, cvp, m, msg)
802 Cmn_unit *cup;
803 Cmn_vcc *cvp;
804 KBuffer *m;
805 char *msg;
806 {
807 char buf[128];
808
809 snprintf(buf, sizeof(buf), "%s vcc=(%d,%d)", msg,
810 cvp->cv_connvc->cvc_vcc->vc_vpi,
811 cvp->cv_connvc->cvc_vcc->vc_vci);
812
813 atm_pdu_print(m, buf);
814 }
815
Cache object: 49c42b4c27dcd14916b4a0337a4fbd15
|