blob: ae3f934810c84b441205fb43b33df19e7b0bb195 [file] [log] [blame]
Hemant Kumar9d6016c2012-01-05 16:27:24 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Hemant Kumar37c35e42011-09-14 23:44:19 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/mii.h>
14#include <linux/if_arp.h>
15#include <linux/etherdevice.h>
16#include <linux/usb.h>
17#include <linux/usb/usbnet.h>
18#include <linux/msm_rmnet.h>
19
20#include "rmnet_usb_ctrl.h"
21
22#define RMNET_DATA_LEN 2000
23#define HEADROOM_FOR_QOS 8
24
Hemant Kumar37c35e42011-09-14 23:44:19 -070025static int data_msg_dbg_mask;
26
27enum {
28 DEBUG_MASK_LVL0 = 1U << 0,
29 DEBUG_MASK_LVL1 = 1U << 1,
30 DEBUG_MASK_LVL2 = 1U << 2,
31};
32
33#define DBG(m, x...) do { \
34 if (data_msg_dbg_mask & m) \
35 pr_info(x); \
36} while (0)
37
38/*echo dbg_mask > /sys/class/net/rmnet_usbx/dbg_mask*/
39static ssize_t dbg_mask_store(struct device *d,
40 struct device_attribute *attr,
41 const char *buf, size_t n)
42{
43 unsigned int dbg_mask;
44 struct net_device *dev = to_net_dev(d);
45 struct usbnet *unet = netdev_priv(dev);
46
47 if (!dev)
48 return -ENODEV;
49
50 sscanf(buf, "%u", &dbg_mask);
51 /*enable dbg msgs for data driver*/
52 data_msg_dbg_mask = dbg_mask;
53
54 /*set default msg level*/
55 unet->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK;
56
57 /*enable netif_xxx msgs*/
58 if (dbg_mask & DEBUG_MASK_LVL0)
59 unet->msg_enable |= NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
60 if (dbg_mask & DEBUG_MASK_LVL1)
61 unet->msg_enable |= NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
62 | NETIF_MSG_TX_QUEUED | NETIF_MSG_TX_DONE
63 | NETIF_MSG_RX_STATUS;
64
65 return n;
66}
67
68static ssize_t dbg_mask_show(struct device *d,
69 struct device_attribute *attr, char *buf)
70{
71 return snprintf(buf, PAGE_SIZE, "%d\n", data_msg_dbg_mask);
72}
73
74static DEVICE_ATTR(dbg_mask, 0644, dbg_mask_show, dbg_mask_store);
75
76#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
77#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
78#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
79
80static void rmnet_usb_setup(struct net_device *);
81static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
82
83static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
84{
85 struct usbnet *unet;
86 struct rmnet_ctrl_dev *dev;
87 int time = 0;
88 int retval = 0;
89
90 unet = usb_get_intfdata(iface);
91 if (!unet) {
92 pr_err("%s:data device not found\n", __func__);
93 retval = -ENODEV;
94 goto fail;
95 }
96
97 dev = (struct rmnet_ctrl_dev *)unet->data[1];
98 if (!dev) {
99 dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
100 __func__);
101 retval = -ENODEV;
102 goto fail;
103 }
104
105 retval = usbnet_suspend(iface, message);
106 if (!retval) {
107 if (message.event & PM_EVENT_SUSPEND) {
108 time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
109 1000);
110 if (!time)
111 usb_kill_anchored_urbs(&dev->tx_submitted);
112
113 retval = rmnet_usb_ctrl_stop_rx(dev);
114 iface->dev.power.power_state.event = message.event;
115 }
116 /* TBD : do we need to set/clear usbnet->udev->reset_resume*/
117 } else
118 dev_dbg(&unet->udev->dev,
119 "%s: device is busy can not suspend\n", __func__);
120
121fail:
122 return retval;
123}
124
125static int rmnet_usb_resume(struct usb_interface *iface)
126{
127 int retval = 0;
128 int oldstate;
129 struct usbnet *unet;
130 struct rmnet_ctrl_dev *dev;
131
132 unet = usb_get_intfdata(iface);
133 if (!unet) {
134 pr_err("%s:data device not found\n", __func__);
135 retval = -ENODEV;
136 goto fail;
137 }
138
139 dev = (struct rmnet_ctrl_dev *)unet->data[1];
140 if (!dev) {
141 dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
142 __func__);
143 retval = -ENODEV;
144 goto fail;
145 }
146 oldstate = iface->dev.power.power_state.event;
147 iface->dev.power.power_state.event = PM_EVENT_ON;
148
149 retval = usbnet_resume(iface);
150 if (!retval) {
Hemant Kumar37c35e42011-09-14 23:44:19 -0700151 if (oldstate & PM_EVENT_SUSPEND)
152 retval = rmnet_usb_ctrl_start(dev);
153 }
154fail:
155 return retval;
156}
157
158static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
159{
160 struct usb_host_endpoint *endpoint = NULL;
161 struct usb_host_endpoint *bulk_in = NULL;
162 struct usb_host_endpoint *bulk_out = NULL;
163 struct usb_host_endpoint *int_in = NULL;
164 struct usb_device *udev;
165 int status = 0;
166 int i;
167 int numends;
168
169 udev = interface_to_usbdev(iface);
170 numends = iface->cur_altsetting->desc.bNumEndpoints;
171 for (i = 0; i < numends; i++) {
172 endpoint = iface->cur_altsetting->endpoint + i;
173 if (!endpoint) {
174 dev_err(&udev->dev, "%s: invalid endpoint %u\n",
175 __func__, i);
176 status = -EINVAL;
177 goto out;
178 }
179 if (usb_endpoint_is_bulk_in(&endpoint->desc))
180 bulk_in = endpoint;
181 else if (usb_endpoint_is_bulk_out(&endpoint->desc))
182 bulk_out = endpoint;
183 else if (usb_endpoint_is_int_in(&endpoint->desc))
184 int_in = endpoint;
185 }
186
187 if (!bulk_in || !bulk_out || !int_in) {
188 dev_err(&udev->dev, "%s: invalid endpoints\n", __func__);
189 status = -EINVAL;
190 goto out;
191 }
192 usbnet->in = usb_rcvbulkpipe(usbnet->udev,
193 bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
194 usbnet->out = usb_sndbulkpipe(usbnet->udev,
195 bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
196 usbnet->status = int_in;
197
198 /*change name of net device to rmnet_usbx here*/
199 strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
200
201 /*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
202out:
203 return status;
204}
205
206static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
207 struct sk_buff *skb, gfp_t flags)
208{
209 struct QMI_QOS_HDR_S *qmih;
210
211 if (test_bit(RMNET_MODE_QOS, &dev->data[0])) {
212 qmih = (struct QMI_QOS_HDR_S *)
213 skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
214 qmih->version = 1;
215 qmih->flags = 0;
216 qmih->flow_id = skb->mark;
217 }
218
219 DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
220 dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
221
222 return skb;
223}
224
225static __be16 rmnet_ip_type_trans(struct sk_buff *skb,
226 struct net_device *dev)
227{
228 __be16 protocol = 0;
229
230 skb->dev = dev;
231
232 switch (skb->data[0] & 0xf0) {
233 case 0x40:
234 protocol = htons(ETH_P_IP);
235 break;
236 case 0x60:
237 protocol = htons(ETH_P_IPV6);
238 break;
239 default:
240 pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
241 dev->name, skb->data[0] & 0xf0);
242 }
243
244 return protocol;
245}
246
247static int rmnet_usb_rx_fixup(struct usbnet *dev,
248 struct sk_buff *skb)
249{
250
251 if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
252 skb->protocol = rmnet_ip_type_trans(skb, dev->net);
253 else /*set zero for eth mode*/
254 skb->protocol = 0;
255
256 DBG1("[%s] Rx packet #%lu len=%d\n",
257 dev->net->name, dev->net->stats.rx_packets, skb->len);
258
259 return 1;
260}
261
Jack Phamf77b9962012-02-23 18:45:43 -0800262static int rmnet_usb_manage_power(struct usbnet *dev, int on)
263{
264 dev->intf->needs_remote_wakeup = on;
265 return 0;
266}
267
Hemant Kumar37c35e42011-09-14 23:44:19 -0700268static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
269{
270 if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
271 return -EINVAL;
272
273 DBG0("[%s] MTU change: old=%d new=%d\n", dev->name, dev->mtu, new_mtu);
274
275 dev->mtu = new_mtu;
276
277 return 0;
278}
279
280static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
281{
282 return &dev->stats;
283}
284
285static const struct net_device_ops rmnet_usb_ops_ether = {
286 .ndo_open = usbnet_open,
287 .ndo_stop = usbnet_stop,
288 .ndo_start_xmit = usbnet_start_xmit,
289 .ndo_get_stats = rmnet_get_stats,
290 /*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
291 .ndo_tx_timeout = usbnet_tx_timeout,
292 .ndo_do_ioctl = rmnet_ioctl,
293 .ndo_change_mtu = usbnet_change_mtu,
294 .ndo_set_mac_address = eth_mac_addr,
295 .ndo_validate_addr = eth_validate_addr,
296};
297
298static const struct net_device_ops rmnet_usb_ops_ip = {
299 .ndo_open = usbnet_open,
300 .ndo_stop = usbnet_stop,
301 .ndo_start_xmit = usbnet_start_xmit,
302 .ndo_get_stats = rmnet_get_stats,
303 /*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
304 .ndo_tx_timeout = usbnet_tx_timeout,
305 .ndo_do_ioctl = rmnet_ioctl,
306 .ndo_change_mtu = rmnet_change_mtu,
307 .ndo_set_mac_address = 0,
308 .ndo_validate_addr = 0,
309};
310
311
312static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
313{
314 struct usbnet *unet = netdev_priv(dev);
315 u32 old_opmode;
316 int prev_mtu = dev->mtu;
317 int rc = 0;
318
319 old_opmode = unet->data[0]; /*data[0] saves operation mode*/
320 /* Process IOCTL command */
321 switch (cmd) {
322 case RMNET_IOCTL_SET_LLP_ETHERNET: /*Set Ethernet protocol*/
323 /* Perform Ethernet config only if in IP mode currently*/
324 if (test_bit(RMNET_MODE_LLP_IP, &unet->data[0])) {
325 ether_setup(dev);
326 random_ether_addr(dev->dev_addr);
327 dev->mtu = prev_mtu;
328 dev->netdev_ops = &rmnet_usb_ops_ether;
329 clear_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
330 set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
331 DBG0("[%s] rmnet_ioctl(): set Ethernet protocol mode\n",
332 dev->name);
333 }
334 break;
335
336 case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol*/
337 /* Perform IP config only if in Ethernet mode currently*/
338 if (test_bit(RMNET_MODE_LLP_ETH, &unet->data[0])) {
339
340 /* Undo config done in ether_setup() */
341 dev->header_ops = 0; /* No header */
342 dev->type = ARPHRD_RAWIP;
343 dev->hard_header_len = 0;
344 dev->mtu = prev_mtu;
345 dev->addr_len = 0;
346 dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
347 dev->needed_headroom = HEADROOM_FOR_QOS;
348 dev->netdev_ops = &rmnet_usb_ops_ip;
349 clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
350 set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
351 DBG0("[%s] rmnet_ioctl(): set IP protocol mode\n",
352 dev->name);
353 }
354 break;
355
356 case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
357 ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
358 & (RMNET_MODE_LLP_ETH
359 | RMNET_MODE_LLP_IP));
360 break;
361
362 case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled*/
363 set_bit(RMNET_MODE_QOS, &unet->data[0]);
364 DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
365 dev->name);
366 break;
367
368 case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
369 clear_bit(RMNET_MODE_QOS, &unet->data[0]);
370 DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
371 dev->name);
372 break;
373
374 case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
375 ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
376 & RMNET_MODE_QOS);
377 break;
378
379 case RMNET_IOCTL_GET_OPMODE: /* Get operation mode*/
380 ifr->ifr_ifru.ifru_data = (void *)unet->data[0];
381 break;
382
383 case RMNET_IOCTL_OPEN: /* Open transport port */
384 rc = usbnet_open(dev);
385 DBG0("[%s] rmnet_ioctl(): open transport port\n", dev->name);
386 break;
387
388 case RMNET_IOCTL_CLOSE: /* Close transport port*/
389 rc = usbnet_stop(dev);
390 DBG0("[%s] rmnet_ioctl(): close transport port\n", dev->name);
391 break;
392
393 default:
394 dev_err(&unet->udev->dev, "[%s] error: "
395 "rmnet_ioct called for unsupported cmd[%d]",
396 dev->name, cmd);
397 return -EINVAL;
398 }
399
400 DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08lx\n",
401 dev->name, __func__, cmd, old_opmode, unet->data[0]);
402
403 return rc;
404}
405
406static void rmnet_usb_setup(struct net_device *dev)
407{
408 /* Using Ethernet mode by default */
409 dev->netdev_ops = &rmnet_usb_ops_ether;
410
411 /* set this after calling ether_setup */
412 dev->mtu = RMNET_DATA_LEN;
413
414 dev->needed_headroom = HEADROOM_FOR_QOS;
415 random_ether_addr(dev->dev_addr);
416 dev->watchdog_timeo = 1000; /* 10 seconds? */
417}
418
419static int rmnet_usb_probe(struct usb_interface *iface,
420 const struct usb_device_id *prod)
421{
422 struct usbnet *unet;
423 struct usb_device *udev;
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800424 struct driver_info *info;
425 unsigned int iface_num;
426 static int first_rmnet_iface_num = -EINVAL;
427 int status = 0;
Hemant Kumar37c35e42011-09-14 23:44:19 -0700428
429 udev = interface_to_usbdev(iface);
430 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
431 if (iface->num_altsetting != 1) {
432 dev_err(&udev->dev, "%s invalid num_altsetting %u\n",
433 __func__, iface->num_altsetting);
434 status = -EINVAL;
435 goto out;
436 }
437
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800438 info = (struct driver_info *)prod->driver_info;
439 if (!test_bit(iface_num, &info->data))
440 return -ENODEV;
Hemant Kumar37c35e42011-09-14 23:44:19 -0700441
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800442 status = usbnet_probe(iface, prod);
443 if (status < 0) {
444 dev_err(&udev->dev, "usbnet_probe failed %d\n", status);
445 goto out;
Hemant Kumar37c35e42011-09-14 23:44:19 -0700446 }
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800447 unet = usb_get_intfdata(iface);
448
449 /*set rmnet operation mode to eth by default*/
450 set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
451
452 /*update net device*/
453 rmnet_usb_setup(unet->net);
454
455 /*create /sys/class/net/rmnet_usbx/dbg_mask*/
456 status = device_create_file(&unet->net->dev, &dev_attr_dbg_mask);
457 if (status)
458 goto out;
459
460 if (first_rmnet_iface_num == -EINVAL)
461 first_rmnet_iface_num = iface_num;
462
463 /*save control device intstance */
464 unet->data[1] = (unsigned long)ctrl_dev \
465 [iface_num - first_rmnet_iface_num];
466
467 status = rmnet_usb_ctrl_probe(iface, unet->status,
468 (struct rmnet_ctrl_dev *)unet->data[1]);
Jack Pham42346cf2012-03-13 12:49:07 -0700469 if (status)
470 goto out;
471
472 /* allow modem to wake up suspended system */
473 device_set_wakeup_enable(&udev->dev, 1);
Hemant Kumar37c35e42011-09-14 23:44:19 -0700474out:
475 return status;
476}
477
478static void rmnet_usb_disconnect(struct usb_interface *intf)
479{
480 struct usbnet *unet;
481 struct usb_device *udev;
482 struct rmnet_ctrl_dev *dev;
Hemant Kumar37c35e42011-09-14 23:44:19 -0700483
484 udev = interface_to_usbdev(intf);
Jack Pham42346cf2012-03-13 12:49:07 -0700485 device_set_wakeup_enable(&udev->dev, 0);
Hemant Kumar37c35e42011-09-14 23:44:19 -0700486
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800487 unet = usb_get_intfdata(intf);
488 if (!unet) {
489 dev_err(&udev->dev, "%s:data device not found\n", __func__);
490 return;
Hemant Kumar37c35e42011-09-14 23:44:19 -0700491 }
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800492
493 dev = (struct rmnet_ctrl_dev *)unet->data[1];
494 if (!dev) {
495 dev_err(&udev->dev, "%s:ctrl device not found\n", __func__);
496 return;
497 }
498 unet->data[0] = 0;
499 unet->data[1] = 0;
500 rmnet_usb_ctrl_disconnect(dev);
501 device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
502 usbnet_disconnect(intf);
Hemant Kumar37c35e42011-09-14 23:44:19 -0700503}
504
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800505/*bit position represents interface number*/
506#define PID9034_IFACE_MASK 0xF0
507#define PID9048_IFACE_MASK 0x1E0
Hemant Kumarbf024812012-02-24 12:58:56 -0800508#define PID904C_IFACE_MASK 0x1C0
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800509
510static const struct driver_info rmnet_info_pid9034 = {
Hemant Kumar37c35e42011-09-14 23:44:19 -0700511 .description = "RmNET net device",
512 .bind = rmnet_usb_bind,
513 .tx_fixup = rmnet_usb_tx_fixup,
514 .rx_fixup = rmnet_usb_rx_fixup,
Jack Phamf77b9962012-02-23 18:45:43 -0800515 .manage_power = rmnet_usb_manage_power,
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800516 .data = PID9034_IFACE_MASK,
517};
518
519static const struct driver_info rmnet_info_pid9048 = {
520 .description = "RmNET net device",
521 .bind = rmnet_usb_bind,
522 .tx_fixup = rmnet_usb_tx_fixup,
523 .rx_fixup = rmnet_usb_rx_fixup,
Jack Phamf77b9962012-02-23 18:45:43 -0800524 .manage_power = rmnet_usb_manage_power,
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800525 .data = PID9048_IFACE_MASK,
Hemant Kumar37c35e42011-09-14 23:44:19 -0700526};
527
Hemant Kumarbf024812012-02-24 12:58:56 -0800528static const struct driver_info rmnet_info_pid904c = {
529 .description = "RmNET net device",
530 .bind = rmnet_usb_bind,
531 .tx_fixup = rmnet_usb_tx_fixup,
532 .rx_fixup = rmnet_usb_rx_fixup,
Jack Phamf77b9962012-02-23 18:45:43 -0800533 .manage_power = rmnet_usb_manage_power,
Hemant Kumarbf024812012-02-24 12:58:56 -0800534 .data = PID904C_IFACE_MASK,
535};
536
Hemant Kumar37c35e42011-09-14 23:44:19 -0700537static const struct usb_device_id vidpids[] = {
538 {
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800539 USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
540 .driver_info = (unsigned long)&rmnet_info_pid9034,
Hemant Kumar37c35e42011-09-14 23:44:19 -0700541 },
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800542 {
543 USB_DEVICE(0x05c6, 0x9048), /* MDM9x15*/
544 .driver_info = (unsigned long)&rmnet_info_pid9048,
545 },
Hemant Kumarbf024812012-02-24 12:58:56 -0800546 {
547 USB_DEVICE(0x05c6, 0x904c), /* MDM9x15*/
548 .driver_info = (unsigned long)&rmnet_info_pid904c,
549 },
Hemant Kumar9d6016c2012-01-05 16:27:24 -0800550
551 { }, /* Terminating entry */
Hemant Kumar37c35e42011-09-14 23:44:19 -0700552};
553
554MODULE_DEVICE_TABLE(usb, vidpids);
555
556static struct usb_driver rmnet_usb = {
557 .name = "rmnet_usb",
558 .id_table = vidpids,
559 .probe = rmnet_usb_probe,
560 .disconnect = rmnet_usb_disconnect,
561 .suspend = rmnet_usb_suspend,
562 .resume = rmnet_usb_resume,
563 .supports_autosuspend = true,
564};
565
566static int __init rmnet_usb_init(void)
567{
568 int retval;
569
570 retval = usb_register(&rmnet_usb);
571 if (retval) {
572 err("usb_register failed: %d", retval);
573 return retval;
574 }
575 /* initialize rmnet ctrl device here*/
576 retval = rmnet_usb_ctrl_init();
577 if (retval) {
578 usb_deregister(&rmnet_usb);
579 err("rmnet_usb_cmux_init failed: %d", retval);
580 return retval;
581 }
582
583 return 0;
584}
585module_init(rmnet_usb_init);
586
587static void __exit rmnet_usb_exit(void)
588{
589 rmnet_usb_ctrl_exit();
590 usb_deregister(&rmnet_usb);
591}
592module_exit(rmnet_usb_exit);
593
594MODULE_DESCRIPTION("msm rmnet usb device");
595MODULE_LICENSE("GPL v2");