]>
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> | |
0bb3cf37 DS |
23 | #include <linux/etherdevice.h> /* for random_ether_addr() */ |
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 | } | |
76 | if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) { | |
77 | usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret); | |
78 | return ret; | |
0bb3cf37 DS |
79 | } |
80 | return 0; | |
81 | } | |
82 | ||
16f76a76 | 83 | static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed) |
233c08e0 DS |
84 | { |
85 | if (claimed) { | |
86 | usb_set_intfdata(intf, NULL); | |
87 | usb_driver_release_interface(&xusbatm_usb_driver, intf); | |
88 | } | |
89 | } | |
90 | ||
0ec3c7e8 | 91 | static int xusbatm_bind(struct usbatm_data *usbatm, |
35644b0c | 92 | struct usb_interface *intf, const struct usb_device_id *id) |
0bb3cf37 DS |
93 | { |
94 | struct usb_device *usb_dev = interface_to_usbdev(intf); | |
95 | int drv_ix = id - xusbatm_usb_ids; | |
233c08e0 DS |
96 | int rx_alt = rx_altsetting[drv_ix]; |
97 | int tx_alt = tx_altsetting[drv_ix]; | |
98 | struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]); | |
99 | struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, tx_alt, tx_endpoint[drv_ix]); | |
100 | int ret; | |
0bb3cf37 | 101 | |
0ec3c7e8 | 102 | usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x" |
233c08e0 | 103 | " rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n", |
0bb3cf37 | 104 | __func__, drv_ix, vendor[drv_ix], product[drv_ix], |
233c08e0 DS |
105 | rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt, |
106 | tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt); | |
107 | ||
108 | if (!rx_intf || !tx_intf) { | |
109 | if (!rx_intf) | |
110 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", | |
111 | __func__, rx_endpoint[drv_ix], rx_alt); | |
112 | if (!tx_intf) | |
113 | usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", | |
114 | __func__, tx_endpoint[drv_ix], tx_alt); | |
115 | return -ENODEV; | |
116 | } | |
0bb3cf37 | 117 | |
233c08e0 | 118 | if ((rx_intf != intf) && (tx_intf != intf)) |
0bb3cf37 | 119 | return -ENODEV; |
233c08e0 DS |
120 | |
121 | if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) { | |
122 | usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n", __func__, | |
123 | rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt); | |
124 | return -EINVAL; | |
0bb3cf37 DS |
125 | } |
126 | ||
233c08e0 DS |
127 | usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__, |
128 | rx_intf->altsetting->desc.bInterfaceNumber, | |
129 | tx_intf->altsetting->desc.bInterfaceNumber); | |
0bb3cf37 | 130 | |
233c08e0 DS |
131 | if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf))) |
132 | return ret; | |
133 | ||
134 | if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) { | |
135 | xusbatm_release_intf(usb_dev, rx_intf, rx_intf != intf); | |
136 | return ret; | |
0bb3cf37 DS |
137 | } |
138 | ||
233c08e0 | 139 | return 0; |
0bb3cf37 DS |
140 | } |
141 | ||
0ec3c7e8 | 142 | static void xusbatm_unbind(struct usbatm_data *usbatm, |
0bb3cf37 DS |
143 | struct usb_interface *intf) |
144 | { | |
145 | struct usb_device *usb_dev = interface_to_usbdev(intf); | |
146 | int i; | |
0ec3c7e8 DS |
147 | |
148 | usb_dbg(usbatm, "%s entered\n", __func__); | |
0bb3cf37 | 149 | |
16f76a76 | 150 | for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { |
233c08e0 DS |
151 | struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; |
152 | ||
153 | if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) { | |
154 | usb_set_intfdata(cur_intf, NULL); | |
155 | usb_driver_release_interface(&xusbatm_usb_driver, cur_intf); | |
156 | } | |
0bb3cf37 DS |
157 | } |
158 | } | |
159 | ||
0ec3c7e8 | 160 | static int xusbatm_atm_start(struct usbatm_data *usbatm, |
0bb3cf37 DS |
161 | struct atm_dev *atm_dev) |
162 | { | |
0ec3c7e8 | 163 | atm_dbg(usbatm, "%s entered\n", __func__); |
0bb3cf37 DS |
164 | |
165 | /* use random MAC as we've no way to get it from the device */ | |
166 | random_ether_addr(atm_dev->esi); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | ||
172 | static int xusbatm_usb_probe(struct usb_interface *intf, | |
173 | const struct usb_device_id *id) | |
174 | { | |
175 | return usbatm_usb_probe(intf, id, | |
176 | xusbatm_drivers + (id - xusbatm_usb_ids)); | |
177 | } | |
178 | ||
179 | static struct usb_driver xusbatm_usb_driver = { | |
0bb3cf37 DS |
180 | .name = xusbatm_driver_name, |
181 | .probe = xusbatm_usb_probe, | |
182 | .disconnect = usbatm_usb_disconnect, | |
183 | .id_table = xusbatm_usb_ids | |
184 | }; | |
185 | ||
186 | static int __init xusbatm_init(void) | |
187 | { | |
188 | int i; | |
189 | ||
190 | dbg("xusbatm_init"); | |
191 | ||
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 | { | |
224 | dbg("xusbatm_exit entered"); | |
225 | ||
226 | usb_deregister(&xusbatm_usb_driver); | |
227 | } | |
228 | module_exit(xusbatm_exit); | |
229 | ||
230 | MODULE_AUTHOR("Roman Kagan, Duncan Sands"); | |
231 | MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace"); | |
232 | MODULE_LICENSE("GPL"); | |
233 | MODULE_VERSION("0.1"); |