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