1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /****************************************************************************** |
3 | * xusbatm.c - dumb usbatm-based driver for modems initialized in userspace |
4 | * |
5 | * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) |
6 | ******************************************************************************/ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/etherdevice.h> /* for eth_random_addr() */ |
10 | |
11 | #include "usbatm.h" |
12 | |
13 | |
14 | #define XUSBATM_DRIVERS_MAX 8 |
15 | |
16 | #define XUSBATM_PARM(name, type, parmtype, desc) \ |
17 | static type name[XUSBATM_DRIVERS_MAX]; \ |
18 | static unsigned int num_##name; \ |
19 | module_param_array(name, parmtype, &num_##name, 0444); \ |
20 | MODULE_PARM_DESC(name, desc) |
21 | |
22 | XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor" ); |
23 | XUSBATM_PARM(product, unsigned short, ushort, "USB device product" ); |
24 | |
25 | XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number" ); |
26 | XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number" ); |
27 | XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)" ); |
28 | XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)" ); |
29 | XUSBATM_PARM(rx_altsetting, unsigned char, byte, "rx altsetting (default 0)" ); |
30 | XUSBATM_PARM(tx_altsetting, unsigned char, byte, "rx altsetting (default 0)" ); |
31 | |
32 | static const char xusbatm_driver_name[] = "xusbatm" ; |
33 | |
34 | static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX]; |
35 | static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1]; |
36 | static struct usb_driver xusbatm_usb_driver; |
37 | |
38 | static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep) |
39 | { |
40 | struct usb_host_interface *alt; |
41 | struct usb_interface *intf; |
42 | int i, j; |
43 | |
44 | for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) |
45 | if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altnum: altsetting))) |
46 | for (j = 0; j < alt->desc.bNumEndpoints; j++) |
47 | if (alt->endpoint[j].desc.bEndpointAddress == ep) |
48 | return intf; |
49 | return NULL; |
50 | } |
51 | |
52 | static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev, |
53 | struct usb_interface *intf, int altsetting, int claim) |
54 | { |
55 | int ifnum = intf->altsetting->desc.bInterfaceNumber; |
56 | int ret; |
57 | |
58 | if (claim && (ret = usb_driver_claim_interface(driver: &xusbatm_usb_driver, iface: intf, data: usbatm))) { |
59 | usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n" , __func__, ifnum, ret); |
60 | return ret; |
61 | } |
62 | ret = usb_set_interface(dev: usb_dev, ifnum, alternate: altsetting); |
63 | if (ret) { |
64 | usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n" , __func__, altsetting, ifnum, ret); |
65 | return ret; |
66 | } |
67 | return 0; |
68 | } |
69 | |
70 | static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed) |
71 | { |
72 | if (claimed) { |
73 | usb_set_intfdata(intf, NULL); |
74 | usb_driver_release_interface(driver: &xusbatm_usb_driver, iface: intf); |
75 | } |
76 | } |
77 | |
78 | static int xusbatm_bind(struct usbatm_data *usbatm, |
79 | struct usb_interface *intf, const struct usb_device_id *id) |
80 | { |
81 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
82 | int drv_ix = id - xusbatm_usb_ids; |
83 | int rx_alt = rx_altsetting[drv_ix]; |
84 | int tx_alt = tx_altsetting[drv_ix]; |
85 | struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, altsetting: rx_alt, ep: rx_endpoint[drv_ix]); |
86 | struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, altsetting: tx_alt, ep: tx_endpoint[drv_ix]); |
87 | int ret; |
88 | |
89 | usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x" |
90 | " rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n" , |
91 | __func__, drv_ix, vendor[drv_ix], product[drv_ix], |
92 | rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt, |
93 | tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt); |
94 | |
95 | if (!rx_intf || !tx_intf) { |
96 | if (!rx_intf) |
97 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n" , |
98 | __func__, rx_endpoint[drv_ix], rx_alt); |
99 | if (!tx_intf) |
100 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n" , |
101 | __func__, tx_endpoint[drv_ix], tx_alt); |
102 | return -ENODEV; |
103 | } |
104 | |
105 | if ((rx_intf != intf) && (tx_intf != intf)) |
106 | return -ENODEV; |
107 | |
108 | if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) { |
109 | usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n" , __func__, |
110 | rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt); |
111 | return -EINVAL; |
112 | } |
113 | |
114 | usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n" , __func__, |
115 | rx_intf->altsetting->desc.bInterfaceNumber, |
116 | tx_intf->altsetting->desc.bInterfaceNumber); |
117 | |
118 | ret = xusbatm_capture_intf(usbatm, usb_dev, intf: rx_intf, altsetting: rx_alt, claim: rx_intf != intf); |
119 | if (ret) |
120 | return ret; |
121 | |
122 | if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, intf: tx_intf, altsetting: tx_alt, claim: tx_intf != intf))) { |
123 | xusbatm_release_intf(usb_dev, intf: rx_intf, claimed: rx_intf != intf); |
124 | return ret; |
125 | } |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | static void xusbatm_unbind(struct usbatm_data *usbatm, |
131 | struct usb_interface *intf) |
132 | { |
133 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
134 | int i; |
135 | |
136 | usb_dbg(usbatm, "%s entered\n" , __func__); |
137 | |
138 | for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { |
139 | struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; |
140 | |
141 | if (cur_intf && (usb_get_intfdata(intf: cur_intf) == usbatm)) { |
142 | usb_set_intfdata(intf: cur_intf, NULL); |
143 | usb_driver_release_interface(driver: &xusbatm_usb_driver, iface: cur_intf); |
144 | } |
145 | } |
146 | } |
147 | |
148 | static int xusbatm_atm_start(struct usbatm_data *usbatm, |
149 | struct atm_dev *atm_dev) |
150 | { |
151 | atm_dbg(usbatm, "%s entered\n" , __func__); |
152 | |
153 | /* use random MAC as we've no way to get it from the device */ |
154 | eth_random_addr(addr: atm_dev->esi); |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | |
160 | static int xusbatm_usb_probe(struct usb_interface *intf, |
161 | const struct usb_device_id *id) |
162 | { |
163 | return usbatm_usb_probe(intf, id, |
164 | driver: xusbatm_drivers + (id - xusbatm_usb_ids)); |
165 | } |
166 | |
167 | static struct usb_driver xusbatm_usb_driver = { |
168 | .name = xusbatm_driver_name, |
169 | .probe = xusbatm_usb_probe, |
170 | .disconnect = usbatm_usb_disconnect, |
171 | .id_table = xusbatm_usb_ids |
172 | }; |
173 | |
174 | static int __init xusbatm_init(void) |
175 | { |
176 | int i; |
177 | |
178 | if (!num_vendor || |
179 | num_vendor != num_product || |
180 | num_vendor != num_rx_endpoint || |
181 | num_vendor != num_tx_endpoint) { |
182 | pr_warn("xusbatm: malformed module parameters\n" ); |
183 | return -EINVAL; |
184 | } |
185 | |
186 | for (i = 0; i < num_vendor; i++) { |
187 | rx_endpoint[i] |= USB_DIR_IN; |
188 | tx_endpoint[i] &= USB_ENDPOINT_NUMBER_MASK; |
189 | |
190 | xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; |
191 | xusbatm_usb_ids[i].idVendor = vendor[i]; |
192 | xusbatm_usb_ids[i].idProduct = product[i]; |
193 | |
194 | xusbatm_drivers[i].driver_name = xusbatm_driver_name; |
195 | xusbatm_drivers[i].bind = xusbatm_bind; |
196 | xusbatm_drivers[i].unbind = xusbatm_unbind; |
197 | xusbatm_drivers[i].atm_start = xusbatm_atm_start; |
198 | xusbatm_drivers[i].bulk_in = rx_endpoint[i]; |
199 | xusbatm_drivers[i].bulk_out = tx_endpoint[i]; |
200 | xusbatm_drivers[i].rx_padding = rx_padding[i]; |
201 | xusbatm_drivers[i].tx_padding = tx_padding[i]; |
202 | } |
203 | |
204 | return usb_register(&xusbatm_usb_driver); |
205 | } |
206 | module_init(xusbatm_init); |
207 | |
208 | static void __exit xusbatm_exit(void) |
209 | { |
210 | usb_deregister(&xusbatm_usb_driver); |
211 | } |
212 | module_exit(xusbatm_exit); |
213 | |
214 | MODULE_AUTHOR("Roman Kagan, Duncan Sands" ); |
215 | MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace" ); |
216 | MODULE_LICENSE("GPL" ); |
217 | |