]>
Commit | Line | Data |
---|---|---|
0bb3cf37 DS |
1 | /****************************************************************************** |
2 | * xusbatm.c - dumb usbatm-based driver for modems initialized in userspace | |
3 | * | |
4 | * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program; if not, write to the Free Software Foundation, Inc., 59 | |
18 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | * | |
20 | ******************************************************************************/ | |
21 | ||
22 | #include <linux/module.h> | |
006c9139 | 23 | #include <linux/etherdevice.h> /* for eth_random_addr() */ |
0bb3cf37 DS |
24 | |
25 | #include "usbatm.h" | |
26 | ||
27 | ||
28 | #define XUSBATM_DRIVERS_MAX 8 | |
29 | ||
30 | #define XUSBATM_PARM(name, type, parmtype, desc) \ | |
31 | static type name[XUSBATM_DRIVERS_MAX]; \ | |
64a6f950 | 32 | static unsigned int num_##name; \ |
0bb3cf37 DS |
33 | module_param_array(name, parmtype, &num_##name, 0444); \ |
34 | MODULE_PARM_DESC(name, desc) | |
35 | ||
36 | XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor"); | |
37 | XUSBATM_PARM(product, unsigned short, ushort, "USB device product"); | |
38 | ||
39 | XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number"); | |
40 | XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number"); | |
41 | XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)"); | |
42 | XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)"); | |
233c08e0 DS |
43 | XUSBATM_PARM(rx_altsetting, unsigned char, byte, "rx altsetting (default 0)"); |
44 | XUSBATM_PARM(tx_altsetting, unsigned char, byte, "rx altsetting (default 0)"); | |
0bb3cf37 DS |
45 | |
46 | static const char xusbatm_driver_name[] = "xusbatm"; | |
47 | ||
48 | static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX]; | |
49 | static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1]; | |
50 | static struct usb_driver xusbatm_usb_driver; | |
51 | ||
16f76a76 | 52 | static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep) |
0bb3cf37 | 53 | { |
233c08e0 DS |
54 | struct usb_host_interface *alt; |
55 | struct usb_interface *intf; | |
0bb3cf37 DS |
56 | int i, j; |
57 | ||
16f76a76 | 58 | for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) |
233c08e0 DS |
59 | if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting))) |
60 | for (j = 0; j < alt->desc.bNumEndpoints; j++) | |
61 | if (alt->endpoint[j].desc.bEndpointAddress == ep) | |
62 | return intf; | |
63 | return NULL; | |
64 | } | |
65 | ||
16f76a76 | 66 | static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev, |
233c08e0 DS |
67 | struct usb_interface *intf, int altsetting, int claim) |
68 | { | |
69 | int ifnum = intf->altsetting->desc.bInterfaceNumber; | |
70 | int ret; | |
71 | ||
72 | if (claim && (ret = usb_driver_claim_interface(&xusbatm_usb_driver, intf, usbatm))) { | |
73 | usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret); | |
74 | return ret; | |
75 | } | |
de519881 GKH |
76 | ret = usb_set_interface(usb_dev, ifnum, altsetting); |
77 | if (ret) { | |
233c08e0 DS |
78 | usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret); |
79 | return ret; | |
0bb3cf37 DS |
80 | } |
81 | return 0; | |
82 | } | |
83 | ||
16f76a76 | 84 | static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed) |
233c08e0 DS |
85 | { |
86 | if (claimed) { | |
87 | usb_set_intfdata(intf, NULL); | |
88 | usb_driver_release_interface(&xusbatm_usb_driver, intf); | |
89 | } | |
90 | } | |
91 | ||
0ec3c7e8 | 92 | static int xusbatm_bind(struct usbatm_data *usbatm, |
35644b0c | 93 | struct usb_interface *intf, const struct usb_device_id *id) |
0bb3cf37 DS |
94 | { |
95 | struct usb_device *usb_dev = interface_to_usbdev(intf); | |
96 | int drv_ix = id - xusbatm_usb_ids; | |
233c08e0 DS |
97 | int rx_alt = rx_altsetting[drv_ix]; |
98 | int tx_alt = tx_altsetting[drv_ix]; | |
99 | struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]); | |
100 | struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, tx_alt, tx_endpoint[drv_ix]); | |
101 | int ret; | |
0bb3cf37 | 102 | |
0ec3c7e8 | 103 | usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x" |
233c08e0 | 104 | " rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n", |
0bb3cf37 | 105 | __func__, drv_ix, vendor[drv_ix], product[drv_ix], |
233c08e0 DS |
106 | rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt, |
107 | tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt); | |
108 | ||
109 | if (!rx_intf || !tx_intf) { | |
110 | if (!rx_intf) | |
111 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", | |
112 | __func__, rx_endpoint[drv_ix], rx_alt); | |
113 | if (!tx_intf) | |
114 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", | |
115 | __func__, tx_endpoint[drv_ix], tx_alt); | |
116 | return -ENODEV; | |
117 | } | |
0bb3cf37 | 118 | |
233c08e0 | 119 | if ((rx_intf != intf) && (tx_intf != intf)) |
0bb3cf37 | 120 | return -ENODEV; |
233c08e0 DS |
121 | |
122 | if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) { | |
123 | usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n", __func__, | |
124 | rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt); | |
125 | return -EINVAL; | |
0bb3cf37 DS |
126 | } |
127 | ||
233c08e0 DS |
128 | usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__, |
129 | rx_intf->altsetting->desc.bInterfaceNumber, | |
130 | tx_intf->altsetting->desc.bInterfaceNumber); | |
0bb3cf37 | 131 | |
de519881 GKH |
132 | ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf); |
133 | if (ret) | |
233c08e0 DS |
134 | return ret; |
135 | ||
136 | if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) { | |
137 | xusbatm_release_intf(usb_dev, rx_intf, rx_intf != intf); | |
138 | return ret; | |
0bb3cf37 DS |
139 | } |
140 | ||
233c08e0 | 141 | return 0; |
0bb3cf37 DS |
142 | } |
143 | ||
0ec3c7e8 | 144 | static void xusbatm_unbind(struct usbatm_data *usbatm, |
0bb3cf37 DS |
145 | struct usb_interface *intf) |
146 | { | |
147 | struct usb_device *usb_dev = interface_to_usbdev(intf); | |
148 | int i; | |
0ec3c7e8 DS |
149 | |
150 | usb_dbg(usbatm, "%s entered\n", __func__); | |
0bb3cf37 | 151 | |
16f76a76 | 152 | for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { |
233c08e0 DS |
153 | struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; |
154 | ||
155 | if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) { | |
156 | usb_set_intfdata(cur_intf, NULL); | |
157 | usb_driver_release_interface(&xusbatm_usb_driver, cur_intf); | |
158 | } | |
0bb3cf37 DS |
159 | } |
160 | } | |
161 | ||
0ec3c7e8 | 162 | static int xusbatm_atm_start(struct usbatm_data *usbatm, |
0bb3cf37 DS |
163 | struct atm_dev *atm_dev) |
164 | { | |
0ec3c7e8 | 165 | atm_dbg(usbatm, "%s entered\n", __func__); |
0bb3cf37 DS |
166 | |
167 | /* use random MAC as we've no way to get it from the device */ | |
006c9139 | 168 | eth_random_addr(atm_dev->esi); |
0bb3cf37 DS |
169 | |
170 | return 0; | |
171 | } | |
172 | ||
173 | ||
174 | static int xusbatm_usb_probe(struct usb_interface *intf, | |
175 | const struct usb_device_id *id) | |
176 | { | |
177 | return usbatm_usb_probe(intf, id, | |
178 | xusbatm_drivers + (id - xusbatm_usb_ids)); | |
179 | } | |
180 | ||
181 | static struct usb_driver xusbatm_usb_driver = { | |
0bb3cf37 DS |
182 | .name = xusbatm_driver_name, |
183 | .probe = xusbatm_usb_probe, | |
184 | .disconnect = usbatm_usb_disconnect, | |
185 | .id_table = xusbatm_usb_ids | |
186 | }; | |
187 | ||
188 | static int __init xusbatm_init(void) | |
189 | { | |
190 | int i; | |
191 | ||
0bb3cf37 DS |
192 | if (!num_vendor || |
193 | num_vendor != num_product || | |
194 | num_vendor != num_rx_endpoint || | |
195 | num_vendor != num_tx_endpoint) { | |
3b6004f3 | 196 | printk(KERN_WARNING "xusbatm: malformed module parameters\n"); |
0bb3cf37 DS |
197 | return -EINVAL; |
198 | } | |
199 | ||
200 | for (i = 0; i < num_vendor; i++) { | |
233c08e0 DS |
201 | rx_endpoint[i] |= USB_DIR_IN; |
202 | tx_endpoint[i] &= USB_ENDPOINT_NUMBER_MASK; | |
203 | ||
0bb3cf37 DS |
204 | xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; |
205 | xusbatm_usb_ids[i].idVendor = vendor[i]; | |
206 | xusbatm_usb_ids[i].idProduct = product[i]; | |
207 | ||
0bb3cf37 DS |
208 | xusbatm_drivers[i].driver_name = xusbatm_driver_name; |
209 | xusbatm_drivers[i].bind = xusbatm_bind; | |
210 | xusbatm_drivers[i].unbind = xusbatm_unbind; | |
211 | xusbatm_drivers[i].atm_start = xusbatm_atm_start; | |
80aae7a1 DS |
212 | xusbatm_drivers[i].bulk_in = rx_endpoint[i]; |
213 | xusbatm_drivers[i].bulk_out = tx_endpoint[i]; | |
0bb3cf37 DS |
214 | xusbatm_drivers[i].rx_padding = rx_padding[i]; |
215 | xusbatm_drivers[i].tx_padding = tx_padding[i]; | |
216 | } | |
217 | ||
218 | return usb_register(&xusbatm_usb_driver); | |
219 | } | |
220 | module_init(xusbatm_init); | |
221 | ||
222 | static void __exit xusbatm_exit(void) | |
223 | { | |
0bb3cf37 DS |
224 | usb_deregister(&xusbatm_usb_driver); |
225 | } | |
226 | module_exit(xusbatm_exit); | |
227 | ||
228 | MODULE_AUTHOR("Roman Kagan, Duncan Sands"); | |
229 | MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace"); | |
230 | MODULE_LICENSE("GPL"); | |
231 | MODULE_VERSION("0.1"); |