|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: * Copyright (c) 1995-1998 Apple Computer, Inc.
27: * All Rights Reserved.
28: *
29: * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF APPLE COMPUTER, INC.
30: * The copyright notice above does not evidence any actual or
31: * intended publication of such source code.
32: */
33:
34: #include <h/adsp_local.h>
35: #include <h/adsp_ioctl.h>
36: #include <at/ddp.h>
37: #include <h/at_ddp.h>
38: #include <h/atlog.h>
39:
40: void SndMsgUp();
41: void adsp_rput();
42: static void adsp_stop();
43: static void adsp_iocack();
44: static void adsp_iocnak();
45: static void adspgetcfg();
46: void adsp_dequeue_ccb();
47: unsigned char adspAssignSocket();
48: int adspallocate(), adsprelease();
49: static int adspInited = 0;
50:
51: at_ddp_cfg_t ddpcfg;
52: atlock_t adspall_lock;
53: atlock_t adspgen_lock;
54: GLOBAL adspGlobal;
55:
56: /**********/
57:
58: int adsp_pidM[256];
59: char adsp_inputC[256];
60: CCB *adsp_inputQ[256];
61: extern char ddp_off_flag;
62: extern int ot_protoCnt;
63: extern char ot_protoT[];
64: extern char ot_adsp_socketM[];
65:
66: static char adsp_off_flag;
67: static CCB *ccb_used_list;
68:
69: void adsp_input(mp)
70: gbuf_t *mp;
71: {
72: gref_t *gref;
73: CCBPtr sp;
74: at_ddp_t *p;
75: int s, l;
76: gbuf_t *mb;
77:
78: switch (gbuf_type(mp)) {
79: case MSG_DATA:
80: p = (at_ddp_t *)gbuf_rptr(mp);
81: ATDISABLE(s, adspall_lock);
82: sp = adsp_inputQ[p->dst_socket];
83: if ((sp == 0) || adsp_off_flag || (sp->gref==0) || (sp->state==sClosed))
84: {
85: ATENABLE(s, adspall_lock);
86: gbuf_freem(mp);
87: return;
88: }
89: else if (sp->otccbLink != 0) {
90: do {
91: if ((sp->remoteAddress.a.node == p->src_node)
92: && (sp->remoteAddress.a.socket == p->src_socket)
93: && (*(short *)sp->remoteAddress.a.net == *(short *)p->src_net))
94: break;
95: } while ((sp = sp->otccbLink) != 0);
96: if (sp == 0)
97: {
98: ATENABLE(s, adspall_lock);
99: gbuf_freem(mp);
100: return;
101: }
102: }
103: if (sp->lockFlag) {
104: gbuf_next(mp) = 0;
105: if (sp->deferred_mb) {
106: for (mb=sp->deferred_mb; gbuf_next(mb); mb=gbuf_next(mb)) ;
107: gbuf_next(mb) = mp;
108: } else
109: sp->deferred_mb = mp;
110: ATENABLE(s, adspall_lock);
111: return;
112: }
113: ATDISABLE(l, sp->lockRemove);
114: sp->lockFlag = 1;
115: ATENABLE(l, adspall_lock);
116: while (mp) {
117: adsp_rput(sp->gref, mp);
118: if ((mp = sp->deferred_mb) != 0) {
119: sp->deferred_mb = gbuf_next(mp);
120: gbuf_next(mp) = 0;
121: }
122: }
123: sp->lockFlag = 0;
124: ATENABLE(s, sp->lockRemove);
125: return;
126:
127: case MSG_IOCACK:
128: case MSG_IOCNAK:
129: gref = (gref_t *)((ioc_t *)gbuf_rptr(mp))->ioc_private;
130: break;
131:
132: case MSG_IOCTL:
133: adsp_stop(mp, *gbuf_rptr(mp));
134: gbuf_set_type(mp, MSG_IOCACK);
135: DDP_OUTPUT(mp);
136: return;
137:
138: default:
139: gbuf_freem(mp);
140: return;
141: }
142:
143: adsp_rput(gref, mp);
144: }
145:
146: /**********/
147: int adsp_readable(gref)
148: gref_t *gref;
149: {
150: int rc;
151: CCBPtr sp;
152:
153: sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
154: rc = sp->rData;
155:
156: return rc;
157: }
158:
159: int adsp_writeable(gref)
160: gref_t *gref;
161: {
162: int s, rc;
163: CCBPtr sp;
164:
165: sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
166: ATDISABLE(s, sp->lock);
167: rc = CalcSendQFree(sp);
168: ATENABLE(s, sp->lock);
169:
170: return rc;
171: }
172:
173: void adsp_init()
174: {
175: adspInited++;
176: InitGlobals();
177: ccb_used_list = 0;
178: adsp_off_flag = 0;
179: bzero(adsp_pidM, sizeof(adsp_pidM));
180: bzero(adsp_inputC, sizeof(adsp_inputC));
181: bzero(adsp_inputQ, sizeof(adsp_inputQ));
182: }
183:
184: /*
185: * Description:
186: * ADSP open and close routines. These routines
187: * initalize and release the ADSP structures. They do not
188: * have anything to do with "connections"
189: */
190:
191: int adsp_open(gref)
192: gref_t *gref;
193: {
194: register CCBPtr sp;
195: int s;
196:
197: if (!adspInited)
198: adsp_init();
199: /*
200: * if not ready, return error
201: */
202: if (adsp_off_flag)
203: return ENOTREADY;
204:
205: if (!adspAllocateCCB(gref))
206: return(ENOBUFS); /* can't get buffers */
207:
208: sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
209: gref->readable = adsp_readable;
210: gref->writeable = adsp_writeable;
211: ATDISABLE(s, adspall_lock);
212: if ((sp->otccbLink = ccb_used_list) != 0)
213: sp->otccbLink->ccbLink = sp;
214: ccb_used_list = sp;
215: ATENABLE(s, adspall_lock);
216: return 0;
217: }
218:
219: int adsp_close(gref)
220: gref_t *gref;
221: {
222: int s, l;
223: unsigned char localSocket;
224:
225: /* make sure we've not yet removed the CCB (e.g., due to TrashSession) */
226: ATDISABLE(l, adspgen_lock);
227: if (gref->info) {
228: CCBPtr sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
229: ATDISABLE(s, sp->lock);
230: ATENABLE(s, adspgen_lock);
231: if (sp->ioDone)
232: {
233: ATENABLE(l, sp->lock);
234: return 0;
235: }
236: localSocket = sp->localSocket;
237: ATENABLE(l, sp->lock);
238: if (localSocket)
239: adspRelease(gref);
240: else
241: {
242: adsp_dequeue_ccb(sp);
243: gbuf_freeb((gbuf_t *)gref->info);
244: }
245: } else
246: ATENABLE(l, adspgen_lock);
247: return 0;
248: }
249:
250:
251: /*
252: * Name:
253: * adsp_rput
254: *
255: * Description:
256: * ADSP streams read put and service routines.
257: */
258:
259: void adsp_rput(gref, mp)
260: gref_t *gref; /* READ queue */
261: gbuf_t *mp;
262: {
263: static first = 1;
264:
265: switch (gbuf_type(mp)) {
266: case MSG_ERROR:
267: case MSG_HANGUP:
268: case MSG_IOCACK:
269: case MSG_IOCNAK:
270:
271: /*
272: * ADSP needs to obtain the network number and node number
273: * for the Host it is running on.
274: * In the AUX implementation it "peeked" inside the ddp
275: * structure pointed to by at_ifDefault;
276: * In a strict streams module implementation "peeking" is
277: * neither kosher, nor permitted.
278: * adspgetcfg() sends a DDP_IOC_GET_CFG ioctl request to DDP.
279: * This code picks up the response from DDP. From this
280: * response, the ADSP streams module obtains the network
281: * and node number.
282: */
283:
284: /*
285: ** Examine the first packet coming from DDP stream.
286: */
287: if (first) {
288: first = 0;
289: if (gbuf_type(mp) == MSG_IOCACK) {
290: ioc_t *iocbp = (ioc_t *) gbuf_rptr(mp);
291:
292: if (iocbp->ioc_cmd == DDP_IOC_GET_CFG) {
293: if (gbuf_rptr(gbuf_cont(mp))) {
294: bcopy((caddr_t) gbuf_rptr(gbuf_cont(mp)),
295: (caddr_t) &ddpcfg,
296: sizeof(at_ddp_cfg_t));
297: dPrintf(D_M_ADSP, D_L_INFO,
298: ("ADSP: Net = 0x%x, Node = 0x%x\n",
299: NET_VALUE(ddpcfg.node_addr.net),
300: ddpcfg.node_addr.node));
301: }
302: gbuf_freem(mp);
303: return;
304: }
305: }
306: }
307: switch (adspReadHandler(gref, mp)) {
308: case STR_PUTNEXT:
309: atalk_putnext(gref, mp);
310: break;
311: case STR_IGNORE:
312: break;
313: }
314: break;
315: default:
316: CheckReadQueue(gbuf_rptr(((gbuf_t *)gref->info)));
317: CheckSend(gbuf_rptr(((gbuf_t *)gref->info)));
318:
319: switch (gbuf_type(mp)) {
320: case MSG_IOCTL:
321: case MSG_DATA:
322: case MSG_PROTO:
323: if (adspReadHandler(gref, mp) == STR_PUTNEXT)
324: atalk_putnext(gref, mp);
325: break;
326: default:
327: atalk_putnext(gref, mp);
328: break;
329: }
330: }
331: }
332:
333: /*
334: * Name:
335: * adsp_wput
336: *
337: * Description:
338: * ADSP streams write put and service routines.
339: *
340: */
341:
342: int adsp_wput(gref, mp)
343: gref_t *gref; /* WRITE queue */
344: gbuf_t *mp;
345: {
346: int rc;
347: int s;
348: gbuf_t *xm;
349: ioc_t *iocbp;
350:
351: if (gbuf_type(mp) == MSG_IOCTL) {
352: iocbp = (ioc_t *)gbuf_rptr(mp);
353: switch (iocbp->ioc_cmd) {
354: case AT_ADSP_LINK:
355: adsp_dequeue_ccb((CCBPtr)gbuf_rptr(((gbuf_t *)gref->info)));
356: ot_protoT[DDP_ADSP] = 1;
357: ot_protoCnt++;
358:
359: iocbp->ioc_rval = 0;
360: adsp_iocack(gref, mp);
361: return 0;
362:
363: case AT_ADSP_UNLINK:
364: ot_protoT[DDP_ADSP] = 0;
365: ot_protoCnt--;
366: ddp_off_flag = 0; /* why do this on an unlink? */
367:
368: if (adspInited) {
369: CleanupGlobals(); /* for 2225395 */
370: adspInited = 0;
371: }
372: iocbp->ioc_rval = 0;
373: adsp_iocack(gref, mp);
374: return 0;
375:
376: case ADSPBINDREQ:
377: {
378: unsigned char v;
379: CCBPtr sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
380:
381: if (gbuf_cont(mp) == NULL) {
382: iocbp->ioc_rval = -1;
383: adsp_iocnak(gref, mp, EINVAL);
384: }
385: v = *(unsigned char *)gbuf_rptr(gbuf_cont(mp));
386: ATDISABLE(s, adspall_lock);
387: if ( (v != 0)
388: && ((v > DDP_SOCKET_LAST) || (v < 2)
389: || (adsp_inputQ[v] != 0) || (ot_adsp_socketM[v] != 0)) ) {
390: ATENABLE(s, adspall_lock);
391: iocbp->ioc_rval = -1;
392: adsp_iocnak(gref, mp, EINVAL);
393: }
394: else {
395: if (v == 0) {
396: ATENABLE(s, adspall_lock);
397: if ((v = adspAssignSocket(gref, 0)) == 0) {
398: iocbp->ioc_rval = -1;
399: adsp_iocnak(gref, mp, EINVAL);
400: return 0;
401: }
402: } else {
403: adsp_inputC[v] = 1;
404: adsp_inputQ[v] = sp;
405: adsp_pidM[v] = sp->pid;
406: ATENABLE(s, adspall_lock);
407: adsp_dequeue_ccb(sp);
408: }
409: *(unsigned char *)gbuf_rptr(gbuf_cont(mp)) = v;
410: sp->localSocket = v;
411: iocbp->ioc_rval = 0;
412: adsp_iocack(gref, mp);
413: }
414: return 0;
415: }
416:
417: case ADSPGETSOCK:
418: case ADSPGETPEER:
419: {
420: at_inet_t *addr;
421: CCBPtr sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
422:
423: if (((xm = gbuf_cont(mp)) == NULL)
424: && ((xm = gbuf_alloc(sizeof(at_inet_t), PRI_MED)) == NULL)) {
425: iocbp->ioc_rval = -1;
426: adsp_iocnak(gref, mp, ENOSR);
427: return 0;
428: }
429: gbuf_cont(mp) = xm;
430: gbuf_wset(xm,sizeof(at_inet_t));
431: addr = (at_inet_t *)gbuf_rptr(xm);
432: if (iocbp->ioc_cmd == ADSPGETSOCK) {
433: adspgetcfg(gref);
434: *addr = ddpcfg.node_addr;
435: addr->socket = sp->localSocket;
436: } else
437: *addr = sp->remoteAddress.a;
438: iocbp->ioc_rval = 0;
439: adsp_iocack(gref, mp);
440: return 0;
441: }
442: } /* switch */
443: }
444:
445: /* Obtain Network and Node Id's from DDP */
446: adspgetcfg(gref);
447:
448: if (!gref->info)
449: gbuf_freem(mp);
450: else {
451: CCBPtr sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
452: ATDISABLE(s, sp->lockClose);
453: rc = adspWriteHandler(gref, mp);
454: ATENABLE(s, sp->lockClose);
455:
456: switch (rc) {
457: case STR_PUTNEXT:
458: if (gbuf_type(mp) == MSG_IOCTL) {
459: iocbp = (ioc_t *)gbuf_rptr(mp);
460: if (iocbp->ioc_cmd == DDP_IOC_GET_CFG) {
461: if ((xm = gbuf_alloc(sizeof(at_ddp_cfg_t), PRI_MED)) == NULL) {
462: adsp_iocnak(gref, mp, ENOBUFS);
463: break;
464: }
465: if (gbuf_cont(mp) != NULL)
466: gbuf_freem(gbuf_cont(mp));
467: gbuf_cont(mp) = xm;
468: gbuf_wset(xm,sizeof(at_ddp_cfg_t));
469: *(at_ddp_cfg_t *)gbuf_rptr(xm) = ddpcfg;
470: ((at_ddp_cfg_t *)gbuf_rptr(xm))->node_addr.socket =
471: ((CCBPtr)gbuf_rptr(((gbuf_t *)gref->info)))->localSocket;
472: adsp_iocack(gref, mp);
473: break;
474: }
475: iocbp->ioc_private = (void *)gref;
476: }
477: DDP_OUTPUT(mp);
478: break;
479: case STR_IGNORE:
480: case STR_IGNORE+99:
481: break;
482: default:
483: gbuf_freem(mp);
484: break;
485: }
486: }
487:
488: return 0;
489: } /* adsp_wput */
490:
491: void adspioc_ack(errno, m, gref)
492: int errno;
493: gbuf_t *m;
494: gref_t *gref;
495: {
496: ioc_t *iocbp;
497:
498: if (m == NULL)
499: return;
500: iocbp = (ioc_t *) gbuf_rptr(m);
501:
502: iocbp->ioc_error = errno; /* set the errno */
503: iocbp->ioc_count = gbuf_msgsize(gbuf_cont(m));
504: if (gbuf_type(m) == MSG_IOCTL) /* if an ioctl, this is an ack */
505: gbuf_set_type(m, MSG_IOCACK); /* and ALWAYS update the user */
506: /* ioctl structure */
507: SndMsgUp(gref, m);
508: }
509:
510: static void adspgetcfg(gref)
511: gref_t *gref;
512: {
513: static adspgetcfgdone = 0;
514: register gbuf_t *mp;
515: ioc_t *iocbp;
516:
517: if (adspgetcfgdone)
518: return;
519:
520: if (gref && (mp = gbuf_alloc(sizeof(ioc_t), PRI_HI))) {
521: adspgetcfgdone = 1;
522: gbuf_set_type(mp, MSG_IOCTL);
523: gbuf_wset(mp,sizeof(ioc_t));
524: iocbp = (ioc_t *) gbuf_rptr(mp);
525: iocbp->ioc_private = (void *)gref;
526: iocbp->ioc_count = ((CCBPtr)gbuf_rptr(((gbuf_t *)gref->info)))->localSocket;
527: iocbp->ioc_error = 7;
528:
529: iocbp->ioc_cmd = DDP_IOC_GET_CFG;
530: DDP_OUTPUT(mp);
531: }
532: }
533:
534: static void adsp_iocack(gref, m)
535: gref_t *gref;
536: register gbuf_t *m;
537: {
538: if (gbuf_type(m) == MSG_IOCTL)
539: gbuf_set_type(m, MSG_IOCACK);
540:
541: if (gbuf_cont(m))
542: ((ioc_t *)gbuf_rptr(m))->ioc_count = gbuf_msgsize(gbuf_cont(m));
543: else
544: ((ioc_t *)gbuf_rptr(m))->ioc_count = 0;
545:
546: SndMsgUp(gref, m);
547: }
548:
549:
550: static void adsp_iocnak(gref, m, err)
551: gref_t *gref;
552: register gbuf_t *m;
553: register int err;
554: {
555: if (gbuf_type(m) == MSG_IOCTL)
556: gbuf_set_type(m, MSG_IOCNAK);
557: ((ioc_t *)gbuf_rptr(m))->ioc_count = 0;
558:
559: if (err == 0)
560: err = ENXIO;
561: ((ioc_t *)gbuf_rptr(m))->ioc_error = err;
562:
563: if (gbuf_cont(m)) {
564: gbuf_freem(gbuf_cont(m));
565: gbuf_cont(m) = NULL;
566: }
567: SndMsgUp(gref, m);
568: }
569:
570: unsigned char
571: adspAssignSocket(gref, flag)
572: gref_t *gref;
573: int flag;
574: {
575: unsigned char sVal, sMax, sMin, sSav, inputC;
576: CCBPtr sp;
577: int s;
578:
579: sMax = flag ? DDP_SOCKET_LAST-46 : DDP_SOCKET_LAST-6;
580: sMin = DDP_SOCKET_1st_DYNAMIC-64;
581:
582: ATDISABLE(s, adspall_lock);
583: for (inputC=255, sVal=sMax; sVal >= sMin; sVal--) {
584: if (!ot_adsp_socketM[sVal]) {
585: if (!adsp_inputQ[sVal])
586: break;
587: else if (flag) {
588: if ((adsp_inputC[sVal] < inputC)
589: && (adsp_inputQ[sVal]->state == sOpen)) {
590: inputC = adsp_inputC[sVal];
591: sSav = sVal;
592: }
593: }
594: }
595: }
596:
597: if (sVal < sMin) {
598: if (!flag || (inputC == 255)) {
599: ATENABLE(s, adspall_lock);
600: return 0;
601: }
602: sVal = sSav;
603: }
604: sp = (CCBPtr)gbuf_rptr(((gbuf_t *)gref->info));
605: ATENABLE(s, adspall_lock);
606: adsp_dequeue_ccb(sp);
607: ATDISABLE(s, adspall_lock);
608: adsp_inputC[sVal]++;
609: sp->otccbLink = adsp_inputQ[sVal];
610: adsp_inputQ[sVal] = sp;
611: if (!flag)
612: adsp_pidM[sVal] = sp->pid;
613: ATENABLE(s, adspall_lock);
614: return sVal;
615: }
616:
617: int
618: adspDeassignSocket(sp)
619: CCBPtr sp;
620: {
621: unsigned char sVal;
622: CCBPtr curr_sp;
623: CCBPtr prev_sp;
624: int pid = 0;
625: int s, l;
626:
627: dPrintf(D_M_ADSP, D_L_TRACE, ("adspDeassignSocket: pid=%d,s=%d\n",
628: sp->pid, sp->localSocket));
629: ATDISABLE(s, adspall_lock);
630: sVal = sp->localSocket;
631: if ((curr_sp = adsp_inputQ[sVal]) != 0) {
632: prev_sp = 0;
633: while (curr_sp != sp) {
634: prev_sp = curr_sp;
635: curr_sp = curr_sp->otccbLink;
636: }
637: if (curr_sp) {
638: ATDISABLE(l, sp->lockRemove);
639: if (prev_sp)
640: prev_sp->otccbLink = sp->otccbLink;
641: else
642: adsp_inputQ[sVal] = sp->otccbLink;
643: ATENABLE(l, sp->lockRemove);
644: if (adsp_inputQ[sVal])
645: adsp_inputC[sVal]--;
646: else {
647: pid = adsp_pidM[sVal];
648: adsp_inputC[sVal] = 0;
649: adsp_pidM[sVal] = 0;
650: }
651: sp->ccbLink = 0;
652: sp->otccbLink = 0;
653: sp->localSocket = 0;
654: ATENABLE(s, adspall_lock);
655: return pid ? 0 : 1;
656: }
657: }
658: ATENABLE(s, adspall_lock);
659:
660: dPrintf(D_M_ADSP, D_L_ERROR,
661: ("adspDeassignSocket: closing, no CCB block, trouble ahead\n"));
662: return -1;
663: }
664:
665: /*
666: * stop ADSP now
667: */
668: static void
669: adsp_stop(m, flag)
670: gbuf_t *m;
671: int flag;
672: {
673: int k, s, *x_wptr;
674: CCB *sp;
675:
676: ATDISABLE(s, adspall_lock);
677: gbuf_rptr(m)[1]++;
678:
679: if (flag)
680: adsp_off_flag = 1;
681:
682: x_wptr = (int *)gbuf_wptr(m);
683: for (sp = ccb_used_list; sp; sp = sp->otccbLink) {
684: if (flag == 2) {
685: sp->ioDone = 1;
686: } else {
687: *(int *)gbuf_wptr(m) = sp->pid;
688: gbuf_winc(m,sizeof(int));
689: }
690: }
691:
692: for (k=0; k < 256; k++) {
693: if ((sp = adsp_inputQ[k]) == 0)
694: continue;
695: do {
696: if (flag == 2) {
697: sp->ioDone = 1;
698: } else {
699: *(int *)gbuf_wptr(m) = sp->pid;
700: gbuf_winc(m,sizeof(int));
701: }
702: } while ((sp = sp->otccbLink) != 0);
703: }
704: ATENABLE(s, adspall_lock);
705:
706: while (x_wptr != (int *)gbuf_wptr(m)) {
707: dPrintf(D_M_ADSP, D_L_TRACE, ("adsp_stop: pid=%d\n", *x_wptr));
708: x_wptr++;
709: }
710: }
711:
712: /*
713: * remove CCB from the use list
714: */
715: void
716: adsp_dequeue_ccb(sp)
717: CCB *sp;
718: {
719: int s;
720:
721: ATDISABLE(s, adspall_lock);
722: if (sp == ccb_used_list) {
723: if ((ccb_used_list = sp->otccbLink) != 0)
724: sp->otccbLink->ccbLink = 0;
725: } else if (sp->ccbLink) {
726: if ((sp->ccbLink->otccbLink = sp->otccbLink) != 0)
727: sp->otccbLink->ccbLink = sp->ccbLink;
728: }
729:
730: sp->otccbLink = 0;
731: sp->ccbLink = 0;
732: ATENABLE(s, adspall_lock);
733: }
734:
735: void SndMsgUp(gref, mp)
736: gref_t *gref; /* WRITE queue */
737: gbuf_t *mp;
738: {
739: atalk_putnext(gref, mp);
740: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.