Revisión | 9b70e00773f0c76a243816b8ec134c3c7dacd531 (tree) |
---|---|
Tiempo | 2011-02-20 04:32:36 |
Autor | Simon Glass <sjg@chro...> |
Commiter | Remy Bohmer |
Add support for ASIX AX88772 USB 2.0 10/100Mbit Ethernet Adaptor
Driver originally written by NVIDIA Corporation, modified to
handle odd-length packets.
Signed-off-by: Simon Glass <sjg@chromium.org>
@@ -25,6 +25,9 @@ LIB := $(obj)libusb_eth.a | ||
25 | 25 | |
26 | 26 | # new USB host ethernet layer dependencies |
27 | 27 | COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o |
28 | +ifdef CONFIG_USB_ETHER_ASIX | |
29 | +COBJS-y += asix.o | |
30 | +endif | |
28 | 31 | |
29 | 32 | COBJS := $(COBJS-y) |
30 | 33 | SRCS := $(COBJS:.o=.c) |
@@ -0,0 +1,635 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2011 The Chromium OS Authors. | |
3 | + * See file CREDITS for list of people who contributed to this | |
4 | + * project. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License as | |
8 | + * published by the Free Software Foundation; either version 2 of | |
9 | + * the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
19 | + * MA 02111-1307 USA | |
20 | + */ | |
21 | + | |
22 | +#include <common.h> | |
23 | +#include <usb.h> | |
24 | +#include <linux/mii.h> | |
25 | +#include "usb_ether.h" | |
26 | + | |
27 | + | |
28 | +/* ASIX AX8817X based USB 2.0 Ethernet Devices */ | |
29 | + | |
30 | +#define AX_CMD_SET_SW_MII 0x06 | |
31 | +#define AX_CMD_READ_MII_REG 0x07 | |
32 | +#define AX_CMD_WRITE_MII_REG 0x08 | |
33 | +#define AX_CMD_SET_HW_MII 0x0a | |
34 | +#define AX_CMD_READ_RX_CTL 0x0f | |
35 | +#define AX_CMD_WRITE_RX_CTL 0x10 | |
36 | +#define AX_CMD_WRITE_IPG0 0x12 | |
37 | +#define AX_CMD_READ_NODE_ID 0x13 | |
38 | +#define AX_CMD_READ_PHY_ID 0x19 | |
39 | +#define AX_CMD_WRITE_MEDIUM_MODE 0x1b | |
40 | +#define AX_CMD_WRITE_GPIOS 0x1f | |
41 | +#define AX_CMD_SW_RESET 0x20 | |
42 | +#define AX_CMD_SW_PHY_SELECT 0x22 | |
43 | + | |
44 | +#define AX_SWRESET_CLEAR 0x00 | |
45 | +#define AX_SWRESET_PRTE 0x04 | |
46 | +#define AX_SWRESET_PRL 0x08 | |
47 | +#define AX_SWRESET_IPRL 0x20 | |
48 | +#define AX_SWRESET_IPPD 0x40 | |
49 | + | |
50 | +#define AX88772_IPG0_DEFAULT 0x15 | |
51 | +#define AX88772_IPG1_DEFAULT 0x0c | |
52 | +#define AX88772_IPG2_DEFAULT 0x12 | |
53 | + | |
54 | +/* AX88772 & AX88178 Medium Mode Register */ | |
55 | +#define AX_MEDIUM_PF 0x0080 | |
56 | +#define AX_MEDIUM_JFE 0x0040 | |
57 | +#define AX_MEDIUM_TFC 0x0020 | |
58 | +#define AX_MEDIUM_RFC 0x0010 | |
59 | +#define AX_MEDIUM_ENCK 0x0008 | |
60 | +#define AX_MEDIUM_AC 0x0004 | |
61 | +#define AX_MEDIUM_FD 0x0002 | |
62 | +#define AX_MEDIUM_GM 0x0001 | |
63 | +#define AX_MEDIUM_SM 0x1000 | |
64 | +#define AX_MEDIUM_SBP 0x0800 | |
65 | +#define AX_MEDIUM_PS 0x0200 | |
66 | +#define AX_MEDIUM_RE 0x0100 | |
67 | + | |
68 | +#define AX88178_MEDIUM_DEFAULT \ | |
69 | + (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ | |
70 | + AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ | |
71 | + AX_MEDIUM_RE) | |
72 | + | |
73 | +#define AX88772_MEDIUM_DEFAULT \ | |
74 | + (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ | |
75 | + AX_MEDIUM_TFC | AX_MEDIUM_PS | \ | |
76 | + AX_MEDIUM_AC | AX_MEDIUM_RE) | |
77 | + | |
78 | +/* AX88772 & AX88178 RX_CTL values */ | |
79 | +#define AX_RX_CTL_SO 0x0080 | |
80 | +#define AX_RX_CTL_AB 0x0008 | |
81 | + | |
82 | +#define AX_DEFAULT_RX_CTL \ | |
83 | + (AX_RX_CTL_SO | AX_RX_CTL_AB) | |
84 | + | |
85 | +/* GPIO 2 toggles */ | |
86 | +#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ | |
87 | +#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ | |
88 | +#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ | |
89 | + | |
90 | +/* local defines */ | |
91 | +#define ASIX_BASE_NAME "asx" | |
92 | +#define USB_CTRL_SET_TIMEOUT 5000 | |
93 | +#define USB_CTRL_GET_TIMEOUT 5000 | |
94 | +#define USB_BULK_SEND_TIMEOUT 5000 | |
95 | +#define USB_BULK_RECV_TIMEOUT 5000 | |
96 | + | |
97 | +#define AX_RX_URB_SIZE 2048 | |
98 | +#define PHY_CONNECT_TIMEOUT 5000 | |
99 | + | |
100 | +/* local vars */ | |
101 | +static int curr_eth_dev; /* index for name of next device detected */ | |
102 | + | |
103 | +/* | |
104 | + * Asix infrastructure commands | |
105 | + */ | |
106 | +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, | |
107 | + u16 size, void *data) | |
108 | +{ | |
109 | + int len; | |
110 | + | |
111 | + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x " | |
112 | + "size=%d\n", cmd, value, index, size); | |
113 | + | |
114 | + len = usb_control_msg( | |
115 | + dev->pusb_dev, | |
116 | + usb_sndctrlpipe(dev->pusb_dev, 0), | |
117 | + cmd, | |
118 | + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
119 | + value, | |
120 | + index, | |
121 | + data, | |
122 | + size, | |
123 | + USB_CTRL_SET_TIMEOUT); | |
124 | + | |
125 | + return len == size ? 0 : -1; | |
126 | +} | |
127 | + | |
128 | +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, | |
129 | + u16 size, void *data) | |
130 | +{ | |
131 | + int len; | |
132 | + | |
133 | + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", | |
134 | + cmd, value, index, size); | |
135 | + | |
136 | + len = usb_control_msg( | |
137 | + dev->pusb_dev, | |
138 | + usb_rcvctrlpipe(dev->pusb_dev, 0), | |
139 | + cmd, | |
140 | + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
141 | + value, | |
142 | + index, | |
143 | + data, | |
144 | + size, | |
145 | + USB_CTRL_GET_TIMEOUT); | |
146 | + return len == size ? 0 : -1; | |
147 | +} | |
148 | + | |
149 | +static inline int asix_set_sw_mii(struct ueth_data *dev) | |
150 | +{ | |
151 | + int ret; | |
152 | + | |
153 | + ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); | |
154 | + if (ret < 0) | |
155 | + debug("Failed to enable software MII access\n"); | |
156 | + return ret; | |
157 | +} | |
158 | + | |
159 | +static inline int asix_set_hw_mii(struct ueth_data *dev) | |
160 | +{ | |
161 | + int ret; | |
162 | + | |
163 | + ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); | |
164 | + if (ret < 0) | |
165 | + debug("Failed to enable hardware MII access\n"); | |
166 | + return ret; | |
167 | +} | |
168 | + | |
169 | +static int asix_mdio_read(struct ueth_data *dev, int phy_id, int loc) | |
170 | +{ | |
171 | + __le16 res; | |
172 | + | |
173 | + asix_set_sw_mii(dev); | |
174 | + asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res); | |
175 | + asix_set_hw_mii(dev); | |
176 | + | |
177 | + debug("asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", | |
178 | + phy_id, loc, le16_to_cpu(res)); | |
179 | + | |
180 | + return le16_to_cpu(res); | |
181 | +} | |
182 | + | |
183 | +static void | |
184 | +asix_mdio_write(struct ueth_data *dev, int phy_id, int loc, int val) | |
185 | +{ | |
186 | + __le16 res = cpu_to_le16(val); | |
187 | + | |
188 | + debug("asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", | |
189 | + phy_id, loc, val); | |
190 | + asix_set_sw_mii(dev); | |
191 | + asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); | |
192 | + asix_set_hw_mii(dev); | |
193 | +} | |
194 | + | |
195 | +/* | |
196 | + * Asix "high level" commands | |
197 | + */ | |
198 | +static int asix_sw_reset(struct ueth_data *dev, u8 flags) | |
199 | +{ | |
200 | + int ret; | |
201 | + | |
202 | + ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); | |
203 | + if (ret < 0) | |
204 | + debug("Failed to send software reset: %02x\n", ret); | |
205 | + else | |
206 | + udelay(150 * 1000); | |
207 | + | |
208 | + return ret; | |
209 | +} | |
210 | + | |
211 | +static inline int asix_get_phy_addr(struct ueth_data *dev) | |
212 | +{ | |
213 | + u8 buf[2]; | |
214 | + int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); | |
215 | + | |
216 | + debug("asix_get_phy_addr()\n"); | |
217 | + | |
218 | + if (ret < 0) { | |
219 | + debug("Error reading PHYID register: %02x\n", ret); | |
220 | + goto out; | |
221 | + } | |
222 | + debug("asix_get_phy_addr() returning 0x%04x\n", *((__le16 *)buf)); | |
223 | + ret = buf[1]; | |
224 | + | |
225 | +out: | |
226 | + return ret; | |
227 | +} | |
228 | + | |
229 | +static int asix_write_medium_mode(struct ueth_data *dev, u16 mode) | |
230 | +{ | |
231 | + int ret; | |
232 | + | |
233 | + debug("asix_write_medium_mode() - mode = 0x%04x\n", mode); | |
234 | + ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, | |
235 | + 0, 0, NULL); | |
236 | + if (ret < 0) { | |
237 | + debug("Failed to write Medium Mode mode to 0x%04x: %02x\n", | |
238 | + mode, ret); | |
239 | + } | |
240 | + return ret; | |
241 | +} | |
242 | + | |
243 | +static u16 asix_read_rx_ctl(struct ueth_data *dev) | |
244 | +{ | |
245 | + __le16 v; | |
246 | + int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); | |
247 | + | |
248 | + if (ret < 0) | |
249 | + debug("Error reading RX_CTL register: %02x\n", ret); | |
250 | + else | |
251 | + ret = le16_to_cpu(v); | |
252 | + return ret; | |
253 | +} | |
254 | + | |
255 | +static int asix_write_rx_ctl(struct ueth_data *dev, u16 mode) | |
256 | +{ | |
257 | + int ret; | |
258 | + | |
259 | + debug("asix_write_rx_ctl() - mode = 0x%04x\n", mode); | |
260 | + ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); | |
261 | + if (ret < 0) { | |
262 | + debug("Failed to write RX_CTL mode to 0x%04x: %02x\n", | |
263 | + mode, ret); | |
264 | + } | |
265 | + return ret; | |
266 | +} | |
267 | + | |
268 | +static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) | |
269 | +{ | |
270 | + int ret; | |
271 | + | |
272 | + debug("asix_write_gpio() - value = 0x%04x\n", value); | |
273 | + ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); | |
274 | + if (ret < 0) { | |
275 | + debug("Failed to write GPIO value 0x%04x: %02x\n", | |
276 | + value, ret); | |
277 | + } | |
278 | + if (sleep) | |
279 | + udelay(sleep * 1000); | |
280 | + | |
281 | + return ret; | |
282 | +} | |
283 | + | |
284 | +/* | |
285 | + * mii commands | |
286 | + */ | |
287 | + | |
288 | +/* | |
289 | + * mii_nway_restart - restart NWay (autonegotiation) for this interface | |
290 | + * | |
291 | + * Returns 0 on success, negative on error. | |
292 | + */ | |
293 | +static int mii_nway_restart(struct ueth_data *dev) | |
294 | +{ | |
295 | + int bmcr; | |
296 | + int r = -1; | |
297 | + | |
298 | + /* if autoneg is off, it's an error */ | |
299 | + bmcr = asix_mdio_read(dev, dev->phy_id, MII_BMCR); | |
300 | + | |
301 | + if (bmcr & BMCR_ANENABLE) { | |
302 | + bmcr |= BMCR_ANRESTART; | |
303 | + asix_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); | |
304 | + r = 0; | |
305 | + } | |
306 | + | |
307 | + return r; | |
308 | +} | |
309 | + | |
310 | +/* | |
311 | + * Asix callbacks | |
312 | + */ | |
313 | +static int asix_init(struct eth_device *eth, bd_t *bd) | |
314 | +{ | |
315 | + int embd_phy; | |
316 | + unsigned char buf[ETH_ALEN]; | |
317 | + u16 rx_ctl; | |
318 | + struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
319 | + int timeout = 0; | |
320 | +#define TIMEOUT_RESOLUTION 50 /* ms */ | |
321 | + int link_detected; | |
322 | + | |
323 | + debug("** %s()\n", __func__); | |
324 | + | |
325 | + if (asix_write_gpio(dev, | |
326 | + AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5) < 0) | |
327 | + goto out_err; | |
328 | + | |
329 | + /* 0x10 is the phy id of the embedded 10/100 ethernet phy */ | |
330 | + embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); | |
331 | + if (asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, | |
332 | + embd_phy, 0, 0, NULL) < 0) { | |
333 | + debug("Select PHY #1 failed\n"); | |
334 | + goto out_err; | |
335 | + } | |
336 | + | |
337 | + if (asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL) < 0) | |
338 | + goto out_err; | |
339 | + | |
340 | + if (asix_sw_reset(dev, AX_SWRESET_CLEAR) < 0) | |
341 | + goto out_err; | |
342 | + | |
343 | + if (embd_phy) { | |
344 | + if (asix_sw_reset(dev, AX_SWRESET_IPRL) < 0) | |
345 | + goto out_err; | |
346 | + } else { | |
347 | + if (asix_sw_reset(dev, AX_SWRESET_PRTE) < 0) | |
348 | + goto out_err; | |
349 | + } | |
350 | + | |
351 | + rx_ctl = asix_read_rx_ctl(dev); | |
352 | + debug("RX_CTL is 0x%04x after software reset\n", rx_ctl); | |
353 | + if (asix_write_rx_ctl(dev, 0x0000) < 0) | |
354 | + goto out_err; | |
355 | + | |
356 | + rx_ctl = asix_read_rx_ctl(dev); | |
357 | + debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); | |
358 | + | |
359 | + /* Get the MAC address */ | |
360 | + if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, | |
361 | + 0, 0, ETH_ALEN, buf) < 0) { | |
362 | + debug("Failed to read MAC address.\n"); | |
363 | + goto out_err; | |
364 | + } | |
365 | + memcpy(eth->enetaddr, buf, ETH_ALEN); | |
366 | + debug("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", | |
367 | + eth->enetaddr[0], eth->enetaddr[1], | |
368 | + eth->enetaddr[2], eth->enetaddr[3], | |
369 | + eth->enetaddr[4], eth->enetaddr[5]); | |
370 | + | |
371 | + dev->phy_id = asix_get_phy_addr(dev); | |
372 | + if (dev->phy_id < 0) | |
373 | + debug("Failed to read phy id\n"); | |
374 | + | |
375 | + if (asix_sw_reset(dev, AX_SWRESET_PRL) < 0) | |
376 | + goto out_err; | |
377 | + | |
378 | + if (asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL) < 0) | |
379 | + goto out_err; | |
380 | + | |
381 | + asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); | |
382 | + asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE, | |
383 | + ADVERTISE_ALL | ADVERTISE_CSMA); | |
384 | + mii_nway_restart(dev); | |
385 | + | |
386 | + if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) | |
387 | + goto out_err; | |
388 | + | |
389 | + if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0, | |
390 | + AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, | |
391 | + AX88772_IPG2_DEFAULT, 0, NULL) < 0) { | |
392 | + debug("Write IPG,IPG1,IPG2 failed\n"); | |
393 | + goto out_err; | |
394 | + } | |
395 | + | |
396 | + if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0) | |
397 | + goto out_err; | |
398 | + | |
399 | + do { | |
400 | + link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) & | |
401 | + BMSR_LSTATUS; | |
402 | + if (!link_detected) { | |
403 | + if (timeout == 0) | |
404 | + printf("Waiting for Ethernet connection... "); | |
405 | + udelay(TIMEOUT_RESOLUTION * 1000); | |
406 | + timeout += TIMEOUT_RESOLUTION; | |
407 | + } | |
408 | + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); | |
409 | + if (link_detected) { | |
410 | + if (timeout != 0) | |
411 | + printf("done.\n"); | |
412 | + } else { | |
413 | + printf("unable to connect.\n"); | |
414 | + goto out_err; | |
415 | + } | |
416 | + | |
417 | + return 0; | |
418 | +out_err: | |
419 | + return -1; | |
420 | +} | |
421 | + | |
422 | +static int asix_send(struct eth_device *eth, volatile void *packet, int length) | |
423 | +{ | |
424 | + struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
425 | + int err; | |
426 | + u32 packet_len; | |
427 | + int actual_len; | |
428 | + unsigned char msg[PKTSIZE + sizeof(packet_len)]; | |
429 | + | |
430 | + debug("** %s(), len %d\n", __func__, length); | |
431 | + | |
432 | + packet_len = (((length) ^ 0x0000ffff) << 16) + (length); | |
433 | + cpu_to_le32s(&packet_len); | |
434 | + | |
435 | + memcpy(msg, &packet_len, sizeof(packet_len)); | |
436 | + memcpy(msg + sizeof(packet_len), (void *)packet, length); | |
437 | + if (length & 1) | |
438 | + length++; | |
439 | + | |
440 | + err = usb_bulk_msg(dev->pusb_dev, | |
441 | + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), | |
442 | + (void *)msg, | |
443 | + length + sizeof(packet_len), | |
444 | + &actual_len, | |
445 | + USB_BULK_SEND_TIMEOUT); | |
446 | + debug("Tx: len = %u, actual = %u, err = %d\n", | |
447 | + length + sizeof(packet_len), actual_len, err); | |
448 | + | |
449 | + return err; | |
450 | +} | |
451 | + | |
452 | +static int asix_recv(struct eth_device *eth) | |
453 | +{ | |
454 | + struct ueth_data *dev = (struct ueth_data *)eth->priv; | |
455 | + static unsigned char recv_buf[AX_RX_URB_SIZE]; | |
456 | + unsigned char *buf_ptr; | |
457 | + int err; | |
458 | + int actual_len; | |
459 | + u32 packet_len; | |
460 | + | |
461 | + debug("** %s()\n", __func__); | |
462 | + | |
463 | + err = usb_bulk_msg(dev->pusb_dev, | |
464 | + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), | |
465 | + (void *)recv_buf, | |
466 | + AX_RX_URB_SIZE, | |
467 | + &actual_len, | |
468 | + USB_BULK_RECV_TIMEOUT); | |
469 | + debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, | |
470 | + actual_len, err); | |
471 | + if (err != 0) { | |
472 | + debug("Rx: failed to receive\n"); | |
473 | + return -1; | |
474 | + } | |
475 | + if (actual_len > AX_RX_URB_SIZE) { | |
476 | + debug("Rx: received too many bytes %d\n", actual_len); | |
477 | + return -1; | |
478 | + } | |
479 | + | |
480 | + buf_ptr = recv_buf; | |
481 | + while (actual_len > 0) { | |
482 | + /* | |
483 | + * 1st 4 bytes contain the length of the actual data as two | |
484 | + * complementary 16-bit words. Extract the length of the data. | |
485 | + */ | |
486 | + if (actual_len < sizeof(packet_len)) { | |
487 | + debug("Rx: incomplete packet length\n"); | |
488 | + return -1; | |
489 | + } | |
490 | + memcpy(&packet_len, buf_ptr, sizeof(packet_len)); | |
491 | + le32_to_cpus(&packet_len); | |
492 | + if (((packet_len >> 16) ^ 0xffff) != (packet_len & 0xffff)) { | |
493 | + debug("Rx: malformed packet length: %#x (%#x:%#x)\n", | |
494 | + packet_len, (packet_len >> 16) ^ 0xffff, | |
495 | + packet_len & 0xffff); | |
496 | + return -1; | |
497 | + } | |
498 | + packet_len = packet_len & 0xffff; | |
499 | + if (packet_len > actual_len - sizeof(packet_len)) { | |
500 | + debug("Rx: too large packet: %d\n", packet_len); | |
501 | + return -1; | |
502 | + } | |
503 | + | |
504 | + /* Notify net stack */ | |
505 | + NetReceive(buf_ptr + sizeof(packet_len), packet_len); | |
506 | + | |
507 | + /* Adjust for next iteration. Packets are padded to 16-bits */ | |
508 | + if (packet_len & 1) | |
509 | + packet_len++; | |
510 | + actual_len -= sizeof(packet_len) + packet_len; | |
511 | + buf_ptr += sizeof(packet_len) + packet_len; | |
512 | + } | |
513 | + | |
514 | + return err; | |
515 | +} | |
516 | + | |
517 | +static void asix_halt(struct eth_device *eth) | |
518 | +{ | |
519 | + debug("** %s()\n", __func__); | |
520 | +} | |
521 | + | |
522 | +/* | |
523 | + * Asix probing functions | |
524 | + */ | |
525 | +void asix_eth_before_probe(void) | |
526 | +{ | |
527 | + curr_eth_dev = 0; | |
528 | +} | |
529 | + | |
530 | +struct asix_dongle { | |
531 | + unsigned short vendor; | |
532 | + unsigned short product; | |
533 | +}; | |
534 | + | |
535 | +static struct asix_dongle asix_dongles[] = { | |
536 | + { 0x05ac, 0x1402 }, /* Apple USB Ethernet Adapter */ | |
537 | + { 0x07d1, 0x3c05 }, /* D-Link DUB-E100 H/W Ver B1 */ | |
538 | + { 0x0b95, 0x772a }, /* Cables-to-Go USB Ethernet Adapter */ | |
539 | + { 0x0b95, 0x7720 }, /* Trendnet TU2-ET100 V3.0R */ | |
540 | + { 0x0b95, 0x1720 }, /* SMC */ | |
541 | + { 0x0db0, 0xa877 }, /* MSI - ASIX 88772a */ | |
542 | + { 0x13b1, 0x0018 }, /* Linksys 200M v2.1 */ | |
543 | + { 0x1557, 0x7720 }, /* 0Q0 cable ethernet */ | |
544 | + { 0x2001, 0x3c05 }, /* DLink DUB-E100 H/W Ver B1 Alternate */ | |
545 | + { 0x0000, 0x0000 } /* END - Do not remove */ | |
546 | +}; | |
547 | + | |
548 | +/* Probe to see if a new device is actually an asix device */ | |
549 | +int asix_eth_probe(struct usb_device *dev, unsigned int ifnum, | |
550 | + struct ueth_data *ss) | |
551 | +{ | |
552 | + struct usb_interface *iface; | |
553 | + struct usb_interface_descriptor *iface_desc; | |
554 | + int i; | |
555 | + | |
556 | + /* let's examine the device now */ | |
557 | + iface = &dev->config.if_desc[ifnum]; | |
558 | + iface_desc = &dev->config.if_desc[ifnum].desc; | |
559 | + | |
560 | + for (i = 0; asix_dongles[i].vendor != 0; i++) { | |
561 | + if (dev->descriptor.idVendor == asix_dongles[i].vendor && | |
562 | + dev->descriptor.idProduct == asix_dongles[i].product) | |
563 | + /* Found a supported dongle */ | |
564 | + break; | |
565 | + } | |
566 | + | |
567 | + if (asix_dongles[i].vendor == 0) | |
568 | + return 0; | |
569 | + | |
570 | + memset(ss, 0, sizeof(struct ueth_data)); | |
571 | + | |
572 | + /* At this point, we know we've got a live one */ | |
573 | + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", | |
574 | + dev->descriptor.idVendor, dev->descriptor.idProduct); | |
575 | + | |
576 | + /* Initialize the ueth_data structure with some useful info */ | |
577 | + ss->ifnum = ifnum; | |
578 | + ss->pusb_dev = dev; | |
579 | + ss->subclass = iface_desc->bInterfaceSubClass; | |
580 | + ss->protocol = iface_desc->bInterfaceProtocol; | |
581 | + | |
582 | + /* | |
583 | + * We are expecting a minimum of 3 endpoints - in, out (bulk), and | |
584 | + * int. We will ignore any others. | |
585 | + */ | |
586 | + for (i = 0; i < iface_desc->bNumEndpoints; i++) { | |
587 | + /* is it an BULK endpoint? */ | |
588 | + if ((iface->ep_desc[i].bmAttributes & | |
589 | + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { | |
590 | + if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN) | |
591 | + ss->ep_in = iface->ep_desc[i].bEndpointAddress & | |
592 | + USB_ENDPOINT_NUMBER_MASK; | |
593 | + else | |
594 | + ss->ep_out = | |
595 | + iface->ep_desc[i].bEndpointAddress & | |
596 | + USB_ENDPOINT_NUMBER_MASK; | |
597 | + } | |
598 | + | |
599 | + /* is it an interrupt endpoint? */ | |
600 | + if ((iface->ep_desc[i].bmAttributes & | |
601 | + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { | |
602 | + ss->ep_int = iface->ep_desc[i].bEndpointAddress & | |
603 | + USB_ENDPOINT_NUMBER_MASK; | |
604 | + ss->irqinterval = iface->ep_desc[i].bInterval; | |
605 | + } | |
606 | + } | |
607 | + debug("Endpoints In %d Out %d Int %d\n", | |
608 | + ss->ep_in, ss->ep_out, ss->ep_int); | |
609 | + | |
610 | + /* Do some basic sanity checks, and bail if we find a problem */ | |
611 | + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || | |
612 | + !ss->ep_in || !ss->ep_out || !ss->ep_int) { | |
613 | + debug("Problems with device\n"); | |
614 | + return 0; | |
615 | + } | |
616 | + dev->privptr = (void *)ss; | |
617 | + return 1; | |
618 | +} | |
619 | + | |
620 | +int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, | |
621 | + struct eth_device *eth) | |
622 | +{ | |
623 | + if (!eth) { | |
624 | + debug("%s: missing parameter.\n", __func__); | |
625 | + return 0; | |
626 | + } | |
627 | + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); | |
628 | + eth->init = asix_init; | |
629 | + eth->send = asix_send; | |
630 | + eth->recv = asix_recv; | |
631 | + eth->halt = asix_halt; | |
632 | + eth->priv = ss; | |
633 | + | |
634 | + return 1; | |
635 | +} |
@@ -38,6 +38,13 @@ struct usb_eth_prob_dev { | ||
38 | 38 | |
39 | 39 | /* driver functions go here, each bracketed by #ifdef CONFIG_USB_ETHER_xxx */ |
40 | 40 | static const struct usb_eth_prob_dev prob_dev[] = { |
41 | +#ifdef CONFIG_USB_ETHER_ASIX | |
42 | + { | |
43 | + .before_probe = asix_eth_before_probe, | |
44 | + .probe = asix_eth_probe, | |
45 | + .get_info = asix_eth_get_info, | |
46 | + }, | |
47 | +#endif | |
41 | 48 | { }, /* END */ |
42 | 49 | }; |
43 | 50 |
@@ -57,5 +57,12 @@ struct ueth_data { | ||
57 | 57 | * Function definitions for each USB ethernet driver go here, bracketed by |
58 | 58 | * #ifdef CONFIG_USB_ETHER_xxx...#endif |
59 | 59 | */ |
60 | +#ifdef CONFIG_USB_ETHER_ASIX | |
61 | +void asix_eth_before_probe(void); | |
62 | +int asix_eth_probe(struct usb_device *dev, unsigned int ifnum, | |
63 | + struct ueth_data *ss); | |
64 | +int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, | |
65 | + struct eth_device *eth); | |
66 | +#endif | |
60 | 67 | |
61 | 68 | #endif /* __USB_ETHER_H__ */ |