1 | // SPDX-License-Identifier: GPL-2.0+ |
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 |
13 | */ |
14 | |
15 | /* #define DEBUG */ |
16 | /* #define VERBOSE_DEBUG */ |
17 | |
18 | #include <linux/kernel.h> |
19 | #include <linux/module.h> |
20 | #include <linux/usb/composite.h> |
21 | |
22 | #include "u_ether.h" |
23 | #include "u_ncm.h" |
24 | |
25 | #define DRIVER_DESC "NCM Gadget" |
26 | |
27 | /*-------------------------------------------------------------------------*/ |
28 | |
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 | /*-------------------------------------------------------------------------*/ |
40 | USB_GADGET_COMPOSITE_OPTIONS(); |
41 | |
42 | USB_ETHERNET_MODULE_PARAMETERS(); |
43 | |
44 | static struct usb_device_descriptor device_desc = { |
45 | .bLength = sizeof device_desc, |
46 | .bDescriptorType = USB_DT_DEVICE, |
47 | |
48 | /* .bcdUSB = DYNAMIC */ |
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 | |
68 | static const struct usb_descriptor_header *otg_desc[2]; |
69 | |
70 | /* string IDs are assigned dynamically */ |
71 | static struct usb_string strings_dev[] = { |
72 | [USB_GADGET_MANUFACTURER_IDX].s = "" , |
73 | [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, |
74 | [USB_GADGET_SERIAL_IDX].s = "" , |
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 | |
88 | static struct usb_function_instance *f_ncm_inst; |
89 | static struct usb_function *f_ncm; |
90 | |
91 | /*-------------------------------------------------------------------------*/ |
92 | |
93 | static int ncm_do_config(struct usb_configuration *c) |
94 | { |
95 | int status; |
96 | |
97 | /* FIXME alloc iConfiguration string, set it in c->strings */ |
98 | |
99 | if (gadget_is_otg(g: c->cdev->gadget)) { |
100 | c->descriptors = otg_desc; |
101 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; |
102 | } |
103 | |
104 | f_ncm = usb_get_function(fi: f_ncm_inst); |
105 | if (IS_ERR(ptr: f_ncm)) |
106 | return PTR_ERR(ptr: f_ncm); |
107 | |
108 | status = usb_add_function(c, f_ncm); |
109 | if (status < 0) { |
110 | usb_put_function(f: f_ncm); |
111 | return status; |
112 | } |
113 | |
114 | return 0; |
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 | |
127 | static int gncm_bind(struct usb_composite_dev *cdev) |
128 | { |
129 | struct usb_gadget *gadget = cdev->gadget; |
130 | struct f_ncm_opts *ncm_opts; |
131 | int status; |
132 | |
133 | f_ncm_inst = usb_get_function_instance(name: "ncm" ); |
134 | if (IS_ERR(ptr: f_ncm_inst)) |
135 | return PTR_ERR(ptr: f_ncm_inst); |
136 | |
137 | ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst); |
138 | |
139 | gether_set_qmult(net: ncm_opts->net, qmult); |
140 | if (!gether_set_host_addr(net: ncm_opts->net, host_addr)) |
141 | pr_info("using host ethernet address: %s" , host_addr); |
142 | if (!gether_set_dev_addr(net: ncm_opts->net, dev_addr)) |
143 | pr_info("using self ethernet address: %s" , dev_addr); |
144 | |
145 | /* Allocate string descriptor numbers ... note that string |
146 | * contents can be overridden by the composite_dev glue. |
147 | */ |
148 | |
149 | status = usb_string_ids_tab(c: cdev, str: strings_dev); |
150 | if (status < 0) |
151 | goto fail; |
152 | device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; |
153 | device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; |
154 | |
155 | if (gadget_is_otg(g: gadget) && !otg_desc[0]) { |
156 | struct usb_descriptor_header *usb_desc; |
157 | |
158 | usb_desc = usb_otg_descriptor_alloc(gadget); |
159 | if (!usb_desc) { |
160 | status = -ENOMEM; |
161 | goto fail; |
162 | } |
163 | usb_otg_descriptor_init(gadget, otg_desc: usb_desc); |
164 | otg_desc[0] = usb_desc; |
165 | otg_desc[1] = NULL; |
166 | } |
167 | |
168 | status = usb_add_config(cdev, &ncm_config_driver, |
169 | ncm_do_config); |
170 | if (status < 0) |
171 | goto fail1; |
172 | |
173 | usb_composite_overwrite_options(cdev, covr: &coverwrite); |
174 | dev_info(&gadget->dev, "%s\n" , DRIVER_DESC); |
175 | |
176 | return 0; |
177 | |
178 | fail1: |
179 | kfree(objp: otg_desc[0]); |
180 | otg_desc[0] = NULL; |
181 | fail: |
182 | usb_put_function_instance(fi: f_ncm_inst); |
183 | return status; |
184 | } |
185 | |
186 | static int gncm_unbind(struct usb_composite_dev *cdev) |
187 | { |
188 | if (!IS_ERR_OR_NULL(ptr: f_ncm)) |
189 | usb_put_function(f: f_ncm); |
190 | if (!IS_ERR_OR_NULL(ptr: f_ncm_inst)) |
191 | usb_put_function_instance(fi: f_ncm_inst); |
192 | kfree(objp: otg_desc[0]); |
193 | otg_desc[0] = NULL; |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static struct usb_composite_driver ncm_driver = { |
199 | .name = "g_ncm" , |
200 | .dev = &device_desc, |
201 | .strings = dev_strings, |
202 | .max_speed = USB_SPEED_SUPER, |
203 | .bind = gncm_bind, |
204 | .unbind = gncm_unbind, |
205 | }; |
206 | |
207 | module_usb_composite_driver(ncm_driver); |
208 | |
209 | MODULE_DESCRIPTION(DRIVER_DESC); |
210 | MODULE_AUTHOR("Yauheni Kaliuta" ); |
211 | MODULE_LICENSE("GPL" ); |
212 | |