nosy.c revision c89db7b8bc88d8288dcfbe7a885b950d2560d564
1/* 2 * nosy - Snoop mode driver for TI PCILynx 1394 controllers 3 * Copyright (C) 2002-2007 Kristian Høgsberg 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software Foundation, 17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 */ 19 20#include <linux/errno.h> 21#include <linux/fs.h> 22#include <linux/init.h> 23#include <linux/interrupt.h> 24#include <linux/io.h> 25#include <linux/kernel.h> 26#include <linux/kref.h> 27#include <linux/miscdevice.h> 28#include <linux/module.h> 29#include <linux/mutex.h> 30#include <linux/pci.h> 31#include <linux/poll.h> 32#include <linux/sched.h> /* required for linux/wait.h */ 33#include <linux/slab.h> 34#include <linux/spinlock.h> 35#include <linux/timex.h> 36#include <linux/uaccess.h> 37#include <linux/wait.h> 38 39#include <asm/atomic.h> 40#include <asm/byteorder.h> 41 42#include "nosy.h" 43#include "nosy-user.h" 44 45#define TCODE_PHY_PACKET 0x10 46#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 47 48#define notify(s, args...) printk(KERN_NOTICE s, ## args) 49#define error(s, args...) printk(KERN_ERR s, ## args) 50#define debug(s, args...) printk(KERN_DEBUG s, ## args) 51 52static char driver_name[] = KBUILD_MODNAME; 53 54struct pcl_status { 55 unsigned int transfer_count : 13; 56 unsigned int reserved0 : 1; 57 unsigned int ack_type : 1; 58 unsigned int ack : 4; 59 unsigned int rcv_speed : 2; 60 unsigned int rcv_dma_channel : 6; 61 unsigned int packet_complete : 1; 62 unsigned int packet_error : 1; 63 unsigned int master_error : 1; 64 unsigned int iso_mode : 1; 65 unsigned int self_id : 1; 66}; 67 68/* this is the physical layout of a PCL, its size is 128 bytes */ 69struct pcl { 70 u32 next; 71 u32 async_error_next; 72 u32 user_data; 73 struct pcl_status pcl_status; 74 u32 remaining_transfer_count; 75 u32 next_data_buffer; 76 struct { 77 u32 control; 78 u32 pointer; 79 } buffer[13] __attribute__ ((packed)); 80} __attribute__ ((packed)); 81 82struct packet { 83 unsigned int length; 84 char data[0]; 85}; 86 87struct packet_buffer { 88 char *data; 89 size_t capacity; 90 long total_packet_count, lost_packet_count; 91 atomic_t size; 92 struct packet *head, *tail; 93 wait_queue_head_t wait; 94}; 95 96struct pcilynx { 97 struct pci_dev *pci_device; 98 __iomem char *registers; 99 100 struct pcl *rcv_start_pcl, *rcv_pcl; 101 u32 *rcv_buffer; 102 103 dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus; 104 105 spinlock_t client_list_lock; 106 struct list_head client_list; 107 108 struct miscdevice misc; 109 struct list_head link; 110 struct kref kref; 111}; 112 113static inline struct pcilynx * 114lynx_get(struct pcilynx *lynx) 115{ 116 kref_get(&lynx->kref); 117 118 return lynx; 119} 120 121static void 122lynx_release(struct kref *kref) 123{ 124 kfree(container_of(kref, struct pcilynx, kref)); 125} 126 127static inline void 128lynx_put(struct pcilynx *lynx) 129{ 130 kref_put(&lynx->kref, lynx_release); 131} 132 133struct client { 134 struct pcilynx *lynx; 135 u32 tcode_mask; 136 struct packet_buffer buffer; 137 struct list_head link; 138}; 139 140static DEFINE_MUTEX(card_mutex); 141static LIST_HEAD(card_list); 142 143static int 144packet_buffer_init(struct packet_buffer *buffer, size_t capacity) 145{ 146 buffer->data = kmalloc(capacity, GFP_KERNEL); 147 if (buffer->data == NULL) 148 return -ENOMEM; 149 buffer->head = (struct packet *) buffer->data; 150 buffer->tail = (struct packet *) buffer->data; 151 buffer->capacity = capacity; 152 buffer->lost_packet_count = 0; 153 atomic_set(&buffer->size, 0); 154 init_waitqueue_head(&buffer->wait); 155 156 return 0; 157} 158 159static void 160packet_buffer_destroy(struct packet_buffer *buffer) 161{ 162 kfree(buffer->data); 163} 164 165static int 166packet_buffer_get(struct client *client, char __user *data, size_t user_length) 167{ 168 struct packet_buffer *buffer = &client->buffer; 169 size_t length; 170 char *end; 171 172 if (wait_event_interruptible(buffer->wait, 173 atomic_read(&buffer->size) > 0) || 174 list_empty(&client->lynx->link)) 175 return -ERESTARTSYS; 176 177 if (atomic_read(&buffer->size) == 0) 178 return -ENODEV; 179 180 /* FIXME: Check length <= user_length. */ 181 182 end = buffer->data + buffer->capacity; 183 length = buffer->head->length; 184 185 if (&buffer->head->data[length] < end) { 186 if (copy_to_user(data, buffer->head->data, length)) 187 return -EFAULT; 188 buffer->head = (struct packet *) &buffer->head->data[length]; 189 } else { 190 size_t split = end - buffer->head->data; 191 192 if (copy_to_user(data, buffer->head->data, split)) 193 return -EFAULT; 194 if (copy_to_user(data + split, buffer->data, length - split)) 195 return -EFAULT; 196 buffer->head = (struct packet *) &buffer->data[length - split]; 197 } 198 199 /* 200 * Decrease buffer->size as the last thing, since this is what 201 * keeps the interrupt from overwriting the packet we are 202 * retrieving from the buffer. 203 */ 204 atomic_sub(sizeof(struct packet) + length, &buffer->size); 205 206 return length; 207} 208 209static void 210packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length) 211{ 212 char *end; 213 214 buffer->total_packet_count++; 215 216 if (buffer->capacity < 217 atomic_read(&buffer->size) + sizeof(struct packet) + length) { 218 buffer->lost_packet_count++; 219 return; 220 } 221 222 end = buffer->data + buffer->capacity; 223 buffer->tail->length = length; 224 225 if (&buffer->tail->data[length] < end) { 226 memcpy(buffer->tail->data, data, length); 227 buffer->tail = (struct packet *) &buffer->tail->data[length]; 228 } else { 229 size_t split = end - buffer->tail->data; 230 231 memcpy(buffer->tail->data, data, split); 232 memcpy(buffer->data, data + split, length - split); 233 buffer->tail = (struct packet *) &buffer->data[length - split]; 234 } 235 236 /* Finally, adjust buffer size and wake up userspace reader. */ 237 238 atomic_add(sizeof(struct packet) + length, &buffer->size); 239 wake_up_interruptible(&buffer->wait); 240} 241 242static inline void 243reg_write(struct pcilynx *lynx, int offset, u32 data) 244{ 245 writel(data, lynx->registers + offset); 246} 247 248static inline u32 249reg_read(struct pcilynx *lynx, int offset) 250{ 251 return readl(lynx->registers + offset); 252} 253 254static inline void 255reg_set_bits(struct pcilynx *lynx, int offset, u32 mask) 256{ 257 reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); 258} 259 260/* 261 * Maybe the pcl programs could be set up to just append data instead 262 * of using a whole packet. 263 */ 264static inline void 265run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, 266 int dmachan) 267{ 268 reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus); 269 reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, 270 DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); 271} 272 273static int 274set_phy_reg(struct pcilynx *lynx, int addr, int val) 275{ 276 if (addr > 15) { 277 debug("PHY register address %d out of range\n", addr); 278 return -1; 279 } 280 281 if (val > 0xff) { 282 debug("PHY register value %d out of range\n", val); 283 return -1; 284 } 285 286 reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | 287 LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); 288 289 return 0; 290} 291 292static int 293nosy_open(struct inode *inode, struct file *file) 294{ 295 int minor = iminor(inode); 296 struct client *client; 297 struct pcilynx *tmp, *lynx = NULL; 298 299 mutex_lock(&card_mutex); 300 list_for_each_entry(tmp, &card_list, link) 301 if (tmp->misc.minor == minor) { 302 lynx = lynx_get(tmp); 303 break; 304 } 305 mutex_unlock(&card_mutex); 306 if (lynx == NULL) 307 return -ENODEV; 308 309 client = kmalloc(sizeof *client, GFP_KERNEL); 310 if (client == NULL) 311 goto fail; 312 313 client->tcode_mask = ~0; 314 client->lynx = lynx; 315 INIT_LIST_HEAD(&client->link); 316 317 if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) 318 goto fail; 319 320 file->private_data = client; 321 322 return 0; 323fail: 324 kfree(client); 325 lynx_put(lynx); 326 327 return -ENOMEM; 328} 329 330static int 331nosy_release(struct inode *inode, struct file *file) 332{ 333 struct client *client = file->private_data; 334 struct pcilynx *lynx = client->lynx; 335 336 spin_lock_irq(&lynx->client_list_lock); 337 list_del_init(&client->link); 338 spin_unlock_irq(&lynx->client_list_lock); 339 340 packet_buffer_destroy(&client->buffer); 341 kfree(client); 342 lynx_put(lynx); 343 344 return 0; 345} 346 347static unsigned int 348nosy_poll(struct file *file, poll_table *pt) 349{ 350 struct client *client = file->private_data; 351 unsigned int ret = 0; 352 353 poll_wait(file, &client->buffer.wait, pt); 354 355 if (atomic_read(&client->buffer.size) > 0) 356 ret = POLLIN | POLLRDNORM; 357 358 if (list_empty(&client->lynx->link)) 359 ret |= POLLHUP; 360 361 return ret; 362} 363 364static ssize_t 365nosy_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) 366{ 367 struct client *client = file->private_data; 368 369 return packet_buffer_get(client, buffer, count); 370} 371 372static long 373nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 374{ 375 struct client *client = file->private_data; 376 spinlock_t *client_list_lock = &client->lynx->client_list_lock; 377 struct nosy_stats stats; 378 379 switch (cmd) { 380 case NOSY_IOC_GET_STATS: 381 spin_lock_irq(client_list_lock); 382 stats.total_packet_count = client->buffer.total_packet_count; 383 stats.lost_packet_count = client->buffer.lost_packet_count; 384 spin_unlock_irq(client_list_lock); 385 386 if (copy_to_user((void __user *) arg, &stats, sizeof stats)) 387 return -EFAULT; 388 else 389 return 0; 390 391 case NOSY_IOC_START: 392 spin_lock_irq(client_list_lock); 393 list_add_tail(&client->link, &client->lynx->client_list); 394 spin_unlock_irq(client_list_lock); 395 396 return 0; 397 398 case NOSY_IOC_STOP: 399 spin_lock_irq(client_list_lock); 400 list_del_init(&client->link); 401 spin_unlock_irq(client_list_lock); 402 403 return 0; 404 405 case NOSY_IOC_FILTER: 406 spin_lock_irq(client_list_lock); 407 client->tcode_mask = arg; 408 spin_unlock_irq(client_list_lock); 409 410 return 0; 411 412 default: 413 return -EINVAL; 414 /* Flush buffer, configure filter. */ 415 } 416} 417 418static const struct file_operations nosy_ops = { 419 .owner = THIS_MODULE, 420 .read = nosy_read, 421 .unlocked_ioctl = nosy_ioctl, 422 .poll = nosy_poll, 423 .open = nosy_open, 424 .release = nosy_release, 425}; 426 427#define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ 428 429struct link_packet { 430 unsigned int priority : 4; 431 unsigned int tcode : 4; 432 unsigned int rt : 2; 433 unsigned int tlabel : 6; 434 unsigned int destination : 16; 435}; 436 437static void 438packet_irq_handler(struct pcilynx *lynx) 439{ 440 struct client *client; 441 u32 tcode_mask; 442 size_t length; 443 struct link_packet *packet; 444 struct timeval tv; 445 446 /* FIXME: Also report rcv_speed. */ 447 448 length = lynx->rcv_pcl->pcl_status.transfer_count; 449 packet = (struct link_packet *) &lynx->rcv_buffer[1]; 450 451 do_gettimeofday(&tv); 452 lynx->rcv_buffer[0] = tv.tv_usec; 453 454 if (length == PHY_PACKET_SIZE) 455 tcode_mask = 1 << TCODE_PHY_PACKET; 456 else 457 tcode_mask = 1 << packet->tcode; 458 459 spin_lock(&lynx->client_list_lock); 460 461 list_for_each_entry(client, &lynx->client_list, link) 462 if (client->tcode_mask & tcode_mask) 463 packet_buffer_put(&client->buffer, 464 lynx->rcv_buffer, length + 4); 465 466 spin_unlock(&lynx->client_list_lock); 467} 468 469static void 470bus_reset_irq_handler(struct pcilynx *lynx) 471{ 472 struct client *client; 473 struct timeval tv; 474 475 do_gettimeofday(&tv); 476 477 spin_lock(&lynx->client_list_lock); 478 479 list_for_each_entry(client, &lynx->client_list, link) 480 packet_buffer_put(&client->buffer, &tv.tv_usec, 4); 481 482 spin_unlock(&lynx->client_list_lock); 483} 484 485static irqreturn_t 486irq_handler(int irq, void *device) 487{ 488 struct pcilynx *lynx = device; 489 u32 pci_int_status; 490 491 pci_int_status = reg_read(lynx, PCI_INT_STATUS); 492 493 if (pci_int_status == ~0) 494 /* Card was ejected. */ 495 return IRQ_NONE; 496 497 if ((pci_int_status & PCI_INT_INT_PEND) == 0) 498 /* Not our interrupt, bail out quickly. */ 499 return IRQ_NONE; 500 501 if ((pci_int_status & PCI_INT_P1394_INT) != 0) { 502 u32 link_int_status; 503 504 link_int_status = reg_read(lynx, LINK_INT_STATUS); 505 reg_write(lynx, LINK_INT_STATUS, link_int_status); 506 507 if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0) 508 bus_reset_irq_handler(lynx); 509 } 510 511 /* Clear the PCI_INT_STATUS register only after clearing the 512 * LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will 513 * be set again immediately. */ 514 515 reg_write(lynx, PCI_INT_STATUS, pci_int_status); 516 517 if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) { 518 packet_irq_handler(lynx); 519 run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); 520 } 521 522 return IRQ_HANDLED; 523} 524 525static void 526remove_card(struct pci_dev *dev) 527{ 528 struct pcilynx *lynx = pci_get_drvdata(dev); 529 struct client *client; 530 531 mutex_lock(&card_mutex); 532 list_del_init(&lynx->link); 533 misc_deregister(&lynx->misc); 534 mutex_unlock(&card_mutex); 535 536 reg_write(lynx, PCI_INT_ENABLE, 0); 537 free_irq(lynx->pci_device->irq, lynx); 538 539 spin_lock_irq(&lynx->client_list_lock); 540 list_for_each_entry(client, &lynx->client_list, link) 541 wake_up_interruptible(&client->buffer.wait); 542 spin_unlock_irq(&lynx->client_list_lock); 543 544 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 545 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); 546 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 547 lynx->rcv_pcl, lynx->rcv_pcl_bus); 548 pci_free_consistent(lynx->pci_device, PAGE_SIZE, 549 lynx->rcv_buffer, lynx->rcv_buffer_bus); 550 551 iounmap(lynx->registers); 552 pci_disable_device(dev); 553 lynx_put(lynx); 554} 555 556#define RCV_BUFFER_SIZE (16 * 1024) 557 558static int __devinit 559add_card(struct pci_dev *dev, const struct pci_device_id *unused) 560{ 561 struct pcilynx *lynx; 562 u32 p, end; 563 int ret, i; 564 565 if (pci_set_dma_mask(dev, 0xffffffff)) { 566 error("DMA address limits not supported " 567 "for PCILynx hardware\n"); 568 return -ENXIO; 569 } 570 if (pci_enable_device(dev)) { 571 error("Failed to enable PCILynx hardware\n"); 572 return -ENXIO; 573 } 574 pci_set_master(dev); 575 576 lynx = kzalloc(sizeof *lynx, GFP_KERNEL); 577 if (lynx == NULL) { 578 error("Failed to allocate control structure memory\n"); 579 ret = -ENOMEM; 580 goto fail_disable; 581 } 582 lynx->pci_device = dev; 583 pci_set_drvdata(dev, lynx); 584 585 spin_lock_init(&lynx->client_list_lock); 586 INIT_LIST_HEAD(&lynx->client_list); 587 kref_init(&lynx->kref); 588 589 lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), 590 PCILYNX_MAX_REGISTER); 591 592 lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device, 593 sizeof(struct pcl), &lynx->rcv_start_pcl_bus); 594 lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device, 595 sizeof(struct pcl), &lynx->rcv_pcl_bus); 596 lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, 597 RCV_BUFFER_SIZE, &lynx->rcv_buffer_bus); 598 if (lynx->rcv_start_pcl == NULL || 599 lynx->rcv_pcl == NULL || 600 lynx->rcv_buffer == NULL) { 601 error("Failed to allocate receive buffer\n"); 602 ret = -ENOMEM; 603 goto fail_deallocate; 604 } 605 lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus; 606 lynx->rcv_pcl->next = PCL_NEXT_INVALID; 607 lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID; 608 609 lynx->rcv_pcl->buffer[0].control = 610 PCL_CMD_RCV | PCL_BIGENDIAN | 2044; 611 lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4; 612 p = lynx->rcv_buffer_bus + 2048; 613 end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; 614 for (i = 1; p < end; i++, p += 2048) { 615 lynx->rcv_pcl->buffer[i].control = 616 PCL_CMD_RCV | PCL_BIGENDIAN | 2048; 617 lynx->rcv_pcl->buffer[i].pointer = p; 618 } 619 lynx->rcv_pcl->buffer[i - 1].control |= PCL_LAST_BUFF; 620 621 reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); 622 /* Fix buggy cards with autoboot pin not tied low: */ 623 reg_write(lynx, DMA0_CHAN_CTRL, 0); 624 reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24); 625 626#if 0 627 /* now, looking for PHY register set */ 628 if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { 629 lynx->phyic.reg_1394a = 1; 630 PRINT(KERN_INFO, lynx->id, 631 "found 1394a conform PHY (using extended register set)"); 632 lynx->phyic.vendor = get_phy_vendorid(lynx); 633 lynx->phyic.product = get_phy_productid(lynx); 634 } else { 635 lynx->phyic.reg_1394a = 0; 636 PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); 637 } 638#endif 639 640 /* Setup the general receive FIFO max size. */ 641 reg_write(lynx, FIFO_SIZES, 255); 642 643 reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); 644 645 reg_write(lynx, LINK_INT_ENABLE, 646 LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD | 647 LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK | 648 LINK_INT_AT_STUCK | LINK_INT_SNTRJ | 649 LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW | 650 LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW); 651 652 /* Disable the L flag in self ID packets. */ 653 set_phy_reg(lynx, 4, 0); 654 655 /* Put this baby into snoop mode */ 656 reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE); 657 658 run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); 659 660 if (request_irq(dev->irq, irq_handler, IRQF_SHARED, 661 driver_name, lynx)) { 662 error("Failed to allocate shared interrupt %d\n", dev->irq); 663 ret = -EIO; 664 goto fail_deallocate; 665 } 666 667 lynx->misc.parent = &dev->dev; 668 lynx->misc.minor = MISC_DYNAMIC_MINOR; 669 lynx->misc.name = "nosy"; 670 lynx->misc.fops = &nosy_ops; 671 672 mutex_lock(&card_mutex); 673 ret = misc_register(&lynx->misc); 674 if (ret) { 675 error("Failed to register misc char device\n"); 676 mutex_unlock(&card_mutex); 677 goto fail_free_irq; 678 } 679 list_add_tail(&lynx->link, &card_list); 680 mutex_unlock(&card_mutex); 681 682 notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); 683 684 return 0; 685 686fail_free_irq: 687 reg_write(lynx, PCI_INT_ENABLE, 0); 688 free_irq(lynx->pci_device->irq, lynx); 689 690fail_deallocate: 691 if (lynx->rcv_start_pcl) 692 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 693 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); 694 if (lynx->rcv_pcl) 695 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 696 lynx->rcv_pcl, lynx->rcv_pcl_bus); 697 if (lynx->rcv_buffer) 698 pci_free_consistent(lynx->pci_device, PAGE_SIZE, 699 lynx->rcv_buffer, lynx->rcv_buffer_bus); 700 iounmap(lynx->registers); 701 kfree(lynx); 702 703fail_disable: 704 pci_disable_device(dev); 705 706 return ret; 707} 708 709static struct pci_device_id pci_table[] __devinitdata = { 710 { 711 .vendor = PCI_VENDOR_ID_TI, 712 .device = PCI_DEVICE_ID_TI_PCILYNX, 713 .subvendor = PCI_ANY_ID, 714 .subdevice = PCI_ANY_ID, 715 }, 716 { } /* Terminating entry */ 717}; 718 719static struct pci_driver lynx_pci_driver = { 720 .name = driver_name, 721 .id_table = pci_table, 722 .probe = add_card, 723 .remove = remove_card, 724}; 725 726MODULE_AUTHOR("Kristian Hoegsberg"); 727MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers"); 728MODULE_LICENSE("GPL"); 729MODULE_DEVICE_TABLE(pci, pci_table); 730 731static int __init nosy_init(void) 732{ 733 return pci_register_driver(&lynx_pci_driver); 734} 735 736static void __exit nosy_cleanup(void) 737{ 738 pci_unregister_driver(&lynx_pci_driver); 739 740 notify("Unloaded %s.\n", driver_name); 741} 742 743module_init(nosy_init); 744module_exit(nosy_cleanup); 745