diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/hardware/eicon/divasmain.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/hardware/eicon/divasmain.c')
-rw-r--r-- | drivers/isdn/hardware/eicon/divasmain.c | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c new file mode 100644 index 000000000000..c9b26e86d183 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasmain.c | |||
@@ -0,0 +1,856 @@ | |||
1 | /* $Id: divasmain.c,v 1.55.4.6 2005/02/09 19:28:20 armin Exp $ | ||
2 | * | ||
3 | * Low level driver for Eicon DIVA Server ISDN cards. | ||
4 | * | ||
5 | * Copyright 2000-2003 by Armin Schindler (mac@melware.de) | ||
6 | * Copyright 2000-2003 Cytronics & Melware (info@melware.de) | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/devfs_fs_kernel.h> | ||
18 | #include <asm/uaccess.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/smp_lock.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/poll.h> | ||
27 | #include <linux/kmod.h> | ||
28 | |||
29 | #include "platform.h" | ||
30 | #undef ID_MASK | ||
31 | #undef N_DATA | ||
32 | #include "pc.h" | ||
33 | #include "di_defs.h" | ||
34 | #include "divasync.h" | ||
35 | #include "diva.h" | ||
36 | #include "di.h" | ||
37 | #include "io.h" | ||
38 | #include "xdi_msg.h" | ||
39 | #include "xdi_adapter.h" | ||
40 | #include "xdi_vers.h" | ||
41 | #include "diva_dma.h" | ||
42 | #include "diva_pci.h" | ||
43 | |||
44 | static char *main_revision = "$Revision: 1.55.4.6 $"; | ||
45 | |||
46 | static int major; | ||
47 | |||
48 | static int dbgmask; | ||
49 | |||
50 | MODULE_DESCRIPTION("Kernel driver for Eicon DIVA Server cards"); | ||
51 | MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); | ||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | module_param(dbgmask, int, 0); | ||
55 | MODULE_PARM_DESC(dbgmask, "initial debug mask"); | ||
56 | |||
57 | static char *DRIVERNAME = | ||
58 | "Eicon DIVA Server driver (http://www.melware.net)"; | ||
59 | static char *DRIVERLNAME = "divas"; | ||
60 | static char *DEVNAME = "Divas"; | ||
61 | char *DRIVERRELEASE_DIVAS = "2.0"; | ||
62 | |||
63 | extern irqreturn_t diva_os_irq_wrapper(int irq, void *context, | ||
64 | struct pt_regs *regs); | ||
65 | extern int create_divas_proc(void); | ||
66 | extern void remove_divas_proc(void); | ||
67 | extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf); | ||
68 | extern int divasfunc_init(int dbgmask); | ||
69 | extern void divasfunc_exit(void); | ||
70 | |||
71 | typedef struct _diva_os_thread_dpc { | ||
72 | struct tasklet_struct divas_task; | ||
73 | diva_os_soft_isr_t *psoft_isr; | ||
74 | } diva_os_thread_dpc_t; | ||
75 | |||
76 | /* -------------------------------------------------------------------------- | ||
77 | PCI driver interface section | ||
78 | -------------------------------------------------------------------------- */ | ||
79 | /* | ||
80 | vendor, device Vendor and device ID to match (or PCI_ANY_ID) | ||
81 | subvendor, Subsystem vendor and device ID to match (or PCI_ANY_ID) | ||
82 | subdevice | ||
83 | class, Device class to match. The class_mask tells which bits | ||
84 | class_mask of the class are honored during the comparison. | ||
85 | driver_data Data private to the driver. | ||
86 | */ | ||
87 | |||
88 | #if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2) | ||
89 | #define PCI_DEVICE_ID_EICON_MAESTRAP_2 0xE015 | ||
90 | #endif | ||
91 | |||
92 | #if !defined(PCI_DEVICE_ID_EICON_4BRI_VOIP) | ||
93 | #define PCI_DEVICE_ID_EICON_4BRI_VOIP 0xE016 | ||
94 | #endif | ||
95 | |||
96 | #if !defined(PCI_DEVICE_ID_EICON_4BRI_2_VOIP) | ||
97 | #define PCI_DEVICE_ID_EICON_4BRI_2_VOIP 0xE017 | ||
98 | #endif | ||
99 | |||
100 | #if !defined(PCI_DEVICE_ID_EICON_BRI2M_2) | ||
101 | #define PCI_DEVICE_ID_EICON_BRI2M_2 0xE018 | ||
102 | #endif | ||
103 | |||
104 | #if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP) | ||
105 | #define PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP 0xE019 | ||
106 | #endif | ||
107 | |||
108 | #if !defined(PCI_DEVICE_ID_EICON_2F) | ||
109 | #define PCI_DEVICE_ID_EICON_2F 0xE01A | ||
110 | #endif | ||
111 | |||
112 | #if !defined(PCI_DEVICE_ID_EICON_BRI2M_2_VOIP) | ||
113 | #define PCI_DEVICE_ID_EICON_BRI2M_2_VOIP 0xE01B | ||
114 | #endif | ||
115 | |||
116 | /* | ||
117 | This table should be sorted by PCI device ID | ||
118 | */ | ||
119 | static struct pci_device_id divas_pci_tbl[] = { | ||
120 | /* Diva Server BRI-2M PCI 0xE010 */ | ||
121 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRA, | ||
122 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_MAESTRA_PCI}, | ||
123 | /* Diva Server 4BRI-8M PCI 0xE012 */ | ||
124 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ, | ||
125 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_PCI}, | ||
126 | /* Diva Server 4BRI-8M 2.0 PCI 0xE013 */ | ||
127 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ_U, | ||
128 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_V2_PCI}, | ||
129 | /* Diva Server PRI-30M PCI 0xE014 */ | ||
130 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP, | ||
131 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_PCI}, | ||
132 | /* Diva Server PRI 2.0 adapter 0xE015 */ | ||
133 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2, | ||
134 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_V2_PCI}, | ||
135 | /* Diva Server Voice 4BRI-8M PCI 0xE016 */ | ||
136 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_VOIP, | ||
137 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_PCI}, | ||
138 | /* Diva Server Voice 4BRI-8M 2.0 PCI 0xE017 */ | ||
139 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_2_VOIP, | ||
140 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI}, | ||
141 | /* Diva Server BRI-2M 2.0 PCI 0xE018 */ | ||
142 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2, | ||
143 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2M_V2_PCI}, | ||
144 | /* Diva Server Voice PRI 2.0 PCI 0xE019 */ | ||
145 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP, | ||
146 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, | ||
147 | CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI}, | ||
148 | /* Diva Server 2FX 0xE01A */ | ||
149 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_2F, | ||
150 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2F_PCI}, | ||
151 | /* Diva Server Voice BRI-2M 2.0 PCI 0xE01B */ | ||
152 | {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2_VOIP, | ||
153 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI}, | ||
154 | {0,} /* 0 terminated list. */ | ||
155 | }; | ||
156 | MODULE_DEVICE_TABLE(pci, divas_pci_tbl); | ||
157 | |||
158 | static int divas_init_one(struct pci_dev *pdev, | ||
159 | const struct pci_device_id *ent); | ||
160 | static void __devexit divas_remove_one(struct pci_dev *pdev); | ||
161 | |||
162 | static struct pci_driver diva_pci_driver = { | ||
163 | .name = "divas", | ||
164 | .probe = divas_init_one, | ||
165 | .remove = __devexit_p(divas_remove_one), | ||
166 | .id_table = divas_pci_tbl, | ||
167 | }; | ||
168 | |||
169 | /********************************************************* | ||
170 | ** little helper functions | ||
171 | *********************************************************/ | ||
172 | static char *getrev(const char *revision) | ||
173 | { | ||
174 | char *rev; | ||
175 | char *p; | ||
176 | if ((p = strchr(revision, ':'))) { | ||
177 | rev = p + 2; | ||
178 | p = strchr(rev, '$'); | ||
179 | *--p = 0; | ||
180 | } else | ||
181 | rev = "1.0"; | ||
182 | return rev; | ||
183 | } | ||
184 | |||
185 | void diva_log_info(unsigned char *format, ...) | ||
186 | { | ||
187 | va_list args; | ||
188 | unsigned char line[160]; | ||
189 | |||
190 | va_start(args, format); | ||
191 | vsprintf(line, format, args); | ||
192 | va_end(args); | ||
193 | |||
194 | printk(KERN_INFO "%s: %s\n", DRIVERLNAME, line); | ||
195 | } | ||
196 | |||
197 | void divas_get_version(char *p) | ||
198 | { | ||
199 | char tmprev[32]; | ||
200 | |||
201 | strcpy(tmprev, main_revision); | ||
202 | sprintf(p, "%s: %s(%s) %s(%s) major=%d\n", DRIVERLNAME, DRIVERRELEASE_DIVAS, | ||
203 | getrev(tmprev), diva_xdi_common_code_build, DIVA_BUILD, major); | ||
204 | } | ||
205 | |||
206 | /* -------------------------------------------------------------------------- | ||
207 | PCI Bus services | ||
208 | -------------------------------------------------------------------------- */ | ||
209 | byte diva_os_get_pci_bus(void *pci_dev_handle) | ||
210 | { | ||
211 | struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle; | ||
212 | return ((byte) pdev->bus->number); | ||
213 | } | ||
214 | |||
215 | byte diva_os_get_pci_func(void *pci_dev_handle) | ||
216 | { | ||
217 | struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle; | ||
218 | return ((byte) pdev->devfn); | ||
219 | } | ||
220 | |||
221 | unsigned long divasa_get_pci_irq(unsigned char bus, unsigned char func, | ||
222 | void *pci_dev_handle) | ||
223 | { | ||
224 | unsigned char irq = 0; | ||
225 | struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; | ||
226 | |||
227 | irq = dev->irq; | ||
228 | |||
229 | return ((unsigned long) irq); | ||
230 | } | ||
231 | |||
232 | unsigned long divasa_get_pci_bar(unsigned char bus, unsigned char func, | ||
233 | int bar, void *pci_dev_handle) | ||
234 | { | ||
235 | unsigned long ret = 0; | ||
236 | struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; | ||
237 | |||
238 | if (bar < 6) { | ||
239 | ret = dev->resource[bar].start; | ||
240 | } | ||
241 | |||
242 | DBG_TRC(("GOT BAR[%d]=%08x", bar, ret)); | ||
243 | |||
244 | { | ||
245 | unsigned long type = (ret & 0x00000001); | ||
246 | if (type & PCI_BASE_ADDRESS_SPACE_IO) { | ||
247 | DBG_TRC((" I/O")); | ||
248 | ret &= PCI_BASE_ADDRESS_IO_MASK; | ||
249 | } else { | ||
250 | DBG_TRC((" memory")); | ||
251 | ret &= PCI_BASE_ADDRESS_MEM_MASK; | ||
252 | } | ||
253 | DBG_TRC((" final=%08x", ret)); | ||
254 | } | ||
255 | |||
256 | return (ret); | ||
257 | } | ||
258 | |||
259 | void PCIwrite(byte bus, byte func, int offset, void *data, int length, | ||
260 | void *pci_dev_handle) | ||
261 | { | ||
262 | struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; | ||
263 | |||
264 | switch (length) { | ||
265 | case 1: /* byte */ | ||
266 | pci_write_config_byte(dev, offset, | ||
267 | *(unsigned char *) data); | ||
268 | break; | ||
269 | case 2: /* word */ | ||
270 | pci_write_config_word(dev, offset, | ||
271 | *(unsigned short *) data); | ||
272 | break; | ||
273 | case 4: /* dword */ | ||
274 | pci_write_config_dword(dev, offset, | ||
275 | *(unsigned int *) data); | ||
276 | break; | ||
277 | |||
278 | default: /* buffer */ | ||
279 | if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */ | ||
280 | dword *p = (dword *) data; | ||
281 | length /= 4; | ||
282 | |||
283 | while (length--) { | ||
284 | pci_write_config_dword(dev, offset, | ||
285 | *(unsigned int *) | ||
286 | p++); | ||
287 | } | ||
288 | } else { /* copy as byte stream */ | ||
289 | byte *p = (byte *) data; | ||
290 | |||
291 | while (length--) { | ||
292 | pci_write_config_byte(dev, offset, | ||
293 | *(unsigned char *) | ||
294 | p++); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | |||
300 | void PCIread(byte bus, byte func, int offset, void *data, int length, | ||
301 | void *pci_dev_handle) | ||
302 | { | ||
303 | struct pci_dev *dev = (struct pci_dev *) pci_dev_handle; | ||
304 | |||
305 | switch (length) { | ||
306 | case 1: /* byte */ | ||
307 | pci_read_config_byte(dev, offset, (unsigned char *) data); | ||
308 | break; | ||
309 | case 2: /* word */ | ||
310 | pci_read_config_word(dev, offset, (unsigned short *) data); | ||
311 | break; | ||
312 | case 4: /* dword */ | ||
313 | pci_read_config_dword(dev, offset, (unsigned int *) data); | ||
314 | break; | ||
315 | |||
316 | default: /* buffer */ | ||
317 | if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */ | ||
318 | dword *p = (dword *) data; | ||
319 | length /= 4; | ||
320 | |||
321 | while (length--) { | ||
322 | pci_read_config_dword(dev, offset, | ||
323 | (unsigned int *) | ||
324 | p++); | ||
325 | } | ||
326 | } else { /* copy as byte stream */ | ||
327 | byte *p = (byte *) data; | ||
328 | |||
329 | while (length--) { | ||
330 | pci_read_config_byte(dev, offset, | ||
331 | (unsigned char *) | ||
332 | p++); | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | Init map with DMA pages. It is not problem if some allocations fail - | ||
340 | the channels that will not get one DMA page will use standard PIO | ||
341 | interface | ||
342 | */ | ||
343 | static void *diva_pci_alloc_consistent(struct pci_dev *hwdev, | ||
344 | size_t size, | ||
345 | dma_addr_t * dma_handle, | ||
346 | void **addr_handle) | ||
347 | { | ||
348 | void *addr = pci_alloc_consistent(hwdev, size, dma_handle); | ||
349 | |||
350 | *addr_handle = addr; | ||
351 | |||
352 | return (addr); | ||
353 | } | ||
354 | |||
355 | void diva_init_dma_map(void *hdev, | ||
356 | struct _diva_dma_map_entry **ppmap, int nentries) | ||
357 | { | ||
358 | struct pci_dev *pdev = (struct pci_dev *) hdev; | ||
359 | struct _diva_dma_map_entry *pmap = | ||
360 | diva_alloc_dma_map(hdev, nentries); | ||
361 | |||
362 | if (pmap) { | ||
363 | int i; | ||
364 | dma_addr_t dma_handle; | ||
365 | void *cpu_addr; | ||
366 | void *addr_handle; | ||
367 | |||
368 | for (i = 0; i < nentries; i++) { | ||
369 | if (!(cpu_addr = diva_pci_alloc_consistent(pdev, | ||
370 | PAGE_SIZE, | ||
371 | &dma_handle, | ||
372 | &addr_handle))) | ||
373 | { | ||
374 | break; | ||
375 | } | ||
376 | diva_init_dma_map_entry(pmap, i, cpu_addr, | ||
377 | (dword) dma_handle, | ||
378 | addr_handle); | ||
379 | DBG_TRC(("dma map alloc [%d]=(%08lx:%08x:%08lx)", | ||
380 | i, (unsigned long) cpu_addr, | ||
381 | (dword) dma_handle, | ||
382 | (unsigned long) addr_handle))} | ||
383 | } | ||
384 | |||
385 | *ppmap = pmap; | ||
386 | } | ||
387 | |||
388 | /* | ||
389 | Free all contained in the map entries and memory used by the map | ||
390 | Should be always called after adapter removal from DIDD array | ||
391 | */ | ||
392 | void diva_free_dma_map(void *hdev, struct _diva_dma_map_entry *pmap) | ||
393 | { | ||
394 | struct pci_dev *pdev = (struct pci_dev *) hdev; | ||
395 | int i; | ||
396 | dword phys_addr; | ||
397 | void *cpu_addr; | ||
398 | dma_addr_t dma_handle; | ||
399 | void *addr_handle; | ||
400 | |||
401 | for (i = 0; (pmap != 0); i++) { | ||
402 | diva_get_dma_map_entry(pmap, i, &cpu_addr, &phys_addr); | ||
403 | if (!cpu_addr) { | ||
404 | break; | ||
405 | } | ||
406 | addr_handle = diva_get_entry_handle(pmap, i); | ||
407 | dma_handle = (dma_addr_t) phys_addr; | ||
408 | pci_free_consistent(pdev, PAGE_SIZE, addr_handle, | ||
409 | dma_handle); | ||
410 | DBG_TRC(("dma map free [%d]=(%08lx:%08x:%08lx)", i, | ||
411 | (unsigned long) cpu_addr, (dword) dma_handle, | ||
412 | (unsigned long) addr_handle)) | ||
413 | } | ||
414 | |||
415 | diva_free_dma_mapping(pmap); | ||
416 | } | ||
417 | |||
418 | |||
419 | /********************************************************* | ||
420 | ** I/O port utilities | ||
421 | *********************************************************/ | ||
422 | |||
423 | int | ||
424 | diva_os_register_io_port(void *adapter, int on, unsigned long port, | ||
425 | unsigned long length, const char *name, int id) | ||
426 | { | ||
427 | if (on) { | ||
428 | if (!request_region(port, length, name)) { | ||
429 | DBG_ERR(("A: I/O: can't register port=%08x", port)) | ||
430 | return (-1); | ||
431 | } | ||
432 | } else { | ||
433 | release_region(port, length); | ||
434 | } | ||
435 | return (0); | ||
436 | } | ||
437 | |||
438 | void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, int id, unsigned long bar, unsigned long area_length) | ||
439 | { | ||
440 | void __iomem *ret = ioremap(bar, area_length); | ||
441 | DBG_TRC(("remap(%08x)->%p", bar, ret)); | ||
442 | return (ret); | ||
443 | } | ||
444 | |||
445 | void divasa_unmap_pci_bar(void __iomem *bar) | ||
446 | { | ||
447 | if (bar) { | ||
448 | iounmap(bar); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | /********************************************************* | ||
453 | ** I/O port access | ||
454 | *********************************************************/ | ||
455 | byte __inline__ inpp(void __iomem *addr) | ||
456 | { | ||
457 | return (inb((unsigned long) addr)); | ||
458 | } | ||
459 | |||
460 | word __inline__ inppw(void __iomem *addr) | ||
461 | { | ||
462 | return (inw((unsigned long) addr)); | ||
463 | } | ||
464 | |||
465 | void __inline__ inppw_buffer(void __iomem *addr, void *P, int length) | ||
466 | { | ||
467 | insw((unsigned long) addr, (word *) P, length >> 1); | ||
468 | } | ||
469 | |||
470 | void __inline__ outppw_buffer(void __iomem *addr, void *P, int length) | ||
471 | { | ||
472 | outsw((unsigned long) addr, (word *) P, length >> 1); | ||
473 | } | ||
474 | |||
475 | void __inline__ outppw(void __iomem *addr, word w) | ||
476 | { | ||
477 | outw(w, (unsigned long) addr); | ||
478 | } | ||
479 | |||
480 | void __inline__ outpp(void __iomem *addr, word p) | ||
481 | { | ||
482 | outb(p, (unsigned long) addr); | ||
483 | } | ||
484 | |||
485 | /* -------------------------------------------------------------------------- | ||
486 | IRQ request / remove | ||
487 | -------------------------------------------------------------------------- */ | ||
488 | int diva_os_register_irq(void *context, byte irq, const char *name) | ||
489 | { | ||
490 | int result = request_irq(irq, diva_os_irq_wrapper, | ||
491 | SA_INTERRUPT | SA_SHIRQ, name, context); | ||
492 | return (result); | ||
493 | } | ||
494 | |||
495 | void diva_os_remove_irq(void *context, byte irq) | ||
496 | { | ||
497 | free_irq(irq, context); | ||
498 | } | ||
499 | |||
500 | /* -------------------------------------------------------------------------- | ||
501 | DPC framework implementation | ||
502 | -------------------------------------------------------------------------- */ | ||
503 | static void diva_os_dpc_proc(unsigned long context) | ||
504 | { | ||
505 | diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context; | ||
506 | diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr; | ||
507 | |||
508 | (*(pisr->callback)) (pisr, pisr->callback_context); | ||
509 | } | ||
510 | |||
511 | int diva_os_initialize_soft_isr(diva_os_soft_isr_t * psoft_isr, | ||
512 | diva_os_soft_isr_callback_t callback, | ||
513 | void *callback_context) | ||
514 | { | ||
515 | diva_os_thread_dpc_t *pdpc; | ||
516 | |||
517 | pdpc = (diva_os_thread_dpc_t *) diva_os_malloc(0, sizeof(*pdpc)); | ||
518 | if (!(psoft_isr->object = pdpc)) { | ||
519 | return (-1); | ||
520 | } | ||
521 | memset(pdpc, 0x00, sizeof(*pdpc)); | ||
522 | psoft_isr->callback = callback; | ||
523 | psoft_isr->callback_context = callback_context; | ||
524 | pdpc->psoft_isr = psoft_isr; | ||
525 | tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc); | ||
526 | |||
527 | return (0); | ||
528 | } | ||
529 | |||
530 | int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr) | ||
531 | { | ||
532 | if (psoft_isr && psoft_isr->object) { | ||
533 | diva_os_thread_dpc_t *pdpc = | ||
534 | (diva_os_thread_dpc_t *) psoft_isr->object; | ||
535 | |||
536 | tasklet_schedule(&pdpc->divas_task); | ||
537 | } | ||
538 | |||
539 | return (1); | ||
540 | } | ||
541 | |||
542 | int diva_os_cancel_soft_isr(diva_os_soft_isr_t * psoft_isr) | ||
543 | { | ||
544 | return (0); | ||
545 | } | ||
546 | |||
547 | void diva_os_remove_soft_isr(diva_os_soft_isr_t * psoft_isr) | ||
548 | { | ||
549 | if (psoft_isr && psoft_isr->object) { | ||
550 | diva_os_thread_dpc_t *pdpc = | ||
551 | (diva_os_thread_dpc_t *) psoft_isr->object; | ||
552 | void *mem; | ||
553 | |||
554 | tasklet_kill(&pdpc->divas_task); | ||
555 | flush_scheduled_work(); | ||
556 | mem = psoft_isr->object; | ||
557 | psoft_isr->object = NULL; | ||
558 | diva_os_free(0, mem); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * kernel/user space copy functions | ||
564 | */ | ||
565 | static int | ||
566 | xdi_copy_to_user(void *os_handle, void __user *dst, const void *src, int length) | ||
567 | { | ||
568 | if (copy_to_user(dst, src, length)) { | ||
569 | return (-EFAULT); | ||
570 | } | ||
571 | return (length); | ||
572 | } | ||
573 | |||
574 | static int | ||
575 | xdi_copy_from_user(void *os_handle, void *dst, const void __user *src, int length) | ||
576 | { | ||
577 | if (copy_from_user(dst, src, length)) { | ||
578 | return (-EFAULT); | ||
579 | } | ||
580 | return (length); | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * device node operations | ||
585 | */ | ||
586 | static int divas_open(struct inode *inode, struct file *file) | ||
587 | { | ||
588 | return (0); | ||
589 | } | ||
590 | |||
591 | static int divas_release(struct inode *inode, struct file *file) | ||
592 | { | ||
593 | if (file->private_data) { | ||
594 | diva_xdi_close_adapter(file->private_data, file); | ||
595 | } | ||
596 | return (0); | ||
597 | } | ||
598 | |||
599 | static ssize_t divas_write(struct file *file, const char __user *buf, | ||
600 | size_t count, loff_t * ppos) | ||
601 | { | ||
602 | int ret = -EINVAL; | ||
603 | |||
604 | if (!file->private_data) { | ||
605 | file->private_data = diva_xdi_open_adapter(file, buf, | ||
606 | count, | ||
607 | xdi_copy_from_user); | ||
608 | } | ||
609 | if (!file->private_data) { | ||
610 | return (-ENODEV); | ||
611 | } | ||
612 | |||
613 | ret = diva_xdi_write(file->private_data, file, | ||
614 | buf, count, xdi_copy_from_user); | ||
615 | switch (ret) { | ||
616 | case -1: /* Message should be removed from rx mailbox first */ | ||
617 | ret = -EBUSY; | ||
618 | break; | ||
619 | case -2: /* invalid adapter was specified in this call */ | ||
620 | ret = -ENOMEM; | ||
621 | break; | ||
622 | case -3: | ||
623 | ret = -ENXIO; | ||
624 | break; | ||
625 | } | ||
626 | DBG_TRC(("write: ret %d", ret)); | ||
627 | return (ret); | ||
628 | } | ||
629 | |||
630 | static ssize_t divas_read(struct file *file, char __user *buf, | ||
631 | size_t count, loff_t * ppos) | ||
632 | { | ||
633 | int ret = -EINVAL; | ||
634 | |||
635 | if (!file->private_data) { | ||
636 | file->private_data = diva_xdi_open_adapter(file, buf, | ||
637 | count, | ||
638 | xdi_copy_from_user); | ||
639 | } | ||
640 | if (!file->private_data) { | ||
641 | return (-ENODEV); | ||
642 | } | ||
643 | |||
644 | ret = diva_xdi_read(file->private_data, file, | ||
645 | buf, count, xdi_copy_to_user); | ||
646 | switch (ret) { | ||
647 | case -1: /* RX mailbox is empty */ | ||
648 | ret = -EAGAIN; | ||
649 | break; | ||
650 | case -2: /* no memory, mailbox was cleared, last command is failed */ | ||
651 | ret = -ENOMEM; | ||
652 | break; | ||
653 | case -3: /* can't copy to user, retry */ | ||
654 | ret = -EFAULT; | ||
655 | break; | ||
656 | } | ||
657 | DBG_TRC(("read: ret %d", ret)); | ||
658 | return (ret); | ||
659 | } | ||
660 | |||
661 | static unsigned int divas_poll(struct file *file, poll_table * wait) | ||
662 | { | ||
663 | if (!file->private_data) { | ||
664 | return (POLLERR); | ||
665 | } | ||
666 | return (POLLIN | POLLRDNORM); | ||
667 | } | ||
668 | |||
669 | static struct file_operations divas_fops = { | ||
670 | .owner = THIS_MODULE, | ||
671 | .llseek = no_llseek, | ||
672 | .read = divas_read, | ||
673 | .write = divas_write, | ||
674 | .poll = divas_poll, | ||
675 | .open = divas_open, | ||
676 | .release = divas_release | ||
677 | }; | ||
678 | |||
679 | static void divas_unregister_chrdev(void) | ||
680 | { | ||
681 | devfs_remove(DEVNAME); | ||
682 | unregister_chrdev(major, DEVNAME); | ||
683 | } | ||
684 | |||
685 | static int DIVA_INIT_FUNCTION divas_register_chrdev(void) | ||
686 | { | ||
687 | if ((major = register_chrdev(0, DEVNAME, &divas_fops)) < 0) | ||
688 | { | ||
689 | printk(KERN_ERR "%s: failed to create /dev entry.\n", | ||
690 | DRIVERLNAME); | ||
691 | return (0); | ||
692 | } | ||
693 | devfs_mk_cdev(MKDEV(major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVNAME); | ||
694 | |||
695 | return (1); | ||
696 | } | ||
697 | |||
698 | /* -------------------------------------------------------------------------- | ||
699 | PCI driver section | ||
700 | -------------------------------------------------------------------------- */ | ||
701 | static int __devinit divas_init_one(struct pci_dev *pdev, | ||
702 | const struct pci_device_id *ent) | ||
703 | { | ||
704 | void *pdiva = NULL; | ||
705 | u8 pci_latency; | ||
706 | u8 new_latency = 32; | ||
707 | |||
708 | DBG_TRC(("%s bus: %08x fn: %08x insertion.\n", | ||
709 | CardProperties[ent->driver_data].Name, | ||
710 | pdev->bus->number, pdev->devfn)) | ||
711 | printk(KERN_INFO "%s: %s bus: %08x fn: %08x insertion.\n", | ||
712 | DRIVERLNAME, CardProperties[ent->driver_data].Name, | ||
713 | pdev->bus->number, pdev->devfn); | ||
714 | |||
715 | if (pci_enable_device(pdev)) { | ||
716 | DBG_TRC(("%s: %s bus: %08x fn: %08x device init failed.\n", | ||
717 | DRIVERLNAME, | ||
718 | CardProperties[ent->driver_data].Name, | ||
719 | pdev->bus->number, | ||
720 | pdev->devfn)) | ||
721 | printk(KERN_ERR | ||
722 | "%s: %s bus: %08x fn: %08x device init failed.\n", | ||
723 | DRIVERLNAME, | ||
724 | CardProperties[ent->driver_data]. | ||
725 | Name, pdev->bus->number, | ||
726 | pdev->devfn); | ||
727 | return (-EIO); | ||
728 | } | ||
729 | |||
730 | pci_set_master(pdev); | ||
731 | |||
732 | pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); | ||
733 | if (!pci_latency) { | ||
734 | DBG_TRC(("%s: bus: %08x fn: %08x fix latency.\n", | ||
735 | DRIVERLNAME, pdev->bus->number, pdev->devfn)) | ||
736 | printk(KERN_INFO | ||
737 | "%s: bus: %08x fn: %08x fix latency.\n", | ||
738 | DRIVERLNAME, pdev->bus->number, pdev->devfn); | ||
739 | pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency); | ||
740 | } | ||
741 | |||
742 | if (!(pdiva = diva_driver_add_card(pdev, ent->driver_data))) { | ||
743 | DBG_TRC(("%s: %s bus: %08x fn: %08x card init failed.\n", | ||
744 | DRIVERLNAME, | ||
745 | CardProperties[ent->driver_data].Name, | ||
746 | pdev->bus->number, | ||
747 | pdev->devfn)) | ||
748 | printk(KERN_ERR | ||
749 | "%s: %s bus: %08x fn: %08x card init failed.\n", | ||
750 | DRIVERLNAME, | ||
751 | CardProperties[ent->driver_data]. | ||
752 | Name, pdev->bus->number, | ||
753 | pdev->devfn); | ||
754 | return (-EIO); | ||
755 | } | ||
756 | |||
757 | pci_set_drvdata(pdev, pdiva); | ||
758 | |||
759 | return (0); | ||
760 | } | ||
761 | |||
762 | static void __devexit divas_remove_one(struct pci_dev *pdev) | ||
763 | { | ||
764 | void *pdiva = pci_get_drvdata(pdev); | ||
765 | |||
766 | DBG_TRC(("bus: %08x fn: %08x removal.\n", | ||
767 | pdev->bus->number, pdev->devfn)) | ||
768 | printk(KERN_INFO "%s: bus: %08x fn: %08x removal.\n", | ||
769 | DRIVERLNAME, pdev->bus->number, pdev->devfn); | ||
770 | |||
771 | if (pdiva) { | ||
772 | diva_driver_remove_card(pdiva); | ||
773 | } | ||
774 | |||
775 | } | ||
776 | |||
777 | /* -------------------------------------------------------------------------- | ||
778 | Driver Load / Startup | ||
779 | -------------------------------------------------------------------------- */ | ||
780 | static int DIVA_INIT_FUNCTION divas_init(void) | ||
781 | { | ||
782 | char tmprev[50]; | ||
783 | int ret = 0; | ||
784 | |||
785 | printk(KERN_INFO "%s\n", DRIVERNAME); | ||
786 | printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_DIVAS); | ||
787 | strcpy(tmprev, main_revision); | ||
788 | printk("%s Build: %s(%s)\n", getrev(tmprev), | ||
789 | diva_xdi_common_code_build, DIVA_BUILD); | ||
790 | printk(KERN_INFO "%s: support for: ", DRIVERLNAME); | ||
791 | #ifdef CONFIG_ISDN_DIVAS_BRIPCI | ||
792 | printk("BRI/PCI "); | ||
793 | #endif | ||
794 | #ifdef CONFIG_ISDN_DIVAS_PRIPCI | ||
795 | printk("PRI/PCI "); | ||
796 | #endif | ||
797 | printk("adapters\n"); | ||
798 | |||
799 | if (!divasfunc_init(dbgmask)) { | ||
800 | printk(KERN_ERR "%s: failed to connect to DIDD.\n", | ||
801 | DRIVERLNAME); | ||
802 | ret = -EIO; | ||
803 | goto out; | ||
804 | } | ||
805 | |||
806 | if (!divas_register_chrdev()) { | ||
807 | #ifdef MODULE | ||
808 | divasfunc_exit(); | ||
809 | #endif | ||
810 | ret = -EIO; | ||
811 | goto out; | ||
812 | } | ||
813 | |||
814 | if (!create_divas_proc()) { | ||
815 | #ifdef MODULE | ||
816 | remove_divas_proc(); | ||
817 | divas_unregister_chrdev(); | ||
818 | divasfunc_exit(); | ||
819 | #endif | ||
820 | printk(KERN_ERR "%s: failed to create proc entry.\n", | ||
821 | DRIVERLNAME); | ||
822 | ret = -EIO; | ||
823 | goto out; | ||
824 | } | ||
825 | |||
826 | if ((ret = pci_register_driver(&diva_pci_driver))) { | ||
827 | #ifdef MODULE | ||
828 | remove_divas_proc(); | ||
829 | divas_unregister_chrdev(); | ||
830 | divasfunc_exit(); | ||
831 | #endif | ||
832 | printk(KERN_ERR "%s: failed to init pci driver.\n", | ||
833 | DRIVERLNAME); | ||
834 | goto out; | ||
835 | } | ||
836 | printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major); | ||
837 | |||
838 | out: | ||
839 | return (ret); | ||
840 | } | ||
841 | |||
842 | /* -------------------------------------------------------------------------- | ||
843 | Driver Unload | ||
844 | -------------------------------------------------------------------------- */ | ||
845 | static void DIVA_EXIT_FUNCTION divas_exit(void) | ||
846 | { | ||
847 | pci_unregister_driver(&diva_pci_driver); | ||
848 | remove_divas_proc(); | ||
849 | divas_unregister_chrdev(); | ||
850 | divasfunc_exit(); | ||
851 | |||
852 | printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); | ||
853 | } | ||
854 | |||
855 | module_init(divas_init); | ||
856 | module_exit(divas_exit); | ||