Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: Davicom DM9601 usbnet driver

This patch adds a driver for the Davicom DM9601 USB 1.1 10/100Mbps
ethernet adaptor using the usbnet framework.

See http://www.davicom.com.tw/eng/products/dm9601.htm for details.

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


authored by

Peter Korsgaard and committed by
Greg Kroah-Hartman
d0374f4f 39c4b38c

+623
+7
MAINTAINERS
··· 3392 3392 S: Maintained 3393 3393 W: http://www.kroah.com/linux-usb/ 3394 3394 3395 + USB DAVICOM DM9601 DRIVER 3396 + P: Peter Korsgaard 3397 + M: jacmet@sunsite.dk 3398 + L: linux-usb-devel@lists.sourceforge.net 3399 + W: http://www.linux-usb.org/usbnet 3400 + S: Maintained 3401 + 3395 3402 USB EHCI DRIVER 3396 3403 P: David Brownell 3397 3404 M: dbrownell@users.sourceforge.net
+9
drivers/usb/net/Kconfig
··· 186 186 IEEE 802 "local assignment" bit is set in the address, a "usbX" 187 187 name is used instead. 188 188 189 + config USB_NET_DM9601 190 + tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices" 191 + depends on USB_USBNET 192 + select CRC32 193 + select USB_USBNET_MII 194 + help 195 + This option adds support for Davicom DM9601 based USB 1.1 196 + 10/100 Ethernet adapters. 197 + 189 198 config USB_NET_GL620A 190 199 tristate "GeneSys GL620USB-A based cables" 191 200 depends on USB_USBNET
+1
drivers/usb/net/Makefile
··· 8 8 obj-$(CONFIG_USB_RTL8150) += rtl8150.o 9 9 obj-$(CONFIG_USB_NET_AX8817X) += asix.o 10 10 obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o 11 + obj-$(CONFIG_USB_NET_DM9601) += dm9601.o 11 12 obj-$(CONFIG_USB_NET_GL620A) += gl620a.o 12 13 obj-$(CONFIG_USB_NET_NET1080) += net1080.o 13 14 obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
+606
drivers/usb/net/dm9601.c
··· 1 + /* 2 + * Davicom DM9601 USB 1.1 10/100Mbps ethernet devices 3 + * 4 + * Peter Korsgaard <jacmet@sunsite.dk> 5 + * 6 + * This file is licensed under the terms of the GNU General Public License 7 + * version 2. This program is licensed "as is" without any warranty of any 8 + * kind, whether express or implied. 9 + */ 10 + 11 + //#define DEBUG 12 + 13 + #include <linux/module.h> 14 + #include <linux/sched.h> 15 + #include <linux/init.h> 16 + #include <linux/netdevice.h> 17 + #include <linux/etherdevice.h> 18 + #include <linux/ethtool.h> 19 + #include <linux/mii.h> 20 + #include <linux/usb.h> 21 + #include <linux/crc32.h> 22 + 23 + #include "usbnet.h" 24 + 25 + /* datasheet: 26 + http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf 27 + */ 28 + 29 + /* control requests */ 30 + #define DM_READ_REGS 0x00 31 + #define DM_WRITE_REGS 0x01 32 + #define DM_READ_MEMS 0x02 33 + #define DM_WRITE_REG 0x03 34 + #define DM_WRITE_MEMS 0x05 35 + #define DM_WRITE_MEM 0x07 36 + 37 + /* registers */ 38 + #define DM_NET_CTRL 0x00 39 + #define DM_RX_CTRL 0x05 40 + #define DM_SHARED_CTRL 0x0b 41 + #define DM_SHARED_ADDR 0x0c 42 + #define DM_SHARED_DATA 0x0d /* low + high */ 43 + #define DM_PHY_ADDR 0x10 /* 6 bytes */ 44 + #define DM_MCAST_ADDR 0x16 /* 8 bytes */ 45 + #define DM_GPR_CTRL 0x1e 46 + #define DM_GPR_DATA 0x1f 47 + 48 + #define DM_MAX_MCAST 64 49 + #define DM_MCAST_SIZE 8 50 + #define DM_EEPROM_LEN 256 51 + #define DM_TX_OVERHEAD 2 /* 2 byte header */ 52 + #define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */ 53 + #define DM_TIMEOUT 1000 54 + 55 + 56 + static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data) 57 + { 58 + devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length); 59 + return usb_control_msg(dev->udev, 60 + usb_rcvctrlpipe(dev->udev, 0), 61 + DM_READ_REGS, 62 + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 63 + 0, reg, data, length, USB_CTRL_SET_TIMEOUT); 64 + } 65 + 66 + static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value) 67 + { 68 + return dm_read(dev, reg, 1, value); 69 + } 70 + 71 + static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data) 72 + { 73 + devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length); 74 + return usb_control_msg(dev->udev, 75 + usb_sndctrlpipe(dev->udev, 0), 76 + DM_WRITE_REGS, 77 + USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, 78 + 0, reg, data, length, USB_CTRL_SET_TIMEOUT); 79 + } 80 + 81 + static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value) 82 + { 83 + devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value); 84 + return usb_control_msg(dev->udev, 85 + usb_sndctrlpipe(dev->udev, 0), 86 + DM_WRITE_REG, 87 + USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, 88 + value, reg, 0, 0, USB_CTRL_SET_TIMEOUT); 89 + } 90 + 91 + static void dm_write_async_callback(struct urb *urb) 92 + { 93 + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; 94 + 95 + if (urb->status < 0) 96 + printk(KERN_DEBUG "dm_write_async_callback() failed with %d", 97 + urb->status); 98 + 99 + kfree(req); 100 + usb_free_urb(urb); 101 + } 102 + 103 + static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) 104 + { 105 + struct usb_ctrlrequest *req; 106 + struct urb *urb; 107 + int status; 108 + 109 + devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length); 110 + 111 + urb = usb_alloc_urb(0, GFP_ATOMIC); 112 + if (!urb) { 113 + deverr(dev, "Error allocating URB in dm_write_async!"); 114 + return; 115 + } 116 + 117 + req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); 118 + if (!req) { 119 + deverr(dev, "Failed to allocate memory for control request"); 120 + usb_free_urb(urb); 121 + return; 122 + } 123 + 124 + req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 125 + req->bRequest = DM_WRITE_REGS; 126 + req->wValue = 0; 127 + req->wIndex = cpu_to_le16(reg); 128 + req->wLength = cpu_to_le16(length); 129 + 130 + usb_fill_control_urb(urb, dev->udev, 131 + usb_sndctrlpipe(dev->udev, 0), 132 + (void *)req, data, length, 133 + dm_write_async_callback, req); 134 + 135 + status = usb_submit_urb(urb, GFP_ATOMIC); 136 + if (status < 0) { 137 + deverr(dev, "Error submitting the control message: status=%d", 138 + status); 139 + kfree(req); 140 + usb_free_urb(urb); 141 + } 142 + } 143 + 144 + static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value) 145 + { 146 + struct usb_ctrlrequest *req; 147 + struct urb *urb; 148 + int status; 149 + 150 + devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x", 151 + reg, value); 152 + 153 + urb = usb_alloc_urb(0, GFP_ATOMIC); 154 + if (!urb) { 155 + deverr(dev, "Error allocating URB in dm_write_async!"); 156 + return; 157 + } 158 + 159 + req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); 160 + if (!req) { 161 + deverr(dev, "Failed to allocate memory for control request"); 162 + usb_free_urb(urb); 163 + return; 164 + } 165 + 166 + req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 167 + req->bRequest = DM_WRITE_REG; 168 + req->wValue = cpu_to_le16(value); 169 + req->wIndex = cpu_to_le16(reg); 170 + req->wLength = 0; 171 + 172 + usb_fill_control_urb(urb, dev->udev, 173 + usb_sndctrlpipe(dev->udev, 0), 174 + (void *)req, 0, 0, dm_write_async_callback, req); 175 + 176 + status = usb_submit_urb(urb, GFP_ATOMIC); 177 + if (status < 0) { 178 + deverr(dev, "Error submitting the control message: status=%d", 179 + status); 180 + kfree(req); 181 + usb_free_urb(urb); 182 + } 183 + } 184 + 185 + static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, u16 *value) 186 + { 187 + int ret, i; 188 + 189 + mutex_lock(&dev->phy_mutex); 190 + 191 + dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); 192 + dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4); 193 + 194 + for (i = 0; i < DM_TIMEOUT; i++) { 195 + u8 tmp; 196 + 197 + udelay(1); 198 + ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); 199 + if (ret < 0) 200 + goto out; 201 + 202 + /* ready */ 203 + if ((tmp & 1) == 0) 204 + break; 205 + } 206 + 207 + if (i == DM_TIMEOUT) { 208 + deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom"); 209 + ret = -EIO; 210 + goto out; 211 + } 212 + 213 + dm_write_reg(dev, DM_SHARED_CTRL, 0x0); 214 + ret = dm_read(dev, DM_SHARED_DATA, 2, value); 215 + 216 + devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d", 217 + phy, reg, *value, ret); 218 + 219 + out: 220 + mutex_unlock(&dev->phy_mutex); 221 + return ret; 222 + } 223 + 224 + static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, u16 value) 225 + { 226 + int ret, i; 227 + 228 + mutex_lock(&dev->phy_mutex); 229 + 230 + ret = dm_write(dev, DM_SHARED_DATA, 2, &value); 231 + if (ret < 0) 232 + goto out; 233 + 234 + dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); 235 + dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1c : 0x14); 236 + 237 + for (i = 0; i < DM_TIMEOUT; i++) { 238 + u8 tmp; 239 + 240 + udelay(1); 241 + ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); 242 + if (ret < 0) 243 + goto out; 244 + 245 + /* ready */ 246 + if ((tmp & 1) == 0) 247 + break; 248 + } 249 + 250 + if (i == DM_TIMEOUT) { 251 + deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom"); 252 + ret = -EIO; 253 + goto out; 254 + } 255 + 256 + dm_write_reg(dev, DM_SHARED_CTRL, 0x0); 257 + 258 + out: 259 + mutex_unlock(&dev->phy_mutex); 260 + return ret; 261 + } 262 + 263 + static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) 264 + { 265 + return dm_read_shared_word(dev, 0, offset, value); 266 + } 267 + 268 + 269 + 270 + static int dm9601_get_eeprom_len(struct net_device *dev) 271 + { 272 + return DM_EEPROM_LEN; 273 + } 274 + 275 + static int dm9601_get_eeprom(struct net_device *net, 276 + struct ethtool_eeprom *eeprom, u8 * data) 277 + { 278 + struct usbnet *dev = netdev_priv(net); 279 + u16 *ebuf = (u16 *) data; 280 + int i; 281 + 282 + /* access is 16bit */ 283 + if ((eeprom->offset % 2) || (eeprom->len % 2)) 284 + return -EINVAL; 285 + 286 + for (i = 0; i < eeprom->len / 2; i++) { 287 + if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i, 288 + &ebuf[i]) < 0) 289 + return -EINVAL; 290 + } 291 + return 0; 292 + } 293 + 294 + static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc) 295 + { 296 + struct usbnet *dev = netdev_priv(netdev); 297 + 298 + u16 res; 299 + 300 + if (phy_id) { 301 + devdbg(dev, "Only internal phy supported"); 302 + return 0; 303 + } 304 + 305 + dm_read_shared_word(dev, 1, loc, &res); 306 + 307 + devdbg(dev, 308 + "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", 309 + phy_id, loc, le16_to_cpu(res)); 310 + 311 + return le16_to_cpu(res); 312 + } 313 + 314 + static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc, 315 + int val) 316 + { 317 + struct usbnet *dev = netdev_priv(netdev); 318 + u16 res = cpu_to_le16(val); 319 + 320 + if (phy_id) { 321 + devdbg(dev, "Only internal phy supported"); 322 + return; 323 + } 324 + 325 + devdbg(dev,"dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", 326 + phy_id, loc, val); 327 + 328 + dm_write_shared_word(dev, 1, loc, res); 329 + } 330 + 331 + static void dm9601_get_drvinfo(struct net_device *net, 332 + struct ethtool_drvinfo *info) 333 + { 334 + /* Inherit standard device info */ 335 + usbnet_get_drvinfo(net, info); 336 + info->eedump_len = DM_EEPROM_LEN; 337 + } 338 + 339 + static u32 dm9601_get_link(struct net_device *net) 340 + { 341 + struct usbnet *dev = netdev_priv(net); 342 + 343 + return mii_link_ok(&dev->mii); 344 + } 345 + 346 + static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd) 347 + { 348 + struct usbnet *dev = netdev_priv(net); 349 + 350 + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 351 + } 352 + 353 + static struct ethtool_ops dm9601_ethtool_ops = { 354 + .get_drvinfo = dm9601_get_drvinfo, 355 + .get_link = dm9601_get_link, 356 + .get_msglevel = usbnet_get_msglevel, 357 + .set_msglevel = usbnet_set_msglevel, 358 + .get_eeprom_len = dm9601_get_eeprom_len, 359 + .get_eeprom = dm9601_get_eeprom, 360 + .get_settings = usbnet_get_settings, 361 + .set_settings = usbnet_set_settings, 362 + .nway_reset = usbnet_nway_reset, 363 + }; 364 + 365 + static void dm9601_set_multicast(struct net_device *net) 366 + { 367 + struct usbnet *dev = netdev_priv(net); 368 + /* We use the 20 byte dev->data for our 8 byte filter buffer 369 + * to avoid allocating memory that is tricky to free later */ 370 + u8 *hashes = (u8 *) & dev->data; 371 + u8 rx_ctl = 0x01; 372 + 373 + memset(hashes, 0x00, DM_MCAST_SIZE); 374 + hashes[DM_MCAST_SIZE - 1] |= 0x80; /* broadcast address */ 375 + 376 + if (net->flags & IFF_PROMISC) { 377 + rx_ctl |= 0x02; 378 + } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) { 379 + rx_ctl |= 0x04; 380 + } else if (net->mc_count) { 381 + struct dev_mc_list *mc_list = net->mc_list; 382 + int i; 383 + 384 + for (i = 0; i < net->mc_count; i++) { 385 + u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; 386 + hashes[crc >> 3] |= 1 << (crc & 0x7); 387 + } 388 + } 389 + 390 + dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes); 391 + dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl); 392 + } 393 + 394 + static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) 395 + { 396 + int ret; 397 + 398 + ret = usbnet_get_endpoints(dev, intf); 399 + if (ret) 400 + goto out; 401 + 402 + dev->net->do_ioctl = dm9601_ioctl; 403 + dev->net->set_multicast_list = dm9601_set_multicast; 404 + dev->net->ethtool_ops = &dm9601_ethtool_ops; 405 + dev->net->hard_header_len += DM_TX_OVERHEAD; 406 + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 407 + dev->rx_urb_size = dev->net->mtu + DM_RX_OVERHEAD; 408 + 409 + dev->mii.dev = dev->net; 410 + dev->mii.mdio_read = dm9601_mdio_read; 411 + dev->mii.mdio_write = dm9601_mdio_write; 412 + dev->mii.phy_id_mask = 0x1f; 413 + dev->mii.reg_num_mask = 0x1f; 414 + 415 + /* reset */ 416 + ret = dm_write_reg(dev, DM_NET_CTRL, 1); 417 + udelay(20); 418 + 419 + /* read MAC */ 420 + ret = dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr); 421 + if (ret < 0) { 422 + printk(KERN_ERR "Error reading MAC address\n"); 423 + ret = -ENODEV; 424 + goto out; 425 + } 426 + 427 + 428 + /* power up phy */ 429 + dm_write_reg(dev, DM_GPR_CTRL, 1); 430 + dm_write_reg(dev, DM_GPR_DATA, 0); 431 + 432 + /* receive broadcast packets */ 433 + dm9601_set_multicast(dev->net); 434 + 435 + dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); 436 + dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, 437 + ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); 438 + mii_nway_restart(&dev->mii); 439 + 440 + out: 441 + return ret; 442 + } 443 + 444 + static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 445 + { 446 + u8 status; 447 + int len; 448 + 449 + /* format: 450 + b0: rx status 451 + b1: packet length (incl crc) low 452 + b2: packet length (incl crc) high 453 + b3..n-4: packet data 454 + bn-3..bn: ethernet crc 455 + */ 456 + 457 + if (unlikely(skb->len < DM_RX_OVERHEAD)) { 458 + dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); 459 + return 0; 460 + } 461 + 462 + status = skb->data[0]; 463 + len = (skb->data[1] | (skb->data[2] << 8)) - 4; 464 + 465 + if (unlikely(status & 0xbf)) { 466 + if (status & 0x01) dev->stats.rx_fifo_errors++; 467 + if (status & 0x02) dev->stats.rx_crc_errors++; 468 + if (status & 0x04) dev->stats.rx_frame_errors++; 469 + if (status & 0x20) dev->stats.rx_missed_errors++; 470 + if (status & 0x90) dev->stats.rx_length_errors++; 471 + return 0; 472 + } 473 + 474 + skb_pull(skb, 3); 475 + skb_trim(skb, len); 476 + 477 + return 1; 478 + } 479 + 480 + static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 481 + gfp_t flags) 482 + { 483 + int len; 484 + 485 + /* format: 486 + b0: packet length low 487 + b1: packet length high 488 + b3..n: packet data 489 + */ 490 + 491 + if (skb_headroom(skb) < DM_TX_OVERHEAD) { 492 + struct sk_buff *skb2; 493 + 494 + skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags); 495 + dev_kfree_skb_any(skb); 496 + skb = skb2; 497 + if (!skb) 498 + return NULL; 499 + } 500 + 501 + __skb_push(skb, DM_TX_OVERHEAD); 502 + 503 + len = skb->len; 504 + /* usbnet adds padding if length is a multiple of packet size 505 + if so, adjust length value in header */ 506 + if ((len % dev->maxpacket) == 0) 507 + len++; 508 + 509 + skb->data[0] = len; 510 + skb->data[1] = len >> 8; 511 + 512 + return skb; 513 + } 514 + 515 + static void dm9601_status(struct usbnet *dev, struct urb *urb) 516 + { 517 + int link; 518 + u8 *buf; 519 + 520 + /* format: 521 + b0: net status 522 + b1: tx status 1 523 + b2: tx status 2 524 + b3: rx status 525 + b4: rx overflow 526 + b5: rx count 527 + b6: tx count 528 + b7: gpr 529 + */ 530 + 531 + if (urb->actual_length < 8) 532 + return; 533 + 534 + buf = urb->transfer_buffer; 535 + 536 + link = !!(buf[0] & 0x40); 537 + if (netif_carrier_ok(dev->net) != link) { 538 + if (link) { 539 + netif_carrier_on(dev->net); 540 + usbnet_defer_kevent (dev, EVENT_LINK_RESET); 541 + } 542 + else 543 + netif_carrier_off(dev->net); 544 + devdbg(dev, "Link Status is: %d", link); 545 + } 546 + } 547 + 548 + static int dm9601_link_reset(struct usbnet *dev) 549 + { 550 + struct ethtool_cmd ecmd; 551 + 552 + mii_check_media(&dev->mii, 1, 1); 553 + mii_ethtool_gset(&dev->mii, &ecmd); 554 + 555 + devdbg(dev, "link_reset() speed: %d duplex: %d", 556 + ecmd.speed, ecmd.duplex); 557 + 558 + return 0; 559 + } 560 + 561 + static const struct driver_info dm9601_info = { 562 + .description = "Davicom DM9601 USB Ethernet", 563 + .flags = FLAG_ETHER, 564 + .bind = dm9601_bind, 565 + .rx_fixup = dm9601_rx_fixup, 566 + .tx_fixup = dm9601_tx_fixup, 567 + .status = dm9601_status, 568 + .link_reset = dm9601_link_reset, 569 + .reset = dm9601_link_reset, 570 + }; 571 + 572 + static const struct usb_device_id products[] = { 573 + { 574 + USB_DEVICE(0x0a46, 0x9601), /* Davicom USB-100 */ 575 + .driver_info = (unsigned long)&dm9601_info, 576 + }, 577 + {}, // END 578 + }; 579 + 580 + MODULE_DEVICE_TABLE(usb, products); 581 + 582 + static struct usb_driver dm9601_driver = { 583 + .name = "dm9601", 584 + .id_table = products, 585 + .probe = usbnet_probe, 586 + .disconnect = usbnet_disconnect, 587 + .suspend = usbnet_suspend, 588 + .resume = usbnet_resume, 589 + }; 590 + 591 + static int __init dm9601_init(void) 592 + { 593 + return usb_register(&dm9601_driver); 594 + } 595 + 596 + static void __exit dm9601_exit(void) 597 + { 598 + usb_deregister(&dm9601_driver); 599 + } 600 + 601 + module_init(dm9601_init); 602 + module_exit(dm9601_exit); 603 + 604 + MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); 605 + MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices"); 606 + MODULE_LICENSE("GPL");