1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/strsubr.h>
27 #include <inet/led.h>
28 #include <sys/softmac_impl.h>
29 
30 mblk_t *
31 softmac_m_tx(void *arg, mblk_t *mp)
32 {
33 	queue_t *wq = ((softmac_t *)arg)->smac_lower->sl_wq;
34 
35 	/*
36 	 * Optimize for the most common case.
37 	 */
38 	if (mp->b_next == NULL) {
39 		if (!SOFTMAC_CANPUTNEXT(wq))
40 			return (mp);
41 
42 		mp->b_flag |= MSGNOLOOP;
43 		putnext(wq, mp);
44 		return (NULL);
45 	}
46 
47 	while (mp != NULL) {
48 		mblk_t *next = mp->b_next;
49 
50 		if (!SOFTMAC_CANPUTNEXT(wq))
51 			break;
52 		mp->b_next = NULL;
53 		mp->b_flag |= MSGNOLOOP;
54 		putnext(wq, mp);
55 		mp = next;
56 	}
57 	return (mp);
58 }
59 
60 void
61 softmac_rput_process_data(softmac_lower_t *slp, mblk_t *mp)
62 {
63 	/*
64 	 * When packets arrive, the softmac might not be fully started.
65 	 */
66 	ASSERT((slp->sl_softmac != NULL));
67 	ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
68 
69 	if (DB_REF(mp) > 1) {
70 		mblk_t *tmp;
71 		uint32_t start, stuff, end, value, flags;
72 
73 		if ((tmp = copymsg(mp)) == NULL) {
74 			cmn_err(CE_WARN, "softmac_rput_process_data: "
75 			    "copymsg failed");
76 			goto failed;
77 		}
78 		hcksum_retrieve(mp, NULL, NULL, &start, &stuff, &end,
79 		    &value, &flags);
80 		VERIFY(hcksum_assoc(tmp, NULL, NULL, start, stuff, end,
81 		    value, flags, KM_NOSLEEP) == 0);
82 		freemsg(mp);
83 		mp = tmp;
84 	}
85 
86 	mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
87 	return;
88 
89 failed:
90 	freemsg(mp);
91 }
92 
93 #define	ACKTIMEOUT	(10 * hz)
94 
95 /*
96  * Serialize control message processing.
97  */
98 static void
99 softmac_serialize_enter(softmac_lower_t *slp)
100 {
101 	mutex_enter(&slp->sl_ctl_mutex);
102 	while (slp->sl_ctl_inprogress)
103 		cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex);
104 
105 	ASSERT(!slp->sl_ctl_inprogress);
106 	ASSERT(!slp->sl_pending_ioctl);
107 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
108 
109 	slp->sl_ctl_inprogress = B_TRUE;
110 	mutex_exit(&slp->sl_ctl_mutex);
111 }
112 
113 static void
114 softmac_serialize_exit(softmac_lower_t *slp)
115 {
116 	mutex_enter(&slp->sl_ctl_mutex);
117 
118 	ASSERT(slp->sl_ctl_inprogress);
119 	ASSERT(!slp->sl_pending_ioctl);
120 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
121 
122 	slp->sl_ctl_inprogress = B_FALSE;
123 	cv_broadcast(&slp->sl_ctl_cv);
124 	mutex_exit(&slp->sl_ctl_mutex);
125 }
126 
127 static int
128 dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno)
129 {
130 	return (error == DL_SYSERR ? unix_errno : EINVAL);
131 }
132 
133 int
134 softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim,
135     t_uscalar_t ack, mblk_t **mpp)
136 {
137 	union DL_primitives	*dlp;
138 	int			err = 0;
139 
140 	softmac_serialize_enter(slp);
141 
142 	/*
143 	 * Record the pending DLPI primitive.
144 	 */
145 	mutex_enter(&slp->sl_mutex);
146 	slp->sl_pending_prim = dl_prim;
147 	mutex_exit(&slp->sl_mutex);
148 
149 	putnext(slp->sl_wq, mp);
150 
151 	mutex_enter(&slp->sl_mutex);
152 	while (slp->sl_pending_prim != DL_PRIM_INVAL) {
153 		if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex,
154 		    lbolt + ACKTIMEOUT) == -1)
155 			break;
156 	}
157 
158 	mp = slp->sl_ack_mp;
159 	slp->sl_ack_mp = NULL;
160 
161 	/*
162 	 * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim
163 	 * won't be set to DL_PRIM_INVAL.
164 	 */
165 	ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL);
166 
167 	slp->sl_pending_prim = DL_PRIM_INVAL;
168 	mutex_exit(&slp->sl_mutex);
169 
170 	if (mp != NULL) {
171 		dlp = (union DL_primitives *)mp->b_rptr;
172 
173 		if (dlp->dl_primitive == DL_ERROR_ACK) {
174 			err = dlpi_get_errno(dlp->error_ack.dl_errno,
175 			    dlp->error_ack.dl_unix_errno);
176 		} else {
177 			ASSERT(dlp->dl_primitive == ack);
178 		}
179 	} else {
180 		err = ENOMSG;
181 	}
182 
183 	if (mpp != NULL)
184 		*mpp = mp;
185 	else
186 		freemsg(mp);
187 
188 	softmac_serialize_exit(slp);
189 	return (err);
190 }
191 
192 void
193 softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
194 {
195 	softmac_serialize_enter(slp);
196 
197 	/*
198 	 * Record that ioctl processing is currently in progress.
199 	 */
200 	mutex_enter(&slp->sl_mutex);
201 	slp->sl_pending_ioctl = B_TRUE;
202 	mutex_exit(&slp->sl_mutex);
203 
204 	putnext(slp->sl_wq, mp);
205 
206 	mutex_enter(&slp->sl_mutex);
207 	while (slp->sl_pending_ioctl)
208 		cv_wait(&slp->sl_cv, &slp->sl_mutex);
209 	mp = slp->sl_ack_mp;
210 	slp->sl_ack_mp = NULL;
211 	mutex_exit(&slp->sl_mutex);
212 
213 	ASSERT(mpp != NULL && mp != NULL);
214 	*mpp = mp;
215 
216 	softmac_serialize_exit(slp);
217 }
218 
219 int
220 softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive,
221 	t_uscalar_t error, t_uscalar_t unix_errno)
222 {
223 	union DL_primitives *dlp;
224 
225 	if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO,
226 	    DL_ERROR_ACK)) == NULL)
227 		return (ENOMEM);
228 
229 	dlp = (union DL_primitives *)(*mpp)->b_rptr;
230 	dlp->error_ack.dl_error_primitive = error_primitive;
231 	dlp->error_ack.dl_errno = error;
232 	dlp->error_ack.dl_unix_errno = unix_errno;
233 
234 	return (0);
235 }
236 
237 int
238 softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
239 {
240 	int err = 0;
241 	t_uscalar_t dl_prim;
242 
243 	dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
244 
245 	ASSERT(slp->sl_softmac != NULL);
246 
247 	switch (dl_prim) {
248 	case DL_ENABMULTI_REQ:
249 	case DL_DISABMULTI_REQ:
250 	case DL_SET_PHYS_ADDR_REQ:
251 	case DL_UNBIND_REQ:
252 	case DL_UDQOS_REQ:
253 	case DL_PROMISCON_REQ:
254 	case DL_PROMISCOFF_REQ:
255 		err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp);
256 		break;
257 	case DL_BIND_REQ:
258 		err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp);
259 		break;
260 	case DL_NOTIFY_REQ:
261 		err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp);
262 		break;
263 	case DL_CONTROL_REQ:
264 		err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp);
265 		break;
266 	case DL_CAPABILITY_REQ:
267 		err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp);
268 		break;
269 	default:
270 		if (mpp != NULL) {
271 			*mpp = mp;
272 			err = softmac_mexchange_error_ack(mpp, dl_prim,
273 			    DL_UNSUPPORTED, 0);
274 		}
275 		break;
276 	}
277 	return (err);
278 }
279