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