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 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * USB Ethernet Control Model
29 *
30 * USB-IF defines three ethernet network related specifications: EEM,
31 * ECM and NCM. This driver focuses specifically on ECM compatible
32 * devices. This kind of devices generally have one pair of bulk
33 * endpoints for in/out packet data and one interrupt endpoint for
34 * device notification.
35 *
36 * Devices which don't report ECM compatibility through descriptors but
37 * implement the ECM functions may also bind to this driver. This driver
38 * will try to find at least a bulk in endpoint and a bulk out endpoint
39 * in this case. If the non-compatible devices use vendor specific data
40 * format, this driver will not function.
41 *
42 * This driver is a normal USBA client driver. It's also a GLDv3 driver,
43 * which provides the necessary interfaces the GLDv3 framework requires.
44 *
45 */
46
47 #include <sys/types.h>
48 #include <sys/strsun.h>
49 #include <sys/ddi.h>
50 #include <sys/sunddi.h>
51 #include <sys/byteorder.h>
52 #include <sys/usb/usba/usbai_version.h>
53 #include <sys/usb/usba.h>
54 #include <sys/usb/usba/usba_types.h>
55 #include <sys/usb/clients/usbcdc/usb_cdc.h>
56 #include <sys/usb/clients/usbecm/usbecm.h>
57 #include <sys/mac_provider.h>
58 #include <sys/strsubr.h>
59 #include <sys/ethernet.h>
60 #include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */
61 #include <sys/random.h> /* random_get_bytes */
62 #include <sys/sdt.h> /* sdt */
63 #include <inet/nd.h>
64
65 /* MAC callbacks */
66 static int usbecm_m_stat(void *arg, uint_t stat, uint64_t *val);
67 static int usbecm_m_start(void *arg);
68 static void usbecm_m_stop(void *arg);
69 static int usbecm_m_unicst(void *arg, const uint8_t *macaddr);
70 static int usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m);
71 static int usbecm_m_promisc(void *arg, boolean_t on);
72 static void usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
73 static mblk_t *usbecm_m_tx(void *arg, mblk_t *mp);
74 static int usbecm_m_getprop(void *arg, const char *pr_name,
75 mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
76 static int usbecm_m_setprop(void *arg, const char *pr_name,
77 mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
78
79 static int usbecm_usb_init(usbecm_state_t *ecmp);
80 static int usbecm_mac_init(usbecm_state_t *ecmp);
81 static int usbecm_mac_fini(usbecm_state_t *ecmp);
82
83
84 /* utils */
85 static void generate_ether_addr(uint8_t *mac_addr);
86 static int usbecm_rx_start(usbecm_state_t *ecmp);
87
88 static void usbecm_pipe_start_polling(usbecm_state_t *ecmp);
89 static void usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
90 static void usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
91 static void usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data);
92
93 static int usbecm_reconnect_event_cb(dev_info_t *dip);
94 static int usbecm_disconnect_event_cb(dev_info_t *dip);
95
96 static int usbecm_open_pipes(usbecm_state_t *ecmp);
97 static void usbecm_close_pipes(usbecm_state_t *ecmp);
98
99 static int usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
100 uint16_t value, mblk_t **data, int len);
101 static int usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
102 uint16_t value, mblk_t **data);
103 static int usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data);
104 static int usbecm_send_zero_data(usbecm_state_t *ecmp);
105 static int usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs,
106 uint32_t *stat_data);
107
108 static int usbecm_create_pm_components(usbecm_state_t *ecmp);
109 static void usbecm_destroy_pm_components(usbecm_state_t *ecmp);
110 static int usbecm_power(dev_info_t *dip, int comp, int level);
111 static void usbecm_pm_set_busy(usbecm_state_t *ecmp);
112 static void usbecm_pm_set_idle(usbecm_state_t *ecmp);
113
114 static int usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
115 static int usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
116
117 static int usbecm_suspend(usbecm_state_t *ecmp);
118 static int usbecm_resume(usbecm_state_t *ecmp);
119 static int usbecm_restore_device_state(usbecm_state_t *ecmp);
120 static void usbecm_cleanup(usbecm_state_t *ecmp);
121
122 /* Driver identification */
123 static char usbecm_ident[] = "usbecm 1.0";
124
125 /* Global state pointer for managing per-device soft states */
126 void *usbecm_statep;
127
128 /* print levels */
129 static uint_t usbecm_errlevel = USB_LOG_L3;
130 static uint_t usbecm_errmask = 0xffffffff;
131 static uint_t usbecm_instance_debug = (uint_t)-1;
132
133 /*
134 * to prevent upper layers packet flood from exhausting system
135 * resources(USBA does not set limitation of requests on a pipe),
136 * we set a upper limit for the transfer queue length.
137 */
138 static int usbecm_tx_max = 32;
139
140 #define SUN_SP_VENDOR_ID 0x0430
141 #define SUN_SP_PRODUCT_ID 0xa4a2
142
143 static uint8_t usbecm_broadcast[ETHERADDRL] = {
144 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
145 };
146
147 static usb_event_t usbecm_events = {
148 usbecm_disconnect_event_cb,
149 usbecm_reconnect_event_cb,
150 NULL, NULL
151 };
152
153 #define ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op))
154
155 /*
156 * MAC Call Back entries
157 */
158 static mac_callbacks_t usbecm_m_callbacks = {
159 MC_IOCTL | MC_SETPROP | MC_GETPROP,
160 usbecm_m_stat, /* Get the value of a statistic */
161 usbecm_m_start, /* Start the device */
162 usbecm_m_stop, /* Stop the device */
163 usbecm_m_promisc, /* Enable or disable promiscuous mode */
164 usbecm_m_multicst, /* Enable or disable a multicast addr */
165 usbecm_m_unicst, /* Set the unicast MAC address */
166 usbecm_m_tx, /* Transmit a packet */
167 NULL,
168 usbecm_m_ioctl, /* Process an unknown ioctl */
169 NULL, /* mc_getcapab */
170 NULL, /* mc_open */
171 NULL, /* mc_close */
172 usbecm_m_setprop, /* mc_setprop */
173 usbecm_m_getprop, /* mc_getprop */
174 NULL
175 };
176
177
178 /*
179 * Module Loading Data & Entry Points
180 * Can't use DDI_DEFINE_STREAM_OPS, since it does
181 * not provide devo_power entry.
182 */
183 static struct cb_ops cb_usbecm = {
184 nulldev, /* cb_open */
185 nulldev, /* cb_close */
186 nodev, /* cb_strategy */
187 nodev, /* cb_print */
188 nodev, /* cb_dump */
189 nodev, /* cb_read */
190 nodev, /* cb_write */
191 nodev, /* cb_ioctl */
192 nodev, /* cb_devmap */
193 nodev, /* cb_mmap */
194 nodev, /* cb_segmap */
195 nochpoll, /* cb_chpoll */
196 ddi_prop_op, /* cb_prop_op */
197 NULL, /* cb_stream */
198 D_MP, /* cb_flag */
199 CB_REV, /* cb_rev */
200 nodev, /* cb_aread */
201 nodev, /* cb_awrite */
202 };
203
204 static struct dev_ops usbecm_devops = {
205 DEVO_REV, /* devo_rev */
206 0, /* devo_refcnt */
207 NULL, /* devo_getinfo */
208 nulldev, /* devo_identify */
209 nulldev, /* devo_probe */
210 usbecm_attach, /* devo_attach */
211 usbecm_detach, /* devo_detach */
212 nodev, /* devo_reset */
213 &(cb_usbecm), /* devo_cb_ops */
214 (struct bus_ops *)NULL, /* devo_bus_ops */
215 usbecm_power, /* devo_power */
216 ddi_quiesce_not_needed /* devo_quiesce */
217 };
218
219 static struct modldrv usbecm_modldrv = {
220 &mod_driverops, /* drv_modops */
221 usbecm_ident, /* drv_linkinfo */
222 &usbecm_devops /* drv_dev_ops */
223 };
224
225 static struct modlinkage usbecm_ml = {
226 MODREV_1, /* ml_rev */
227 &usbecm_modldrv, NULL /* ml_linkage */
228 };
229
230
231 /*
232 * Device operations
233 */
234 /*
235 * Binding the driver to a device.
236 *
237 * Concurrency: Until usbecm_attach() returns with success,
238 * the only other entry point that can be executed is getinfo().
239 * Thus no locking here yet.
240 */
241 static int
usbecm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)242 usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
243 {
244 char strbuf[32];
245 int instance;
246 int err;
247 usbecm_state_t *ecmp = NULL;
248
249 switch (cmd) {
250 case DDI_ATTACH:
251 break;
252
253 case DDI_RESUME:
254 ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
255 ddi_get_instance(dip));
256
257 (void) usbecm_resume(ecmp);
258
259 return (DDI_SUCCESS);
260
261 default:
262 return (DDI_FAILURE);
263 }
264
265 instance = ddi_get_instance(dip);
266
267 if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) {
268 ecmp = ddi_get_soft_state(usbecm_statep, instance);
269 }
270 if (ecmp == NULL) {
271 cmn_err(CE_WARN, "usbecm_attach: fail to get soft state");
272
273 return (DDI_FAILURE);
274 }
275
276 ecmp->ecm_dip = dip;
277
278 ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm",
279 &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0);
280
281 if (usbecm_usb_init(ecmp) != USB_SUCCESS) {
282 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
283 "usbecm_attach: failed to init usb");
284
285 goto fail;
286 }
287
288 if (ECM_DS_OP_VALID(ecm_ds_init)) {
289 if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) {
290 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
291 "usbecm_attach: failed to init DS");
292
293 goto fail;
294 }
295 }
296
297 if (usbecm_mac_init(ecmp) != DDI_SUCCESS) {
298 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
299 "usbecm_attach: failed to init mac");
300
301 goto fail;
302 }
303 ecmp->ecm_init_flags |= USBECM_INIT_MAC;
304
305 /*
306 * Create minor node of type usb_net. Not necessary to create
307 * DDI_NT_NET since it's created in mac_register(). Otherwise,
308 * system will panic.
309 */
310 (void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance);
311 err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
312 instance + 1, "usb_net", 0);
313 if (err != DDI_SUCCESS) {
314 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
315 "failed to create minor node");
316
317 goto fail;
318 }
319
320 /* always busy. May change to a more precise PM in future */
321 usbecm_pm_set_busy(ecmp);
322
323 ddi_report_dev(dip);
324
325 USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
326 "usbecm_attach: succeed!");
327
328 return (DDI_SUCCESS);
329
330 fail:
331 USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
332 "usbecm_attach: Attach fail");
333
334 usbecm_cleanup(ecmp);
335 ddi_prop_remove_all(dip);
336 ddi_soft_state_free(usbecm_statep, instance);
337
338 return (DDI_FAILURE);
339
340 }
341
342
343 /*
344 * Detach the driver from a device.
345 *
346 * Concurrency: Will be called only after a successful attach
347 * (and not concurrently).
348 */
349 static int
usbecm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)350 usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
351 {
352 usbecm_state_t *ecmp = NULL;
353 int instance;
354
355 instance = ddi_get_instance(dip);
356 ecmp = ddi_get_soft_state(usbecm_statep, instance);
357 ASSERT(ecmp != NULL);
358
359 USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
360 "usbecm_detach: entry ");
361
362 switch (cmd) {
363 case DDI_DETACH:
364 break;
365
366 case DDI_SUSPEND:
367
368 return (usbecm_suspend(ecmp));
369
370 default:
371 return (DDI_FAILURE);
372 }
373
374 usbecm_pm_set_idle(ecmp);
375
376 if (ECM_DS_OP_VALID(ecm_ds_fini)) {
377 if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) {
378 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
379 "usbecm_detach: deinitialize DS fail!");
380
381 return (DDI_FAILURE);
382 }
383 }
384
385 if (usbecm_mac_fini(ecmp) != 0) {
386
387 return (DDI_FAILURE);
388 }
389
390 USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
391 "usbecm_detach: exit");
392
393 usbecm_cleanup(ecmp);
394 ddi_soft_state_free(usbecm_statep, instance);
395
396 return (DDI_SUCCESS);
397 }
398
399
400 /*
401 * Mac Call Back functions
402 */
403
404 /*
405 * Read device statistic information.
406 */
407 static int
usbecm_m_stat(void * arg,uint_t stat,uint64_t * val)408 usbecm_m_stat(void *arg, uint_t stat, uint64_t *val)
409 {
410 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
411 uint32_t stats;
412 int rval;
413 uint32_t fs;
414
415 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
416 "usbecm_m_stat: entry, stat=%d", stat);
417
418 /*
419 * Some of the stats are MII specific. We try to
420 * resolve all the statistics we understand. If
421 * the usb device can't provide it, return ENOTSUP.
422 */
423 switch (stat) {
424 case MAC_STAT_IFSPEED:
425 /* return link speed */
426 mutex_enter(&ecmp->ecm_mutex);
427 if (ecmp->ecm_stat.es_downspeed) {
428 *val = ecmp->ecm_stat.es_downspeed;
429 } else {
430 *val = 10 * 1000000ull; /* set a default value */
431 }
432 mutex_exit(&ecmp->ecm_mutex);
433
434 return (0);
435 case ETHER_STAT_LINK_DUPLEX:
436 *val = LINK_DUPLEX_FULL;
437
438 return (0);
439
440 case ETHER_STAT_SQE_ERRORS:
441 *val = 0;
442
443 return (0);
444
445 /* Map MAC/Ether stats to ECM statistics */
446 case MAC_STAT_NORCVBUF:
447 fs = ECM_RCV_NO_BUFFER;
448
449 break;
450 case MAC_STAT_NOXMTBUF:
451 fs = ECM_XMIT_ERROR;
452
453 break;
454 case MAC_STAT_IERRORS:
455 fs = ECM_RCV_ERROR;
456
457 break;
458 case MAC_STAT_OERRORS:
459 fs = ECM_XMIT_ERROR;
460
461 break;
462 case MAC_STAT_RBYTES:
463 fs = ECM_DIRECTED_BYTES_RCV;
464
465 break;
466 case MAC_STAT_IPACKETS:
467 fs = ECM_RCV_OK; /* frames */
468
469 break;
470 case MAC_STAT_OBYTES:
471 fs = ECM_DIRECTED_BYTES_XMIT;
472
473 break;
474 case MAC_STAT_OPACKETS:
475 fs = ECM_XMIT_OK; /* frames */
476
477 break;
478 case MAC_STAT_MULTIRCV:
479 fs = ECM_MULTICAST_FRAMES_RCV;
480
481 break;
482 case MAC_STAT_BRDCSTRCV:
483 fs = ECM_BROADCAST_FRAMES_RCV;
484
485 break;
486 case MAC_STAT_MULTIXMT:
487 fs = ECM_MULTICAST_FRAMES_XMIT;
488
489 break;
490 case MAC_STAT_BRDCSTXMT:
491 fs = ECM_BROADCAST_FRAMES_XMIT;
492
493 break;
494 case MAC_STAT_COLLISIONS:
495 fs = ECM_XMIT_MAX_COLLISIONS;
496
497 break;
498 case MAC_STAT_OVERFLOWS:
499 fs = ECM_RCV_OVERRUN;
500
501 break;
502 case MAC_STAT_UNDERFLOWS:
503 fs = ECM_XMIT_UNDERRUN;
504
505 break;
506 case ETHER_STAT_FCS_ERRORS:
507 fs = ECM_RCV_CRC_ERROR;
508
509 break;
510 case ETHER_STAT_ALIGN_ERRORS:
511 fs = ECM_RCV_ERROR_ALIGNMENT;
512
513 break;
514 case ETHER_STAT_DEFER_XMTS:
515 fs = ECM_XMIT_DEFERRED;
516
517 break;
518 case ETHER_STAT_FIRST_COLLISIONS:
519 fs = ECM_XMIT_ONE_COLLISION;
520
521 break;
522 case ETHER_STAT_MULTI_COLLISIONS:
523 fs = ECM_XMIT_MORE_COLLISIONS;
524
525 break;
526 case ETHER_STAT_TX_LATE_COLLISIONS:
527 fs = ECM_XMIT_LATE_COLLISIONS;
528
529 break;
530
531 default:
532 return (ENOTSUP);
533 }
534
535 /*
536 * we need to access device to get required stats,
537 * so check device state first
538 */
539 mutex_enter(&ecmp->ecm_mutex);
540 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
541 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
542 "usbecm_m_stat: device not ONLINE");
543
544 mutex_exit(&ecmp->ecm_mutex);
545
546 return (EIO);
547 }
548 mutex_exit(&ecmp->ecm_mutex);
549
550 rval = usbecm_get_statistics(ecmp,
551 ECM_STAT_SELECTOR(fs), &stats);
552 if (rval != USB_SUCCESS) {
553 mutex_enter(&ecmp->ecm_mutex);
554 switch (stat) {
555 case MAC_STAT_IERRORS:
556 *val = ecmp->ecm_stat.es_ierrors;
557
558 break;
559 case MAC_STAT_OERRORS:
560 *val = ecmp->ecm_stat.es_oerrors;
561
562 break;
563 case MAC_STAT_RBYTES:
564 *val = ecmp->ecm_stat.es_ibytes;
565
566 break;
567 case MAC_STAT_IPACKETS:
568 *val = ecmp->ecm_stat.es_ipackets;
569
570 break;
571 case MAC_STAT_OBYTES:
572 *val = ecmp->ecm_stat.es_obytes;
573
574 break;
575 case MAC_STAT_OPACKETS:
576 *val = ecmp->ecm_stat.es_opackets;
577
578 break;
579 case MAC_STAT_MULTIRCV:
580 *val = ecmp->ecm_stat.es_multircv;
581
582 break;
583 case MAC_STAT_MULTIXMT:
584 *val = ecmp->ecm_stat.es_multixmt;
585
586 break;
587 case MAC_STAT_BRDCSTRCV:
588 *val = ecmp->ecm_stat.es_brdcstrcv;
589
590 break;
591 case MAC_STAT_BRDCSTXMT:
592 *val = ecmp->ecm_stat.es_brdcstxmt;
593
594 break;
595 case ETHER_STAT_MACXMT_ERRORS:
596 *val = ecmp->ecm_stat.es_macxmt_err;
597 break;
598 default:
599 *val = 0;
600
601 break;
602 }
603 mutex_exit(&ecmp->ecm_mutex);
604 } else {
605 *val = stats;
606 }
607
608 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
609 "usbecm_m_stat: end");
610
611 return (0);
612 }
613
614
615 /*
616 * Start the device:
617 * - Set proper altsettings of the data interface
618 * - Open status and data endpoints
619 * - Start status polling
620 * - Get bulk-in ep ready to receive data from ethernet
621 *
622 * Concurrency: Presumably fully concurrent, must lock.
623 */
624 static int
usbecm_m_start(void * arg)625 usbecm_m_start(void *arg)
626 {
627 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
628 int rval;
629
630 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
631 "usbecm_m_start: entry");
632
633 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
634 mutex_enter(&ecmp->ecm_mutex);
635 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
636 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
637 "usbecm_m_start: device not online");
638 rval = ENODEV;
639 mutex_exit(&ecmp->ecm_mutex);
640
641 goto fail;
642 }
643 mutex_exit(&ecmp->ecm_mutex);
644
645 if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
646 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
647 "usbecm_m_start: open pipes fail");
648 rval = EIO;
649
650 goto fail;
651 }
652
653 mutex_enter(&ecmp->ecm_mutex);
654 if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
655 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
656 "usbecm_m_start: fail to start_rx");
657 mutex_exit(&ecmp->ecm_mutex);
658 rval = EIO;
659
660 goto fail;
661 }
662 ecmp->ecm_mac_state = USBECM_MAC_STARTED;
663 mutex_exit(&ecmp->ecm_mutex);
664
665 /* set the device to receive all multicast/broadcast pkts */
666 rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
667 CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST |
668 CDC_ECM_PKT_TYPE_BCAST, NULL);
669 if (rval != USB_SUCCESS) {
670 USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
671 "usbecm_m_start: set packet filters fail,"
672 " rval=%d, continue", rval);
673 }
674
675 if (ECM_DS_OP_VALID(ecm_ds_start)) {
676 if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) {
677 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
678 "usbecm_m_start: Can't start hardware");
679
680 goto fail;
681 }
682 }
683
684 usb_release_access(ecmp->ecm_ser_acc);
685
686 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
687 "usbecm_m_start: end");
688
689 /*
690 * To mark the link as RUNNING.
691 *
692 * ECM spec doesn't provide a way for host to get the status
693 * of the physical link initiatively. Only the device can
694 * report the link state through interrupt endpoints.
695 */
696 mac_link_update(ecmp->ecm_mh, LINK_STATE_UP);
697 mutex_enter(&ecmp->ecm_mutex);
698 ecmp->ecm_stat.es_linkstate = LINK_STATE_UP;
699 mutex_exit(&ecmp->ecm_mutex);
700
701 return (DDI_SUCCESS);
702 fail:
703 usb_release_access(ecmp->ecm_ser_acc);
704
705 return (rval);
706 }
707
708 /*
709 * Stop the device.
710 */
711 static void
usbecm_m_stop(void * arg)712 usbecm_m_stop(void *arg)
713 {
714 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
715
716 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
717 "usbecm_m_stop: entry");
718
719 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
720 if (ECM_DS_OP_VALID(ecm_ds_stop)) {
721 if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) {
722 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
723 "usbecm_m_stop: fail to stop hardware");
724 }
725 }
726
727 usbecm_close_pipes(ecmp);
728 usb_release_access(ecmp->ecm_ser_acc);
729
730 mutex_enter(&ecmp->ecm_mutex);
731 ecmp->ecm_mac_state = USBECM_MAC_STOPPED;
732 mutex_exit(&ecmp->ecm_mutex);
733
734 mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
735 mutex_enter(&ecmp->ecm_mutex);
736 ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
737 mutex_exit(&ecmp->ecm_mutex);
738
739 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
740 "usbecm_m_stop: end");
741 }
742
743 /*
744 * Change the MAC address of the device.
745 */
746 /*ARGSUSED*/
747 static int
usbecm_m_unicst(void * arg,const uint8_t * macaddr)748 usbecm_m_unicst(void *arg, const uint8_t *macaddr)
749 {
750 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
751 uint16_t filter;
752 int rval;
753
754 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
755 "usbecm_m_unicst: entry");
756
757 /*
758 * The device doesn't support to set a different MAC addr.
759 * Hence, it's not necessary to stop the device first if
760 * the mac addresses are identical. And we just set unicast
761 * filter only.
762 */
763 if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) {
764 USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
765 "usbecm_m_unicst: not supported to set a"
766 " different MAC addr");
767
768 return (DDI_FAILURE);
769 }
770 mutex_enter(&ecmp->ecm_mutex);
771 filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED;
772 mutex_exit(&ecmp->ecm_mutex);
773
774 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
775 rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
776 filter, NULL);
777 usb_release_access(ecmp->ecm_ser_acc);
778
779 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
780 "usbecm_m_unicst: rval = %d", rval);
781
782 /* some devices may not support this request, we just return success */
783 return (DDI_SUCCESS);
784 }
785
786 /*
787 * Enable/disable multicast.
788 */
789 /*ARGSUSED*/
790 static int
usbecm_m_multicst(void * arg,boolean_t add,const uint8_t * m)791 usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m)
792 {
793 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
794 uint16_t filter;
795 int rval = 0;
796
797 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
798 "usbecm_m_multicst: entry");
799 mutex_enter(&ecmp->ecm_mutex);
800
801 /*
802 * To simplify the implementation, we support switching
803 * all multicast on/off feature only
804 */
805 if (add == B_TRUE) {
806 ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST;
807 } else {
808 ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST;
809 }
810 filter = ecmp->ecm_pkt_flt;
811 mutex_exit(&ecmp->ecm_mutex);
812
813 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
814 if (ecmp->ecm_compatibility &&
815 (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) {
816 /* Device supports SetEthernetMulticastFilters request */
817 rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
818 filter, NULL);
819 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
820 "usbecm_m_multicst: rval = %d", rval);
821 }
822 usb_release_access(ecmp->ecm_ser_acc);
823
824 /* some devices may not support this request, we just return success */
825 return (DDI_SUCCESS);
826 }
827
828 /*
829 * Enable/disable promiscuous mode.
830 */
831 static int
usbecm_m_promisc(void * arg,boolean_t on)832 usbecm_m_promisc(void *arg, boolean_t on)
833 {
834 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
835 uint16_t filter;
836 int rval;
837
838 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
839 "usbecm_m_promisc: entry");
840
841 mutex_enter(&ecmp->ecm_mutex);
842 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
843 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
844 "usbecm_m_promisc: device not ONLINE");
845 mutex_exit(&ecmp->ecm_mutex);
846
847 return (DDI_FAILURE);
848 }
849
850
851 if (on == B_TRUE) {
852 ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC;
853 } else {
854 ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC;
855 }
856 filter = ecmp->ecm_pkt_flt;
857 mutex_exit(&ecmp->ecm_mutex);
858
859 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
860 rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
861 filter, NULL);
862 usb_release_access(ecmp->ecm_ser_acc);
863
864 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
865 "usbecm_m_promisc: rval=%d", rval);
866
867 /*
868 * devices may not support this request, we just
869 * return success to let upper layer to do further
870 * operation.
871 */
872 return (DDI_SUCCESS);
873 }
874
875 /*
876 * IOCTL request: Does not do anything. Will be enhanced
877 * in future.
878 */
879 static void
usbecm_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)880 usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
881 {
882 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
883 struct iocblk *iocp;
884 int cmd;
885
886 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
887 "usbecm_m_ioctl: entry");
888
889 mutex_enter(&ecmp->ecm_mutex);
890 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
891 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
892 "usbecm_m_ioctl: device not ONLINE");
893 mutex_exit(&ecmp->ecm_mutex);
894
895 miocnak(wq, mp, 0, EIO);
896
897 return;
898 }
899 mutex_exit(&ecmp->ecm_mutex);
900
901 iocp = (void *)mp->b_rptr;
902 iocp->ioc_error = 0;
903 cmd = iocp->ioc_cmd;
904
905 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
906
907 switch (cmd) {
908 default:
909 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
910 "unknown cmd 0x%x", cmd);
911 usb_release_access(ecmp->ecm_ser_acc);
912 miocnak(wq, mp, 0, EINVAL);
913
914 return;
915 }
916 }
917
918 /*
919 * callback functions for get/set properties
920 * Does not do anything. Will be enhanced to
921 * support set/get properties in future.
922 */
923 /*ARGSUSED*/
924 static int
usbecm_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)925 usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
926 uint_t wldp_length, const void *wldp_buf)
927 {
928 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
929 int err = ENOTSUP;
930
931 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
932 "usbecm_m_setprop: entry");
933
934 return (err);
935 }
936
937 /*ARGSUSED*/
usbecm_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)938 static int usbecm_m_getprop(void *arg, const char *pr_name,
939 mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf)
940 {
941 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
942 int err = ENOTSUP;
943
944 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
945 "usbecm_m_getprop: entry");
946
947 mutex_enter(&ecmp->ecm_mutex);
948 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
949 mutex_exit(&ecmp->ecm_mutex);
950
951 return (EIO);
952 }
953 mutex_exit(&ecmp->ecm_mutex);
954
955 return (err);
956 }
957
958 /*
959 * Transmit a data frame.
960 */
961 static mblk_t *
usbecm_m_tx(void * arg,mblk_t * mp)962 usbecm_m_tx(void *arg, mblk_t *mp)
963 {
964 usbecm_state_t *ecmp = (usbecm_state_t *)arg;
965 mblk_t *next;
966 int count = 0;
967
968 ASSERT(mp != NULL);
969
970 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
971 "usbecm_m_tx: entry");
972
973 mutex_enter(&ecmp->ecm_mutex);
974 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
975 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
976 "usbecm_m_tx: device not ONLINE");
977 mutex_exit(&ecmp->ecm_mutex);
978
979 return (mp);
980 }
981 mutex_exit(&ecmp->ecm_mutex);
982
983 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
984
985 /*
986 * To make use of the device maximum capability,
987 * concatenate msg blocks in a msg to ETHERMAX length.
988 */
989 while (mp != NULL) {
990 next = mp->b_next;
991 mp->b_next = NULL;
992
993 if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) {
994 USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
995 "usbecm_m_tx: send data fail");
996
997 /* failure statistics */
998 mutex_enter(&ecmp->ecm_mutex);
999 ecmp->ecm_stat.es_oerrors++;
1000 mutex_exit(&ecmp->ecm_mutex);
1001
1002 mp->b_next = next;
1003
1004 break;
1005 }
1006
1007 /*
1008 * To make it simple, we count all packets, no matter
1009 * the device supports ethernet statistics or not.
1010 */
1011 mutex_enter(&ecmp->ecm_mutex);
1012 ecmp->ecm_stat.es_opackets++;
1013 ecmp->ecm_stat.es_obytes += MBLKL(mp);
1014 mutex_exit(&ecmp->ecm_mutex);
1015
1016 freemsg(mp); /* free this msg upon success */
1017
1018 mp = next;
1019 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1020 "usbecm_m_tx: %d msgs processed", ++count);
1021 }
1022
1023 usb_release_access(ecmp->ecm_ser_acc);
1024
1025 return (mp);
1026 }
1027
1028 /*
1029 * usbecm_bulkin_cb:
1030 * Bulk In regular and exeception callback;
1031 * USBA framework will call this callback
1032 * after deal with bulkin request.
1033 */
1034 /*ARGSUSED*/
1035 static void
usbecm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)1036 usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
1037 {
1038 usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
1039 mblk_t *data, *mp;
1040 int data_len;
1041 int max_pkt_size = ecmp->ecm_bulkin_sz;
1042
1043 data = req->bulk_data;
1044 data_len = (data) ? MBLKL(data) : 0;
1045
1046 ASSERT(data->b_cont == NULL);
1047
1048 mutex_enter(&ecmp->ecm_mutex);
1049
1050 USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
1051 "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state,
1052 data_len);
1053
1054 /*
1055 * may receive a zero length packet according
1056 * to USB short packet semantics
1057 */
1058 if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
1059 (req->bulk_completion_reason == USB_CR_OK)) {
1060 if (data_len) {
1061 if (ecmp->ecm_rcv_queue == NULL) {
1062 ecmp->ecm_rcv_queue = data;
1063 } else {
1064 if ((msgsize(ecmp->ecm_rcv_queue) + data_len)
1065 > ETHERMAX) {
1066 /*
1067 * Exceed the ethernet maximum length, we think
1068 * something is wrong with this frame and hence
1069 * free older data. Accept new data instead.
1070 */
1071 freemsg(ecmp->ecm_rcv_queue);
1072 ecmp->ecm_rcv_queue = data;
1073 } else {
1074 linkb(ecmp->ecm_rcv_queue, data);
1075 }
1076 }
1077 } else {
1078 /*
1079 * Do not put zero length packet to receive queue.
1080 * Otherwise, msgpullup will dupmsg() a zero length
1081 * mblk, which will cause memleaks.
1082 */
1083 freemsg(data);
1084 }
1085
1086 /*
1087 * ECM V1.2, section 3.3.1, a short(including zero length)
1088 * packet signifies end of frame. We can submit this frame
1089 * to upper layer now.
1090 */
1091 if ((data_len < max_pkt_size) &&
1092 (msgsize(ecmp->ecm_rcv_queue) > 0)) {
1093 mp = msgpullup(ecmp->ecm_rcv_queue, -1);
1094 freemsg(ecmp->ecm_rcv_queue);
1095 ecmp->ecm_rcv_queue = NULL;
1096
1097 ecmp->ecm_stat.es_ipackets++;
1098 ecmp->ecm_stat.es_ibytes += msgsize(mp);
1099 if (mp && (mp->b_rptr[0] & 0x01)) {
1100 if (bcmp(mp->b_rptr, usbecm_broadcast,
1101 ETHERADDRL) != 0) {
1102 ecmp->ecm_stat.es_multircv++;
1103 } else {
1104 ecmp->ecm_stat.es_brdcstrcv++;
1105 }
1106 }
1107
1108 if (mp) {
1109 mutex_exit(&ecmp->ecm_mutex);
1110 mac_rx(ecmp->ecm_mh, NULL, mp);
1111 mutex_enter(&ecmp->ecm_mutex);
1112 }
1113 }
1114
1115 /* prevent USBA from freeing data along with the request */
1116 req->bulk_data = NULL;
1117 } else if (req->bulk_completion_reason != USB_CR_OK) {
1118 ecmp->ecm_stat.es_ierrors++;
1119 }
1120 mutex_exit(&ecmp->ecm_mutex);
1121
1122 usb_free_bulk_req(req);
1123
1124 /* receive more */
1125 mutex_enter(&ecmp->ecm_mutex);
1126 if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) ||
1127 (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) &&
1128 (ecmp->ecm_dev_state == USB_DEV_ONLINE)) {
1129 if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
1130 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1131 "usbecm_bulkin_cb: restart rx fail "
1132 "ecmp_state = %d", ecmp->ecm_bulkin_state);
1133 }
1134 } else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) {
1135 ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
1136 }
1137 mutex_exit(&ecmp->ecm_mutex);
1138 }
1139
1140 /*
1141 * usbsecm_rx_start:
1142 * start data receipt
1143 */
1144 static int
usbecm_rx_start(usbecm_state_t * ecmp)1145 usbecm_rx_start(usbecm_state_t *ecmp)
1146 {
1147 usb_bulk_req_t *br;
1148 int rval = USB_FAILURE;
1149 int data_len;
1150
1151 ASSERT(mutex_owned(&ecmp->ecm_mutex));
1152
1153 DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz,
1154 int, ecmp->ecm_bulkin_sz);
1155
1156 ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY;
1157 data_len = ecmp->ecm_bulkin_sz;
1158
1159 mutex_exit(&ecmp->ecm_mutex);
1160 br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP);
1161 if (br == NULL) {
1162 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1163 "usbsecm_rx_start: allocate bulk request failed");
1164
1165 mutex_enter(&ecmp->ecm_mutex);
1166
1167 return (USB_FAILURE);
1168 }
1169 /* initialize bulk in request. */
1170 br->bulk_len = data_len;
1171 br->bulk_timeout = 0;
1172 br->bulk_cb = usbecm_bulkin_cb;
1173 br->bulk_exc_cb = usbecm_bulkin_cb;
1174 br->bulk_client_private = (usb_opaque_t)ecmp;
1175 br->bulk_attributes = USB_ATTRS_AUTOCLEARING
1176 | USB_ATTRS_SHORT_XFER_OK;
1177
1178 rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0);
1179 mutex_enter(&ecmp->ecm_mutex);
1180 if (rval != USB_SUCCESS) {
1181 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1182 "usbsecm_rx_start: bulk transfer failed %d", rval);
1183 usb_free_bulk_req(br);
1184 ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
1185 }
1186
1187 return (rval);
1188 }
1189
1190 /*
1191 * usbecm_bulkout_cb:
1192 * Bulk Out regular and exeception callback;
1193 * USBA framework will call this callback function
1194 * after deal with bulkout request.
1195 */
1196 /*ARGSUSED*/
1197 static void
usbecm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)1198 usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
1199 {
1200 usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
1201 int data_len;
1202 boolean_t need_update = B_FALSE;
1203
1204 data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
1205
1206 USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
1207 "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len,
1208 req->bulk_completion_reason);
1209
1210 mutex_enter(&ecmp->ecm_mutex);
1211 if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) {
1212 if (ecmp->ecm_tx_cnt == usbecm_tx_max) {
1213 need_update = B_TRUE;
1214 }
1215 ecmp->ecm_tx_cnt--;
1216 }
1217 mutex_exit(&ecmp->ecm_mutex);
1218
1219 if (req->bulk_completion_reason && (data_len > 0)) {
1220 mutex_enter(&ecmp->ecm_mutex);
1221 ecmp->ecm_stat.es_oerrors++;
1222 mutex_exit(&ecmp->ecm_mutex);
1223
1224 need_update = B_TRUE;
1225 }
1226
1227 /*
1228 * notify MAC layer to retransfer the failed packet
1229 * Or notity MAC that we have more buffer now.
1230 */
1231 if (need_update) {
1232 mac_tx_update(ecmp->ecm_mh);
1233 }
1234
1235 usb_free_bulk_req(req);
1236 }
1237
1238 static int
usbecm_send_data(usbecm_state_t * ecmp,mblk_t * data)1239 usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data)
1240 {
1241 usb_bulk_req_t *br;
1242 int rval = USB_FAILURE;
1243 int data_len = MBLKL(data);
1244 int max_pkt_size;
1245 mblk_t *new_data = NULL;
1246 int new_data_len = 0;
1247
1248 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1249 "usbecm_send_data: length = %d, total len=%d",
1250 data_len, (int)msgdsize(data));
1251
1252 mutex_enter(&ecmp->ecm_mutex);
1253 if (ecmp->ecm_tx_cnt >= usbecm_tx_max) {
1254 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1255 "usbecm_send_data: (%d) exceeds TX max queue length",
1256 ecmp->ecm_tx_cnt);
1257 mutex_exit(&ecmp->ecm_mutex);
1258
1259 return (USB_FAILURE);
1260 }
1261 mutex_exit(&ecmp->ecm_mutex);
1262
1263 data_len = msgsize(data);
1264 if (data_len > ETHERMAX) {
1265 mutex_enter(&ecmp->ecm_mutex);
1266 ecmp->ecm_stat.es_macxmt_err++;
1267 mutex_exit(&ecmp->ecm_mutex);
1268
1269 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1270 "usbecm_send_data: packet too long, %d", data_len);
1271
1272 return (USB_FAILURE);
1273 }
1274
1275 if (data_len < ETHERMIN) {
1276 mblk_t *tmp;
1277
1278 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1279 "usbecm_send_data: short packet, padding to ETHERMIN");
1280
1281 new_data_len = ETHERMIN;
1282 if ((new_data = allocb(new_data_len, 0)) == NULL) {
1283 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1284 "usbecm_send_data: fail to allocb");
1285
1286 return (USB_FAILURE);
1287 }
1288 bzero(new_data->b_wptr, new_data_len);
1289 for (tmp = data; tmp != NULL; tmp = tmp->b_cont) {
1290 bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp));
1291 new_data->b_wptr += MBLKL(tmp);
1292 }
1293
1294 new_data->b_wptr = new_data->b_rptr + new_data_len;
1295 }
1296
1297 br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
1298 if (br == NULL) {
1299 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1300 "usbecm_send_data: alloc req failed.");
1301
1302 return (USB_FAILURE);
1303 }
1304
1305 /* initialize the bulk out request */
1306 if (new_data) {
1307 br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */
1308 br->bulk_len = new_data_len;
1309 } else {
1310 br->bulk_data = msgpullup(data, -1); /* msg allocated! */
1311 br->bulk_len = data_len;
1312 }
1313
1314 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1315 "usbecm_send_data: bulk_len = %d", br->bulk_len);
1316
1317 br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
1318 br->bulk_cb = usbecm_bulkout_cb;
1319 br->bulk_exc_cb = usbecm_bulkout_cb;
1320 br->bulk_client_private = (usb_opaque_t)ecmp;
1321 br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
1322
1323 if (br->bulk_data != NULL) {
1324 if (br->bulk_data->b_rptr[0] & 0x01) {
1325 mutex_enter(&ecmp->ecm_mutex);
1326 if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast,
1327 ETHERADDRL) != 0) {
1328 ecmp->ecm_stat.es_multixmt++;
1329 } else {
1330 ecmp->ecm_stat.es_brdcstxmt++;
1331 }
1332 mutex_exit(&ecmp->ecm_mutex);
1333 }
1334 rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
1335 }
1336
1337 if (rval != USB_SUCCESS) {
1338 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1339 "usbecm_send_data: Send Data failed.");
1340
1341 /*
1342 * br->bulk_data should be freed because we allocated
1343 * it in this function.
1344 */
1345 usb_free_bulk_req(br);
1346
1347 } else {
1348 mutex_enter(&ecmp->ecm_mutex);
1349 ecmp->ecm_tx_cnt++;
1350 mutex_exit(&ecmp->ecm_mutex);
1351
1352 /*
1353 * ECM V1.2, section 3.3.1, a short(including zero length)
1354 * packet signifies end of frame. We should send a zero length
1355 * packet to device if the total data lenght is multiple of
1356 * bulkout endpoint's max packet size.
1357 */
1358 max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize;
1359 if ((data_len % max_pkt_size) == 0) {
1360 if ((rval = usbecm_send_zero_data(ecmp))
1361 != USB_SUCCESS) {
1362 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1363 "usbecm_send_data: fail to send padding");
1364 }
1365 }
1366 }
1367
1368 if (new_data) {
1369 freemsg(new_data);
1370 }
1371
1372 USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1373 "usbecm_send_data: len(%d) data sent, rval=%d",
1374 new_data_len ? new_data_len : data_len, rval);
1375
1376 return (rval);
1377 }
1378
1379 static int
usbecm_send_zero_data(usbecm_state_t * ecmp)1380 usbecm_send_zero_data(usbecm_state_t *ecmp)
1381 {
1382 usb_bulk_req_t *br;
1383 int rval = USB_FAILURE;
1384
1385 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1386 "usbecm_send_zero_data: entry");
1387
1388 br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
1389 if (br == NULL) {
1390 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1391 "usbecm_send_data: alloc req failed.");
1392
1393 return (USB_FAILURE);
1394 }
1395
1396 /* initialize the bulk out request */
1397 br->bulk_len = 0;
1398 br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
1399 br->bulk_cb = usbecm_bulkout_cb;
1400 br->bulk_exc_cb = usbecm_bulkout_cb;
1401 br->bulk_client_private = (usb_opaque_t)ecmp;
1402 br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
1403
1404 rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
1405
1406 if (rval != USB_SUCCESS) {
1407 USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
1408 "usbecm_send_zero_data: Send data failed, rval=%d",
1409 rval);
1410
1411 /*
1412 * br->bulk_data should be freed because we allocated
1413 * it in this function.
1414 */
1415 usb_free_bulk_req(br);
1416
1417 }
1418
1419 USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
1420 "usbecm_send_zero_data: end");
1421
1422 return (rval);
1423 }
1424
1425 /*
1426 * Loadable module configuration entry points
1427 */
1428
1429 /*
1430 * _init module entry point.
1431 *
1432 * Called when the module is being loaded into memory.
1433 */
1434 int
_init(void)1435 _init(void)
1436 {
1437 int err;
1438
1439 err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1);
1440
1441 if (err != DDI_SUCCESS)
1442 return (err);
1443
1444 mac_init_ops(&usbecm_devops, "usbecm");
1445 err = mod_install(&usbecm_ml);
1446
1447 if (err != DDI_SUCCESS) {
1448 mac_fini_ops(&usbecm_devops);
1449 ddi_soft_state_fini(&usbecm_statep);
1450 }
1451
1452 return (err);
1453 }
1454
1455 /*
1456 * _info module entry point.
1457 *
1458 * Called to obtain information about the module.
1459 */
1460 int
_info(struct modinfo * modinfop)1461 _info(struct modinfo *modinfop)
1462 {
1463 return (mod_info(&usbecm_ml, modinfop));
1464 }
1465
1466 /*
1467 * _fini module entry point.
1468 *
1469 * Called when the module is being unloaded.
1470 */
1471 int
_fini(void)1472 _fini(void)
1473 {
1474 int err;
1475
1476 err = mod_remove(&usbecm_ml);
1477 if (err == DDI_SUCCESS) {
1478 mac_fini_ops(&usbecm_devops);
1479 ddi_soft_state_fini(&usbecm_statep);
1480 }
1481
1482 return (err);
1483 }
1484
1485 /*
1486 * usbecm_pipe_start_polling:
1487 * start polling on the interrupt pipe
1488 */
1489 static void
usbecm_pipe_start_polling(usbecm_state_t * ecmp)1490 usbecm_pipe_start_polling(usbecm_state_t *ecmp)
1491 {
1492 usb_intr_req_t *intr;
1493 int rval;
1494
1495 USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
1496 "usbecm_pipe_start_polling: ");
1497
1498 if (ecmp->ecm_intr_ph == NULL) {
1499
1500 return;
1501 }
1502
1503 intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
1504
1505 /*
1506 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
1507 * called with SLEEP flag.
1508 */
1509 if (!intr) {
1510 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
1511 "usbecm_pipe_start_polling: alloc req failed.");
1512
1513 return;
1514 }
1515
1516 /* initialize the interrupt request. */
1517 intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
1518 USB_ATTRS_AUTOCLEARING;
1519 intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize;
1520 intr->intr_client_private = (usb_opaque_t)ecmp;
1521 intr->intr_cb = usbecm_intr_cb;
1522 intr->intr_exc_cb = usbecm_intr_ex_cb;
1523
1524 rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP);
1525
1526 mutex_enter(&ecmp->ecm_mutex);
1527 if (rval == USB_SUCCESS) {
1528 ecmp->ecm_intr_state = USBECM_PIPE_BUSY;
1529 } else {
1530 usb_free_intr_req(intr);
1531 ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
1532 USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
1533 "usbecm_pipe_start_polling: failed (%d)", rval);
1534 }
1535 mutex_exit(&ecmp->ecm_mutex);
1536
1537 USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
1538 "usbecm_pipe_start_polling: end, rval=%d", rval);
1539 }
1540
1541
1542 /*
1543 * usbsecm_intr_cb:
1544 * interrupt pipe normal callback
1545 */
1546 /*ARGSUSED*/
1547 static void
usbecm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)1548 usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
1549 {
1550 usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
1551 mblk_t *data = req->intr_data;
1552 int data_len;
1553
1554 data_len = (data) ? MBLKL(data) : 0;
1555
1556 DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len);
1557
1558 /* check data length */
1559 if (data_len < 8) {
1560 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1561 "usbsecm_intr_cb: %d packet too short", data_len);
1562 usb_free_intr_req(req);
1563
1564 return;
1565 }
1566 req->intr_data = NULL;
1567 usb_free_intr_req(req);
1568
1569 mutex_enter(&ecmp->ecm_mutex);
1570 /* parse interrupt data -- notifications */
1571 usbecm_parse_intr_data(ecmp, data);
1572 mutex_exit(&ecmp->ecm_mutex);
1573 }
1574
1575
1576 /*
1577 * usbsecm_intr_ex_cb:
1578 * interrupt pipe exception callback
1579 */
1580 /*ARGSUSED*/
1581 static void
usbecm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)1582 usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
1583 {
1584 usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
1585 usb_cr_t cr = req->intr_completion_reason;
1586
1587 DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state,
1588 (usb_cr_t), cr);
1589
1590 usb_free_intr_req(req);
1591
1592 /*
1593 * If completion reason isn't USB_CR_PIPE_CLOSING and
1594 * USB_CR_STOPPED_POLLING, restart polling.
1595 */
1596 if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
1597 mutex_enter(&ecmp->ecm_mutex);
1598
1599 if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
1600
1601 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1602 "usbsecm_intr_ex_cb: state = %d",
1603 ecmp->ecm_dev_state);
1604
1605 mutex_exit(&ecmp->ecm_mutex);
1606
1607 return;
1608 }
1609 mutex_exit(&ecmp->ecm_mutex);
1610
1611 usbecm_pipe_start_polling(ecmp);
1612 }
1613 }
1614
1615
1616 /*
1617 * usbsecm_parse_intr_data:
1618 * Parse data received from interrupt callback
1619 */
1620 static void
usbecm_parse_intr_data(usbecm_state_t * ecmp,mblk_t * data)1621 usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data)
1622 {
1623 uint8_t bmRequestType;
1624 uint8_t bNotification;
1625 uint16_t wValue;
1626 uint16_t wLength;
1627 int linkstate;
1628
1629 bmRequestType = data->b_rptr[0];
1630 bNotification = data->b_rptr[1];
1631 /*
1632 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
1633 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
1634 * mLength is 2. So we directly get the value from the byte.
1635 */
1636 wValue = data->b_rptr[2];
1637 wLength = data->b_rptr[6];
1638
1639 if (ecmp->ecm_compatibility) {
1640 if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
1641 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1642 "usbsecm_parse_intr_data: unknown request "
1643 "type - 0x%x", bmRequestType);
1644
1645 freemsg(data);
1646
1647 return;
1648 }
1649 } else {
1650 /* non-compatible device specific parsing */
1651 if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) {
1652 if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data)
1653 != USB_SUCCESS) {
1654 USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
1655 "usbsecm_parse_intr_data: unknown request"
1656 "type - 0x%x", bmRequestType);
1657 }
1658 }
1659 freemsg(data);
1660
1661 return;
1662 }
1663
1664 /*
1665 * Check the return value of compatible devices
1666 */
1667 switch (bNotification) {
1668 case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
1669 USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
1670 "usbsecm_parse_intr_data: %s network!",
1671 wValue ? "connected to" :"disconnected from");
1672
1673 linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN;
1674 if (ecmp->ecm_stat.es_linkstate == linkstate) {
1675 /* no changes to previous state */
1676 break;
1677 }
1678
1679 ecmp->ecm_stat.es_linkstate = linkstate;
1680 mutex_exit(&ecmp->ecm_mutex);
1681 mac_link_update(ecmp->ecm_mh, linkstate);
1682 mutex_enter(&ecmp->ecm_mutex);
1683
1684 break;
1685 case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
1686 USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
1687 "usbsecm_parse_intr_data: A response is a available.");
1688
1689 break;
1690 case USB_CDC_NOTIFICATION_SPEED_CHANGE:
1691 USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
1692 "usbsecm_parse_intr_data: speed change");
1693
1694 /* check the parameter's length. */
1695 if (wLength != 8) {
1696 USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
1697 "usbsecm_parse_intr_data: error data length.");
1698 } else {
1699 uint32_t us_rate, ds_rate;
1700 uint8_t *sp;
1701
1702 sp = &data->b_rptr[8];
1703 LE_TO_UINT32(sp, us_rate);
1704 sp = &data->b_rptr[12];
1705 LE_TO_UINT32(sp, ds_rate);
1706 ecmp->ecm_stat.es_upspeed = us_rate;
1707 ecmp->ecm_stat.es_downspeed = ds_rate;
1708 }
1709
1710 break;
1711 default:
1712 USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
1713 "usbsecm_parse_intr_data: unknown notification - 0x%x!",
1714 bNotification);
1715
1716 break;
1717 }
1718
1719 freemsg(data);
1720 }
1721
1722 /*
1723 * usbecm_restore_device_state:
1724 * restore device state after CPR resume or reconnect
1725 */
1726 static int
usbecm_restore_device_state(usbecm_state_t * ecmp)1727 usbecm_restore_device_state(usbecm_state_t *ecmp)
1728 {
1729 int state;
1730
1731 USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1732 "usbecm_restore_device_state: ");
1733
1734 mutex_enter(&ecmp->ecm_mutex);
1735 state = ecmp->ecm_dev_state;
1736 mutex_exit(&ecmp->ecm_mutex);
1737
1738 /* Check device status */
1739 if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
1740
1741 return (state);
1742 }
1743
1744 /* Check if we are talking to the same device */
1745 if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0,
1746 -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
1747 mutex_enter(&ecmp->ecm_mutex);
1748 state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
1749 mutex_exit(&ecmp->ecm_mutex);
1750
1751 return (state);
1752 }
1753
1754 if (state == USB_DEV_DISCONNECTED) {
1755 USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1756 "usbecm_restore_device_state: Device has been reconnected "
1757 "but data may have been lost");
1758 }
1759
1760 /* if MAC was started, restarted it */
1761 mutex_enter(&ecmp->ecm_mutex);
1762 if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) {
1763 USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1764 "usbecm_restore_device_state: MAC was started");
1765
1766 mutex_exit(&ecmp->ecm_mutex);
1767 /* Do the same operation as usbecm_m_start() does */
1768 if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
1769
1770 return (state);
1771 }
1772
1773 mutex_enter(&ecmp->ecm_mutex);
1774 if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
1775 mutex_exit(&ecmp->ecm_mutex);
1776
1777 return (state);
1778 }
1779 }
1780 mutex_exit(&ecmp->ecm_mutex);
1781
1782 /*
1783 * init device state
1784 */
1785 mutex_enter(&ecmp->ecm_mutex);
1786 state = ecmp->ecm_dev_state = USB_DEV_ONLINE;
1787 mutex_exit(&ecmp->ecm_mutex);
1788
1789 return (state);
1790 }
1791
1792 /*
1793 * usbecm_reconnect_event_cb:
1794 * called upon when the device is hotplugged back
1795 */
1796 /*ARGSUSED*/
1797 static int
usbecm_reconnect_event_cb(dev_info_t * dip)1798 usbecm_reconnect_event_cb(dev_info_t *dip)
1799 {
1800 usbecm_state_t *ecmp =
1801 (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
1802 ddi_get_instance(dip));
1803
1804 ASSERT(ecmp != NULL);
1805
1806 USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1807 "usbecm_reconnect_event_cb: entry");
1808
1809 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
1810
1811 mutex_enter(&ecmp->ecm_mutex);
1812 ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED);
1813
1814 mutex_exit(&ecmp->ecm_mutex);
1815
1816 if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) {
1817 usb_release_access(ecmp->ecm_ser_acc);
1818
1819 return (USB_FAILURE);
1820 }
1821
1822 usb_release_access(ecmp->ecm_ser_acc);
1823
1824 return (USB_SUCCESS);
1825 }
1826
1827
1828 /*
1829 * usbecm_disconnect_event_cb:
1830 * callback for disconnect events
1831 */
1832 /*ARGSUSED*/
1833 static int
usbecm_disconnect_event_cb(dev_info_t * dip)1834 usbecm_disconnect_event_cb(dev_info_t *dip)
1835 {
1836 usbecm_state_t *ecmp = (usbecm_state_t *)ddi_get_soft_state(
1837 usbecm_statep, ddi_get_instance(dip));
1838
1839 USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1840 "usbecm_disconnect_event_cb: entry");
1841
1842 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
1843
1844 mutex_enter(&ecmp->ecm_mutex);
1845 ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
1846 mutex_exit(&ecmp->ecm_mutex);
1847
1848 usbecm_close_pipes(ecmp);
1849
1850 usb_release_access(ecmp->ecm_ser_acc);
1851
1852 USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
1853 "usbecm_disconnect_event_cb: End");
1854
1855 return (USB_SUCCESS);
1856 }
1857
1858 /*
1859 * power management
1860 * ----------------
1861 *
1862 * usbecm_create_pm_components:
1863 * create PM components
1864 */
1865 static int
usbecm_create_pm_components(usbecm_state_t * ecmp)1866 usbecm_create_pm_components(usbecm_state_t *ecmp)
1867 {
1868 dev_info_t *dip = ecmp->ecm_dip;
1869 usbecm_pm_t *pm;
1870 uint_t pwr_states;
1871
1872 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
1873 "usbecm_create_pm_components: entry");
1874
1875 if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
1876 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
1877 "usbecm_create_pm_components: failed");
1878
1879 /* don't fail the attach process */
1880 return (USB_SUCCESS);
1881 }
1882
1883 pm = ecmp->ecm_pm =
1884 (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP);
1885
1886 pm->pm_pwr_states = (uint8_t)pwr_states;
1887 pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
1888 pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
1889 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
1890
1891 (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1892
1893 return (USB_SUCCESS);
1894 }
1895
1896 /*
1897 * usbecm_cleanup:
1898 * Release resources of current device during detach.
1899 */
1900 static void
usbecm_cleanup(usbecm_state_t * ecmp)1901 usbecm_cleanup(usbecm_state_t *ecmp)
1902 {
1903 USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
1904 "usbecm_cleanup: ");
1905
1906 if (ecmp == NULL) {
1907
1908 return;
1909 }
1910
1911 usbecm_close_pipes(ecmp);
1912
1913 /* unregister callback function */
1914 if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) {
1915 USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
1916 "usbecm_cleanup: unregister events");
1917
1918 usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events);
1919 }
1920
1921 /* destroy power management components */
1922 if (ecmp->ecm_pm != NULL) {
1923 USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
1924 "usbecm_cleanup: destroy pm");
1925 usbecm_destroy_pm_components(ecmp);
1926 }
1927
1928 /* free description of device tree. */
1929 if (ecmp->ecm_def_ph != NULL) {
1930 mutex_destroy(&ecmp->ecm_mutex);
1931
1932 usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data);
1933 ecmp->ecm_def_ph = NULL;
1934 }
1935
1936 if (ecmp->ecm_lh != NULL) {
1937 usb_free_log_hdl(ecmp->ecm_lh);
1938 ecmp->ecm_lh = NULL;
1939 }
1940
1941 /* detach client device */
1942 if (ecmp->ecm_dev_data != NULL) {
1943 usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data);
1944 }
1945
1946 if (ecmp->ecm_init_flags & USBECM_INIT_MAC) {
1947 (void) usbecm_mac_fini(ecmp);
1948 }
1949
1950 if (ecmp->ecm_init_flags & USBECM_INIT_SER) {
1951 usb_fini_serialization(ecmp->ecm_ser_acc);
1952 }
1953
1954 ddi_prop_remove_all(ecmp->ecm_dip);
1955 ddi_remove_minor_node(ecmp->ecm_dip, NULL);
1956 }
1957
1958 /*
1959 * usbecm_destroy_pm_components:
1960 * destroy PM components
1961 */
1962 static void
usbecm_destroy_pm_components(usbecm_state_t * ecmp)1963 usbecm_destroy_pm_components(usbecm_state_t *ecmp)
1964 {
1965 usbecm_pm_t *pm = ecmp->ecm_pm;
1966 dev_info_t *dip = ecmp->ecm_dip;
1967 int rval;
1968
1969 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
1970 "usbecm_destroy_pm_components: ");
1971
1972 if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) {
1973 if (pm->pm_wakeup_enabled) {
1974 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1975 if (rval != DDI_SUCCESS) {
1976 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
1977 "usbecm_destroy_pm_components: "
1978 "raising power failed (%d)", rval);
1979 }
1980
1981 rval = usb_handle_remote_wakeup(dip,
1982 USB_REMOTE_WAKEUP_DISABLE);
1983 if (rval != USB_SUCCESS) {
1984 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
1985 "usbecm_destroy_pm_components: "
1986 "disable remote wakeup failed (%d)", rval);
1987 }
1988 }
1989
1990 (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
1991 }
1992 kmem_free((caddr_t)pm, sizeof (usbecm_pm_t));
1993 ecmp->ecm_pm = NULL;
1994 }
1995
1996 /*
1997 * usbecm_pm_set_busy:
1998 * mark device busy and raise power
1999 */
2000 static void
usbecm_pm_set_busy(usbecm_state_t * ecmp)2001 usbecm_pm_set_busy(usbecm_state_t *ecmp)
2002 {
2003 usbecm_pm_t *pm = ecmp->ecm_pm;
2004 dev_info_t *dip = ecmp->ecm_dip;
2005 int rval;
2006
2007 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2008 "usbecm_pm_set_busy: pm = 0x%p", (void *)pm);
2009
2010 if (pm == NULL) {
2011
2012 return;
2013 }
2014
2015 mutex_enter(&ecmp->ecm_mutex);
2016 /* if already marked busy, just increment the counter */
2017 if (pm->pm_busy_cnt++ > 0) {
2018 mutex_exit(&ecmp->ecm_mutex);
2019
2020 return;
2021 }
2022
2023 (void) pm_busy_component(dip, 0);
2024
2025 if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
2026 mutex_exit(&ecmp->ecm_mutex);
2027
2028 return;
2029 }
2030
2031 /* need to raise power */
2032 pm->pm_raise_power = B_TRUE;
2033 mutex_exit(&ecmp->ecm_mutex);
2034
2035 rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
2036 if (rval != DDI_SUCCESS) {
2037 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2038 "usbecm_pm_set_busy: raising power failed");
2039 }
2040
2041 mutex_enter(&ecmp->ecm_mutex);
2042 pm->pm_raise_power = B_FALSE;
2043 mutex_exit(&ecmp->ecm_mutex);
2044 }
2045
2046
2047 /*
2048 * usbecm_pm_set_idle:
2049 * mark device idle
2050 */
2051 static void
usbecm_pm_set_idle(usbecm_state_t * ecmp)2052 usbecm_pm_set_idle(usbecm_state_t *ecmp)
2053 {
2054 usbecm_pm_t *pm = ecmp->ecm_pm;
2055 dev_info_t *dip = ecmp->ecm_dip;
2056
2057 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2058 "usbecm_pm_set_idle: ");
2059
2060 if (pm == NULL) {
2061
2062 return;
2063 }
2064
2065 mutex_enter(&ecmp->ecm_mutex);
2066 if (--pm->pm_busy_cnt > 0) {
2067 mutex_exit(&ecmp->ecm_mutex);
2068
2069 return;
2070 }
2071
2072 if (pm) {
2073 (void) pm_idle_component(dip, 0);
2074 }
2075 mutex_exit(&ecmp->ecm_mutex);
2076 }
2077
2078
2079 /*
2080 * usbecm_pwrlvl0:
2081 * Functions to handle power transition for OS levels 0 -> 3
2082 * The same level as OS state, different from USB state
2083 */
2084 static int
usbecm_pwrlvl0(usbecm_state_t * ecmp)2085 usbecm_pwrlvl0(usbecm_state_t *ecmp)
2086 {
2087 int rval;
2088
2089 ASSERT(mutex_owned(&ecmp->ecm_mutex));
2090
2091 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2092 "usbecm_pwrlvl0: ");
2093
2094 switch (ecmp->ecm_dev_state) {
2095 case USB_DEV_ONLINE:
2096 /* issue USB D3 command to the device */
2097 rval = usb_set_device_pwrlvl3(ecmp->ecm_dip);
2098 ASSERT(rval == USB_SUCCESS);
2099 if ((ecmp->ecm_intr_ph != NULL) &&
2100 (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) {
2101 mutex_exit(&ecmp->ecm_mutex);
2102 usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
2103 USB_FLAGS_SLEEP);
2104 mutex_enter(&ecmp->ecm_mutex);
2105
2106 ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
2107 }
2108 ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN;
2109 ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
2110
2111 /* FALLTHRU */
2112 case USB_DEV_DISCONNECTED:
2113 case USB_DEV_SUSPENDED:
2114 /* allow a disconnect/cpr'ed device to go to lower power */
2115
2116 return (USB_SUCCESS);
2117 case USB_DEV_PWRED_DOWN:
2118 default:
2119 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2120 "usbecm_pwrlvl0: illegal device state");
2121
2122 return (USB_FAILURE);
2123 }
2124 }
2125
2126
2127 /*
2128 * usbecm_pwrlvl1:
2129 * Functions to handle power transition for OS levels 1 -> 2
2130 */
2131 static int
usbecm_pwrlvl1(usbecm_state_t * ecmp)2132 usbecm_pwrlvl1(usbecm_state_t *ecmp)
2133 {
2134 /* issue USB D2 command to the device */
2135 (void) usb_set_device_pwrlvl2(ecmp->ecm_dip);
2136
2137 return (USB_FAILURE);
2138 }
2139
2140
2141 /*
2142 * usbecm_pwrlvl2:
2143 * Functions to handle power transition for OS levels 2 -> 1
2144 */
2145 static int
usbecm_pwrlvl2(usbecm_state_t * ecmp)2146 usbecm_pwrlvl2(usbecm_state_t *ecmp)
2147 {
2148 /* issue USB D1 command to the device */
2149 (void) usb_set_device_pwrlvl1(ecmp->ecm_dip);
2150
2151 return (USB_FAILURE);
2152 }
2153
2154
2155 /*
2156 * usbecm_pwrlvl3:
2157 * Functions to handle power transition for OS levels 3 -> 0
2158 * The same level as OS state, different from USB state
2159 */
2160 static int
usbecm_pwrlvl3(usbecm_state_t * ecmp)2161 usbecm_pwrlvl3(usbecm_state_t *ecmp)
2162 {
2163 int rval;
2164
2165 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2166 "usbecm_pwrlvl3: ");
2167
2168 ASSERT(mutex_owned(&ecmp->ecm_mutex));
2169
2170 switch (ecmp->ecm_dev_state) {
2171 case USB_DEV_PWRED_DOWN:
2172 /* Issue USB D0 command to the device here */
2173 rval = usb_set_device_pwrlvl0(ecmp->ecm_dip);
2174 ASSERT(rval == USB_SUCCESS);
2175
2176 if (ecmp->ecm_intr_ph != NULL &&
2177 ecmp->ecm_intr_state == USBECM_PIPE_IDLE) {
2178 mutex_exit(&ecmp->ecm_mutex);
2179 usbecm_pipe_start_polling(ecmp);
2180 mutex_enter(&ecmp->ecm_mutex);
2181 }
2182
2183 ecmp->ecm_dev_state = USB_DEV_ONLINE;
2184 ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
2185
2186 /* FALLTHRU */
2187 case USB_DEV_ONLINE:
2188 /* we are already in full power */
2189
2190 /* FALLTHRU */
2191 case USB_DEV_DISCONNECTED:
2192 case USB_DEV_SUSPENDED:
2193
2194 return (USB_SUCCESS);
2195 default:
2196 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2197 "usbecm_pwrlvl3: illegal device state");
2198
2199 return (USB_FAILURE);
2200 }
2201 }
2202
2203 /*ARGSUSED*/
2204 static int
usbecm_power(dev_info_t * dip,int comp,int level)2205 usbecm_power(dev_info_t *dip, int comp, int level)
2206 {
2207 usbecm_state_t *ecmp;
2208 usbecm_pm_t *pm;
2209 int rval = USB_SUCCESS;
2210
2211 ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip));
2212 pm = ecmp->ecm_pm;
2213
2214 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2215 "usbecm_power: entry");
2216
2217 /* check if pm is NULL */
2218 if (pm == NULL) {
2219 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2220 "usbecm_power: pm is NULL.");
2221
2222 return (USB_FAILURE);
2223 }
2224
2225 mutex_enter(&ecmp->ecm_mutex);
2226 /*
2227 * check if we are transitioning to a legal power level
2228 */
2229 if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
2230 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2231 "usbecm_power: "
2232 "illegal power level %d, pwr_states=%x",
2233 level, pm->pm_pwr_states);
2234 mutex_exit(&ecmp->ecm_mutex);
2235
2236 return (USB_FAILURE);
2237 }
2238
2239 /*
2240 * if we are about to raise power and asked to lower power, fail
2241 */
2242 if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
2243 USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
2244 "usbecm_power: wrong condition.");
2245 mutex_exit(&ecmp->ecm_mutex);
2246
2247 return (USB_FAILURE);
2248 }
2249
2250 /*
2251 * Set the power status of device by request level.
2252 */
2253 switch (level) {
2254 case USB_DEV_OS_PWR_OFF:
2255 rval = usbecm_pwrlvl0(ecmp);
2256
2257 break;
2258 case USB_DEV_OS_PWR_1:
2259 rval = usbecm_pwrlvl1(ecmp);
2260
2261 break;
2262 case USB_DEV_OS_PWR_2:
2263 rval = usbecm_pwrlvl2(ecmp);
2264
2265 break;
2266 case USB_DEV_OS_FULL_PWR:
2267 rval = usbecm_pwrlvl3(ecmp);
2268
2269 break;
2270 }
2271
2272 mutex_exit(&ecmp->ecm_mutex);
2273
2274 return (rval);
2275 }
2276
2277 /*
2278 * Register with the MAC layer.
2279 */
2280 static int
usbecm_mac_init(usbecm_state_t * ecmp)2281 usbecm_mac_init(usbecm_state_t *ecmp)
2282 {
2283 mac_register_t *macp;
2284 int err;
2285
2286 /*
2287 * Initialize mac structure
2288 */
2289 macp = mac_alloc(MAC_VERSION);
2290 if (macp == NULL) {
2291 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2292 "failed to allocate MAC structure");
2293
2294 return (USB_FAILURE);
2295 }
2296
2297 /*
2298 * Initialize pointer to device specific functions
2299 */
2300 macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
2301 macp->m_driver = ecmp;
2302 macp->m_dip = ecmp->ecm_dip;
2303
2304 macp->m_src_addr = ecmp->ecm_srcaddr;
2305 macp->m_callbacks = &usbecm_m_callbacks;
2306 macp->m_min_sdu = 0;
2307 macp->m_max_sdu = ETHERMTU;
2308
2309 /*
2310 * Register the macp to mac
2311 */
2312 err = mac_register(macp, &ecmp->ecm_mh);
2313 mac_free(macp);
2314
2315 if (err != DDI_SUCCESS) {
2316 USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
2317 "failed to register MAC structure");
2318
2319 return (USB_FAILURE);
2320 }
2321
2322 mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
2323 ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
2324 ecmp->ecm_tx_cnt = 0;
2325
2326 return (USB_SUCCESS);
2327 }
2328
2329 static int
usbecm_mac_fini(usbecm_state_t * ecmp)2330 usbecm_mac_fini(usbecm_state_t *ecmp)
2331 {
2332 int rval = DDI_SUCCESS;
2333
2334 if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) {
2335 return (DDI_SUCCESS);
2336 }
2337
2338 ecmp->ecm_init_flags &= ~USBECM_INIT_MAC;
2339 if ((rval = mac_disable(ecmp->ecm_mh)) != 0) {
2340 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2341 "failed to disable MAC");
2342
2343 return (rval);
2344 }
2345
2346 (void) mac_unregister(ecmp->ecm_mh);
2347
2348 return (rval);
2349 }
2350
2351 static int
usbecm_resume(usbecm_state_t * ecmp)2352 usbecm_resume(usbecm_state_t *ecmp)
2353 {
2354 int current_state;
2355 int ret;
2356
2357 USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
2358 "usbecm_resume: ");
2359
2360 mutex_enter(&ecmp->ecm_mutex);
2361 current_state = ecmp->ecm_dev_state;
2362 mutex_exit(&ecmp->ecm_mutex);
2363
2364 /* restore the status of device */
2365 if (current_state != USB_DEV_ONLINE) {
2366 ret = usbecm_restore_device_state(ecmp);
2367 } else {
2368 ret = USB_DEV_ONLINE;
2369 }
2370
2371 return (ret);
2372 }
2373
2374 static int
usbecm_suspend(usbecm_state_t * ecmp)2375 usbecm_suspend(usbecm_state_t *ecmp)
2376 {
2377 (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
2378
2379 mutex_enter(&ecmp->ecm_mutex);
2380 ecmp->ecm_dev_state = USB_DEV_SUSPENDED;
2381 mutex_exit(&ecmp->ecm_mutex);
2382
2383 usbecm_close_pipes(ecmp);
2384
2385 usb_release_access(ecmp->ecm_ser_acc);
2386
2387 return (0);
2388 }
2389
2390 /*
2391 * Translate MAC address from string to 6 bytes array int value
2392 * Can't use ether_aton() since it requires format of x:x:x:x:x:x
2393 */
2394 void
label_to_mac(char * hex,unsigned char * mac)2395 label_to_mac(char *hex, unsigned char *mac)
2396 {
2397 int i;
2398 char c;
2399
2400 /* can only count 6 bytes! */
2401 for (i = 0; i < 6; i++) {
2402 /* upper 4 bits */
2403 if (!isdigit(hex[2*i])) {
2404 c = (toupper(hex[2 * i]) - 'A' + 10);
2405 } else {
2406 c = (hex[2 * i] - '0');
2407 }
2408 mac[i] = c * 16;
2409
2410 /* lower 4 bits */
2411 if (!isdigit(hex[2*i + 1])) {
2412 c = (toupper(hex[2 * i + 1]) - 'A' + 10);
2413 } else {
2414 c = hex[2 * i + 1] - '0';
2415 }
2416 mac[i] += c;
2417 }
2418 }
2419
2420 /*
2421 * usbecm_get_descriptors:
2422 * parse functional descriptors of ecm compatible device
2423 */
2424 static int
usbecm_get_descriptors(usbecm_state_t * ecmp)2425 usbecm_get_descriptors(usbecm_state_t *ecmp)
2426 {
2427 int i;
2428 usb_cfg_data_t *cfg;
2429 usb_alt_if_data_t *altif;
2430 usb_cvs_data_t *cvs;
2431 int16_t master_if = -1, slave_if = -1;
2432 usb_cdc_ecm_descr_t ecm_desc;
2433 usb_ep_data_t *ep_data;
2434 usb_dev_descr_t *usb_dev_desc;
2435
2436 USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
2437 "usbecm_get_descriptors: ");
2438
2439 usb_dev_desc = ecmp->ecm_dev_data->dev_descr;
2440
2441 /*
2442 * Special treatment of Sun's SP Ethernet device.
2443 */
2444 if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) &&
2445 (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) {
2446 if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index,
2447 USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) {
2448 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2449 "usbecm_get_descriptors: fail to set cfg ");
2450 } else {
2451 usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data);
2452 if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
2453 USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
2454 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2455 "usbecm_get_descriptors: fail to get"
2456 " dev_data");
2457
2458 return (USB_FAILURE);
2459 }
2460 }
2461 }
2462
2463 cfg = ecmp->ecm_dev_data->dev_curr_cfg;
2464
2465 /* set default control and data interface */
2466 ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0;
2467
2468 /* get current interfaces */
2469 ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if;
2470 if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) {
2471 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2472 "usbecm_get_descriptors: elements in if_alt is %d",
2473 cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
2474
2475 return (USB_FAILURE);
2476 }
2477
2478 altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0];
2479
2480 /*
2481 * Based on CDC specification, ECM devices usually include the
2482 * following function descriptors: Header, Union and ECM
2483 * Contry Selection function descriptors. This loop search tree data
2484 * structure for each ecm class descriptor.
2485 */
2486 for (i = 0; i < altif->altif_n_cvs; i++) {
2487 cvs = &altif->altif_cvs[i];
2488
2489 if ((cvs->cvs_buf == NULL) ||
2490 (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
2491 continue;
2492 }
2493
2494 switch (cvs->cvs_buf[2]) {
2495 case USB_CDC_DESCR_TYPE_HEADER:
2496 /*
2497 * parse header functional descriptor
2498 * Just to check integrity.
2499 */
2500 if (cvs->cvs_buf_len != 5) {
2501 return (USB_FAILURE);
2502 }
2503 break;
2504 case USB_CDC_DESCR_TYPE_ETHERNET:
2505 /* parse ECM functional descriptor */
2506 if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) {
2507 char buf[USB_MAXSTRINGLEN];
2508
2509 if (usb_parse_data("4cl2sc", cvs->cvs_buf,
2510 cvs->cvs_buf_len, (void *)&ecm_desc,
2511 (size_t)USB_CDC_ECM_LEN) <
2512 USB_CDC_ECM_LEN) {
2513
2514 return (USB_FAILURE);
2515 }
2516
2517 /* get the MAC address */
2518 if (usb_get_string_descr(ecmp->ecm_dip,
2519 USB_LANG_ID, ecm_desc.iMACAddress, buf,
2520 USB_MAXSTRINGLEN) != USB_SUCCESS) {
2521
2522 return (USB_FAILURE);
2523 }
2524
2525 USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
2526 "usbecm_get_descriptors: macaddr=%s ",
2527 buf);
2528
2529 /* expects 12 characters */
2530 if (strlen(buf) < 12) {
2531 return (USB_FAILURE);
2532 }
2533 label_to_mac(buf, ecmp->ecm_srcaddr);
2534
2535 bcopy(&ecm_desc, &ecmp->ecm_desc,
2536 USB_CDC_ECM_LEN);
2537 }
2538 break;
2539 case USB_CDC_DESCR_TYPE_UNION:
2540 /* parse Union functional descriptor. */
2541 if (cvs->cvs_buf_len >= 5) {
2542 master_if = cvs->cvs_buf[3];
2543 slave_if = cvs->cvs_buf[4];
2544 }
2545 break;
2546 default:
2547 break;
2548 }
2549 }
2550
2551 /* For usb ecm devices, it must satisfy the following options. */
2552 if (cfg->cfg_n_if < 2) {
2553 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2554 "usbecm_get_descriptors: # of interfaces %d < 2",
2555 cfg->cfg_n_if);
2556
2557 return (USB_FAILURE);
2558 }
2559
2560 if (ecmp->ecm_data_if_no == 0 &&
2561 slave_if != ecmp->ecm_data_if_no) {
2562 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2563 "usbecm_get_descriptors: Device has no call management "
2564 "descriptor and use Union Descriptor.");
2565
2566 ecmp->ecm_data_if_no = slave_if;
2567 }
2568
2569 if ((master_if != ecmp->ecm_ctrl_if_no) ||
2570 (slave_if != ecmp->ecm_data_if_no)) {
2571 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2572 "usbecm_get_descriptors: control interface or "
2573 "data interface don't match.");
2574
2575 return (USB_FAILURE);
2576 }
2577
2578 if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) ||
2579 (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) {
2580 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2581 "usbecm_get_descriptors: control interface %d or "
2582 "data interface %d out of range.",
2583 ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no);
2584
2585 return (USB_FAILURE);
2586 }
2587
2588 /* ECM data interface has a minimal of two altsettings */
2589 if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) {
2590 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2591 "usbecm_get_descriptors: elements in if_alt is %d,"
2592 " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
2593
2594 return (USB_FAILURE);
2595 }
2596
2597 /* control interface must have interrupt endpoint */
2598 if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
2599 ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
2600 USB_EP_DIR_IN)) == NULL) {
2601 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2602 "usbecm_get_descriptors: "
2603 "ctrl interface %d has no interrupt endpoint",
2604 ecmp->ecm_data_if_no);
2605
2606 return (USB_FAILURE);
2607 }
2608 ecmp->ecm_intr_ep = ep_data;
2609
2610 /* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */
2611 if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
2612 ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
2613 USB_EP_DIR_IN)) == NULL) {
2614 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2615 "usbecm_get_descriptors: "
2616 "data interface %d has no bulk in endpoint",
2617 ecmp->ecm_data_if_no);
2618
2619 return (USB_FAILURE);
2620 }
2621 ecmp->ecm_bulk_in_ep = ep_data;
2622
2623 if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
2624 ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
2625 USB_EP_DIR_OUT)) == NULL) {
2626 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2627 "usbecm_get_descriptors: "
2628 "data interface %d has no bulk out endpoint",
2629 ecmp->ecm_data_if_no);
2630
2631 return (USB_FAILURE);
2632 }
2633 ecmp->ecm_bulk_out_ep = ep_data;
2634
2635 /* set default value for ethernet packet filter */
2636 ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED;
2637
2638 return (USB_SUCCESS);
2639 }
2640
2641 /* Generate IEEE802 style MAC address */
2642 static void
generate_ether_addr(uint8_t * mac_addr)2643 generate_ether_addr(uint8_t *mac_addr)
2644 {
2645 (void) random_get_bytes(mac_addr, 6);
2646 mac_addr [0] &= 0xfe; /* unicast only */
2647 mac_addr [0] |= 0x02; /* set locally administered bit */
2648 }
2649
2650 /*
2651 * Find a pair of bulk In/Out endpoints
2652 */
usbecm_find_bulk_in_out_eps(usbecm_state_t * ecmp,uint16_t ifc,usb_if_data_t * intf)2653 int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp,
2654 uint16_t ifc, usb_if_data_t *intf)
2655 {
2656 uint16_t alt, alt_num;
2657 usb_ep_data_t *intr_ep = NULL;
2658 usb_ep_data_t *bulk_in, *bulk_out, *ep;
2659
2660 alt_num = intf->if_n_alt;
2661
2662 /*
2663 * for the non-compatible devices, to make it simple, we
2664 * suppose the devices have this kind of configuration:
2665 * INTR In EP(if exists) + BULK In + Bulk Out in the
2666 * same altsetting of the same interface
2667 */
2668 for (alt = 0; alt < alt_num; alt++) {
2669 /* search pair of bulk in/out EPs */
2670 if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip,
2671 ecmp->ecm_dev_data, ifc, alt, 0,
2672 USB_EP_ATTR_BULK,
2673 USB_EP_DIR_IN)) == NULL) ||
2674 (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip,
2675 ecmp->ecm_dev_data, ifc, alt, 0,
2676 USB_EP_ATTR_BULK,
2677 USB_EP_DIR_OUT)) == NULL) {
2678
2679 continue;
2680 }
2681
2682 /*
2683 * search interrupt pipe.
2684 */
2685 if ((ep = usb_lookup_ep_data(ecmp->ecm_dip,
2686 ecmp->ecm_dev_data, ifc, alt, 0,
2687 USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
2688 intr_ep = ep;
2689 }
2690
2691
2692 ecmp->ecm_data_if_no = ifc;
2693 ecmp->ecm_data_if_alt = alt;
2694 ecmp->ecm_intr_ep = intr_ep;
2695 ecmp->ecm_ctrl_if_no = ifc;
2696 ecmp->ecm_bulk_in_ep = bulk_in;
2697 ecmp->ecm_bulk_out_ep = bulk_out;
2698
2699 return (USB_SUCCESS);
2700 }
2701
2702 return (USB_FAILURE);
2703 }
2704
2705 static int
usbecm_init_non_compatible_device(usbecm_state_t * ecmp)2706 usbecm_init_non_compatible_device(usbecm_state_t *ecmp)
2707 {
2708 usb_if_data_t *cur_if;
2709 uint16_t if_num, i;
2710
2711 /*
2712 * If device don't conform to spec, search pairs of bulk in/out
2713 * endpoints and fill related structure. We suppose this driver
2714 * is bound to a interface.
2715 */
2716 cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if;
2717 if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if;
2718
2719 /* search each interface which have bulk in and out */
2720 for (i = 0; i < if_num; i++) {
2721 if (usbecm_find_bulk_in_out_eps(ecmp, i,
2722 cur_if) == USB_SUCCESS) {
2723
2724 break;
2725 }
2726 cur_if++;
2727 }
2728
2729 USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
2730 "usbecm_init_non_compatible_device: ctrl_if=%d,"
2731 " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no,
2732 ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt);
2733
2734 return (USB_SUCCESS);
2735 }
2736
2737 static boolean_t
usbecm_is_compatible(usbecm_state_t * ecmp)2738 usbecm_is_compatible(usbecm_state_t *ecmp)
2739 {
2740 usb_cfg_data_t *cfg_data;
2741 usb_if_data_t *intf;
2742 usb_alt_if_data_t *alt;
2743 int alt_num, if_num, cfg_num;
2744 int i, j, cfg_index;
2745
2746 cfg_num = ecmp->ecm_dev_data->dev_n_cfg;
2747 USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
2748 "usbecm_is_compatible: entry, cfg_num=%d", cfg_num);
2749
2750 for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) {
2751 cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]);
2752
2753 USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
2754 "usbecm_is_compatible: cfg_index=%d, value=%d",
2755 cfg_index, cfg_data->cfg_descr.bConfigurationValue);
2756
2757 intf = cfg_data->cfg_if;
2758 if_num = cfg_data->cfg_n_if;
2759
2760 for (i = 0; i < if_num; i++) {
2761 alt_num = intf->if_n_alt;
2762 for (j = 0; j < alt_num; j++) {
2763 alt = &intf->if_alt[j];
2764 if ((alt->altif_descr.bInterfaceClass == 0x02) &&
2765 (alt->altif_descr.bInterfaceSubClass == 0x06)) {
2766 ecmp->ecm_cfg_index = cfg_index;
2767
2768 USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
2769 "usbecm_is_compatible: cfg_index=%d",
2770 cfg_index);
2771
2772 return (B_TRUE);
2773 }
2774 }
2775 intf++;
2776 }
2777 }
2778
2779 return (B_FALSE);
2780 }
2781
2782
2783 static int
usbecm_usb_init(usbecm_state_t * ecmp)2784 usbecm_usb_init(usbecm_state_t *ecmp)
2785 {
2786
2787 if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) !=
2788 USB_SUCCESS) {
2789 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2790 "usbecm_usb_init: fail to attach");
2791
2792 return (USB_FAILURE);
2793 }
2794
2795 /* Get the configuration information of device */
2796 if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
2797 USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
2798 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2799 "usbecm_usb_init: fail to get_dev_data");
2800
2801 return (USB_FAILURE);
2802 }
2803 ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph;
2804 ecmp->ecm_dev_state = USB_DEV_ONLINE;
2805
2806 mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER,
2807 ecmp->ecm_dev_data->dev_iblock_cookie);
2808
2809 if ((strcmp(ddi_binding_name(ecmp->ecm_dip),
2810 "usbif,class2.6") == 0) ||
2811 ((strcmp(ddi_binding_name(ecmp->ecm_dip),
2812 "usb,class2.6.0") == 0))) {
2813 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2814 "usbecm_usb_init: A CDC ECM device is attached");
2815 ecmp->ecm_compatibility = B_TRUE;
2816 } else if (usb_owns_device(ecmp->ecm_dip) &&
2817 usbecm_is_compatible(ecmp)) {
2818 /*
2819 * Current Sun SP ECM device has two configurations. Hence
2820 * USBA doesn't create interface level compatible names
2821 * for it, see usba_ready_device_node(). We have to check
2822 * manually to see if compatible interfaces exist, when
2823 * the driver owns the entire device.
2824 */
2825 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2826 "usbecm_usb_init: A CDC ECM device is attached");
2827 ecmp->ecm_compatibility = B_TRUE;
2828 } else {
2829 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2830 "usbecm_usb_init: A nonstandard device is attached to "
2831 "usbecm(4D) driver. This device doesn't conform to "
2832 "usb cdc spec.");
2833 ecmp->ecm_compatibility = B_FALSE;
2834
2835 /* generate a random MAC addr */
2836 generate_ether_addr(ecmp->ecm_srcaddr);
2837 }
2838
2839 if ((ecmp->ecm_compatibility == B_TRUE) &&
2840 (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) {
2841 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2842 "usbecm_usb_init: A compatible device is attached, but "
2843 "fail to get standard descriptors");
2844
2845 return (USB_FAILURE);
2846 }
2847
2848 if (ecmp->ecm_compatibility == B_FALSE) {
2849 (void) usbecm_init_non_compatible_device(ecmp);
2850 }
2851
2852 /* Create power management components */
2853 if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) {
2854 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2855 "usbecm_usb_init: create pm components failed.");
2856
2857 return (USB_FAILURE);
2858 }
2859
2860 /* Register to get callbacks for USB events */
2861 if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0)
2862 != USB_SUCCESS) {
2863 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2864 "usbsecm_attach: register event callback failed.");
2865
2866 return (USB_FAILURE);
2867 }
2868 ecmp->ecm_init_flags |= USBECM_INIT_EVENTS;
2869
2870
2871 /* Get max data size of bulk transfer */
2872 if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip,
2873 &ecmp->ecm_xfer_sz) != USB_SUCCESS) {
2874 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2875 "usbsecm_ds_attach: get max size of transfer failed.");
2876
2877 return (USB_FAILURE);
2878 }
2879
2880
2881 ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip,
2882 USB_INIT_SER_CHECK_SAME_THREAD);
2883 ecmp->ecm_init_flags |= USBECM_INIT_SER;
2884
2885 return (USB_SUCCESS);
2886 }
2887
2888
2889 /*
2890 * Open operation pipes. Each ECM device should have Bulk In, Bulk Out
2891 * and Interrupt In endpoints
2892 */
2893 static int
usbecm_open_pipes(usbecm_state_t * ecmp)2894 usbecm_open_pipes(usbecm_state_t *ecmp)
2895 {
2896 int rval = USB_SUCCESS;
2897 usb_ep_data_t *in_data, *out_data, *intr_pipe;
2898 usb_pipe_policy_t policy;
2899 int altif;
2900
2901 ASSERT(!mutex_owned(&ecmp->ecm_mutex));
2902
2903 USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
2904 "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp);
2905
2906 if (ecmp->ecm_compatibility == B_TRUE) {
2907 /* compatible device has minimum of 2 altsetting, select alt 1 */
2908 altif = 1;
2909 } else {
2910 altif = ecmp->ecm_data_if_alt;
2911 }
2912 intr_pipe = ecmp->ecm_intr_ep;
2913 in_data = ecmp->ecm_bulk_in_ep;
2914 out_data = ecmp->ecm_bulk_out_ep;
2915
2916 /* Bulk in and out must exist simultaneously. */
2917 if ((in_data == NULL) || (out_data == NULL)) {
2918 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
2919 "usbsecm_open_pipes: look up bulk pipe failed in "
2920 "interface %d ",
2921 ecmp->ecm_data_if_no);
2922
2923 return (USB_FAILURE);
2924 }
2925 /*
2926 * If device conform to ecm spec, it must have an interrupt pipe
2927 * for this device.
2928 */
2929 if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) {
2930 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
2931 "usbecm_open_pipes: look up interrupt pipe failed in "
2932 "interface %d", ecmp->ecm_ctrl_if_no);
2933
2934 return (USB_FAILURE);
2935 }
2936
2937 USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
2938 "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x",
2939 intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0,
2940 in_data->ep_descr.bEndpointAddress,
2941 out_data->ep_descr.bEndpointAddress);
2942
2943 USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
2944 "usbsecm_open_pipes: set data if(%d) alt(%d) ",
2945 ecmp->ecm_data_if_no, altif);
2946
2947 if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
2948 altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
2949 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
2950 "usbecm_open_pipes: set alternate failed (%d)",
2951 rval);
2952
2953 return (rval);
2954 }
2955
2956 policy.pp_max_async_reqs = 2;
2957
2958 /* Open bulk in endpoint */
2959 if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy,
2960 USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) {
2961 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
2962 "usbecm_open_pipes: open bulkin pipe failed!");
2963
2964 return (USB_FAILURE);
2965 }
2966
2967 /* Open bulk out endpoint */
2968 if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy,
2969 USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) {
2970 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
2971 "usbecm_open_pipes: open bulkout pipe failed!");
2972
2973 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
2974 USB_FLAGS_SLEEP, NULL, NULL);
2975
2976 return (USB_FAILURE);
2977 }
2978
2979 /* Open interrupt endpoint if found. */
2980 if (intr_pipe != NULL) {
2981 if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy,
2982 USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) {
2983 USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
2984 "usbecm_open_pipes: "
2985 "open intr pipe failed");
2986
2987 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
2988 USB_FLAGS_SLEEP, NULL, NULL);
2989 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
2990 USB_FLAGS_SLEEP, NULL, NULL);
2991
2992 return (USB_FAILURE);
2993 }
2994 }
2995
2996 /* initialize the pipe related data */
2997 mutex_enter(&ecmp->ecm_mutex);
2998 ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize;
2999 ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
3000 ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE;
3001 if (ecmp->ecm_intr_ph != NULL) {
3002 ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
3003 }
3004 mutex_exit(&ecmp->ecm_mutex);
3005
3006 if (ecmp->ecm_intr_ph != NULL) {
3007
3008 usbecm_pipe_start_polling(ecmp);
3009 }
3010
3011 USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
3012 "usbsecm_open_pipes: end");
3013
3014 return (rval);
3015 }
3016
3017
3018 /*
3019 * usbsecm_close_pipes:
3020 * Close pipes
3021 * Each device could include three pipes: bulk in, bulk out and interrupt.
3022 */
3023 static void
usbecm_close_pipes(usbecm_state_t * ecmp)3024 usbecm_close_pipes(usbecm_state_t *ecmp)
3025 {
3026
3027 mutex_enter(&ecmp->ecm_mutex);
3028
3029 USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
3030 "usbsecm_close_pipes: ecm_bulkin_state = %d",
3031 ecmp->ecm_bulkin_state);
3032
3033 /*
3034 * Check the status of the pipes. If pipe is closing or closed,
3035 * return directly.
3036 */
3037 if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) ||
3038 (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) {
3039 USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh,
3040 "usbsecm_close_pipes: pipe is closing or has closed");
3041 mutex_exit(&ecmp->ecm_mutex);
3042
3043 return;
3044 }
3045
3046 ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING;
3047 mutex_exit(&ecmp->ecm_mutex);
3048
3049 /* reset the data interface's altsetting to 0 */
3050 if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
3051 (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
3052 0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) {
3053 USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
3054 "usbecm_close_pipes: reset alternate failed ");
3055 }
3056
3057 /* Close pipes */
3058 usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
3059 USB_FLAGS_SLEEP, NULL, 0);
3060 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
3061 USB_FLAGS_SLEEP, NULL, 0);
3062 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
3063 USB_FLAGS_SLEEP, NULL, 0);
3064
3065 if (ecmp->ecm_intr_ph != NULL) {
3066 usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
3067 USB_FLAGS_SLEEP);
3068 usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph,
3069 USB_FLAGS_SLEEP, NULL, 0);
3070 }
3071
3072 mutex_enter(&ecmp->ecm_mutex);
3073 /* Reset the status of pipes to closed */
3074 ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED;
3075 ecmp->ecm_bulkin_ph = NULL;
3076 ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED;
3077 ecmp->ecm_bulkout_ph = NULL;
3078 if (ecmp->ecm_intr_ph != NULL) {
3079 ecmp->ecm_intr_state = USBECM_PIPE_CLOSED;
3080 ecmp->ecm_intr_ph = NULL;
3081 }
3082
3083 mutex_exit(&ecmp->ecm_mutex);
3084
3085 USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
3086 "usbsecm_close_pipes: pipes have been closed.");
3087 }
3088
3089
3090 static int
usbecm_ctrl_write(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data)3091 usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
3092 uint16_t value, mblk_t **data)
3093 {
3094 usb_ctrl_setup_t setup;
3095 usb_cb_flags_t cb_flags;
3096 usb_cr_t cr;
3097 int rval;
3098
3099 USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
3100 "usbecm_ctrl_write: ");
3101
3102 /* initialize the control request. */
3103 setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
3104 USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
3105 setup.bRequest = request;
3106 setup.wValue = value;
3107 setup.wIndex = ecmp->ecm_ctrl_if_no;
3108 setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
3109 setup.attrs = 0;
3110
3111 rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
3112 &cr, &cb_flags, 0);
3113
3114 USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
3115 "usbecm_ctrl_write: rval = %d", rval);
3116
3117 return (rval);
3118 }
3119
3120 static int
usbecm_ctrl_read(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data,int len)3121 usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
3122 uint16_t value, mblk_t **data, int len)
3123 {
3124 usb_ctrl_setup_t setup;
3125 usb_cb_flags_t cb_flags;
3126 usb_cr_t cr;
3127
3128 USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
3129 "usbecm_ctrl_read: ");
3130
3131 /* initialize the control request. */
3132 setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST |
3133 USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
3134 setup.bRequest = request;
3135 setup.wValue = value;
3136 setup.wIndex = ecmp->ecm_ctrl_if_no;
3137 setup.wLength = (uint16_t)len;
3138 setup.attrs = 0;
3139
3140 return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
3141 &cr, &cb_flags, 0));
3142 }
3143
3144 /* Get specific statistic data from device */
3145 static int
usbecm_get_statistics(usbecm_state_t * ecmp,uint32_t fs,uint32_t * stat_data)3146 usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data)
3147 {
3148 mblk_t *data = NULL;
3149 uint32_t stat;
3150
3151 /* first check to see if this stat is collected by device */
3152 if ((ecmp->ecm_compatibility == B_TRUE) &&
3153 (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) {
3154 if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT,
3155 ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) {
3156
3157 return (USB_FAILURE);
3158 }
3159 stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) |
3160 (data->b_rptr[1] << 8) | (data->b_rptr[0]);
3161 *stat_data = stat;
3162
3163 freemsg(data);
3164
3165 return (USB_SUCCESS);
3166 }
3167
3168 return (USB_FAILURE);
3169 }
3170