diff options
author | Yauheni Kaliuta <yauheni.kaliuta@nokia.com> | 2010-12-08 06:12:06 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-10 17:29:43 -0500 |
commit | 6c34d2888221ca3df81e29f598873b4fb6cf838d (patch) | |
tree | 9110eacc57030877245f7992651facddf18f7445 /drivers/usb | |
parent | 9f6ce4240a2bf456402c15c06768059e5973f28c (diff) |
usb: gadget: g_ncm added
This patches makes possible to use composite framework and f_ncm
NCM function driver to build a standalone NCM gadget device.
Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 13 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/ncm.c | 248 |
3 files changed, 263 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7201b4e32778..a776c35ca980 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
@@ -741,6 +741,19 @@ config USB_ETH_EEM | |||
741 | If you say "y" here, the Ethernet gadget driver will use the EEM | 741 | If you say "y" here, the Ethernet gadget driver will use the EEM |
742 | protocol rather than ECM. If unsure, say "n". | 742 | protocol rather than ECM. If unsure, say "n". |
743 | 743 | ||
744 | config USB_G_NCM | ||
745 | tristate "Network Control Model (NCM) support" | ||
746 | depends on NET | ||
747 | select CRC32 | ||
748 | help | ||
749 | This driver implements USB CDC NCM subclass standard. NCM is | ||
750 | an advanced protocol for Ethernet encapsulation, allows grouping | ||
751 | of several ethernet frames into one USB transfer and diffferent | ||
752 | alignment possibilities. | ||
753 | |||
754 | Say "y" to link the driver statically, or "m" to build a | ||
755 | dynamically linked module called "g_ncm". | ||
756 | |||
744 | config USB_GADGETFS | 757 | config USB_GADGETFS |
745 | tristate "Gadget Filesystem (EXPERIMENTAL)" | 758 | tristate "Gadget Filesystem (EXPERIMENTAL)" |
746 | depends on EXPERIMENTAL | 759 | depends on EXPERIMENTAL |
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fbf5db4d456d..55f5e8ae5924 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile | |||
@@ -47,6 +47,7 @@ g_hid-y := hid.o | |||
47 | g_dbgp-y := dbgp.o | 47 | g_dbgp-y := dbgp.o |
48 | g_nokia-y := nokia.o | 48 | g_nokia-y := nokia.o |
49 | g_webcam-y := webcam.o | 49 | g_webcam-y := webcam.o |
50 | g_ncm-y := ncm.o | ||
50 | 51 | ||
51 | obj-$(CONFIG_USB_ZERO) += g_zero.o | 52 | obj-$(CONFIG_USB_ZERO) += g_zero.o |
52 | obj-$(CONFIG_USB_AUDIO) += g_audio.o | 53 | obj-$(CONFIG_USB_AUDIO) += g_audio.o |
@@ -64,3 +65,4 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o | |||
64 | obj-$(CONFIG_USB_G_MULTI) += g_multi.o | 65 | obj-$(CONFIG_USB_G_MULTI) += g_multi.o |
65 | obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o | 66 | obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o |
66 | obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o | 67 | obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o |
68 | obj-$(CONFIG_USB_G_NCM) += g_ncm.o | ||
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c new file mode 100644 index 000000000000..99c179ad729d --- /dev/null +++ b/drivers/usb/gadget/ncm.c | |||
@@ -0,0 +1,248 @@ | |||
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. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | */ | ||
27 | |||
28 | /* #define DEBUG */ | ||
29 | /* #define VERBOSE_DEBUG */ | ||
30 | |||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/utsname.h> | ||
33 | |||
34 | |||
35 | #include "u_ether.h" | ||
36 | |||
37 | #define DRIVER_DESC "NCM Gadget" | ||
38 | |||
39 | /*-------------------------------------------------------------------------*/ | ||
40 | |||
41 | /* | ||
42 | * Kbuild is not very cooperative with respect to linking separately | ||
43 | * compiled library objects into one module. So for now we won't use | ||
44 | * separate compilation ... ensuring init/exit sections work to shrink | ||
45 | * the runtime footprint, and giving us at least some parts of what | ||
46 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | ||
47 | */ | ||
48 | #include "composite.c" | ||
49 | #include "usbstring.c" | ||
50 | #include "config.c" | ||
51 | #include "epautoconf.c" | ||
52 | |||
53 | #include "f_ncm.c" | ||
54 | #include "u_ether.c" | ||
55 | |||
56 | /*-------------------------------------------------------------------------*/ | ||
57 | |||
58 | /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! | ||
59 | * Instead: allocate your own, using normal USB-IF procedures. | ||
60 | */ | ||
61 | |||
62 | /* Thanks to NetChip Technologies for donating this product ID. | ||
63 | * It's for devices with only CDC Ethernet configurations. | ||
64 | */ | ||
65 | #define CDC_VENDOR_NUM 0x0525 /* NetChip */ | ||
66 | #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ | ||
67 | |||
68 | /*-------------------------------------------------------------------------*/ | ||
69 | |||
70 | static struct usb_device_descriptor device_desc = { | ||
71 | .bLength = sizeof device_desc, | ||
72 | .bDescriptorType = USB_DT_DEVICE, | ||
73 | |||
74 | .bcdUSB = cpu_to_le16 (0x0200), | ||
75 | |||
76 | .bDeviceClass = USB_CLASS_COMM, | ||
77 | .bDeviceSubClass = 0, | ||
78 | .bDeviceProtocol = 0, | ||
79 | /* .bMaxPacketSize0 = f(hardware) */ | ||
80 | |||
81 | /* Vendor and product id defaults change according to what configs | ||
82 | * we support. (As does bNumConfigurations.) These values can | ||
83 | * also be overridden by module parameters. | ||
84 | */ | ||
85 | .idVendor = cpu_to_le16 (CDC_VENDOR_NUM), | ||
86 | .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM), | ||
87 | /* .bcdDevice = f(hardware) */ | ||
88 | /* .iManufacturer = DYNAMIC */ | ||
89 | /* .iProduct = DYNAMIC */ | ||
90 | /* NO SERIAL NUMBER */ | ||
91 | .bNumConfigurations = 1, | ||
92 | }; | ||
93 | |||
94 | static struct usb_otg_descriptor otg_descriptor = { | ||
95 | .bLength = sizeof otg_descriptor, | ||
96 | .bDescriptorType = USB_DT_OTG, | ||
97 | |||
98 | /* REVISIT SRP-only hardware is possible, although | ||
99 | * it would not be called "OTG" ... | ||
100 | */ | ||
101 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, | ||
102 | }; | ||
103 | |||
104 | static const struct usb_descriptor_header *otg_desc[] = { | ||
105 | (struct usb_descriptor_header *) &otg_descriptor, | ||
106 | NULL, | ||
107 | }; | ||
108 | |||
109 | |||
110 | /* string IDs are assigned dynamically */ | ||
111 | |||
112 | #define STRING_MANUFACTURER_IDX 0 | ||
113 | #define STRING_PRODUCT_IDX 1 | ||
114 | |||
115 | static char manufacturer[50]; | ||
116 | |||
117 | static struct usb_string strings_dev[] = { | ||
118 | [STRING_MANUFACTURER_IDX].s = manufacturer, | ||
119 | [STRING_PRODUCT_IDX].s = DRIVER_DESC, | ||
120 | { } /* end of list */ | ||
121 | }; | ||
122 | |||
123 | static struct usb_gadget_strings stringtab_dev = { | ||
124 | .language = 0x0409, /* en-us */ | ||
125 | .strings = strings_dev, | ||
126 | }; | ||
127 | |||
128 | static struct usb_gadget_strings *dev_strings[] = { | ||
129 | &stringtab_dev, | ||
130 | NULL, | ||
131 | }; | ||
132 | |||
133 | static u8 hostaddr[ETH_ALEN]; | ||
134 | |||
135 | /*-------------------------------------------------------------------------*/ | ||
136 | |||
137 | static int __init ncm_do_config(struct usb_configuration *c) | ||
138 | { | ||
139 | /* FIXME alloc iConfiguration string, set it in c->strings */ | ||
140 | |||
141 | if (gadget_is_otg(c->cdev->gadget)) { | ||
142 | c->descriptors = otg_desc; | ||
143 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||
144 | } | ||
145 | |||
146 | return ncm_bind_config(c, hostaddr); | ||
147 | } | ||
148 | |||
149 | static struct usb_configuration ncm_config_driver = { | ||
150 | /* .label = f(hardware) */ | ||
151 | .label = "CDC Ethernet (NCM)", | ||
152 | .bConfigurationValue = 1, | ||
153 | /* .iConfiguration = DYNAMIC */ | ||
154 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | ||
155 | }; | ||
156 | |||
157 | /*-------------------------------------------------------------------------*/ | ||
158 | |||
159 | static int __init gncm_bind(struct usb_composite_dev *cdev) | ||
160 | { | ||
161 | int gcnum; | ||
162 | struct usb_gadget *gadget = cdev->gadget; | ||
163 | int status; | ||
164 | |||
165 | /* set up network link layer */ | ||
166 | status = gether_setup(cdev->gadget, hostaddr); | ||
167 | if (status < 0) | ||
168 | return status; | ||
169 | |||
170 | gcnum = usb_gadget_controller_number(gadget); | ||
171 | if (gcnum >= 0) | ||
172 | device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); | ||
173 | else { | ||
174 | /* We assume that can_support_ecm() tells the truth; | ||
175 | * but if the controller isn't recognized at all then | ||
176 | * that assumption is a bit more likely to be wrong. | ||
177 | */ | ||
178 | dev_warn(&gadget->dev, | ||
179 | "controller '%s' not recognized; trying %s\n", | ||
180 | gadget->name, | ||
181 | ncm_config_driver.label); | ||
182 | device_desc.bcdDevice = | ||
183 | cpu_to_le16(0x0300 | 0x0099); | ||
184 | } | ||
185 | |||
186 | |||
187 | /* Allocate string descriptor numbers ... note that string | ||
188 | * contents can be overridden by the composite_dev glue. | ||
189 | */ | ||
190 | |||
191 | /* device descriptor strings: manufacturer, product */ | ||
192 | snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", | ||
193 | init_utsname()->sysname, init_utsname()->release, | ||
194 | gadget->name); | ||
195 | status = usb_string_id(cdev); | ||
196 | if (status < 0) | ||
197 | goto fail; | ||
198 | strings_dev[STRING_MANUFACTURER_IDX].id = status; | ||
199 | device_desc.iManufacturer = status; | ||
200 | |||
201 | status = usb_string_id(cdev); | ||
202 | if (status < 0) | ||
203 | goto fail; | ||
204 | strings_dev[STRING_PRODUCT_IDX].id = status; | ||
205 | device_desc.iProduct = status; | ||
206 | |||
207 | status = usb_add_config(cdev, &ncm_config_driver, | ||
208 | ncm_do_config); | ||
209 | if (status < 0) | ||
210 | goto fail; | ||
211 | |||
212 | dev_info(&gadget->dev, "%s\n", DRIVER_DESC); | ||
213 | |||
214 | return 0; | ||
215 | |||
216 | fail: | ||
217 | gether_cleanup(); | ||
218 | return status; | ||
219 | } | ||
220 | |||
221 | static int __exit gncm_unbind(struct usb_composite_dev *cdev) | ||
222 | { | ||
223 | gether_cleanup(); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static struct usb_composite_driver ncm_driver = { | ||
228 | .name = "g_ncm", | ||
229 | .dev = &device_desc, | ||
230 | .strings = dev_strings, | ||
231 | .unbind = __exit_p(gncm_unbind), | ||
232 | }; | ||
233 | |||
234 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
235 | MODULE_AUTHOR("Yauheni Kaliuta"); | ||
236 | MODULE_LICENSE("GPL"); | ||
237 | |||
238 | static int __init init(void) | ||
239 | { | ||
240 | return usb_composite_probe(&ncm_driver, gncm_bind); | ||
241 | } | ||
242 | module_init(init); | ||
243 | |||
244 | static void __exit cleanup(void) | ||
245 | { | ||
246 | usb_composite_unregister(&ncm_driver); | ||
247 | } | ||
248 | module_exit(cleanup); | ||