aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/nosy.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-27 04:26:33 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-27 05:04:10 -0400
commit286468210d83ce0ca1e37e346ed9f4457a161650 (patch)
treee445a09a6a074e3ae65479e417d41d4d1c41f571 /drivers/firewire/nosy.c
parente40152ee1e1c7a63f4777791863215e3faa37a86 (diff)
firewire: new driver: nosy - IEEE 1394 traffic sniffer
This adds the traffic sniffer driver for Texas Instruments PCILynx/ PCILynx2 based cards. The use cases for nosy are analysis of nonstandard protocols and as an aid in development of drivers, applications, or firmwares. Author of the driver is Kristian Høgsberg. Known contributers are Jody McIntyre and Jonathan Woithe. Nosy programs PCILynx chips to operate in promiscuous mode, which is a feature that is not found in OHCI-1394 controllers. Hence, only special hardware as mentioned in the Kconfig help text is suitable for nosy. This is only the kernelspace part of nosy. There is a userspace interface to it, called nosy-dump, proposed to be added into the tools/ subdirectory of the kernel sources in a subsequent change. Kernelspace and userspave component of nosy communicate via a 'misc' character device file called /dev/nosy with a simple ioctl() and read() based protocol, as described by nosy-user.h. The files added here are taken from git://anongit.freedesktop.org/~krh/nosy commit ee29be97 (2009-11-10) with the following changes by Stefan Richter: - Kconfig and Makefile hunks are written from scratch. - Commented out version printk in nosy.c. - Included missing <linux/sched.h>, reported by Stephen Rothwell. "git shortlog nosy{-user.h,.c,.h}" from nosy's git repository: Jonathan Woithe (2): Nosy updates for recent kernels Fix uninitialised memory (needed for 2.6.31 kernel) Kristian Høgsberg (5): Pull over nosy from mercurial repo. Use a misc device instead. Add simple AV/C decoder. Don't break down on big payloads. Set parent device for misc device. As a low-level IEEE 1394 driver, its files are placed into drivers/firewire/ although nosy is not part of the firewire driver stack. I am aware of the following literature from Texas Instruments about PCILynx programming: SCPA020A - PCILynx 1394 to PCI Bus Interface TSB12LV21BPGF Functional Specification SLLA023 - Initialization and Asynchronous Programming of the TSB12LV21A 1394 Device Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Acked-by: Kristian Høgsberg <krh@bitplanet.net>
Diffstat (limited to 'drivers/firewire/nosy.c')
-rw-r--r--drivers/firewire/nosy.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c
new file mode 100644
index 000000000000..079710bf1197
--- /dev/null
+++ b/drivers/firewire/nosy.c
@@ -0,0 +1,695 @@
1/* -*- c-file-style: "linux" -*-
2 *
3 * nosy.c - Snoop mode driver for TI pcilynx 1394 controllers
4 * Copyright (C) 2002 Kristian Høgsberg
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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 Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <linux/kernel.h>
22#include <linux/slab.h>
23#include <linux/interrupt.h>
24#include <linux/sched.h> /* required for linux/wait.h */
25#include <linux/wait.h>
26#include <linux/errno.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/pci.h>
30#include <linux/fs.h>
31#include <linux/poll.h>
32#include <linux/miscdevice.h>
33#include <asm/byteorder.h>
34#include <asm/atomic.h>
35#include <asm/io.h>
36#include <asm/uaccess.h>
37#include <asm/timex.h>
38
39#include "nosy.h"
40#include "nosy-user.h"
41
42#define TCODE_PHY_PACKET 0x10
43#define PCI_DEVICE_ID_TI_PCILYNX 0x8000
44
45#define notify(s, args...) printk(KERN_NOTICE s, ## args)
46#define error(s, args...) printk(KERN_ERR s, ## args)
47#define debug(s, args...) printk(KERN_DEBUG s, ## args)
48
49static const char driver_name[] = "nosy";
50
51struct pcl_status {
52 unsigned int transfer_count : 13;
53 unsigned int reserved0 : 1;
54 unsigned int ack_type : 1;
55 unsigned int ack : 4;
56 unsigned int rcv_speed : 2;
57 unsigned int rcv_dma_channel : 6;
58 unsigned int packet_complete : 1;
59 unsigned int packet_error : 1;
60 unsigned int master_error : 1;
61 unsigned int iso_mode : 1;
62 unsigned int self_id : 1;
63};
64
65/* this is the physical layout of a PCL, its size is 128 bytes */
66struct pcl {
67 u32 next;
68 u32 async_error_next;
69 u32 user_data;
70 struct pcl_status pcl_status;
71 u32 remaining_transfer_count;
72 u32 next_data_buffer;
73 struct {
74 u32 control;
75 u32 pointer;
76 } buffer[13] __attribute__ ((packed));
77} __attribute__ ((packed));
78
79struct packet {
80 unsigned int length : 16;
81 unsigned int code : 16;
82 char data[0];
83};
84
85struct packet_buffer {
86 char *data;
87 size_t capacity;
88 long total_packet_count, lost_packet_count;
89 atomic_t size;
90 struct packet *head, *tail;
91 wait_queue_head_t wait;
92};
93
94struct pcilynx {
95 struct pci_dev *pci_device;
96 unsigned char *registers;
97
98 struct pcl *rcv_start_pcl, *rcv_pcl;
99 u32 *rcv_buffer;
100
101 dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus;
102
103 spinlock_t client_list_lock;
104 struct list_head client_list;
105
106 struct miscdevice misc;
107};
108
109
110struct client {
111 struct pcilynx *lynx;
112 unsigned long tcode_mask;
113 struct packet_buffer buffer;
114 struct list_head link;
115};
116
117#define MAX_MINORS 64
118struct pcilynx *minors[MAX_MINORS];
119
120static int
121packet_buffer_init(struct packet_buffer *buffer, size_t capacity)
122{
123 buffer->data = kmalloc(capacity, GFP_KERNEL);
124 if (buffer->data == NULL)
125 return -ENOMEM;
126 buffer->head = (struct packet *) buffer->data;
127 buffer->tail = (struct packet *) buffer->data;
128 buffer->capacity = capacity;
129 buffer->lost_packet_count = 0;
130 atomic_set(&buffer->size, 0);
131 init_waitqueue_head(&buffer->wait);
132
133 return 0;
134}
135
136static void
137packet_buffer_destroy(struct packet_buffer *buffer)
138{
139 kfree(buffer->data);
140}
141
142static int
143packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length)
144{
145 size_t length;
146 char *end;
147
148 if (wait_event_interruptible(buffer->wait,
149 atomic_read(&buffer->size) > 0))
150 return -ERESTARTSYS;
151
152 /* FIXME: Check length <= user_length. */
153
154 end = buffer->data + buffer->capacity;
155 length = buffer->head->length;
156
157 if (&buffer->head->data[length] < end) {
158 if (copy_to_user(data, buffer->head->data, length))
159 return -EFAULT;
160 buffer->head = (struct packet *) &buffer->head->data[length];
161 }
162 else {
163 size_t split = end - buffer->head->data;
164
165 if (copy_to_user(data, buffer->head->data, split))
166 return -EFAULT;
167 if (copy_to_user(data + split, buffer->data, length - split))
168 return -EFAULT;
169 buffer->head = (struct packet *) &buffer->data[length - split];
170 }
171
172 /* Decrease buffer->size as the last thing, since this is what
173 * keeps the interrupt from overwriting the packet we are
174 * retrieving from the buffer. */
175
176 atomic_sub(sizeof (struct packet) + length, &buffer->size);
177
178 return length;
179}
180
181static void
182packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length)
183{
184 char *end;
185
186 buffer->total_packet_count++;
187
188 if (buffer->capacity <
189 atomic_read(&buffer->size) + sizeof (struct packet) + length) {
190 buffer->lost_packet_count++;
191 return;
192 }
193
194 end = buffer->data + buffer->capacity;
195 buffer->tail->length = length;
196
197 if (&buffer->tail->data[length] < end) {
198 memcpy(buffer->tail->data, data, length);
199 buffer->tail = (struct packet *) &buffer->tail->data[length];
200 }
201 else {
202 size_t split = end - buffer->tail->data;
203
204 memcpy(buffer->tail->data, data, split);
205 memcpy(buffer->data, data + split, length - split);
206 buffer->tail = (struct packet *) &buffer->data[length - split];
207 }
208
209 /* Finally, adjust buffer size and wake up userspace reader. */
210
211 atomic_add(sizeof (struct packet) + length, &buffer->size);
212 wake_up_interruptible(&buffer->wait);
213}
214
215static inline void
216reg_write(struct pcilynx *lynx, int offset, u32 data)
217{
218 writel(data, lynx->registers + offset);
219}
220
221static inline u32
222reg_read(struct pcilynx *lynx, int offset)
223{
224 return readl(lynx->registers + offset);
225}
226
227static inline void
228reg_set_bits(struct pcilynx *lynx, int offset, u32 mask)
229{
230 reg_write(lynx, offset, (reg_read(lynx, offset) | mask));
231}
232
233/* Maybe the pcl programs could be setup to just append data instead
234 * of using a whole packet. */
235
236static inline void
237run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, int dmachan)
238{
239 reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus);
240 reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20,
241 DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK);
242}
243
244static int
245set_phy_reg(struct pcilynx *lynx, int addr, int val)
246{
247 if (addr > 15) {
248 debug("%s: PHY register address %d out of range",
249 __FUNCTION__, addr);
250 return -1;
251 }
252
253 if (val > 0xff) {
254 debug("%s: PHY register value %d out of range",
255 __FUNCTION__, val);
256 return -1;
257 }
258
259 reg_write(lynx, LINK_PHY, LINK_PHY_WRITE |
260 LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val));
261
262 return 0;
263}
264
265static void
266nosy_start_snoop(struct client *client)
267{
268 unsigned long flags;
269
270 spin_lock_irqsave(&client->lynx->client_list_lock, flags);
271 list_add_tail(&client->link, &client->lynx->client_list);
272 spin_unlock_irqrestore(&client->lynx->client_list_lock, flags);
273}
274
275static void
276nosy_stop_snoop(struct client *client)
277{
278 unsigned long flags;
279
280 spin_lock_irqsave(&client->lynx->client_list_lock, flags);
281 list_del(&client->link);
282 spin_unlock_irqrestore(&client->lynx->client_list_lock, flags);
283}
284
285static struct client *
286nosy_add_client(struct pcilynx *lynx)
287{
288 struct client *client;
289
290 client = kmalloc(sizeof *client, GFP_KERNEL);
291 client->tcode_mask = ~0;
292 client->lynx = lynx;
293 INIT_LIST_HEAD(&client->link);
294
295 if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) {
296 kfree(client);
297 debug("Failed to allocate packet buffer\n");
298 return NULL;
299 }
300
301 return client;
302}
303
304static void
305nosy_remove_client(struct client *client)
306{
307 nosy_stop_snoop(client);
308 packet_buffer_destroy(&client->buffer);
309 kfree(client);
310}
311
312static int
313nosy_open(struct inode *inode, struct file *file)
314{
315 int minor = iminor(inode);
316
317 if (minor > MAX_MINORS || minors[minor] == NULL)
318 return -ENODEV;
319
320 file->private_data = nosy_add_client(minors[minor]);
321 if (file->private_data == NULL)
322 return -ENOMEM;
323 else
324 return 0;
325}
326
327static int
328nosy_release(struct inode *inode, struct file *file)
329{
330 nosy_remove_client(file->private_data);
331
332 return 0;
333}
334
335static unsigned int
336nosy_poll(struct file *file, poll_table *pt)
337{
338 struct client *client = file->private_data;
339
340 poll_wait(file, &client->buffer.wait, pt);
341
342 if (atomic_read(&client->buffer.size) > 0)
343 return POLLIN | POLLRDNORM;
344 else
345 return 0;
346}
347
348static ssize_t
349nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset)
350{
351 struct client *client = file->private_data;
352
353 return packet_buffer_get(&client->buffer, buffer, count);
354}
355
356static int
357nosy_ioctl(struct inode *inode, struct file *file,
358 unsigned int cmd, unsigned long arg)
359{
360 struct client *client = file->private_data;
361
362 switch (cmd) {
363 case NOSY_IOC_GET_STATS: {
364 struct nosy_stats stats;
365
366 stats.total_packet_count = client->buffer.total_packet_count;
367 stats.lost_packet_count = client->buffer.lost_packet_count;
368 if (copy_to_user((void *) arg, &stats, sizeof stats))
369 return -EFAULT;
370 else
371 return 0;
372 }
373
374 case NOSY_IOC_START:
375 nosy_start_snoop(client);
376 return 0;
377
378 case NOSY_IOC_STOP:
379 nosy_stop_snoop(client);
380 return 0;
381
382 case NOSY_IOC_FILTER:
383 client->tcode_mask = arg;
384 return 0;
385
386 default:
387 return -EINVAL;
388 /* Flush buffer, configure filter. */
389 }
390}
391
392static struct file_operations nosy_ops = {
393 .owner = THIS_MODULE,
394 .read = nosy_read,
395 .ioctl = nosy_ioctl,
396 .poll = nosy_poll,
397 .open = nosy_open,
398 .release = nosy_release,
399};
400
401#define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */
402
403struct link_packet {
404 unsigned int priority : 4;
405 unsigned int tcode : 4;
406 unsigned int rt : 2;
407 unsigned int tlabel : 6;
408 unsigned int destination : 16;
409};
410
411static void
412packet_handler(struct pcilynx *lynx)
413{
414 unsigned long flags;
415 struct list_head *pos;
416 struct client *client;
417 unsigned long tcode_mask;
418 size_t length;
419 struct link_packet *packet;
420 struct timeval tv;
421
422 /* FIXME: Also report rcv_speed. */
423
424 length = lynx->rcv_pcl->pcl_status.transfer_count;
425 packet = (struct link_packet *) &lynx->rcv_buffer[1];
426
427 do_gettimeofday(&tv);
428 lynx->rcv_buffer[0] = tv.tv_usec;
429
430 if (length == PHY_PACKET_SIZE)
431 tcode_mask = 1 << TCODE_PHY_PACKET;
432 else
433 tcode_mask = 1 << packet->tcode;
434
435 spin_lock_irqsave(&lynx->client_list_lock, flags);
436
437 list_for_each(pos, &lynx->client_list) {
438 client = list_entry(pos, struct client, link);
439 if (client->tcode_mask & tcode_mask)
440 packet_buffer_put(&client->buffer,
441 lynx->rcv_buffer, length + 4);
442 }
443
444 spin_unlock_irqrestore(&lynx->client_list_lock, flags);
445}
446
447static void
448bus_reset_handler(struct pcilynx *lynx)
449{
450 unsigned long flags;
451 struct list_head *pos;
452 struct client *client;
453 struct timeval tv;
454
455 do_gettimeofday(&tv);
456
457 spin_lock_irqsave(&lynx->client_list_lock, flags);
458
459 list_for_each(pos, &lynx->client_list) {
460 client = list_entry(pos, struct client, link);
461 packet_buffer_put(&client->buffer, &tv.tv_usec, 4);
462 }
463
464 spin_unlock_irqrestore(&lynx->client_list_lock, flags);
465}
466
467
468
469static irqreturn_t
470irq_handler(int irq, void *device)
471{
472 struct pcilynx *lynx = (struct pcilynx *) device;
473 u32 pci_int_status;
474
475 pci_int_status = reg_read(lynx, PCI_INT_STATUS);
476
477 if ((pci_int_status & PCI_INT_INT_PEND) == 0)
478 /* Not our interrupt, bail out quickly. */
479 return IRQ_NONE;
480
481 if ((pci_int_status & PCI_INT_P1394_INT) != 0) {
482 u32 link_int_status;
483
484 link_int_status = reg_read(lynx, LINK_INT_STATUS);
485 reg_write(lynx, LINK_INT_STATUS, link_int_status);
486
487 if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0)
488 bus_reset_handler(lynx);
489 }
490
491 /* Clear the PCI_INT_STATUS register only after clearing the
492 * LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will
493 * be set again immediately. */
494
495 reg_write(lynx, PCI_INT_STATUS, pci_int_status);
496
497 if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) {
498 packet_handler(lynx);
499 run_pcl(lynx, lynx->rcv_start_pcl_bus, 0);
500 }
501
502 return IRQ_HANDLED;
503}
504
505static void
506remove_card(struct pci_dev *dev)
507{
508 struct pcilynx *lynx;
509
510 lynx = pci_get_drvdata(dev);
511 if (!lynx)
512 return;
513 pci_set_drvdata(dev, NULL);
514
515 reg_write(lynx, PCI_INT_ENABLE, 0);
516 free_irq(lynx->pci_device->irq, lynx);
517
518 pci_free_consistent(lynx->pci_device, sizeof (struct pcl),
519 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus);
520 pci_free_consistent(lynx->pci_device, sizeof (struct pcl),
521 lynx->rcv_pcl, lynx->rcv_pcl_bus);
522 pci_free_consistent(lynx->pci_device, PAGE_SIZE,
523 lynx->rcv_buffer, lynx->rcv_buffer_bus);
524
525 iounmap(lynx->registers);
526
527 minors[lynx->misc.minor] = NULL;
528 misc_deregister(&lynx->misc);
529
530 kfree(lynx);
531}
532
533#define RCV_BUFFER_SIZE (16 * 1024)
534
535#define FAIL(s, args...) \
536 do { \
537 error(s, ## args); \
538 return err; \
539 } while (0)
540
541static int __devinit
542add_card(struct pci_dev *dev, const struct pci_device_id *unused)
543{
544 struct pcilynx *lynx;
545 u32 p, end;
546 int err, i;
547
548 err = -ENXIO;
549
550 if (pci_set_dma_mask(dev, 0xffffffff))
551 FAIL("DMA address limits not supported "
552 "for PCILynx hardware.\n");
553 if (pci_enable_device(dev))
554 FAIL("Failed to enable PCILynx hardware.\n");
555 pci_set_master(dev);
556
557 err = -ENOMEM;
558
559 lynx = kzalloc(sizeof *lynx, GFP_KERNEL);
560 if (lynx == NULL)
561 FAIL("Failed to allocate control structure memory.\n");
562
563 lynx->pci_device = dev;
564 pci_set_drvdata(dev, lynx);
565
566 spin_lock_init(&lynx->client_list_lock);
567 INIT_LIST_HEAD(&lynx->client_list);
568
569 lynx->registers = ioremap_nocache(pci_resource_start(dev, 0),
570 PCILYNX_MAX_REGISTER);
571
572 lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device,
573 sizeof(struct pcl),
574 &lynx->rcv_start_pcl_bus);
575 lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device,
576 sizeof(struct pcl),
577 &lynx->rcv_pcl_bus);
578 lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, RCV_BUFFER_SIZE,
579 &lynx->rcv_buffer_bus);
580 if (lynx->rcv_start_pcl == NULL ||
581 lynx->rcv_pcl == NULL ||
582 lynx->rcv_buffer == NULL)
583 /* FIXME: do proper error handling. */
584 FAIL("Failed to allocate receive buffer.\n");
585
586 lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus;
587 lynx->rcv_pcl->next = PCL_NEXT_INVALID;
588 lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID;
589
590 lynx->rcv_pcl->buffer[0].control =
591 PCL_CMD_RCV | PCL_BIGENDIAN | 2044;
592 lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4;
593 p = lynx->rcv_buffer_bus + 2048;
594 end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE;
595 for (i = 1; p < end; i++, p += 2048) {
596 lynx->rcv_pcl->buffer[i].control =
597 PCL_CMD_RCV | PCL_BIGENDIAN | 2048;
598 lynx->rcv_pcl->buffer[i].pointer = p;
599 }
600 lynx->rcv_pcl->buffer[i - 1].control |= PCL_LAST_BUFF;
601
602 reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
603 /* Fix buggy cards with autoboot pin not tied low: */
604 reg_write(lynx, DMA0_CHAN_CTRL, 0);
605 reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24);
606
607#if 0
608 /* now, looking for PHY register set */
609 if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) {
610 lynx->phyic.reg_1394a = 1;
611 PRINT(KERN_INFO, lynx->id,
612 "found 1394a conform PHY (using extended register set)");
613 lynx->phyic.vendor = get_phy_vendorid(lynx);
614 lynx->phyic.product = get_phy_productid(lynx);
615 } else {
616 lynx->phyic.reg_1394a = 0;
617 PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
618 }
619#endif
620
621 /* Setup the general receive FIFO max size. */
622 reg_write(lynx, FIFO_SIZES, 255);
623
624 reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
625
626 reg_write(lynx, LINK_INT_ENABLE,
627 LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD |
628 LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK |
629 LINK_INT_AT_STUCK | LINK_INT_SNTRJ |
630 LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW |
631 LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW);
632
633 /* Disable the L flag in self ID packets. */
634 set_phy_reg(lynx, 4, 0);
635
636 /* Put this baby into snoop mode */
637 reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE);
638
639 run_pcl(lynx, lynx->rcv_start_pcl_bus, 0);
640
641 if (request_irq(dev->irq, irq_handler, IRQF_SHARED, driver_name, lynx))
642 FAIL("Failed to allocate shared interrupt %d.", dev->irq);
643
644 lynx->misc.parent = &dev->dev;
645 lynx->misc.minor = MISC_DYNAMIC_MINOR;
646 lynx->misc.name = "nosy";
647 lynx->misc.fops = &nosy_ops;
648 if (misc_register(&lynx->misc))
649 FAIL("Failed to register misc char device.");
650 minors[lynx->misc.minor] = lynx;
651
652 notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq);
653
654 return 0;
655}
656
657static struct pci_device_id pci_table[] __devinitdata = {
658 {
659 .vendor = PCI_VENDOR_ID_TI,
660 .device = PCI_DEVICE_ID_TI_PCILYNX,
661 .subvendor = PCI_ANY_ID,
662 .subdevice = PCI_ANY_ID,
663 },
664 { } /* Terminating entry */
665};
666
667static struct pci_driver lynx_pci_driver = {
668 .name = (char *) driver_name,
669 .id_table = pci_table,
670 .probe = add_card,
671 .remove = __devexit_p(remove_card),
672};
673
674MODULE_AUTHOR("Kristian Høgsberg");
675MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers");
676MODULE_LICENSE("GPL");
677MODULE_DEVICE_TABLE(pci, pci_table);
678
679static int __init nosy_init(void)
680{
681 /* notify("Loaded %s version %s.\n", driver_name, VERSION); */
682
683 return pci_register_driver(&lynx_pci_driver);
684}
685
686static void __exit nosy_cleanup(void)
687{
688 pci_unregister_driver(&lynx_pci_driver);
689
690 notify("Unloaded %s.\n", driver_name);
691}
692
693
694module_init(nosy_init);
695module_exit(nosy_cleanup);