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