Commit | Line | Data |
---|---|---|
6c34d288 YK |
1 | /* |
2 | * ncm.c -- NCM gadget driver | |
3 | * | |
4 | * Copyright (C) 2010 Nokia Corporation | |
5 | * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com> | |
6 | * | |
7 | * The driver borrows from ether.c which is: | |
8 | * | |
9 | * Copyright (C) 2003-2005,2008 David Brownell | |
10 | * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger | |
11 | * Copyright (C) 2008 Nokia Corporation | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or | |
16 | * (at your option) any later version. | |
6c34d288 YK |
17 | */ |
18 | ||
19 | /* #define DEBUG */ | |
20 | /* #define VERBOSE_DEBUG */ | |
21 | ||
22 | #include <linux/kernel.h> | |
721e2e91 SAS |
23 | #include <linux/module.h> |
24 | #include <linux/usb/composite.h> | |
6c34d288 YK |
25 | |
26 | #include "u_ether.h" | |
9575bcf9 | 27 | #include "u_ncm.h" |
6c34d288 YK |
28 | |
29 | #define DRIVER_DESC "NCM Gadget" | |
30 | ||
31 | /*-------------------------------------------------------------------------*/ | |
32 | ||
6c34d288 YK |
33 | /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! |
34 | * Instead: allocate your own, using normal USB-IF procedures. | |
35 | */ | |
36 | ||
37 | /* Thanks to NetChip Technologies for donating this product ID. | |
38 | * It's for devices with only CDC Ethernet configurations. | |
39 | */ | |
40 | #define CDC_VENDOR_NUM 0x0525 /* NetChip */ | |
41 | #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ | |
42 | ||
43 | /*-------------------------------------------------------------------------*/ | |
7d16e8d3 | 44 | USB_GADGET_COMPOSITE_OPTIONS(); |
6c34d288 | 45 | |
f1a1823f AP |
46 | USB_ETHERNET_MODULE_PARAMETERS(); |
47 | ||
6c34d288 YK |
48 | static struct usb_device_descriptor device_desc = { |
49 | .bLength = sizeof device_desc, | |
50 | .bDescriptorType = USB_DT_DEVICE, | |
51 | ||
0aecfc1b | 52 | /* .bcdUSB = DYNAMIC */ |
6c34d288 YK |
53 | |
54 | .bDeviceClass = USB_CLASS_COMM, | |
55 | .bDeviceSubClass = 0, | |
56 | .bDeviceProtocol = 0, | |
57 | /* .bMaxPacketSize0 = f(hardware) */ | |
58 | ||
59 | /* Vendor and product id defaults change according to what configs | |
60 | * we support. (As does bNumConfigurations.) These values can | |
61 | * also be overridden by module parameters. | |
62 | */ | |
63 | .idVendor = cpu_to_le16 (CDC_VENDOR_NUM), | |
64 | .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM), | |
65 | /* .bcdDevice = f(hardware) */ | |
66 | /* .iManufacturer = DYNAMIC */ | |
67 | /* .iProduct = DYNAMIC */ | |
68 | /* NO SERIAL NUMBER */ | |
69 | .bNumConfigurations = 1, | |
70 | }; | |
71 | ||
1156e91d | 72 | static const struct usb_descriptor_header *otg_desc[2]; |
6c34d288 | 73 | |
6c34d288 | 74 | /* string IDs are assigned dynamically */ |
6c34d288 | 75 | static struct usb_string strings_dev[] = { |
cc2683c3 | 76 | [USB_GADGET_MANUFACTURER_IDX].s = "", |
276e2e4f SAS |
77 | [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, |
78 | [USB_GADGET_SERIAL_IDX].s = "", | |
6c34d288 YK |
79 | { } /* end of list */ |
80 | }; | |
81 | ||
82 | static struct usb_gadget_strings stringtab_dev = { | |
83 | .language = 0x0409, /* en-us */ | |
84 | .strings = strings_dev, | |
85 | }; | |
86 | ||
87 | static struct usb_gadget_strings *dev_strings[] = { | |
88 | &stringtab_dev, | |
89 | NULL, | |
90 | }; | |
91 | ||
9575bcf9 AP |
92 | static struct usb_function_instance *f_ncm_inst; |
93 | static struct usb_function *f_ncm; | |
6c34d288 YK |
94 | |
95 | /*-------------------------------------------------------------------------*/ | |
96 | ||
c94e289f | 97 | static int ncm_do_config(struct usb_configuration *c) |
6c34d288 | 98 | { |
9575bcf9 AP |
99 | int status; |
100 | ||
6c34d288 YK |
101 | /* FIXME alloc iConfiguration string, set it in c->strings */ |
102 | ||
103 | if (gadget_is_otg(c->cdev->gadget)) { | |
104 | c->descriptors = otg_desc; | |
105 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | |
106 | } | |
107 | ||
9575bcf9 AP |
108 | f_ncm = usb_get_function(f_ncm_inst); |
109 | if (IS_ERR(f_ncm)) { | |
110 | status = PTR_ERR(f_ncm); | |
111 | return status; | |
112 | } | |
113 | ||
114 | status = usb_add_function(c, f_ncm); | |
115 | if (status < 0) { | |
116 | usb_put_function(f_ncm); | |
117 | return status; | |
118 | } | |
119 | ||
120 | return 0; | |
6c34d288 YK |
121 | } |
122 | ||
123 | static struct usb_configuration ncm_config_driver = { | |
124 | /* .label = f(hardware) */ | |
125 | .label = "CDC Ethernet (NCM)", | |
126 | .bConfigurationValue = 1, | |
127 | /* .iConfiguration = DYNAMIC */ | |
128 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | |
129 | }; | |
130 | ||
131 | /*-------------------------------------------------------------------------*/ | |
132 | ||
c94e289f | 133 | static int gncm_bind(struct usb_composite_dev *cdev) |
6c34d288 | 134 | { |
6c34d288 | 135 | struct usb_gadget *gadget = cdev->gadget; |
9575bcf9 | 136 | struct f_ncm_opts *ncm_opts; |
6c34d288 YK |
137 | int status; |
138 | ||
9575bcf9 AP |
139 | f_ncm_inst = usb_get_function_instance("ncm"); |
140 | if (IS_ERR(f_ncm_inst)) | |
141 | return PTR_ERR(f_ncm_inst); | |
142 | ||
143 | ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst); | |
144 | ||
145 | gether_set_qmult(ncm_opts->net, qmult); | |
146 | if (!gether_set_host_addr(ncm_opts->net, host_addr)) | |
147 | pr_info("using host ethernet address: %s", host_addr); | |
148 | if (!gether_set_dev_addr(ncm_opts->net, dev_addr)) | |
149 | pr_info("using self ethernet address: %s", dev_addr); | |
6c34d288 | 150 | |
6c34d288 YK |
151 | /* Allocate string descriptor numbers ... note that string |
152 | * contents can be overridden by the composite_dev glue. | |
153 | */ | |
154 | ||
e1f15ccb | 155 | status = usb_string_ids_tab(cdev, strings_dev); |
6c34d288 YK |
156 | if (status < 0) |
157 | goto fail; | |
276e2e4f SAS |
158 | device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; |
159 | device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | |
6c34d288 | 160 | |
1156e91d LJ |
161 | if (gadget_is_otg(gadget) && !otg_desc[0]) { |
162 | struct usb_descriptor_header *usb_desc; | |
163 | ||
164 | usb_desc = usb_otg_descriptor_alloc(gadget); | |
165 | if (!usb_desc) | |
166 | goto fail; | |
167 | usb_otg_descriptor_init(gadget, usb_desc); | |
168 | otg_desc[0] = usb_desc; | |
169 | otg_desc[1] = NULL; | |
170 | } | |
171 | ||
6c34d288 YK |
172 | status = usb_add_config(cdev, &ncm_config_driver, |
173 | ncm_do_config); | |
174 | if (status < 0) | |
1156e91d | 175 | goto fail1; |
6c34d288 | 176 | |
7d16e8d3 | 177 | usb_composite_overwrite_options(cdev, &coverwrite); |
6c34d288 YK |
178 | dev_info(&gadget->dev, "%s\n", DRIVER_DESC); |
179 | ||
180 | return 0; | |
181 | ||
1156e91d LJ |
182 | fail1: |
183 | kfree(otg_desc[0]); | |
184 | otg_desc[0] = NULL; | |
6c34d288 | 185 | fail: |
9575bcf9 | 186 | usb_put_function_instance(f_ncm_inst); |
6c34d288 YK |
187 | return status; |
188 | } | |
189 | ||
c94e289f | 190 | static int gncm_unbind(struct usb_composite_dev *cdev) |
6c34d288 | 191 | { |
9575bcf9 AP |
192 | if (!IS_ERR_OR_NULL(f_ncm)) |
193 | usb_put_function(f_ncm); | |
194 | if (!IS_ERR_OR_NULL(f_ncm_inst)) | |
195 | usb_put_function_instance(f_ncm_inst); | |
1156e91d LJ |
196 | kfree(otg_desc[0]); |
197 | otg_desc[0] = NULL; | |
198 | ||
6c34d288 YK |
199 | return 0; |
200 | } | |
201 | ||
c94e289f | 202 | static struct usb_composite_driver ncm_driver = { |
6c34d288 YK |
203 | .name = "g_ncm", |
204 | .dev = &device_desc, | |
205 | .strings = dev_strings, | |
35a0e0bf | 206 | .max_speed = USB_SPEED_HIGH, |
03e42bd5 | 207 | .bind = gncm_bind, |
c94e289f | 208 | .unbind = gncm_unbind, |
6c34d288 YK |
209 | }; |
210 | ||
909346a8 TK |
211 | module_usb_composite_driver(ncm_driver); |
212 | ||
6c34d288 YK |
213 | MODULE_DESCRIPTION(DRIVER_DESC); |
214 | MODULE_AUTHOR("Yauheni Kaliuta"); | |
215 | MODULE_LICENSE("GPL"); |