1 |
62 |
marcus.erl |
/****************************************************************
|
2 |
|
|
*
|
3 |
|
|
* kaweth.c - driver for KL5KUSB101 based USB->Ethernet
|
4 |
|
|
*
|
5 |
|
|
* (c) 2000 Interlan Communications
|
6 |
|
|
* (c) 2000 Stephane Alnet
|
7 |
|
|
* (C) 2001 Brad Hards
|
8 |
|
|
* (C) 2002 Oliver Neukum
|
9 |
|
|
*
|
10 |
|
|
* Original author: The Zapman <zapman@interlan.net>
|
11 |
|
|
* Inspired by, and much credit goes to Michael Rothwell
|
12 |
|
|
* <rothwell@interlan.net> for the test equipment, help, and patience
|
13 |
|
|
* Based off of (and with thanks to) Petko Manolov's pegaus.c driver.
|
14 |
|
|
* Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki
|
15 |
|
|
* for providing the firmware and driver resources.
|
16 |
|
|
*
|
17 |
|
|
* This program is free software; you can redistribute it and/or
|
18 |
|
|
* modify it under the terms of the GNU General Public License as
|
19 |
|
|
* published by the Free Software Foundation; either version 2, or
|
20 |
|
|
* (at your option) any later version.
|
21 |
|
|
*
|
22 |
|
|
* This program is distributed in the hope that it will be useful,
|
23 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
24 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
25 |
|
|
* GNU General Public License for more details.
|
26 |
|
|
*
|
27 |
|
|
* You should have received a copy of the GNU General Public License
|
28 |
|
|
* along with this program; if not, write to the Free Software Foundation,
|
29 |
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
30 |
|
|
*
|
31 |
|
|
****************************************************************/
|
32 |
|
|
|
33 |
|
|
/* TODO:
|
34 |
|
|
* Fix in_interrupt() problem
|
35 |
|
|
* Develop test procedures for USB net interfaces
|
36 |
|
|
* Run test procedures
|
37 |
|
|
* Fix bugs from previous two steps
|
38 |
|
|
* Snoop other OSs for any tricks we're not doing
|
39 |
|
|
* SMP locking
|
40 |
|
|
* Reduce arbitrary timeouts
|
41 |
|
|
* Smart multicast support
|
42 |
|
|
* Temporary MAC change support
|
43 |
|
|
* Tunable SOFs parameter - ioctl()?
|
44 |
|
|
* Ethernet stats collection
|
45 |
|
|
* Code formatting improvements
|
46 |
|
|
*/
|
47 |
|
|
|
48 |
|
|
#include <linux/module.h>
|
49 |
|
|
#include <linux/slab.h>
|
50 |
|
|
#include <linux/string.h>
|
51 |
|
|
#include <linux/init.h>
|
52 |
|
|
#include <linux/delay.h>
|
53 |
|
|
#include <linux/netdevice.h>
|
54 |
|
|
#include <linux/etherdevice.h>
|
55 |
|
|
#include <linux/usb.h>
|
56 |
|
|
#include <linux/types.h>
|
57 |
|
|
#include <linux/ethtool.h>
|
58 |
|
|
#include <linux/dma-mapping.h>
|
59 |
|
|
#include <linux/wait.h>
|
60 |
|
|
#include <asm/uaccess.h>
|
61 |
|
|
#include <asm/semaphore.h>
|
62 |
|
|
#include <asm/byteorder.h>
|
63 |
|
|
|
64 |
|
|
#undef DEBUG
|
65 |
|
|
|
66 |
|
|
#include "kawethfw.h"
|
67 |
|
|
|
68 |
|
|
#define KAWETH_MTU 1514
|
69 |
|
|
#define KAWETH_BUF_SIZE 1664
|
70 |
|
|
#define KAWETH_TX_TIMEOUT (5 * HZ)
|
71 |
|
|
#define KAWETH_SCRATCH_SIZE 32
|
72 |
|
|
#define KAWETH_FIRMWARE_BUF_SIZE 4096
|
73 |
|
|
#define KAWETH_CONTROL_TIMEOUT (30000)
|
74 |
|
|
|
75 |
|
|
#define KAWETH_STATUS_BROKEN 0x0000001
|
76 |
|
|
#define KAWETH_STATUS_CLOSING 0x0000002
|
77 |
|
|
#define KAWETH_STATUS_SUSPENDING 0x0000004
|
78 |
|
|
|
79 |
|
|
#define KAWETH_STATUS_BLOCKED (KAWETH_STATUS_CLOSING | KAWETH_STATUS_SUSPENDING)
|
80 |
|
|
|
81 |
|
|
#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01
|
82 |
|
|
#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02
|
83 |
|
|
#define KAWETH_PACKET_FILTER_DIRECTED 0x04
|
84 |
|
|
#define KAWETH_PACKET_FILTER_BROADCAST 0x08
|
85 |
|
|
#define KAWETH_PACKET_FILTER_MULTICAST 0x10
|
86 |
|
|
|
87 |
|
|
/* Table 7 */
|
88 |
|
|
#define KAWETH_COMMAND_GET_ETHERNET_DESC 0x00
|
89 |
|
|
#define KAWETH_COMMAND_MULTICAST_FILTERS 0x01
|
90 |
|
|
#define KAWETH_COMMAND_SET_PACKET_FILTER 0x02
|
91 |
|
|
#define KAWETH_COMMAND_STATISTICS 0x03
|
92 |
|
|
#define KAWETH_COMMAND_SET_TEMP_MAC 0x06
|
93 |
|
|
#define KAWETH_COMMAND_GET_TEMP_MAC 0x07
|
94 |
|
|
#define KAWETH_COMMAND_SET_URB_SIZE 0x08
|
95 |
|
|
#define KAWETH_COMMAND_SET_SOFS_WAIT 0x09
|
96 |
|
|
#define KAWETH_COMMAND_SCAN 0xFF
|
97 |
|
|
|
98 |
|
|
#define KAWETH_SOFS_TO_WAIT 0x05
|
99 |
|
|
|
100 |
|
|
#define INTBUFFERSIZE 4
|
101 |
|
|
|
102 |
|
|
#define STATE_OFFSET 0
|
103 |
|
|
#define STATE_MASK 0x40
|
104 |
|
|
#define STATE_SHIFT 5
|
105 |
|
|
|
106 |
|
|
#define IS_BLOCKED(s) (s & KAWETH_STATUS_BLOCKED)
|
107 |
|
|
|
108 |
|
|
|
109 |
|
|
MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr>, Brad Hards <bhards@bigpond.net.au> and Oliver Neukum <oliver@neukum.org>");
|
110 |
|
|
MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
|
111 |
|
|
MODULE_LICENSE("GPL");
|
112 |
|
|
|
113 |
|
|
static const char driver_name[] = "kaweth";
|
114 |
|
|
|
115 |
|
|
static int kaweth_probe(
|
116 |
|
|
struct usb_interface *intf,
|
117 |
|
|
const struct usb_device_id *id /* from id_table */
|
118 |
|
|
);
|
119 |
|
|
static void kaweth_disconnect(struct usb_interface *intf);
|
120 |
|
|
static int kaweth_internal_control_msg(struct usb_device *usb_dev,
|
121 |
|
|
unsigned int pipe,
|
122 |
|
|
struct usb_ctrlrequest *cmd, void *data,
|
123 |
|
|
int len, int timeout);
|
124 |
|
|
static int kaweth_suspend(struct usb_interface *intf, pm_message_t message);
|
125 |
|
|
static int kaweth_resume(struct usb_interface *intf);
|
126 |
|
|
|
127 |
|
|
/****************************************************************
|
128 |
|
|
* usb_device_id
|
129 |
|
|
****************************************************************/
|
130 |
|
|
static struct usb_device_id usb_klsi_table[] = {
|
131 |
|
|
{ USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */
|
132 |
|
|
{ USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */
|
133 |
|
|
{ USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */
|
134 |
|
|
{ USB_DEVICE(0x0506, 0x11f8) }, /* 3Com 3C460 */
|
135 |
|
|
{ USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */
|
136 |
|
|
{ USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */
|
137 |
|
|
{ USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */
|
138 |
|
|
{ USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */
|
139 |
|
|
{ USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */
|
140 |
|
|
{ USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */
|
141 |
|
|
{ USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */
|
142 |
|
|
{ USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */
|
143 |
|
|
{ USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */
|
144 |
|
|
{ USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */
|
145 |
|
|
{ USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */
|
146 |
|
|
{ USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */
|
147 |
|
|
{ USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */
|
148 |
|
|
{ USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */
|
149 |
|
|
{ USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */
|
150 |
|
|
{ USB_DEVICE(0x085a, 0x0008) }, /* PortGear Ethernet Adapter */
|
151 |
|
|
{ USB_DEVICE(0x085a, 0x0009) }, /* PortGear Ethernet Adapter */
|
152 |
|
|
{ USB_DEVICE(0x087d, 0x5704) }, /* Jaton USB Ethernet Device Adapter */
|
153 |
|
|
{ USB_DEVICE(0x0951, 0x0008) }, /* Kingston Technology USB Ethernet Adapter */
|
154 |
|
|
{ USB_DEVICE(0x095a, 0x3003) }, /* Portsmith Express Ethernet Adapter */
|
155 |
|
|
{ USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */
|
156 |
|
|
{ USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */
|
157 |
|
|
{ USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */
|
158 |
|
|
{ USB_DEVICE(0x1485, 0x0001) }, /* Silicom U2E */
|
159 |
|
|
{ USB_DEVICE(0x1485, 0x0002) }, /* Psion Dacom Gold Port Ethernet */
|
160 |
|
|
{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */
|
161 |
|
|
{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */
|
162 |
|
|
{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */
|
163 |
|
|
{ USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */
|
164 |
|
|
{ USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */
|
165 |
|
|
{} /* Null terminator */
|
166 |
|
|
};
|
167 |
|
|
|
168 |
|
|
MODULE_DEVICE_TABLE (usb, usb_klsi_table);
|
169 |
|
|
|
170 |
|
|
/****************************************************************
|
171 |
|
|
* kaweth_driver
|
172 |
|
|
****************************************************************/
|
173 |
|
|
static struct usb_driver kaweth_driver = {
|
174 |
|
|
.name = driver_name,
|
175 |
|
|
.probe = kaweth_probe,
|
176 |
|
|
.disconnect = kaweth_disconnect,
|
177 |
|
|
.suspend = kaweth_suspend,
|
178 |
|
|
.resume = kaweth_resume,
|
179 |
|
|
.id_table = usb_klsi_table,
|
180 |
|
|
.supports_autosuspend = 1,
|
181 |
|
|
};
|
182 |
|
|
|
183 |
|
|
typedef __u8 eth_addr_t[6];
|
184 |
|
|
|
185 |
|
|
/****************************************************************
|
186 |
|
|
* usb_eth_dev
|
187 |
|
|
****************************************************************/
|
188 |
|
|
struct usb_eth_dev {
|
189 |
|
|
char *name;
|
190 |
|
|
__u16 vendor;
|
191 |
|
|
__u16 device;
|
192 |
|
|
void *pdata;
|
193 |
|
|
};
|
194 |
|
|
|
195 |
|
|
/****************************************************************
|
196 |
|
|
* kaweth_ethernet_configuration
|
197 |
|
|
* Refer Table 8
|
198 |
|
|
****************************************************************/
|
199 |
|
|
struct kaweth_ethernet_configuration
|
200 |
|
|
{
|
201 |
|
|
__u8 size;
|
202 |
|
|
__u8 reserved1;
|
203 |
|
|
__u8 reserved2;
|
204 |
|
|
eth_addr_t hw_addr;
|
205 |
|
|
__u32 statistics_mask;
|
206 |
|
|
__le16 segment_size;
|
207 |
|
|
__u16 max_multicast_filters;
|
208 |
|
|
__u8 reserved3;
|
209 |
|
|
} __attribute__ ((packed));
|
210 |
|
|
|
211 |
|
|
/****************************************************************
|
212 |
|
|
* kaweth_device
|
213 |
|
|
****************************************************************/
|
214 |
|
|
struct kaweth_device
|
215 |
|
|
{
|
216 |
|
|
spinlock_t device_lock;
|
217 |
|
|
|
218 |
|
|
__u32 status;
|
219 |
|
|
int end;
|
220 |
|
|
int suspend_lowmem_rx;
|
221 |
|
|
int suspend_lowmem_ctrl;
|
222 |
|
|
int linkstate;
|
223 |
|
|
int opened;
|
224 |
|
|
struct delayed_work lowmem_work;
|
225 |
|
|
|
226 |
|
|
struct usb_device *dev;
|
227 |
|
|
struct usb_interface *intf;
|
228 |
|
|
struct net_device *net;
|
229 |
|
|
wait_queue_head_t term_wait;
|
230 |
|
|
|
231 |
|
|
struct urb *rx_urb;
|
232 |
|
|
struct urb *tx_urb;
|
233 |
|
|
struct urb *irq_urb;
|
234 |
|
|
|
235 |
|
|
dma_addr_t intbufferhandle;
|
236 |
|
|
__u8 *intbuffer;
|
237 |
|
|
dma_addr_t rxbufferhandle;
|
238 |
|
|
__u8 *rx_buf;
|
239 |
|
|
|
240 |
|
|
|
241 |
|
|
struct sk_buff *tx_skb;
|
242 |
|
|
|
243 |
|
|
__u8 *firmware_buf;
|
244 |
|
|
__u8 scratch[KAWETH_SCRATCH_SIZE];
|
245 |
|
|
__u16 packet_filter_bitmap;
|
246 |
|
|
|
247 |
|
|
struct kaweth_ethernet_configuration configuration;
|
248 |
|
|
|
249 |
|
|
struct net_device_stats stats;
|
250 |
|
|
};
|
251 |
|
|
|
252 |
|
|
|
253 |
|
|
/****************************************************************
|
254 |
|
|
* kaweth_control
|
255 |
|
|
****************************************************************/
|
256 |
|
|
static int kaweth_control(struct kaweth_device *kaweth,
|
257 |
|
|
unsigned int pipe,
|
258 |
|
|
__u8 request,
|
259 |
|
|
__u8 requesttype,
|
260 |
|
|
__u16 value,
|
261 |
|
|
__u16 index,
|
262 |
|
|
void *data,
|
263 |
|
|
__u16 size,
|
264 |
|
|
int timeout)
|
265 |
|
|
{
|
266 |
|
|
struct usb_ctrlrequest *dr;
|
267 |
|
|
|
268 |
|
|
dbg("kaweth_control()");
|
269 |
|
|
|
270 |
|
|
if(in_interrupt()) {
|
271 |
|
|
dbg("in_interrupt()");
|
272 |
|
|
return -EBUSY;
|
273 |
|
|
}
|
274 |
|
|
|
275 |
|
|
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
|
276 |
|
|
|
277 |
|
|
if (!dr) {
|
278 |
|
|
dbg("kmalloc() failed");
|
279 |
|
|
return -ENOMEM;
|
280 |
|
|
}
|
281 |
|
|
|
282 |
|
|
dr->bRequestType= requesttype;
|
283 |
|
|
dr->bRequest = request;
|
284 |
|
|
dr->wValue = cpu_to_le16p(&value);
|
285 |
|
|
dr->wIndex = cpu_to_le16p(&index);
|
286 |
|
|
dr->wLength = cpu_to_le16p(&size);
|
287 |
|
|
|
288 |
|
|
return kaweth_internal_control_msg(kaweth->dev,
|
289 |
|
|
pipe,
|
290 |
|
|
dr,
|
291 |
|
|
data,
|
292 |
|
|
size,
|
293 |
|
|
timeout);
|
294 |
|
|
}
|
295 |
|
|
|
296 |
|
|
/****************************************************************
|
297 |
|
|
* kaweth_read_configuration
|
298 |
|
|
****************************************************************/
|
299 |
|
|
static int kaweth_read_configuration(struct kaweth_device *kaweth)
|
300 |
|
|
{
|
301 |
|
|
int retval;
|
302 |
|
|
|
303 |
|
|
dbg("Reading kaweth configuration");
|
304 |
|
|
|
305 |
|
|
retval = kaweth_control(kaweth,
|
306 |
|
|
usb_rcvctrlpipe(kaweth->dev, 0),
|
307 |
|
|
KAWETH_COMMAND_GET_ETHERNET_DESC,
|
308 |
|
|
USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
|
309 |
|
|
0,
|
310 |
|
|
0,
|
311 |
|
|
(void *)&kaweth->configuration,
|
312 |
|
|
sizeof(kaweth->configuration),
|
313 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
314 |
|
|
|
315 |
|
|
return retval;
|
316 |
|
|
}
|
317 |
|
|
|
318 |
|
|
/****************************************************************
|
319 |
|
|
* kaweth_set_urb_size
|
320 |
|
|
****************************************************************/
|
321 |
|
|
static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)
|
322 |
|
|
{
|
323 |
|
|
int retval;
|
324 |
|
|
|
325 |
|
|
dbg("Setting URB size to %d", (unsigned)urb_size);
|
326 |
|
|
|
327 |
|
|
retval = kaweth_control(kaweth,
|
328 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
329 |
|
|
KAWETH_COMMAND_SET_URB_SIZE,
|
330 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
331 |
|
|
urb_size,
|
332 |
|
|
0,
|
333 |
|
|
(void *)&kaweth->scratch,
|
334 |
|
|
0,
|
335 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
336 |
|
|
|
337 |
|
|
return retval;
|
338 |
|
|
}
|
339 |
|
|
|
340 |
|
|
/****************************************************************
|
341 |
|
|
* kaweth_set_sofs_wait
|
342 |
|
|
****************************************************************/
|
343 |
|
|
static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)
|
344 |
|
|
{
|
345 |
|
|
int retval;
|
346 |
|
|
|
347 |
|
|
dbg("Set SOFS wait to %d", (unsigned)sofs_wait);
|
348 |
|
|
|
349 |
|
|
retval = kaweth_control(kaweth,
|
350 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
351 |
|
|
KAWETH_COMMAND_SET_SOFS_WAIT,
|
352 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
353 |
|
|
sofs_wait,
|
354 |
|
|
0,
|
355 |
|
|
(void *)&kaweth->scratch,
|
356 |
|
|
0,
|
357 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
358 |
|
|
|
359 |
|
|
return retval;
|
360 |
|
|
}
|
361 |
|
|
|
362 |
|
|
/****************************************************************
|
363 |
|
|
* kaweth_set_receive_filter
|
364 |
|
|
****************************************************************/
|
365 |
|
|
static int kaweth_set_receive_filter(struct kaweth_device *kaweth,
|
366 |
|
|
__u16 receive_filter)
|
367 |
|
|
{
|
368 |
|
|
int retval;
|
369 |
|
|
|
370 |
|
|
dbg("Set receive filter to %d", (unsigned)receive_filter);
|
371 |
|
|
|
372 |
|
|
retval = kaweth_control(kaweth,
|
373 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
374 |
|
|
KAWETH_COMMAND_SET_PACKET_FILTER,
|
375 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
376 |
|
|
receive_filter,
|
377 |
|
|
0,
|
378 |
|
|
(void *)&kaweth->scratch,
|
379 |
|
|
0,
|
380 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
381 |
|
|
|
382 |
|
|
return retval;
|
383 |
|
|
}
|
384 |
|
|
|
385 |
|
|
/****************************************************************
|
386 |
|
|
* kaweth_download_firmware
|
387 |
|
|
****************************************************************/
|
388 |
|
|
static int kaweth_download_firmware(struct kaweth_device *kaweth,
|
389 |
|
|
__u8 *data,
|
390 |
|
|
__u16 data_len,
|
391 |
|
|
__u8 interrupt,
|
392 |
|
|
__u8 type)
|
393 |
|
|
{
|
394 |
|
|
if(data_len > KAWETH_FIRMWARE_BUF_SIZE) {
|
395 |
|
|
err("Firmware too big: %d", data_len);
|
396 |
|
|
return -ENOSPC;
|
397 |
|
|
}
|
398 |
|
|
|
399 |
|
|
memcpy(kaweth->firmware_buf, data, data_len);
|
400 |
|
|
|
401 |
|
|
kaweth->firmware_buf[2] = (data_len & 0xFF) - 7;
|
402 |
|
|
kaweth->firmware_buf[3] = data_len >> 8;
|
403 |
|
|
kaweth->firmware_buf[4] = type;
|
404 |
|
|
kaweth->firmware_buf[5] = interrupt;
|
405 |
|
|
|
406 |
|
|
dbg("High: %i, Low:%i", kaweth->firmware_buf[3],
|
407 |
|
|
kaweth->firmware_buf[2]);
|
408 |
|
|
|
409 |
|
|
dbg("Downloading firmware at %p to kaweth device at %p",
|
410 |
|
|
data,
|
411 |
|
|
kaweth);
|
412 |
|
|
dbg("Firmware length: %d", data_len);
|
413 |
|
|
|
414 |
|
|
return kaweth_control(kaweth,
|
415 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
416 |
|
|
KAWETH_COMMAND_SCAN,
|
417 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
418 |
|
|
0,
|
419 |
|
|
0,
|
420 |
|
|
(void *)kaweth->firmware_buf,
|
421 |
|
|
data_len,
|
422 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
423 |
|
|
}
|
424 |
|
|
|
425 |
|
|
/****************************************************************
|
426 |
|
|
* kaweth_trigger_firmware
|
427 |
|
|
****************************************************************/
|
428 |
|
|
static int kaweth_trigger_firmware(struct kaweth_device *kaweth,
|
429 |
|
|
__u8 interrupt)
|
430 |
|
|
{
|
431 |
|
|
kaweth->firmware_buf[0] = 0xB6;
|
432 |
|
|
kaweth->firmware_buf[1] = 0xC3;
|
433 |
|
|
kaweth->firmware_buf[2] = 0x01;
|
434 |
|
|
kaweth->firmware_buf[3] = 0x00;
|
435 |
|
|
kaweth->firmware_buf[4] = 0x06;
|
436 |
|
|
kaweth->firmware_buf[5] = interrupt;
|
437 |
|
|
kaweth->firmware_buf[6] = 0x00;
|
438 |
|
|
kaweth->firmware_buf[7] = 0x00;
|
439 |
|
|
|
440 |
|
|
dbg("Triggering firmware");
|
441 |
|
|
|
442 |
|
|
return kaweth_control(kaweth,
|
443 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
444 |
|
|
KAWETH_COMMAND_SCAN,
|
445 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
446 |
|
|
0,
|
447 |
|
|
0,
|
448 |
|
|
(void *)kaweth->firmware_buf,
|
449 |
|
|
8,
|
450 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
451 |
|
|
}
|
452 |
|
|
|
453 |
|
|
/****************************************************************
|
454 |
|
|
* kaweth_reset
|
455 |
|
|
****************************************************************/
|
456 |
|
|
static int kaweth_reset(struct kaweth_device *kaweth)
|
457 |
|
|
{
|
458 |
|
|
int result;
|
459 |
|
|
|
460 |
|
|
dbg("kaweth_reset(%p)", kaweth);
|
461 |
|
|
result = kaweth_control(kaweth,
|
462 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
463 |
|
|
USB_REQ_SET_CONFIGURATION,
|
464 |
|
|
0,
|
465 |
|
|
kaweth->dev->config[0].desc.bConfigurationValue,
|
466 |
|
|
0,
|
467 |
|
|
NULL,
|
468 |
|
|
0,
|
469 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
470 |
|
|
|
471 |
|
|
mdelay(10);
|
472 |
|
|
|
473 |
|
|
dbg("kaweth_reset() returns %d.",result);
|
474 |
|
|
|
475 |
|
|
return result;
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
static void kaweth_usb_receive(struct urb *);
|
479 |
|
|
static int kaweth_resubmit_rx_urb(struct kaweth_device *, gfp_t);
|
480 |
|
|
|
481 |
|
|
/****************************************************************
|
482 |
|
|
int_callback
|
483 |
|
|
*****************************************************************/
|
484 |
|
|
|
485 |
|
|
static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, gfp_t mf)
|
486 |
|
|
{
|
487 |
|
|
int status;
|
488 |
|
|
|
489 |
|
|
status = usb_submit_urb (kaweth->irq_urb, mf);
|
490 |
|
|
if (unlikely(status == -ENOMEM)) {
|
491 |
|
|
kaweth->suspend_lowmem_ctrl = 1;
|
492 |
|
|
schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
|
493 |
|
|
} else {
|
494 |
|
|
kaweth->suspend_lowmem_ctrl = 0;
|
495 |
|
|
}
|
496 |
|
|
|
497 |
|
|
if (status)
|
498 |
|
|
err ("can't resubmit intr, %s-%s, status %d",
|
499 |
|
|
kaweth->dev->bus->bus_name,
|
500 |
|
|
kaweth->dev->devpath, status);
|
501 |
|
|
}
|
502 |
|
|
|
503 |
|
|
static void int_callback(struct urb *u)
|
504 |
|
|
{
|
505 |
|
|
struct kaweth_device *kaweth = u->context;
|
506 |
|
|
int act_state;
|
507 |
|
|
|
508 |
|
|
switch (u->status) {
|
509 |
|
|
case 0: /* success */
|
510 |
|
|
break;
|
511 |
|
|
case -ECONNRESET: /* unlink */
|
512 |
|
|
case -ENOENT:
|
513 |
|
|
case -ESHUTDOWN:
|
514 |
|
|
return;
|
515 |
|
|
/* -EPIPE: should clear the halt */
|
516 |
|
|
default: /* error */
|
517 |
|
|
goto resubmit;
|
518 |
|
|
}
|
519 |
|
|
|
520 |
|
|
/* we check the link state to report changes */
|
521 |
|
|
if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) {
|
522 |
|
|
if (act_state)
|
523 |
|
|
netif_carrier_on(kaweth->net);
|
524 |
|
|
else
|
525 |
|
|
netif_carrier_off(kaweth->net);
|
526 |
|
|
|
527 |
|
|
kaweth->linkstate = act_state;
|
528 |
|
|
}
|
529 |
|
|
resubmit:
|
530 |
|
|
kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC);
|
531 |
|
|
}
|
532 |
|
|
|
533 |
|
|
static void kaweth_resubmit_tl(struct work_struct *work)
|
534 |
|
|
{
|
535 |
|
|
struct kaweth_device *kaweth =
|
536 |
|
|
container_of(work, struct kaweth_device, lowmem_work.work);
|
537 |
|
|
|
538 |
|
|
if (IS_BLOCKED(kaweth->status))
|
539 |
|
|
return;
|
540 |
|
|
|
541 |
|
|
if (kaweth->suspend_lowmem_rx)
|
542 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_NOIO);
|
543 |
|
|
|
544 |
|
|
if (kaweth->suspend_lowmem_ctrl)
|
545 |
|
|
kaweth_resubmit_int_urb(kaweth, GFP_NOIO);
|
546 |
|
|
}
|
547 |
|
|
|
548 |
|
|
|
549 |
|
|
/****************************************************************
|
550 |
|
|
* kaweth_resubmit_rx_urb
|
551 |
|
|
****************************************************************/
|
552 |
|
|
static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
|
553 |
|
|
gfp_t mem_flags)
|
554 |
|
|
{
|
555 |
|
|
int result;
|
556 |
|
|
|
557 |
|
|
usb_fill_bulk_urb(kaweth->rx_urb,
|
558 |
|
|
kaweth->dev,
|
559 |
|
|
usb_rcvbulkpipe(kaweth->dev, 1),
|
560 |
|
|
kaweth->rx_buf,
|
561 |
|
|
KAWETH_BUF_SIZE,
|
562 |
|
|
kaweth_usb_receive,
|
563 |
|
|
kaweth);
|
564 |
|
|
kaweth->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
565 |
|
|
kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle;
|
566 |
|
|
|
567 |
|
|
if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) {
|
568 |
|
|
if (result == -ENOMEM) {
|
569 |
|
|
kaweth->suspend_lowmem_rx = 1;
|
570 |
|
|
schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
|
571 |
|
|
}
|
572 |
|
|
err("resubmitting rx_urb %d failed", result);
|
573 |
|
|
} else {
|
574 |
|
|
kaweth->suspend_lowmem_rx = 0;
|
575 |
|
|
}
|
576 |
|
|
|
577 |
|
|
return result;
|
578 |
|
|
}
|
579 |
|
|
|
580 |
|
|
static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth);
|
581 |
|
|
|
582 |
|
|
/****************************************************************
|
583 |
|
|
* kaweth_usb_receive
|
584 |
|
|
****************************************************************/
|
585 |
|
|
static void kaweth_usb_receive(struct urb *urb)
|
586 |
|
|
{
|
587 |
|
|
struct kaweth_device *kaweth = urb->context;
|
588 |
|
|
struct net_device *net = kaweth->net;
|
589 |
|
|
|
590 |
|
|
int count = urb->actual_length;
|
591 |
|
|
int count2 = urb->transfer_buffer_length;
|
592 |
|
|
|
593 |
|
|
__u16 pkt_len = le16_to_cpup((__le16 *)kaweth->rx_buf);
|
594 |
|
|
|
595 |
|
|
struct sk_buff *skb;
|
596 |
|
|
|
597 |
|
|
if(unlikely(urb->status == -ECONNRESET || urb->status == -ESHUTDOWN))
|
598 |
|
|
/* we are killed - set a flag and wake the disconnect handler */
|
599 |
|
|
{
|
600 |
|
|
kaweth->end = 1;
|
601 |
|
|
wake_up(&kaweth->term_wait);
|
602 |
|
|
return;
|
603 |
|
|
}
|
604 |
|
|
|
605 |
|
|
spin_lock(&kaweth->device_lock);
|
606 |
|
|
if (IS_BLOCKED(kaweth->status)) {
|
607 |
|
|
spin_unlock(&kaweth->device_lock);
|
608 |
|
|
return;
|
609 |
|
|
}
|
610 |
|
|
spin_unlock(&kaweth->device_lock);
|
611 |
|
|
|
612 |
|
|
if(urb->status && urb->status != -EREMOTEIO && count != 1) {
|
613 |
|
|
err("%s RX status: %d count: %d packet_len: %d",
|
614 |
|
|
net->name,
|
615 |
|
|
urb->status,
|
616 |
|
|
count,
|
617 |
|
|
(int)pkt_len);
|
618 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
|
619 |
|
|
return;
|
620 |
|
|
}
|
621 |
|
|
|
622 |
|
|
if(kaweth->net && (count > 2)) {
|
623 |
|
|
if(pkt_len > (count - 2)) {
|
624 |
|
|
err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count);
|
625 |
|
|
err("Packet len & 2047: %x", pkt_len & 2047);
|
626 |
|
|
err("Count 2: %x", count2);
|
627 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
|
628 |
|
|
return;
|
629 |
|
|
}
|
630 |
|
|
|
631 |
|
|
if(!(skb = dev_alloc_skb(pkt_len+2))) {
|
632 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
|
633 |
|
|
return;
|
634 |
|
|
}
|
635 |
|
|
|
636 |
|
|
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
|
637 |
|
|
|
638 |
|
|
skb_copy_to_linear_data(skb, kaweth->rx_buf + 2, pkt_len);
|
639 |
|
|
|
640 |
|
|
skb_put(skb, pkt_len);
|
641 |
|
|
|
642 |
|
|
skb->protocol = eth_type_trans(skb, net);
|
643 |
|
|
|
644 |
|
|
netif_rx(skb);
|
645 |
|
|
|
646 |
|
|
kaweth->stats.rx_packets++;
|
647 |
|
|
kaweth->stats.rx_bytes += pkt_len;
|
648 |
|
|
}
|
649 |
|
|
|
650 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
|
651 |
|
|
}
|
652 |
|
|
|
653 |
|
|
/****************************************************************
|
654 |
|
|
* kaweth_open
|
655 |
|
|
****************************************************************/
|
656 |
|
|
static int kaweth_open(struct net_device *net)
|
657 |
|
|
{
|
658 |
|
|
struct kaweth_device *kaweth = netdev_priv(net);
|
659 |
|
|
int res;
|
660 |
|
|
|
661 |
|
|
dbg("Opening network device.");
|
662 |
|
|
|
663 |
|
|
res = usb_autopm_get_interface(kaweth->intf);
|
664 |
|
|
if (res) {
|
665 |
|
|
err("Interface cannot be resumed.");
|
666 |
|
|
return -EIO;
|
667 |
|
|
}
|
668 |
|
|
res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
|
669 |
|
|
if (res)
|
670 |
|
|
goto err_out;
|
671 |
|
|
|
672 |
|
|
usb_fill_int_urb(
|
673 |
|
|
kaweth->irq_urb,
|
674 |
|
|
kaweth->dev,
|
675 |
|
|
usb_rcvintpipe(kaweth->dev, 3),
|
676 |
|
|
kaweth->intbuffer,
|
677 |
|
|
INTBUFFERSIZE,
|
678 |
|
|
int_callback,
|
679 |
|
|
kaweth,
|
680 |
|
|
250); /* overriding the descriptor */
|
681 |
|
|
kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle;
|
682 |
|
|
kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
683 |
|
|
|
684 |
|
|
res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL);
|
685 |
|
|
if (res) {
|
686 |
|
|
usb_kill_urb(kaweth->rx_urb);
|
687 |
|
|
goto err_out;
|
688 |
|
|
}
|
689 |
|
|
kaweth->opened = 1;
|
690 |
|
|
|
691 |
|
|
netif_start_queue(net);
|
692 |
|
|
|
693 |
|
|
kaweth_async_set_rx_mode(kaweth);
|
694 |
|
|
return 0;
|
695 |
|
|
|
696 |
|
|
err_out:
|
697 |
|
|
usb_autopm_enable(kaweth->intf);
|
698 |
|
|
return -EIO;
|
699 |
|
|
}
|
700 |
|
|
|
701 |
|
|
/****************************************************************
|
702 |
|
|
* kaweth_kill_urbs
|
703 |
|
|
****************************************************************/
|
704 |
|
|
static void kaweth_kill_urbs(struct kaweth_device *kaweth)
|
705 |
|
|
{
|
706 |
|
|
usb_kill_urb(kaweth->irq_urb);
|
707 |
|
|
usb_kill_urb(kaweth->rx_urb);
|
708 |
|
|
usb_kill_urb(kaweth->tx_urb);
|
709 |
|
|
|
710 |
|
|
flush_scheduled_work();
|
711 |
|
|
|
712 |
|
|
/* a scheduled work may have resubmitted,
|
713 |
|
|
we hit them again */
|
714 |
|
|
usb_kill_urb(kaweth->irq_urb);
|
715 |
|
|
usb_kill_urb(kaweth->rx_urb);
|
716 |
|
|
}
|
717 |
|
|
|
718 |
|
|
/****************************************************************
|
719 |
|
|
* kaweth_close
|
720 |
|
|
****************************************************************/
|
721 |
|
|
static int kaweth_close(struct net_device *net)
|
722 |
|
|
{
|
723 |
|
|
struct kaweth_device *kaweth = netdev_priv(net);
|
724 |
|
|
|
725 |
|
|
netif_stop_queue(net);
|
726 |
|
|
kaweth->opened = 0;
|
727 |
|
|
|
728 |
|
|
kaweth->status |= KAWETH_STATUS_CLOSING;
|
729 |
|
|
|
730 |
|
|
kaweth_kill_urbs(kaweth);
|
731 |
|
|
|
732 |
|
|
kaweth->status &= ~KAWETH_STATUS_CLOSING;
|
733 |
|
|
|
734 |
|
|
usb_autopm_enable(kaweth->intf);
|
735 |
|
|
|
736 |
|
|
return 0;
|
737 |
|
|
}
|
738 |
|
|
|
739 |
|
|
static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
740 |
|
|
{
|
741 |
|
|
struct kaweth_device *kaweth = netdev_priv(dev);
|
742 |
|
|
|
743 |
|
|
strlcpy(info->driver, driver_name, sizeof(info->driver));
|
744 |
|
|
usb_make_path(kaweth->dev, info->bus_info, sizeof (info->bus_info));
|
745 |
|
|
}
|
746 |
|
|
|
747 |
|
|
static u32 kaweth_get_link(struct net_device *dev)
|
748 |
|
|
{
|
749 |
|
|
struct kaweth_device *kaweth = netdev_priv(dev);
|
750 |
|
|
|
751 |
|
|
return kaweth->linkstate;
|
752 |
|
|
}
|
753 |
|
|
|
754 |
|
|
static struct ethtool_ops ops = {
|
755 |
|
|
.get_drvinfo = kaweth_get_drvinfo,
|
756 |
|
|
.get_link = kaweth_get_link
|
757 |
|
|
};
|
758 |
|
|
|
759 |
|
|
/****************************************************************
|
760 |
|
|
* kaweth_usb_transmit_complete
|
761 |
|
|
****************************************************************/
|
762 |
|
|
static void kaweth_usb_transmit_complete(struct urb *urb)
|
763 |
|
|
{
|
764 |
|
|
struct kaweth_device *kaweth = urb->context;
|
765 |
|
|
struct sk_buff *skb = kaweth->tx_skb;
|
766 |
|
|
|
767 |
|
|
if (unlikely(urb->status != 0))
|
768 |
|
|
if (urb->status != -ENOENT)
|
769 |
|
|
dbg("%s: TX status %d.", kaweth->net->name, urb->status);
|
770 |
|
|
|
771 |
|
|
netif_wake_queue(kaweth->net);
|
772 |
|
|
dev_kfree_skb_irq(skb);
|
773 |
|
|
}
|
774 |
|
|
|
775 |
|
|
/****************************************************************
|
776 |
|
|
* kaweth_start_xmit
|
777 |
|
|
****************************************************************/
|
778 |
|
|
static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
|
779 |
|
|
{
|
780 |
|
|
struct kaweth_device *kaweth = netdev_priv(net);
|
781 |
|
|
__le16 *private_header;
|
782 |
|
|
|
783 |
|
|
int res;
|
784 |
|
|
|
785 |
|
|
spin_lock(&kaweth->device_lock);
|
786 |
|
|
|
787 |
|
|
kaweth_async_set_rx_mode(kaweth);
|
788 |
|
|
netif_stop_queue(net);
|
789 |
|
|
if (IS_BLOCKED(kaweth->status)) {
|
790 |
|
|
goto skip;
|
791 |
|
|
}
|
792 |
|
|
|
793 |
|
|
/* We now decide whether we can put our special header into the sk_buff */
|
794 |
|
|
if (skb_cloned(skb) || skb_headroom(skb) < 2) {
|
795 |
|
|
/* no such luck - we make our own */
|
796 |
|
|
struct sk_buff *copied_skb;
|
797 |
|
|
copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC);
|
798 |
|
|
dev_kfree_skb_irq(skb);
|
799 |
|
|
skb = copied_skb;
|
800 |
|
|
if (!copied_skb) {
|
801 |
|
|
kaweth->stats.tx_errors++;
|
802 |
|
|
netif_start_queue(net);
|
803 |
|
|
spin_unlock(&kaweth->device_lock);
|
804 |
|
|
return 0;
|
805 |
|
|
}
|
806 |
|
|
}
|
807 |
|
|
|
808 |
|
|
private_header = (__le16 *)__skb_push(skb, 2);
|
809 |
|
|
*private_header = cpu_to_le16(skb->len-2);
|
810 |
|
|
kaweth->tx_skb = skb;
|
811 |
|
|
|
812 |
|
|
usb_fill_bulk_urb(kaweth->tx_urb,
|
813 |
|
|
kaweth->dev,
|
814 |
|
|
usb_sndbulkpipe(kaweth->dev, 2),
|
815 |
|
|
private_header,
|
816 |
|
|
skb->len,
|
817 |
|
|
kaweth_usb_transmit_complete,
|
818 |
|
|
kaweth);
|
819 |
|
|
kaweth->end = 0;
|
820 |
|
|
|
821 |
|
|
if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
|
822 |
|
|
{
|
823 |
|
|
warn("kaweth failed tx_urb %d", res);
|
824 |
|
|
skip:
|
825 |
|
|
kaweth->stats.tx_errors++;
|
826 |
|
|
|
827 |
|
|
netif_start_queue(net);
|
828 |
|
|
dev_kfree_skb_irq(skb);
|
829 |
|
|
}
|
830 |
|
|
else
|
831 |
|
|
{
|
832 |
|
|
kaweth->stats.tx_packets++;
|
833 |
|
|
kaweth->stats.tx_bytes += skb->len;
|
834 |
|
|
net->trans_start = jiffies;
|
835 |
|
|
}
|
836 |
|
|
|
837 |
|
|
spin_unlock(&kaweth->device_lock);
|
838 |
|
|
|
839 |
|
|
return 0;
|
840 |
|
|
}
|
841 |
|
|
|
842 |
|
|
/****************************************************************
|
843 |
|
|
* kaweth_set_rx_mode
|
844 |
|
|
****************************************************************/
|
845 |
|
|
static void kaweth_set_rx_mode(struct net_device *net)
|
846 |
|
|
{
|
847 |
|
|
struct kaweth_device *kaweth = netdev_priv(net);
|
848 |
|
|
|
849 |
|
|
__u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED |
|
850 |
|
|
KAWETH_PACKET_FILTER_BROADCAST |
|
851 |
|
|
KAWETH_PACKET_FILTER_MULTICAST;
|
852 |
|
|
|
853 |
|
|
dbg("Setting Rx mode to %d", packet_filter_bitmap);
|
854 |
|
|
|
855 |
|
|
netif_stop_queue(net);
|
856 |
|
|
|
857 |
|
|
if (net->flags & IFF_PROMISC) {
|
858 |
|
|
packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS;
|
859 |
|
|
}
|
860 |
|
|
else if ((net->mc_count) || (net->flags & IFF_ALLMULTI)) {
|
861 |
|
|
packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST;
|
862 |
|
|
}
|
863 |
|
|
|
864 |
|
|
kaweth->packet_filter_bitmap = packet_filter_bitmap;
|
865 |
|
|
netif_wake_queue(net);
|
866 |
|
|
}
|
867 |
|
|
|
868 |
|
|
/****************************************************************
|
869 |
|
|
* kaweth_async_set_rx_mode
|
870 |
|
|
****************************************************************/
|
871 |
|
|
static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
|
872 |
|
|
{
|
873 |
|
|
__u16 packet_filter_bitmap = kaweth->packet_filter_bitmap;
|
874 |
|
|
kaweth->packet_filter_bitmap = 0;
|
875 |
|
|
if (packet_filter_bitmap == 0)
|
876 |
|
|
return;
|
877 |
|
|
|
878 |
|
|
{
|
879 |
|
|
int result;
|
880 |
|
|
result = kaweth_control(kaweth,
|
881 |
|
|
usb_sndctrlpipe(kaweth->dev, 0),
|
882 |
|
|
KAWETH_COMMAND_SET_PACKET_FILTER,
|
883 |
|
|
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
|
884 |
|
|
packet_filter_bitmap,
|
885 |
|
|
0,
|
886 |
|
|
(void *)&kaweth->scratch,
|
887 |
|
|
0,
|
888 |
|
|
KAWETH_CONTROL_TIMEOUT);
|
889 |
|
|
|
890 |
|
|
if(result < 0) {
|
891 |
|
|
err("Failed to set Rx mode: %d", result);
|
892 |
|
|
}
|
893 |
|
|
else {
|
894 |
|
|
dbg("Set Rx mode to %d", packet_filter_bitmap);
|
895 |
|
|
}
|
896 |
|
|
}
|
897 |
|
|
}
|
898 |
|
|
|
899 |
|
|
/****************************************************************
|
900 |
|
|
* kaweth_netdev_stats
|
901 |
|
|
****************************************************************/
|
902 |
|
|
static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev)
|
903 |
|
|
{
|
904 |
|
|
struct kaweth_device *kaweth = netdev_priv(dev);
|
905 |
|
|
return &kaweth->stats;
|
906 |
|
|
}
|
907 |
|
|
|
908 |
|
|
/****************************************************************
|
909 |
|
|
* kaweth_tx_timeout
|
910 |
|
|
****************************************************************/
|
911 |
|
|
static void kaweth_tx_timeout(struct net_device *net)
|
912 |
|
|
{
|
913 |
|
|
struct kaweth_device *kaweth = netdev_priv(net);
|
914 |
|
|
|
915 |
|
|
warn("%s: Tx timed out. Resetting.", net->name);
|
916 |
|
|
kaweth->stats.tx_errors++;
|
917 |
|
|
net->trans_start = jiffies;
|
918 |
|
|
|
919 |
|
|
usb_unlink_urb(kaweth->tx_urb);
|
920 |
|
|
}
|
921 |
|
|
|
922 |
|
|
/****************************************************************
|
923 |
|
|
* kaweth_suspend
|
924 |
|
|
****************************************************************/
|
925 |
|
|
static int kaweth_suspend(struct usb_interface *intf, pm_message_t message)
|
926 |
|
|
{
|
927 |
|
|
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
928 |
|
|
unsigned long flags;
|
929 |
|
|
|
930 |
|
|
dbg("Suspending device");
|
931 |
|
|
spin_lock_irqsave(&kaweth->device_lock, flags);
|
932 |
|
|
kaweth->status |= KAWETH_STATUS_SUSPENDING;
|
933 |
|
|
spin_unlock_irqrestore(&kaweth->device_lock, flags);
|
934 |
|
|
|
935 |
|
|
kaweth_kill_urbs(kaweth);
|
936 |
|
|
return 0;
|
937 |
|
|
}
|
938 |
|
|
|
939 |
|
|
/****************************************************************
|
940 |
|
|
* kaweth_resume
|
941 |
|
|
****************************************************************/
|
942 |
|
|
static int kaweth_resume(struct usb_interface *intf)
|
943 |
|
|
{
|
944 |
|
|
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
945 |
|
|
unsigned long flags;
|
946 |
|
|
|
947 |
|
|
dbg("Resuming device");
|
948 |
|
|
spin_lock_irqsave(&kaweth->device_lock, flags);
|
949 |
|
|
kaweth->status &= ~KAWETH_STATUS_SUSPENDING;
|
950 |
|
|
spin_unlock_irqrestore(&kaweth->device_lock, flags);
|
951 |
|
|
|
952 |
|
|
if (!kaweth->opened)
|
953 |
|
|
return 0;
|
954 |
|
|
kaweth_resubmit_rx_urb(kaweth, GFP_NOIO);
|
955 |
|
|
kaweth_resubmit_int_urb(kaweth, GFP_NOIO);
|
956 |
|
|
|
957 |
|
|
return 0;
|
958 |
|
|
}
|
959 |
|
|
|
960 |
|
|
/****************************************************************
|
961 |
|
|
* kaweth_probe
|
962 |
|
|
****************************************************************/
|
963 |
|
|
static int kaweth_probe(
|
964 |
|
|
struct usb_interface *intf,
|
965 |
|
|
const struct usb_device_id *id /* from id_table */
|
966 |
|
|
)
|
967 |
|
|
{
|
968 |
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
969 |
|
|
struct kaweth_device *kaweth;
|
970 |
|
|
struct net_device *netdev;
|
971 |
|
|
const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
972 |
|
|
int result = 0;
|
973 |
|
|
|
974 |
|
|
dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x",
|
975 |
|
|
dev->devnum,
|
976 |
|
|
le16_to_cpu(dev->descriptor.idVendor),
|
977 |
|
|
le16_to_cpu(dev->descriptor.idProduct),
|
978 |
|
|
le16_to_cpu(dev->descriptor.bcdDevice));
|
979 |
|
|
|
980 |
|
|
dbg("Device at %p", dev);
|
981 |
|
|
|
982 |
|
|
dbg("Descriptor length: %x type: %x",
|
983 |
|
|
(int)dev->descriptor.bLength,
|
984 |
|
|
(int)dev->descriptor.bDescriptorType);
|
985 |
|
|
|
986 |
|
|
netdev = alloc_etherdev(sizeof(*kaweth));
|
987 |
|
|
if (!netdev)
|
988 |
|
|
return -ENOMEM;
|
989 |
|
|
|
990 |
|
|
kaweth = netdev_priv(netdev);
|
991 |
|
|
kaweth->dev = dev;
|
992 |
|
|
kaweth->net = netdev;
|
993 |
|
|
|
994 |
|
|
spin_lock_init(&kaweth->device_lock);
|
995 |
|
|
init_waitqueue_head(&kaweth->term_wait);
|
996 |
|
|
|
997 |
|
|
dbg("Resetting.");
|
998 |
|
|
|
999 |
|
|
kaweth_reset(kaweth);
|
1000 |
|
|
|
1001 |
|
|
/*
|
1002 |
|
|
* If high byte of bcdDevice is nonzero, firmware is already
|
1003 |
|
|
* downloaded. Don't try to do it again, or we'll hang the device.
|
1004 |
|
|
*/
|
1005 |
|
|
|
1006 |
|
|
if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) {
|
1007 |
|
|
info("Firmware present in device.");
|
1008 |
|
|
} else {
|
1009 |
|
|
/* Download the firmware */
|
1010 |
|
|
info("Downloading firmware...");
|
1011 |
|
|
kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
|
1012 |
|
|
if ((result = kaweth_download_firmware(kaweth,
|
1013 |
|
|
kaweth_new_code,
|
1014 |
|
|
len_kaweth_new_code,
|
1015 |
|
|
100,
|
1016 |
|
|
2)) < 0) {
|
1017 |
|
|
err("Error downloading firmware (%d)", result);
|
1018 |
|
|
goto err_fw;
|
1019 |
|
|
}
|
1020 |
|
|
|
1021 |
|
|
if ((result = kaweth_download_firmware(kaweth,
|
1022 |
|
|
kaweth_new_code_fix,
|
1023 |
|
|
len_kaweth_new_code_fix,
|
1024 |
|
|
100,
|
1025 |
|
|
3)) < 0) {
|
1026 |
|
|
err("Error downloading firmware fix (%d)", result);
|
1027 |
|
|
goto err_fw;
|
1028 |
|
|
}
|
1029 |
|
|
|
1030 |
|
|
if ((result = kaweth_download_firmware(kaweth,
|
1031 |
|
|
kaweth_trigger_code,
|
1032 |
|
|
len_kaweth_trigger_code,
|
1033 |
|
|
126,
|
1034 |
|
|
2)) < 0) {
|
1035 |
|
|
err("Error downloading trigger code (%d)", result);
|
1036 |
|
|
goto err_fw;
|
1037 |
|
|
|
1038 |
|
|
}
|
1039 |
|
|
|
1040 |
|
|
if ((result = kaweth_download_firmware(kaweth,
|
1041 |
|
|
kaweth_trigger_code_fix,
|
1042 |
|
|
len_kaweth_trigger_code_fix,
|
1043 |
|
|
126,
|
1044 |
|
|
3)) < 0) {
|
1045 |
|
|
err("Error downloading trigger code fix (%d)", result);
|
1046 |
|
|
goto err_fw;
|
1047 |
|
|
}
|
1048 |
|
|
|
1049 |
|
|
|
1050 |
|
|
if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) {
|
1051 |
|
|
err("Error triggering firmware (%d)", result);
|
1052 |
|
|
goto err_fw;
|
1053 |
|
|
}
|
1054 |
|
|
|
1055 |
|
|
/* Device will now disappear for a moment... */
|
1056 |
|
|
info("Firmware loaded. I'll be back...");
|
1057 |
|
|
err_fw:
|
1058 |
|
|
free_page((unsigned long)kaweth->firmware_buf);
|
1059 |
|
|
free_netdev(netdev);
|
1060 |
|
|
return -EIO;
|
1061 |
|
|
}
|
1062 |
|
|
|
1063 |
|
|
result = kaweth_read_configuration(kaweth);
|
1064 |
|
|
|
1065 |
|
|
if(result < 0) {
|
1066 |
|
|
err("Error reading configuration (%d), no net device created", result);
|
1067 |
|
|
goto err_free_netdev;
|
1068 |
|
|
}
|
1069 |
|
|
|
1070 |
|
|
info("Statistics collection: %x", kaweth->configuration.statistics_mask);
|
1071 |
|
|
info("Multicast filter limit: %x", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1));
|
1072 |
|
|
info("MTU: %d", le16_to_cpu(kaweth->configuration.segment_size));
|
1073 |
|
|
info("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
|
1074 |
|
|
(int)kaweth->configuration.hw_addr[0],
|
1075 |
|
|
(int)kaweth->configuration.hw_addr[1],
|
1076 |
|
|
(int)kaweth->configuration.hw_addr[2],
|
1077 |
|
|
(int)kaweth->configuration.hw_addr[3],
|
1078 |
|
|
(int)kaweth->configuration.hw_addr[4],
|
1079 |
|
|
(int)kaweth->configuration.hw_addr[5]);
|
1080 |
|
|
|
1081 |
|
|
if(!memcmp(&kaweth->configuration.hw_addr,
|
1082 |
|
|
&bcast_addr,
|
1083 |
|
|
sizeof(bcast_addr))) {
|
1084 |
|
|
err("Firmware not functioning properly, no net device created");
|
1085 |
|
|
goto err_free_netdev;
|
1086 |
|
|
}
|
1087 |
|
|
|
1088 |
|
|
if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) {
|
1089 |
|
|
dbg("Error setting URB size");
|
1090 |
|
|
goto err_free_netdev;
|
1091 |
|
|
}
|
1092 |
|
|
|
1093 |
|
|
if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) {
|
1094 |
|
|
err("Error setting SOFS wait");
|
1095 |
|
|
goto err_free_netdev;
|
1096 |
|
|
}
|
1097 |
|
|
|
1098 |
|
|
result = kaweth_set_receive_filter(kaweth,
|
1099 |
|
|
KAWETH_PACKET_FILTER_DIRECTED |
|
1100 |
|
|
KAWETH_PACKET_FILTER_BROADCAST |
|
1101 |
|
|
KAWETH_PACKET_FILTER_MULTICAST);
|
1102 |
|
|
|
1103 |
|
|
if(result < 0) {
|
1104 |
|
|
err("Error setting receive filter");
|
1105 |
|
|
goto err_free_netdev;
|
1106 |
|
|
}
|
1107 |
|
|
|
1108 |
|
|
dbg("Initializing net device.");
|
1109 |
|
|
|
1110 |
|
|
kaweth->intf = intf;
|
1111 |
|
|
|
1112 |
|
|
kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
1113 |
|
|
if (!kaweth->tx_urb)
|
1114 |
|
|
goto err_free_netdev;
|
1115 |
|
|
kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
1116 |
|
|
if (!kaweth->rx_urb)
|
1117 |
|
|
goto err_only_tx;
|
1118 |
|
|
kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
|
1119 |
|
|
if (!kaweth->irq_urb)
|
1120 |
|
|
goto err_tx_and_rx;
|
1121 |
|
|
|
1122 |
|
|
kaweth->intbuffer = usb_buffer_alloc( kaweth->dev,
|
1123 |
|
|
INTBUFFERSIZE,
|
1124 |
|
|
GFP_KERNEL,
|
1125 |
|
|
&kaweth->intbufferhandle);
|
1126 |
|
|
if (!kaweth->intbuffer)
|
1127 |
|
|
goto err_tx_and_rx_and_irq;
|
1128 |
|
|
kaweth->rx_buf = usb_buffer_alloc( kaweth->dev,
|
1129 |
|
|
KAWETH_BUF_SIZE,
|
1130 |
|
|
GFP_KERNEL,
|
1131 |
|
|
&kaweth->rxbufferhandle);
|
1132 |
|
|
if (!kaweth->rx_buf)
|
1133 |
|
|
goto err_all_but_rxbuf;
|
1134 |
|
|
|
1135 |
|
|
memcpy(netdev->broadcast, &bcast_addr, sizeof(bcast_addr));
|
1136 |
|
|
memcpy(netdev->dev_addr, &kaweth->configuration.hw_addr,
|
1137 |
|
|
sizeof(kaweth->configuration.hw_addr));
|
1138 |
|
|
|
1139 |
|
|
netdev->open = kaweth_open;
|
1140 |
|
|
netdev->stop = kaweth_close;
|
1141 |
|
|
|
1142 |
|
|
netdev->watchdog_timeo = KAWETH_TX_TIMEOUT;
|
1143 |
|
|
netdev->tx_timeout = kaweth_tx_timeout;
|
1144 |
|
|
|
1145 |
|
|
netdev->hard_start_xmit = kaweth_start_xmit;
|
1146 |
|
|
netdev->set_multicast_list = kaweth_set_rx_mode;
|
1147 |
|
|
netdev->get_stats = kaweth_netdev_stats;
|
1148 |
|
|
netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size);
|
1149 |
|
|
SET_ETHTOOL_OPS(netdev, &ops);
|
1150 |
|
|
|
1151 |
|
|
/* kaweth is zeroed as part of alloc_netdev */
|
1152 |
|
|
|
1153 |
|
|
INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl);
|
1154 |
|
|
|
1155 |
|
|
usb_set_intfdata(intf, kaweth);
|
1156 |
|
|
|
1157 |
|
|
#if 0
|
1158 |
|
|
// dma_supported() is deeply broken on almost all architectures
|
1159 |
|
|
if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
|
1160 |
|
|
kaweth->net->features |= NETIF_F_HIGHDMA;
|
1161 |
|
|
#endif
|
1162 |
|
|
|
1163 |
|
|
SET_NETDEV_DEV(netdev, &intf->dev);
|
1164 |
|
|
if (register_netdev(netdev) != 0) {
|
1165 |
|
|
err("Error registering netdev.");
|
1166 |
|
|
goto err_intfdata;
|
1167 |
|
|
}
|
1168 |
|
|
|
1169 |
|
|
info("kaweth interface created at %s", kaweth->net->name);
|
1170 |
|
|
|
1171 |
|
|
dbg("Kaweth probe returning.");
|
1172 |
|
|
|
1173 |
|
|
return 0;
|
1174 |
|
|
|
1175 |
|
|
err_intfdata:
|
1176 |
|
|
usb_set_intfdata(intf, NULL);
|
1177 |
|
|
usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle);
|
1178 |
|
|
err_all_but_rxbuf:
|
1179 |
|
|
usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle);
|
1180 |
|
|
err_tx_and_rx_and_irq:
|
1181 |
|
|
usb_free_urb(kaweth->irq_urb);
|
1182 |
|
|
err_tx_and_rx:
|
1183 |
|
|
usb_free_urb(kaweth->rx_urb);
|
1184 |
|
|
err_only_tx:
|
1185 |
|
|
usb_free_urb(kaweth->tx_urb);
|
1186 |
|
|
err_free_netdev:
|
1187 |
|
|
free_netdev(netdev);
|
1188 |
|
|
|
1189 |
|
|
return -EIO;
|
1190 |
|
|
}
|
1191 |
|
|
|
1192 |
|
|
/****************************************************************
|
1193 |
|
|
* kaweth_disconnect
|
1194 |
|
|
****************************************************************/
|
1195 |
|
|
static void kaweth_disconnect(struct usb_interface *intf)
|
1196 |
|
|
{
|
1197 |
|
|
struct kaweth_device *kaweth = usb_get_intfdata(intf);
|
1198 |
|
|
struct net_device *netdev;
|
1199 |
|
|
|
1200 |
|
|
info("Unregistering");
|
1201 |
|
|
|
1202 |
|
|
usb_set_intfdata(intf, NULL);
|
1203 |
|
|
if (!kaweth) {
|
1204 |
|
|
warn("unregistering non-existant device");
|
1205 |
|
|
return;
|
1206 |
|
|
}
|
1207 |
|
|
netdev = kaweth->net;
|
1208 |
|
|
|
1209 |
|
|
dbg("Unregistering net device");
|
1210 |
|
|
unregister_netdev(netdev);
|
1211 |
|
|
|
1212 |
|
|
usb_free_urb(kaweth->rx_urb);
|
1213 |
|
|
usb_free_urb(kaweth->tx_urb);
|
1214 |
|
|
usb_free_urb(kaweth->irq_urb);
|
1215 |
|
|
|
1216 |
|
|
usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle);
|
1217 |
|
|
usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle);
|
1218 |
|
|
|
1219 |
|
|
free_netdev(netdev);
|
1220 |
|
|
}
|
1221 |
|
|
|
1222 |
|
|
|
1223 |
|
|
// FIXME this completion stuff is a modified clone of
|
1224 |
|
|
// an OLD version of some stuff in usb.c ...
|
1225 |
|
|
struct usb_api_data {
|
1226 |
|
|
wait_queue_head_t wqh;
|
1227 |
|
|
int done;
|
1228 |
|
|
};
|
1229 |
|
|
|
1230 |
|
|
/*-------------------------------------------------------------------*
|
1231 |
|
|
* completion handler for compatibility wrappers (sync control/bulk) *
|
1232 |
|
|
*-------------------------------------------------------------------*/
|
1233 |
|
|
static void usb_api_blocking_completion(struct urb *urb)
|
1234 |
|
|
{
|
1235 |
|
|
struct usb_api_data *awd = (struct usb_api_data *)urb->context;
|
1236 |
|
|
|
1237 |
|
|
awd->done=1;
|
1238 |
|
|
wake_up(&awd->wqh);
|
1239 |
|
|
}
|
1240 |
|
|
|
1241 |
|
|
/*-------------------------------------------------------------------*
|
1242 |
|
|
* COMPATIBILITY STUFF *
|
1243 |
|
|
*-------------------------------------------------------------------*/
|
1244 |
|
|
|
1245 |
|
|
// Starts urb and waits for completion or timeout
|
1246 |
|
|
static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
|
1247 |
|
|
{
|
1248 |
|
|
struct usb_api_data awd;
|
1249 |
|
|
int status;
|
1250 |
|
|
|
1251 |
|
|
init_waitqueue_head(&awd.wqh);
|
1252 |
|
|
awd.done = 0;
|
1253 |
|
|
|
1254 |
|
|
urb->context = &awd;
|
1255 |
|
|
status = usb_submit_urb(urb, GFP_NOIO);
|
1256 |
|
|
if (status) {
|
1257 |
|
|
// something went wrong
|
1258 |
|
|
usb_free_urb(urb);
|
1259 |
|
|
return status;
|
1260 |
|
|
}
|
1261 |
|
|
|
1262 |
|
|
if (!wait_event_timeout(awd.wqh, awd.done, timeout)) {
|
1263 |
|
|
// timeout
|
1264 |
|
|
warn("usb_control/bulk_msg: timeout");
|
1265 |
|
|
usb_kill_urb(urb); // remove urb safely
|
1266 |
|
|
status = -ETIMEDOUT;
|
1267 |
|
|
}
|
1268 |
|
|
else {
|
1269 |
|
|
status = urb->status;
|
1270 |
|
|
}
|
1271 |
|
|
|
1272 |
|
|
if (actual_length) {
|
1273 |
|
|
*actual_length = urb->actual_length;
|
1274 |
|
|
}
|
1275 |
|
|
|
1276 |
|
|
usb_free_urb(urb);
|
1277 |
|
|
return status;
|
1278 |
|
|
}
|
1279 |
|
|
|
1280 |
|
|
/*-------------------------------------------------------------------*/
|
1281 |
|
|
// returns status (negative) or length (positive)
|
1282 |
|
|
static int kaweth_internal_control_msg(struct usb_device *usb_dev,
|
1283 |
|
|
unsigned int pipe,
|
1284 |
|
|
struct usb_ctrlrequest *cmd, void *data,
|
1285 |
|
|
int len, int timeout)
|
1286 |
|
|
{
|
1287 |
|
|
struct urb *urb;
|
1288 |
|
|
int retv;
|
1289 |
|
|
int length = 0; /* shut up GCC */
|
1290 |
|
|
|
1291 |
|
|
urb = usb_alloc_urb(0, GFP_NOIO);
|
1292 |
|
|
if (!urb)
|
1293 |
|
|
return -ENOMEM;
|
1294 |
|
|
|
1295 |
|
|
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data,
|
1296 |
|
|
len, usb_api_blocking_completion, NULL);
|
1297 |
|
|
|
1298 |
|
|
retv = usb_start_wait_urb(urb, timeout, &length);
|
1299 |
|
|
if (retv < 0) {
|
1300 |
|
|
return retv;
|
1301 |
|
|
}
|
1302 |
|
|
else {
|
1303 |
|
|
return length;
|
1304 |
|
|
}
|
1305 |
|
|
}
|
1306 |
|
|
|
1307 |
|
|
|
1308 |
|
|
/****************************************************************
|
1309 |
|
|
* kaweth_init
|
1310 |
|
|
****************************************************************/
|
1311 |
|
|
static int __init kaweth_init(void)
|
1312 |
|
|
{
|
1313 |
|
|
dbg("Driver loading");
|
1314 |
|
|
return usb_register(&kaweth_driver);
|
1315 |
|
|
}
|
1316 |
|
|
|
1317 |
|
|
/****************************************************************
|
1318 |
|
|
* kaweth_exit
|
1319 |
|
|
****************************************************************/
|
1320 |
|
|
static void __exit kaweth_exit(void)
|
1321 |
|
|
{
|
1322 |
|
|
usb_deregister(&kaweth_driver);
|
1323 |
|
|
}
|
1324 |
|
|
|
1325 |
|
|
module_init(kaweth_init);
|
1326 |
|
|
module_exit(kaweth_exit);
|
1327 |
|
|
|
1328 |
|
|
|
1329 |
|
|
|
1330 |
|
|
|
1331 |
|
|
|
1332 |
|
|
|
1333 |
|
|
|
1334 |
|
|
|
1335 |
|
|
|