| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| /* Copyright (c) 1990 Mentat Inc. */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <sys/types.h> |
| #include <inet/common.h> /* for various inet/mi.h and inet/nd.h needs */ |
| #include <sys/stream.h> |
| #include <sys/stropts.h> |
| #include <sys/strsun.h> |
| #include <sys/sysmacros.h> |
| #include <inet/nd.h> |
| #include <inet/mi.h> |
| #define _SUN_TPI_VERSION 2 |
| #include <sys/tihdr.h> |
| #include <sys/timod.h> |
| #include <sys/vtrace.h> |
| #include <sys/kmem.h> |
| #include <sys/mkdev.h> |
| #include <sys/strlog.h> |
| #include <sys/ddi.h> |
| #include <sys/suntpi.h> |
| #include <sys/cmn_err.h> |
| #include <sys/debug.h> |
| #include <sys/kobj.h> |
| |
| #define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9') |
| #define ISUPPER(ch) ((ch) >= 'A' && (ch) <= 'Z') |
| #define tolower(ch) ('a' + ((ch) - 'A')) |
| |
| #define MI_IS_TRANSPARENT(mp) (mp->b_cont && \ |
| (mp->b_cont->b_rptr != mp->b_cont->b_wptr)) |
| |
| /* |
| * NOTE: Whenever anything is allocated by mi_alloc or mi_alloc_sleep (below), |
| * the size of the requested allocation is increased by one word. This extra |
| * word is used to store the size of the object being allocated, and is located |
| * at the beginning of the allocated block. The pointer returned to the caller |
| * is a pointer to the *second* word in the newly-allocated block. The IP |
| * module of mdb is aware of this, and will need to be changed if this |
| * allocation strategy is changed. |
| */ |
| |
| typedef struct stroptions *STROPTP; |
| typedef union T_primitives *TPRIMP; |
| |
| /* Timer block states. */ |
| #define TB_RUNNING 1 |
| #define TB_IDLE 2 |
| /* |
| * Could not stop/free before putq |
| */ |
| #define TB_RESCHED 3 /* mtb_time_left contains tick count */ |
| #define TB_CANCELLED 4 |
| #define TB_TO_BE_FREED 5 |
| |
| typedef struct mtb_s { |
| int mtb_state; |
| timeout_id_t mtb_tid; |
| queue_t *mtb_q; |
| MBLKP mtb_mp; |
| clock_t mtb_time_left; |
| } MTB, *MTBP; |
| |
| static int mi_timer_fire(MTBP); |
| static int mi_iprintf(char *, va_list, pfi_t, char *); |
| static void mi_tpi_addr_and_opt(MBLKP, char *, t_scalar_t, char *, t_scalar_t); |
| static MBLKP mi_tpi_trailer_alloc(MBLKP, size_t, t_scalar_t); |
| |
| /* ARGSUSED1 */ |
| void * |
| mi_alloc(size_t size, uint_t pri) |
| { |
| size_t *ptr; |
| |
| size += sizeof (size); |
| if (ptr = kmem_alloc(size, KM_NOSLEEP)) { |
| *ptr = size; |
| return (ptr + 1); |
| } |
| return (NULL); |
| } |
| |
| /* ARGSUSED1 */ |
| void * |
| mi_alloc_sleep(size_t size, uint_t pri) |
| { |
| size_t *ptr; |
| |
| size += sizeof (size); |
| ptr = kmem_alloc(size, KM_SLEEP); |
| *ptr = size; |
| return (ptr + 1); |
| } |
| |
| int |
| mi_close_comm(void **mi_headp, queue_t *q) |
| { |
| IDP ptr; |
| |
| ptr = q->q_ptr; |
| mi_close_unlink(mi_headp, ptr); |
| mi_close_free(ptr); |
| q->q_ptr = WR(q)->q_ptr = NULL; |
| return (0); |
| } |
| |
| void |
| mi_close_unlink(void **mi_headp, IDP ptr) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP mi_o; |
| dev_t dev; |
| |
| mi_o = (MI_OP)ptr; |
| if (!mi_o) |
| return; |
| mi_o--; |
| |
| if (mi_o->mi_o_next == NULL) { |
| /* Not in list */ |
| ASSERT(mi_o->mi_o_prev == NULL); |
| return; |
| } |
| |
| /* Free minor number */ |
| dev = mi_o->mi_o_dev; |
| if ((dev != OPENFAIL) && (dev != 0) && (dev <= MAXMIN)) |
| inet_minor_free(mi_head->mh_arena, dev); |
| |
| /* Unlink from list */ |
| ASSERT(mi_o->mi_o_next != NULL); |
| ASSERT(mi_o->mi_o_prev != NULL); |
| ASSERT(mi_o->mi_o_next->mi_o_prev == mi_o); |
| ASSERT(mi_o->mi_o_prev->mi_o_next == mi_o); |
| |
| mi_o->mi_o_next->mi_o_prev = mi_o->mi_o_prev; |
| mi_o->mi_o_prev->mi_o_next = mi_o->mi_o_next; |
| mi_o->mi_o_next = mi_o->mi_o_prev = NULL; |
| |
| mi_o->mi_o_dev = (dev_t)OPENFAIL; |
| |
| /* If list now empty free the list head */ |
| if (mi_head->mh_o.mi_o_next == &mi_head->mh_o) { |
| ASSERT(mi_head->mh_o.mi_o_prev == &mi_head->mh_o); |
| if (mi_head->mh_arena != NULL) |
| inet_minor_destroy(mi_head->mh_arena); |
| mi_free((IDP)mi_head); |
| *mi_headp = NULL; |
| } |
| } |
| |
| void |
| mi_close_free(IDP ptr) |
| { |
| MI_OP mi_o; |
| |
| mi_o = (MI_OP)ptr; |
| if (!mi_o) |
| return; |
| mi_o--; |
| |
| ASSERT(mi_o->mi_o_next == NULL && mi_o->mi_o_prev == NULL); |
| mi_free((IDP)mi_o); |
| } |
| |
| /* |
| * mi_copyin - takes care of transparent or non-transparent ioctl for the |
| * calling function so that they have to deal with just M_IOCDATA type |
| * and not worry about M_COPYIN. |
| * |
| * mi_copyin checks to see if the ioctl is transparent or non transparent. |
| * In case of a non_transparent ioctl, it packs the data into a M_IOCDATA |
| * message and puts it back onto the current queue for further processing. |
| * In case of transparent ioctl, it sends a M_COPYIN message up to the |
| * streamhead so that a M_IOCDATA with the information comes back down. |
| */ |
| void |
| mi_copyin(queue_t *q, MBLKP mp, char *uaddr, size_t len) |
| { |
| struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| struct copyreq *cq = (struct copyreq *)mp->b_rptr; |
| struct copyresp *cp = (struct copyresp *)mp->b_rptr; |
| int err; |
| MBLKP mp1; |
| |
| ASSERT(mp->b_datap->db_type == M_IOCTL && !uaddr); |
| |
| /* A transparent ioctl. Send a M_COPYIN message to the streamhead. */ |
| if (iocp->ioc_count == TRANSPARENT) { |
| MI_COPY_COUNT(mp) = 1; |
| MI_COPY_DIRECTION(mp) = MI_COPY_IN; |
| cq->cq_private = mp->b_cont; |
| cq->cq_size = len; |
| cq->cq_flag = 0; |
| bcopy(mp->b_cont->b_rptr, &cq->cq_addr, sizeof (cq->cq_addr)); |
| mp->b_cont = NULL; |
| mp->b_datap->db_type = M_COPYIN; |
| qreply(q, mp); |
| return; |
| } |
| |
| /* |
| * A non-transparent ioctl. Need to convert into M_IOCDATA message. |
| * |
| * We allocate a 0 byte message block and put its address in |
| * cp_private. It also makes the b_prev field = 1 and b_next |
| * field = MI_COPY_IN for this 0 byte block. This is done to |
| * maintain compatibility with old code in mi_copy_state |
| * (which removes the empty block). |
| */ |
| err = miocpullup(mp, len); |
| if (err != 0) |
| goto err_ret; |
| |
| mp1 = allocb(0, BPRI_MED); |
| if (mp1 == NULL) { |
| err = ENOMEM; |
| goto err_ret; |
| } |
| |
| /* |
| * Temporarily insert mp1 between the M_IOCTL and M_DATA blocks so |
| * that we can use the MI_COPY_COUNT & MI_COPY_DIRECTION macros. |
| */ |
| mp1->b_cont = mp->b_cont; |
| mp->b_cont = mp1; |
| MI_COPY_COUNT(mp) = 1; |
| MI_COPY_DIRECTION(mp) = MI_COPY_IN; |
| mp->b_cont = mp1->b_cont; |
| mp1->b_cont = NULL; |
| |
| /* |
| * Leave a pointer to the 0 byte block in cp_private field for |
| * future use by the mi_copy_* routines. |
| */ |
| mp->b_datap->db_type = M_IOCDATA; |
| cp->cp_private = mp1; |
| cp->cp_rval = NULL; |
| put(q, mp); |
| return; |
| |
| err_ret: |
| iocp->ioc_error = err; |
| iocp->ioc_count = 0; |
| if (mp->b_cont) { |
| freemsg(mp->b_cont); |
| mp->b_cont = NULL; |
| } |
| mp->b_datap->db_type = M_IOCACK; |
| qreply(q, mp); |
| } |
| |
| /* |
| * Allows transparent IOCTLs to have multiple copyins. This is needed |
| * for some variable-length structures, where the total size is only known |
| * after the first part is copied in. Rather than setting MI_COPY_COUNT to |
| * 1, as in mi_coypin(), it is simply incremented here. This value can |
| * then be checked in the returned IOCBLK. |
| * |
| * As this deals with copyins that follow the initial copyin, the byte |
| * offset into the user buffer from which copying should begin must be |
| * passed in in the offset parameter. |
| * |
| * Unlike mi_coypin(), this function expects to be passed an mblk chain |
| * headed by an M_IOCBLK, as that's the chain that will be in use for |
| * copies after the first one (copies where n != 1). |
| */ |
| void |
| mi_copyin_n(queue_t *q, MBLKP mp, size_t offset, size_t len) |
| { |
| struct copyreq *cq = (struct copyreq *)mp->b_rptr; |
| |
| ASSERT(mp->b_datap->db_type == M_IOCDATA); |
| |
| MI_COPY_COUNT(mp)++; |
| MI_COPY_DIRECTION(mp) = MI_COPY_IN; |
| cq->cq_private = mp->b_cont; |
| cq->cq_size = len; |
| cq->cq_flag = 0; |
| bcopy(mp->b_cont->b_rptr, &cq->cq_addr, sizeof (cq->cq_addr)); |
| cq->cq_addr += offset; |
| mp->b_cont = NULL; |
| mp->b_datap->db_type = M_COPYIN; |
| qreply(q, mp); |
| } |
| |
| void |
| mi_copyout(queue_t *q, MBLKP mp) |
| { |
| struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| struct copyreq *cq = (struct copyreq *)iocp; |
| struct copyresp *cp = (struct copyresp *)cq; |
| MBLKP mp1; |
| MBLKP mp2; |
| |
| if (mp->b_datap->db_type != M_IOCDATA || !mp->b_cont) { |
| mi_copy_done(q, mp, EPROTO); |
| return; |
| } |
| /* Check completion of previous copyout operation. */ |
| mp1 = mp->b_cont; |
| if ((int)(uintptr_t)cp->cp_rval || !mp1->b_cont) { |
| mi_copy_done(q, mp, (int)(uintptr_t)cp->cp_rval); |
| return; |
| } |
| if (!mp1->b_cont->b_cont && !MI_IS_TRANSPARENT(mp)) { |
| mp1->b_next = NULL; |
| mp1->b_prev = NULL; |
| mp->b_cont = mp1->b_cont; |
| freeb(mp1); |
| mp1 = mp->b_cont; |
| mp1->b_next = NULL; |
| mp1->b_prev = NULL; |
| iocp->ioc_count = mp1->b_wptr - mp1->b_rptr; |
| iocp->ioc_error = 0; |
| mp->b_datap->db_type = M_IOCACK; |
| qreply(q, mp); |
| return; |
| } |
| if (MI_COPY_DIRECTION(mp) == MI_COPY_IN) { |
| /* Set up for first copyout. */ |
| MI_COPY_DIRECTION(mp) = MI_COPY_OUT; |
| MI_COPY_COUNT(mp) = 1; |
| } else { |
| ++MI_COPY_COUNT(mp); |
| } |
| cq->cq_private = mp1; |
| /* Find message preceding last. */ |
| for (mp2 = mp1; mp2->b_cont->b_cont; mp2 = mp2->b_cont) |
| ; |
| if (mp2 == mp1) |
| bcopy((char *)mp1->b_rptr, (char *)&cq->cq_addr, |
| sizeof (cq->cq_addr)); |
| else |
| cq->cq_addr = (char *)mp2->b_cont->b_next; |
| mp1 = mp2->b_cont; |
| mp->b_datap->db_type = M_COPYOUT; |
| mp->b_cont = mp1; |
| mp2->b_cont = NULL; |
| mp1->b_next = NULL; |
| cq->cq_size = mp1->b_wptr - mp1->b_rptr; |
| cq->cq_flag = 0; |
| qreply(q, mp); |
| } |
| |
| MBLKP |
| mi_copyout_alloc(queue_t *q, MBLKP mp, char *uaddr, size_t len, |
| boolean_t free_on_error) |
| { |
| struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| MBLKP mp1; |
| |
| if (mp->b_datap->db_type == M_IOCTL) { |
| if (iocp->ioc_count != TRANSPARENT) { |
| mp1 = allocb(0, BPRI_MED); |
| if (mp1 == NULL) { |
| if (free_on_error) { |
| iocp->ioc_error = ENOMEM; |
| iocp->ioc_count = 0; |
| freemsg(mp->b_cont); |
| mp->b_cont = NULL; |
| mp->b_datap->db_type = M_IOCACK; |
| qreply(q, mp); |
| } |
| return (NULL); |
| } |
| mp1->b_cont = mp->b_cont; |
| mp->b_cont = mp1; |
| } |
| MI_COPY_COUNT(mp) = 0; |
| MI_COPY_DIRECTION(mp) = MI_COPY_OUT; |
| /* Make sure it looks clean to mi_copyout. */ |
| mp->b_datap->db_type = M_IOCDATA; |
| ((struct copyresp *)iocp)->cp_rval = NULL; |
| } |
| mp1 = allocb(len, BPRI_MED); |
| if (mp1 == NULL) { |
| if (free_on_error) |
| mi_copy_done(q, mp, ENOMEM); |
| return (NULL); |
| } |
| linkb(mp, mp1); |
| mp1->b_next = (MBLKP)uaddr; |
| return (mp1); |
| } |
| |
| void |
| mi_copy_done(queue_t *q, MBLKP mp, int err) |
| { |
| struct iocblk *iocp; |
| MBLKP mp1; |
| |
| if (!mp) |
| return; |
| if (!q || (mp->b_wptr - mp->b_rptr) < sizeof (struct iocblk)) { |
| freemsg(mp); |
| return; |
| } |
| iocp = (struct iocblk *)mp->b_rptr; |
| mp->b_datap->db_type = M_IOCACK; |
| iocp->ioc_error = err; |
| |
| iocp->ioc_count = 0; |
| if ((mp1 = mp->b_cont) != NULL) { |
| for (; mp1; mp1 = mp1->b_cont) { |
| mp1->b_prev = NULL; |
| mp1->b_next = NULL; |
| } |
| freemsg(mp->b_cont); |
| mp->b_cont = NULL; |
| } |
| qreply(q, mp); |
| } |
| |
| int |
| mi_copy_state(queue_t *q, MBLKP mp, MBLKP *mpp) |
| { |
| struct iocblk *iocp = (struct iocblk *)mp->b_rptr; |
| struct copyresp *cp = (struct copyresp *)iocp; |
| MBLKP mp1; |
| |
| mp1 = mp->b_cont; |
| mp->b_cont = cp->cp_private; |
| if (mp1) { |
| if (mp1->b_cont && !pullupmsg(mp1, -1)) { |
| mi_copy_done(q, mp, ENOMEM); |
| return (-1); |
| } |
| linkb(mp->b_cont, mp1); |
| } |
| if ((int)(uintptr_t)cp->cp_rval) { |
| mi_copy_done(q, mp, (int)(uintptr_t)cp->cp_rval); |
| return (-1); |
| } |
| if (mpp && MI_COPY_DIRECTION(mp) == MI_COPY_IN) |
| *mpp = mp1; |
| return (MI_COPY_STATE(mp)); |
| } |
| |
| void |
| mi_free(void *ptr) |
| { |
| size_t size; |
| |
| if (!ptr) |
| return; |
| if ((size = ((size_t *)ptr)[-1]) <= 0) |
| cmn_err(CE_PANIC, "mi_free"); |
| |
| kmem_free((void *) ((size_t *)ptr - 1), size); |
| } |
| |
| static int |
| mi_iprintf(char *fmt, va_list ap, pfi_t putc_func, char *cookie) |
| { |
| int base; |
| char buf[(sizeof (long) * 3) + 1]; |
| static char hex_val[] = "0123456789abcdef"; |
| int ch; |
| int count; |
| char *cp1; |
| int digits; |
| char *fcp; |
| boolean_t is_long; |
| ulong_t uval; |
| long val; |
| boolean_t zero_filled; |
| |
| if (!fmt) |
| return (-1); |
| count = 0; |
| while (*fmt) { |
| if (*fmt != '%' || *++fmt == '%') { |
| count += (*putc_func)(cookie, *fmt++); |
| continue; |
| } |
| if (*fmt == '0') { |
| zero_filled = B_TRUE; |
| fmt++; |
| if (!*fmt) |
| break; |
| } else |
| zero_filled = B_FALSE; |
| base = 0; |
| for (digits = 0; ISDIGIT(*fmt); fmt++) { |
| digits *= 10; |
| digits += (*fmt - '0'); |
| } |
| if (!*fmt) |
| break; |
| is_long = B_FALSE; |
| if (*fmt == 'l') { |
| is_long = B_TRUE; |
| fmt++; |
| } |
| if (!*fmt) |
| break; |
| ch = *fmt++; |
| if (ISUPPER(ch)) { |
| ch = tolower(ch); |
| is_long = B_TRUE; |
| } |
| switch (ch) { |
| case 'c': |
| count += (*putc_func)(cookie, va_arg(ap, int *)); |
| continue; |
| case 'd': |
| base = 10; |
| break; |
| case 'm': /* Print out memory, 2 hex chars per byte */ |
| if (is_long) |
| fcp = va_arg(ap, char *); |
| else { |
| if ((cp1 = va_arg(ap, char *)) != NULL) |
| fcp = (char *)cp1; |
| else |
| fcp = NULL; |
| } |
| if (!fcp) { |
| for (fcp = (char *)"(NULL)"; *fcp; fcp++) |
| count += (*putc_func)(cookie, *fcp); |
| } else { |
| while (digits--) { |
| int u1 = *fcp++ & 0xFF; |
| count += (*putc_func)(cookie, |
| hex_val[(u1>>4)& 0xF]); |
| count += (*putc_func)(cookie, |
| hex_val[u1& 0xF]); |
| } |
| } |
| continue; |
| case 'o': |
| base = 8; |
| break; |
| case 'p': |
| is_long = B_TRUE; |
| /* FALLTHRU */ |
| case 'x': |
| base = 16; |
| break; |
| case 's': |
| if (is_long) |
| fcp = va_arg(ap, char *); |
| else { |
| if ((cp1 = va_arg(ap, char *)) != NULL) |
| fcp = (char *)cp1; |
| else |
| fcp = NULL; |
| } |
| if (!fcp) |
| fcp = (char *)"(NULL)"; |
| while (*fcp) { |
| count += (*putc_func)(cookie, *fcp++); |
| if (digits && --digits == 0) |
| break; |
| } |
| while (digits > 0) { |
| count += (*putc_func)(cookie, ' '); |
| digits--; |
| } |
| continue; |
| case 'u': |
| base = 10; |
| break; |
| default: |
| return (count); |
| } |
| if (is_long) |
| val = va_arg(ap, long); |
| else |
| val = va_arg(ap, int); |
| if (base == 10 && ch != 'u') { |
| if (val < 0) { |
| count += (*putc_func)(cookie, '-'); |
| val = -val; |
| } |
| uval = val; |
| } else { |
| if (is_long) |
| uval = val; |
| else |
| uval = (uint_t)val; |
| } |
| /* Hand overload/restore the register variable 'fmt' */ |
| cp1 = fmt; |
| fmt = A_END(buf); |
| *--fmt = '\0'; |
| do { |
| if (fmt > buf) |
| *--fmt = hex_val[uval % base]; |
| if (digits && --digits == 0) |
| break; |
| } while (uval /= base); |
| if (zero_filled) { |
| while (digits > 0 && fmt > buf) { |
| *--fmt = '0'; |
| digits--; |
| } |
| } |
| while (*fmt) |
| count += (*putc_func)(cookie, *fmt++); |
| fmt = cp1; |
| } |
| return (count); |
| } |
| |
| /* PRINTFLIKE2 */ |
| int |
| mi_mpprintf(MBLKP mp, char *fmt, ...) |
| { |
| va_list ap; |
| int count = -1; |
| |
| va_start(ap, fmt); |
| if (mp) { |
| count = mi_iprintf(fmt, ap, (pfi_t)mi_mpprintf_putc, |
| (char *)mp); |
| if (count != -1) |
| (void) mi_mpprintf_putc((char *)mp, '\0'); |
| } |
| va_end(ap); |
| return (count); |
| } |
| |
| /* PRINTFLIKE2 */ |
| int |
| mi_mpprintf_nr(MBLKP mp, char *fmt, ...) |
| { |
| va_list ap; |
| int count = -1; |
| |
| va_start(ap, fmt); |
| if (mp) { |
| (void) adjmsg(mp, -1); |
| count = mi_iprintf(fmt, ap, (pfi_t)mi_mpprintf_putc, |
| (char *)mp); |
| if (count != -1) |
| (void) mi_mpprintf_putc((char *)mp, '\0'); |
| } |
| va_end(ap); |
| return (count); |
| } |
| |
| int |
| mi_mpprintf_putc(char *cookie, int ch) |
| { |
| MBLKP mp = (MBLKP)cookie; |
| |
| while (mp->b_cont) |
| mp = mp->b_cont; |
| if (mp->b_wptr >= mp->b_datap->db_lim) { |
| mp->b_cont = allocb(1024, BPRI_HI); |
| mp = mp->b_cont; |
| if (!mp) |
| return (0); |
| } |
| *mp->b_wptr++ = (unsigned char)ch; |
| return (1); |
| } |
| |
| IDP |
| mi_first_ptr(void **mi_headp) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP mi_op; |
| |
| mi_op = mi_head->mh_o.mi_o_next; |
| if (mi_op && mi_op != &mi_head->mh_o) |
| return ((IDP)&mi_op[1]); |
| return (NULL); |
| } |
| |
| /* |
| * Clients can choose to have both module instances and device instances |
| * in the same list. Return the first device instance in the list. |
| */ |
| IDP |
| mi_first_dev_ptr(void **mi_headp) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP mi_op; |
| |
| mi_op = mi_head->mh_o.mi_o_next; |
| while ((mi_op != NULL) && (mi_op != &mi_head->mh_o)) { |
| if (mi_op->mi_o_isdev) |
| return ((IDP)&mi_op[1]); |
| mi_op = mi_op->mi_o_next; |
| } |
| return (NULL); |
| } |
| |
| IDP |
| mi_next_ptr(void **mi_headp, IDP ptr) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP mi_op = ((MI_OP)ptr) - 1; |
| |
| if ((mi_op = mi_op->mi_o_next) != NULL && mi_op != &mi_head->mh_o) |
| return ((IDP)&mi_op[1]); |
| return (NULL); |
| } |
| |
| /* |
| * Clients can choose to have both module instances and device instances |
| * in the same list. Return the next device instance in the list. |
| */ |
| IDP |
| mi_next_dev_ptr(void **mi_headp, IDP ptr) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP mi_op = ((MI_OP)ptr) - 1; |
| |
| mi_op = mi_op->mi_o_next; |
| while ((mi_op != NULL) && (mi_op != &mi_head->mh_o)) { |
| if (mi_op->mi_o_isdev) |
| return ((IDP)&mi_op[1]); |
| mi_op = mi_op->mi_o_next; |
| } |
| return (NULL); |
| } |
| |
| /* |
| * Self clone the device |
| * XXX - should we still support clone device |
| */ |
| /* ARGSUSED4 */ |
| int |
| mi_open_comm(void **mi_headp, size_t size, queue_t *q, dev_t *devp, |
| int flag, int sflag, cred_t *credp) |
| { |
| int error; |
| IDP ptr; |
| |
| if (q->q_ptr != NULL) |
| return (0); |
| |
| ptr = mi_open_alloc_sleep(size); |
| q->q_ptr = WR(q)->q_ptr = ptr; |
| error = mi_open_link(mi_headp, ptr, devp, flag, sflag, credp); |
| if (error != 0) { |
| q->q_ptr = WR(q)->q_ptr = NULL; |
| mi_close_free(ptr); |
| } |
| return (error); |
| } |
| |
| IDP |
| mi_open_alloc_sleep(size_t size) |
| { |
| MI_OP mi_o; |
| |
| if (size > (UINT_MAX - sizeof (MI_O))) |
| return (NULL); |
| |
| mi_o = (MI_OP)mi_zalloc_sleep(size + sizeof (MI_O)); |
| mi_o++; |
| return ((IDP)mi_o); |
| } |
| |
| IDP |
| mi_open_alloc(size_t size) |
| { |
| MI_OP mi_o; |
| |
| if (size > (UINT_MAX - sizeof (MI_O))) |
| return (NULL); |
| |
| if ((mi_o = (MI_OP)mi_zalloc(size + sizeof (MI_O))) == NULL) |
| return (NULL); |
| mi_o++; |
| return ((IDP)mi_o); |
| } |
| |
| /* |
| * MODOPEN means just link in without respect of mi_o_dev. |
| * A NULL devp can be used to create a detached instance |
| * Otherwise self-clone the device. |
| */ |
| /* ARGSUSED3 */ |
| int |
| mi_open_link(void **mi_headp, IDP ptr, dev_t *devp, int flag, int sflag, |
| cred_t *credp) |
| { |
| mi_head_t *mi_head = *(mi_head_t **)mi_headp; |
| MI_OP insert; |
| MI_OP mi_o; |
| dev_t dev; |
| |
| if (mi_head == NULL) { |
| char arena_name[50]; |
| char *head_name; |
| ulong_t offset; |
| |
| head_name = kobj_getsymname((uintptr_t)mi_headp, &offset); |
| if (head_name != NULL && offset == 0) { |
| (void) sprintf(arena_name, "%s_", head_name); |
| } else { |
| (void) sprintf(arena_name, "Hex0x%p_", |
| (void *)mi_headp); |
| } |
| (void) sprintf(strchr(arena_name, '_') + 1, "minor"); |
| mi_head = (mi_head_t *)mi_zalloc_sleep(sizeof (mi_head_t)); |
| *mi_headp = (void *)mi_head; |
| /* Setup doubly linked list */ |
| mi_head->mh_o.mi_o_next = &mi_head->mh_o; |
| mi_head->mh_o.mi_o_prev = &mi_head->mh_o; |
| mi_head->mh_o.mi_o_dev = 0; /* For asserts only */ |
| mi_head->mh_arena = (vmem_t *)inet_minor_create(arena_name, |
| INET_MIN_DEV, KM_SLEEP); |
| } |
| ASSERT(ptr != NULL); |
| mi_o = (MI_OP)ptr; |
| mi_o--; |
| |
| if (sflag == MODOPEN) { |
| devp = NULL; |
| /* |
| * Set device number to MAXMIN + incrementing number. |
| */ |
| dev = MAXMIN + ++mi_head->mh_module_dev; |
| /* check for wraparound */ |
| if (dev <= MAXMIN) { |
| dev = MAXMIN + 1; |
| mi_head->mh_module_dev = 1; |
| } |
| } else if (devp == NULL) { |
| /* Detached open */ |
| dev = (dev_t)OPENFAIL; |
| } else if ((dev = inet_minor_alloc(mi_head->mh_arena)) == 0) { |
| return (EBUSY); |
| } |
| |
| mi_o->mi_o_dev = dev; |
| insert = (&mi_head->mh_o); |
| mi_o->mi_o_next = insert; |
| insert->mi_o_prev->mi_o_next = mi_o; |
| mi_o->mi_o_prev = insert->mi_o_prev; |
| insert->mi_o_prev = mi_o; |
| |
| if (sflag == MODOPEN) |
| mi_o->mi_o_isdev = B_FALSE; |
| else |
| mi_o->mi_o_isdev = B_TRUE; |
| |
| if (devp) |
| *devp = makedevice(getemajor(*devp), (minor_t)dev); |
| return (0); |
| } |
| |
| uint8_t * |
| mi_offset_param(mblk_t *mp, size_t offset, size_t len) |
| { |
| size_t msg_len; |
| |
| if (!mp) |
| return (NULL); |
| msg_len = mp->b_wptr - mp->b_rptr; |
| if (msg_len == 0 || offset > msg_len || len > msg_len || |
| (offset + len) > msg_len || len == 0) |
| return (NULL); |
| return (&mp->b_rptr[offset]); |
| } |
| |
| uint8_t * |
| mi_offset_paramc(mblk_t *mp, size_t offset, size_t len) |
| { |
| uint8_t *param; |
| |
| for (; mp; mp = mp->b_cont) { |
| int type = mp->b_datap->db_type; |
| if (datamsg(type)) { |
| if (param = mi_offset_param(mp, offset, len)) |
| return (param); |
| if (offset < mp->b_wptr - mp->b_rptr) |
| break; |
| offset -= mp->b_wptr - mp->b_rptr; |
| } |
| } |
| return (NULL); |
| } |
| |
| |
| boolean_t |
| mi_set_sth_hiwat(queue_t *q, size_t size) |
| { |
| MBLKP mp; |
| STROPTP stropt; |
| |
| if (!(mp = allocb(sizeof (*stropt), BPRI_LO))) |
| return (B_FALSE); |
| mp->b_datap->db_type = M_SETOPTS; |
| mp->b_wptr += sizeof (*stropt); |
| stropt = (STROPTP)mp->b_rptr; |
| stropt->so_flags = SO_HIWAT; |
| stropt->so_hiwat = size; |
| putnext(q, mp); |
| return (B_TRUE); |
| } |
| |
| boolean_t |
| mi_set_sth_lowat(queue_t *q, size_t size) |
| { |
| MBLKP mp; |
| STROPTP stropt; |
| |
| if (!(mp = allocb(sizeof (*stropt), BPRI_LO))) |
| return (B_FALSE); |
| mp->b_datap->db_type = M_SETOPTS; |
| mp->b_wptr += sizeof (*stropt); |
| stropt = (STROPTP)mp->b_rptr; |
| stropt->so_flags = SO_LOWAT; |
| stropt->so_lowat = size; |
| putnext(q, mp); |
| return (B_TRUE); |
| } |
| |
| /* ARGSUSED */ |
| boolean_t |
| mi_set_sth_maxblk(queue_t *q, ssize_t size) |
| { |
| MBLKP mp; |
| STROPTP stropt; |
| |
| if (!(mp = allocb(sizeof (*stropt), BPRI_LO))) |
| return (B_FALSE); |
| mp->b_datap->db_type = M_SETOPTS; |
| mp->b_wptr += sizeof (*stropt); |
| stropt = (STROPTP)mp->b_rptr; |
| stropt->so_flags = SO_MAXBLK; |
| stropt->so_maxblk = size; |
| putnext(q, mp); |
| return (B_TRUE); |
| } |
| |
| boolean_t |
| mi_set_sth_copyopt(queue_t *q, int copyopt) |
| { |
| MBLKP mp; |
| STROPTP stropt; |
| |
| if (!(mp = allocb(sizeof (*stropt), BPRI_LO))) |
| return (B_FALSE); |
| mp->b_datap->db_type = M_SETOPTS; |
| mp->b_wptr += sizeof (*stropt); |
| stropt = (STROPTP)mp->b_rptr; |
| stropt->so_flags = SO_COPYOPT; |
| stropt->so_copyopt = (ushort_t)copyopt; |
| putnext(q, mp); |
| return (B_TRUE); |
| } |
| |
| boolean_t |
| mi_set_sth_wroff(queue_t *q, size_t size) |
| { |
| MBLKP mp; |
| STROPTP stropt; |
| |
| if (!(mp = allocb(sizeof (*stropt), BPRI_LO))) |
| return (B_FALSE); |
| mp->b_datap->db_type = M_SETOPTS; |
| mp->b_wptr += sizeof (*stropt); |
| stropt = (STROPTP)mp->b_rptr; |
| stropt->so_flags = SO_WROFF; |
| stropt->so_wroff = (ushort_t)size; |
| putnext(q, mp); |
| return (B_TRUE); |
| } |
| |
| int |
| mi_sprintf(char *buf, char *fmt, ...) |
| { |
| va_list ap; |
| int count = -1; |
| va_start(ap, fmt); |
| if (buf) { |
| count = mi_iprintf(fmt, ap, (pfi_t)mi_sprintf_putc, |
| (char *)&buf); |
| if (count != -1) |
| (void) mi_sprintf_putc((char *)&buf, '\0'); |
| } |
| va_end(ap); |
| return (count); |
| } |
| |
| /* Used to count without writing data */ |
| /* ARGSUSED1 */ |
| static int |
| mi_sprintf_noop(char *cookie, int ch) |
| { |
| char **cpp = (char **)cookie; |
| |
| (*cpp)++; |
| return (1); |
| } |
| |
| int |
| mi_sprintf_putc(char *cookie, int ch) |
| { |
| char **cpp = (char **)cookie; |
| |
| **cpp = (char)ch; |
| (*cpp)++; |
| return (1); |
| } |
| |
| int |
| mi_strcmp(const char *cp1, const char *cp2) |
| { |
| while (*cp1++ == *cp2++) { |
| if (!cp2[-1]) |
| return (0); |
| } |
| return ((uint_t)cp2[-1] & 0xFF) - ((uint_t)cp1[-1] & 0xFF); |
| } |
| |
| size_t |
| mi_strlen(const char *str) |
| { |
| const char *cp = str; |
| |
| while (*cp != '\0') |
| cp++; |
| return ((int)(cp - str)); |
| } |
| |
| int |
| mi_strlog(queue_t *q, char level, ushort_t flags, char *fmt, ...) |
| { |
| va_list ap; |
| char buf[200]; |
| char *alloc_buf = buf; |
| int count = -1; |
| char *cp; |
| short mid; |
| int ret; |
| short sid; |
| |
| sid = 0; |
| mid = 0; |
| if (q != NULL) { |
| mid = q->q_qinfo->qi_minfo->mi_idnum; |
| } |
| |
| /* Find out how many bytes we need and allocate if necesary */ |
| va_start(ap, fmt); |
| cp = buf; |
| count = mi_iprintf(fmt, ap, mi_sprintf_noop, (char *)&cp); |
| if (count > sizeof (buf) && |
| !(alloc_buf = mi_alloc((uint_t)count + 2, BPRI_MED))) { |
| va_end(ap); |
| return (-1); |
| } |
| va_end(ap); |
| |
| va_start(ap, fmt); |
| cp = alloc_buf; |
| count = mi_iprintf(fmt, ap, mi_sprintf_putc, (char *)&cp); |
| if (count != -1) |
| (void) mi_sprintf_putc((char *)&cp, '\0'); |
| else |
| alloc_buf[0] = '\0'; |
| va_end(ap); |
| |
| ret = strlog(mid, sid, level, flags, alloc_buf); |
| if (alloc_buf != buf) |
| mi_free(alloc_buf); |
| return (ret); |
| } |
| |
| long |
| mi_strtol(const char *str, char **ptr, int base) |
| { |
| const char *cp; |
| int digits; |
| long value; |
| boolean_t is_negative; |
| |
| cp = str; |
| while (*cp == ' ' || *cp == '\t' || *cp == '\n') |
| cp++; |
| is_negative = (*cp == '-'); |
| if (is_negative) |
| cp++; |
| if (base == 0) { |
| base = 10; |
| if (*cp == '0') { |
| base = 8; |
| cp++; |
| if (*cp == 'x' || *cp == 'X') { |
| base = 16; |
| cp++; |
| } |
| } |
| } |
| value = 0; |
| for (; *cp != '\0'; cp++) { |
| if (*cp >= '0' && *cp <= '9') |
| digits = *cp - '0'; |
| else if (*cp >= 'a' && *cp <= 'f') |
| digits = *cp - 'a' + 10; |
| else if (*cp >= 'A' && *cp <= 'F') |
| digits = *cp - 'A' + 10; |
| else |
| break; |
| if (digits >= base) |
| break; |
| value = (value * base) + digits; |
| } |
| /* Note: we cast away const here deliberately */ |
| if (ptr != NULL) |
| *ptr = (char *)cp; |
| if (is_negative) |
| value = -value; |
| return (value); |
| } |
| |
| /* |
| * mi_timer mechanism. |
| * |
| * Each timer is represented by a timer mblk and a (streams) queue. When the |
| * timer fires the timer mblk will be put on the associated streams queue |
| * so that the streams module can process the timer even in its service |
| * procedure. |
| * |
| * The interface consists of 4 entry points: |
| * mi_timer_alloc - create a timer mblk |
| * mi_timer_free - free a timer mblk |
| * mi_timer - start, restart, stop, or move the |
| * timer to a different queue |
| * mi_timer_valid - called by streams module to verify that |
| * the timer did indeed fire. |
| */ |
| |
| |
| |
| |
| /* |
| * Start, restart, stop, or move the timer to a new queue. |
| * If "tim" is -2 the timer is moved to a different queue. |
| * If "tim" is -1 the timer is stopped. |
| * Otherwise, the timer is stopped if it is already running, and |
| * set to fire tim milliseconds from now. |
| */ |
| |
| void |
| mi_timer(queue_t *q, MBLKP mp, clock_t tim) |
| { |
| MTBP mtb; |
| int state; |
| |
| ASSERT(tim >= -2); |
| if (!q || !mp || (mp->b_rptr - mp->b_datap->db_base) != sizeof (MTB)) |
| return; |
| mtb = (MTBP)mp->b_datap->db_base; |
| ASSERT(mp->b_datap->db_type == M_PCSIG); |
| if (tim >= 0) { |
| mtb->mtb_q = q; |
| state = mtb->mtb_state; |
| tim = MSEC_TO_TICK(tim); |
| if (state == TB_RUNNING) { |
| if (untimeout(mtb->mtb_tid) < 0) { |
| /* Message has already been putq */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_state = TB_RESCHED; |
| mtb->mtb_time_left = tim; |
| /* mi_timer_valid will start timer */ |
| return; |
| } |
| } else if (state != TB_IDLE) { |
| ASSERT(state != TB_TO_BE_FREED); |
| if (state == TB_CANCELLED) { |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_state = TB_RESCHED; |
| mtb->mtb_time_left = tim; |
| /* mi_timer_valid will start timer */ |
| return; |
| } |
| if (state == TB_RESCHED) { |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_time_left = tim; |
| /* mi_timer_valid will start timer */ |
| return; |
| } |
| } |
| mtb->mtb_state = TB_RUNNING; |
| mtb->mtb_tid = timeout((pfv_t)mi_timer_fire, mtb, tim); |
| return; |
| } |
| switch (tim) { |
| case -1: |
| mi_timer_stop(mp); |
| break; |
| case -2: |
| mi_timer_move(q, mp); |
| break; |
| } |
| } |
| |
| /* |
| * Allocate an M_PCSIG timer message. The space between db_base and |
| * b_rptr is used by the mi_timer mechanism, and after b_rptr there are |
| * "size" bytes that the caller can use for its own purposes. |
| * |
| * Note that db_type has to be a priority message since otherwise |
| * the putq will not cause the service procedure to run when |
| * there is flow control. |
| */ |
| MBLKP |
| mi_timer_alloc(size_t size) |
| { |
| MBLKP mp; |
| MTBP mtb; |
| |
| if ((mp = allocb(size + sizeof (MTB), BPRI_HI)) != NULL) { |
| mp->b_datap->db_type = M_PCSIG; |
| mtb = (MTBP)mp->b_datap->db_base; |
| mp->b_rptr = (uchar_t *)&mtb[1]; |
| mp->b_wptr = mp->b_rptr + size; |
| mtb->mtb_state = TB_IDLE; |
| mtb->mtb_mp = mp; |
| mtb->mtb_q = NULL; |
| return (mp); |
| } |
| return (NULL); |
| } |
| |
| /* |
| * timeout() callback function. |
| * Put the message on the current queue. |
| * If the timer is stopped or moved to a different queue after |
| * it has fired then mi_timer() and mi_timer_valid() will clean |
| * things up. |
| */ |
| static int |
| mi_timer_fire(MTBP mtb) |
| { |
| ASSERT(mtb == (MTBP)mtb->mtb_mp->b_datap->db_base); |
| ASSERT(mtb->mtb_mp->b_datap->db_type == M_PCSIG); |
| return (putq(mtb->mtb_q, mtb->mtb_mp)); |
| } |
| |
| /* |
| * Logically free a timer mblk (that might have a pending timeout().) |
| * If the timer has fired and the mblk has been put on the queue then |
| * mi_timer_valid will free the mblk. |
| */ |
| |
| void |
| mi_timer_free(MBLKP mp) |
| { |
| MTBP mtb; |
| int state; |
| |
| if (!mp || (mp->b_rptr - mp->b_datap->db_base) != sizeof (MTB)) |
| return; |
| mtb = (MTBP)mp->b_datap->db_base; |
| state = mtb->mtb_state; |
| if (state == TB_RUNNING) { |
| if (untimeout(mtb->mtb_tid) < 0) { |
| /* Message has already been putq */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_state = TB_TO_BE_FREED; |
| /* mi_timer_valid will free the mblk */ |
| return; |
| } |
| } else if (state != TB_IDLE) { |
| /* Message has already been putq */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| ASSERT(state != TB_TO_BE_FREED); |
| mtb->mtb_state = TB_TO_BE_FREED; |
| /* mi_timer_valid will free the mblk */ |
| return; |
| } |
| ASSERT(mtb->mtb_q == NULL || mtb->mtb_q->q_first != mp); |
| freemsg(mp); |
| } |
| |
| /* |
| * Called from mi_timer(,,-2) |
| */ |
| void |
| mi_timer_move(queue_t *q, MBLKP mp) |
| { |
| MTBP mtb; |
| clock_t tim; |
| |
| if (!q || !mp || (mp->b_rptr - mp->b_datap->db_base) != sizeof (MTB)) |
| return; |
| |
| mtb = (MTBP)mp->b_datap->db_base; |
| /* |
| * Need to untimeout and restart to make |
| * sure that the mblk is not about to be putq on the old queue |
| * by mi_timer_fire. |
| */ |
| if (mtb->mtb_state == TB_RUNNING) { |
| if ((tim = untimeout(mtb->mtb_tid)) < 0) { |
| /* |
| * Message has already been putq. Move from old queue |
| * to new queue. |
| */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| rmvq(mtb->mtb_q, mp); |
| ASSERT(mtb->mtb_q->q_first != mp && |
| mp->b_prev == NULL && mp->b_next == NULL); |
| mtb->mtb_q = q; |
| (void) putq(mtb->mtb_q, mp); |
| return; |
| } |
| mtb->mtb_q = q; |
| mtb->mtb_state = TB_RUNNING; |
| mtb->mtb_tid = timeout((pfv_t)mi_timer_fire, mtb, tim); |
| } else if (mtb->mtb_state != TB_IDLE) { |
| ASSERT(mtb->mtb_state != TB_TO_BE_FREED); |
| /* |
| * Message is already sitting on queue. Move to new queue. |
| */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| rmvq(mtb->mtb_q, mp); |
| ASSERT(mtb->mtb_q->q_first != mp && |
| mp->b_prev == NULL && mp->b_next == NULL); |
| mtb->mtb_q = q; |
| (void) putq(mtb->mtb_q, mp); |
| } else |
| mtb->mtb_q = q; |
| } |
| |
| /* |
| * Called from mi_timer(,,-1) |
| */ |
| void |
| mi_timer_stop(MBLKP mp) |
| { |
| MTBP mtb; |
| int state; |
| |
| if (!mp || (mp->b_rptr - mp->b_datap->db_base) != sizeof (MTB)) |
| return; |
| |
| mtb = (MTBP)mp->b_datap->db_base; |
| state = mtb->mtb_state; |
| if (state == TB_RUNNING) { |
| if (untimeout(mtb->mtb_tid) < 0) { |
| /* Message has already been putq */ |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_state = TB_CANCELLED; |
| } else { |
| mtb->mtb_state = TB_IDLE; |
| } |
| } else if (state == TB_RESCHED) { |
| ASSERT(mtb->mtb_q->q_first == mp || |
| mp->b_prev || mp->b_next); |
| mtb->mtb_state = TB_CANCELLED; |
| } |
| } |
| |
| /* |
| * The user of the mi_timer mechanism is required to call mi_timer_valid() for |
| * each M_PCSIG message processed in the service procedures. |
| * mi_timer_valid will return "true" if the timer actually did fire. |
| */ |
| |
| boolean_t |
| mi_timer_valid(MBLKP mp) |
| { |
| MTBP mtb; |
| int state; |
| |
| if (!mp || (mp->b_rptr - mp->b_datap->db_base) != sizeof (MTB) || |
| mp->b_datap->db_type != M_PCSIG) |
| return (B_FALSE); |
| mtb = (MTBP)mp->b_datap->db_base; |
| state = mtb->mtb_state; |
| if (state != TB_RUNNING) { |
| ASSERT(state != TB_IDLE); |
| if (state == TB_TO_BE_FREED) { |
| /* |
| * mi_timer_free was called after the message |
| * was putq'ed. |
| */ |
| freemsg(mp); |
| return (B_FALSE); |
| } |
| if (state == TB_CANCELLED) { |
| /* The timer was stopped after the mblk was putq'ed */ |
| mtb->mtb_state = TB_IDLE; |
| return (B_FALSE); |
| } |
| if (state == TB_RESCHED) { |
| /* |
| * The timer was stopped and then restarted after |
| * the mblk was putq'ed. |
| * mtb_time_left contains the number of ticks that |
| * the timer was restarted with. |
| */ |
| mtb->mtb_state = TB_RUNNING; |
| mtb->mtb_tid = timeout((pfv_t)mi_timer_fire, |
| mtb, mtb->mtb_time_left); |
| return (B_FALSE); |
| } |
| } |
| mtb->mtb_state = TB_IDLE; |
| return (B_TRUE); |
| } |
| |
| static void |
| mi_tpi_addr_and_opt(MBLKP mp, char *addr, t_scalar_t addr_length, |
| char *opt, t_scalar_t opt_length) |
| { |
| struct T_unitdata_ind *tudi; |
| |
| /* |
| * This code is used more than just for unitdata ind |
| * (also for T_CONN_IND and T_CONN_CON) and |
| * relies on correct functioning on the happy |
| * coincidence that the the address and option buffers |
| * represented by length/offset in all these primitives |
| * are isomorphic in terms of offset from start of data |
| * structure |
| */ |
| tudi = (struct T_unitdata_ind *)mp->b_rptr; |
| tudi->SRC_offset = (t_scalar_t)(mp->b_wptr - mp->b_rptr); |
| tudi->SRC_length = addr_length; |
| if (addr_length > 0) { |
| bcopy(addr, (char *)mp->b_wptr, addr_length); |
| mp->b_wptr += addr_length; |
| } |
| tudi->OPT_offset = (t_scalar_t)(mp->b_wptr - mp->b_rptr); |
| tudi->OPT_length = opt_length; |
| if (opt_length > 0) { |
| bcopy(opt, (char *)mp->b_wptr, opt_length); |
| mp->b_wptr += opt_length; |
| } |
| } |
| |
| MBLKP |
| mi_tpi_conn_con(MBLKP trailer_mp, char *src, t_scalar_t src_length, char *opt, |
| t_scalar_t opt_length) |
| { |
| size_t len; |
| MBLKP mp; |
| |
| len = sizeof (struct T_conn_con) + src_length + opt_length; |
| if ((mp = mi_tpi_trailer_alloc(trailer_mp, len, T_CONN_CON)) != NULL) { |
| mp->b_wptr = &mp->b_rptr[sizeof (struct T_conn_con)]; |
| mi_tpi_addr_and_opt(mp, src, src_length, opt, opt_length); |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_conn_ind(MBLKP trailer_mp, char *src, t_scalar_t src_length, char *opt, |
| t_scalar_t opt_length, t_scalar_t seqnum) |
| { |
| size_t len; |
| MBLKP mp; |
| |
| len = sizeof (struct T_conn_ind) + src_length + opt_length; |
| if ((mp = mi_tpi_trailer_alloc(trailer_mp, len, T_CONN_IND)) != NULL) { |
| mp->b_wptr = &mp->b_rptr[sizeof (struct T_conn_ind)]; |
| mi_tpi_addr_and_opt(mp, src, src_length, opt, opt_length); |
| ((struct T_conn_ind *)mp->b_rptr)->SEQ_number = seqnum; |
| mp->b_datap->db_type = M_PROTO; |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_extconn_ind(MBLKP trailer_mp, char *src, t_scalar_t src_length, |
| char *opt, t_scalar_t opt_length, char *dst, t_scalar_t dst_length, |
| t_scalar_t seqnum) |
| { |
| size_t len; |
| MBLKP mp; |
| |
| len = sizeof (struct T_extconn_ind) + src_length + opt_length + |
| dst_length; |
| if ((mp = mi_tpi_trailer_alloc(trailer_mp, len, T_EXTCONN_IND)) != |
| NULL) { |
| mp->b_wptr = &mp->b_rptr[sizeof (struct T_extconn_ind)]; |
| mi_tpi_addr_and_opt(mp, src, src_length, opt, opt_length); |
| ((struct T_extconn_ind *)mp->b_rptr)->DEST_length = dst_length; |
| ((struct T_extconn_ind *)mp->b_rptr)->DEST_offset = |
| (t_scalar_t)(mp->b_wptr - mp->b_rptr); |
| if (dst_length > 0) { |
| bcopy(dst, (char *)mp->b_wptr, dst_length); |
| mp->b_wptr += dst_length; |
| } |
| ((struct T_extconn_ind *)mp->b_rptr)->SEQ_number = seqnum; |
| mp->b_datap->db_type = M_PROTO; |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_discon_ind(MBLKP trailer_mp, t_scalar_t reason, t_scalar_t seqnum) |
| { |
| MBLKP mp; |
| struct T_discon_ind *tdi; |
| |
| if ((mp = mi_tpi_trailer_alloc(trailer_mp, |
| sizeof (struct T_discon_ind), T_DISCON_IND)) != NULL) { |
| tdi = (struct T_discon_ind *)mp->b_rptr; |
| tdi->DISCON_reason = reason; |
| tdi->SEQ_number = seqnum; |
| } |
| return (mp); |
| } |
| |
| /* |
| * Allocate and fill in a TPI err ack packet using the 'mp' passed in |
| * for the 'error_prim' context as well as sacrifice. |
| */ |
| MBLKP |
| mi_tpi_err_ack_alloc(MBLKP mp, t_scalar_t tlierr, int unixerr) |
| { |
| struct T_error_ack *teackp; |
| t_scalar_t error_prim; |
| |
| if (!mp) |
| return (NULL); |
| error_prim = ((TPRIMP)mp->b_rptr)->type; |
| if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack), |
| M_PCPROTO, T_ERROR_ACK)) != NULL) { |
| teackp = (struct T_error_ack *)mp->b_rptr; |
| teackp->ERROR_prim = error_prim; |
| teackp->TLI_error = tlierr; |
| teackp->UNIX_error = unixerr; |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_ok_ack_alloc_extra(MBLKP mp, int extra) |
| { |
| t_scalar_t correct_prim; |
| |
| if (!mp) |
| return (NULL); |
| correct_prim = ((TPRIMP)mp->b_rptr)->type; |
| if ((mp = tpi_ack_alloc(mp, sizeof (struct T_ok_ack) + extra, |
| M_PCPROTO, T_OK_ACK)) != NULL) { |
| ((struct T_ok_ack *)mp->b_rptr)->CORRECT_prim = correct_prim; |
| mp->b_wptr -= extra; |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_ok_ack_alloc(MBLKP mp) |
| { |
| return (mi_tpi_ok_ack_alloc_extra(mp, 0)); |
| } |
| |
| MBLKP |
| mi_tpi_ordrel_ind(void) |
| { |
| MBLKP mp; |
| |
| if ((mp = allocb(sizeof (struct T_ordrel_ind), BPRI_HI)) != NULL) { |
| mp->b_datap->db_type = M_PROTO; |
| ((struct T_ordrel_ind *)mp->b_rptr)->PRIM_type = T_ORDREL_IND; |
| mp->b_wptr += sizeof (struct T_ordrel_ind); |
| } |
| return (mp); |
| } |
| |
| static MBLKP |
| mi_tpi_trailer_alloc(MBLKP trailer_mp, size_t size, t_scalar_t type) |
| { |
| MBLKP mp; |
| |
| if ((mp = allocb(size, BPRI_MED)) != NULL) { |
| mp->b_cont = trailer_mp; |
| mp->b_datap->db_type = M_PROTO; |
| ((union T_primitives *)mp->b_rptr)->type = type; |
| mp->b_wptr += size; |
| } |
| return (mp); |
| } |
| |
| MBLKP |
| mi_tpi_uderror_ind(char *dest, t_scalar_t dest_length, char *opt, |
| t_scalar_t opt_length, t_scalar_t error) |
| { |
| size_t len; |
| MBLKP mp; |
| struct T_uderror_ind *tudei; |
| |
| len = sizeof (struct T_uderror_ind) + dest_length + opt_length; |
| if ((mp = allocb(len, BPRI_HI)) != NULL) { |
| mp->b_datap->db_type = M_PROTO; |
| tudei = (struct T_uderror_ind *)mp->b_rptr; |
| tudei->PRIM_type = T_UDERROR_IND; |
| tudei->ERROR_type = error; |
| mp->b_wptr = &mp->b_rptr[sizeof (struct T_uderror_ind)]; |
| mi_tpi_addr_and_opt(mp, dest, dest_length, opt, opt_length); |
| } |
| return (mp); |
| } |
| |
| IDP |
| mi_zalloc(size_t size) |
| { |
| IDP ptr; |
| |
| if (ptr = mi_alloc(size, BPRI_LO)) |
| bzero(ptr, size); |
| return (ptr); |
| } |
| |
| IDP |
| mi_zalloc_sleep(size_t size) |
| { |
| IDP ptr; |
| |
| if (ptr = mi_alloc_sleep(size, BPRI_LO)) |
| bzero(ptr, size); |
| return (ptr); |
| } |