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/divasi.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/divasi.c')
-rw-r--r-- | drivers/isdn/hardware/eicon/divasi.c | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c new file mode 100644 index 000000000000..df715b47e2b4 --- /dev/null +++ b/drivers/isdn/hardware/eicon/divasi.c | |||
@@ -0,0 +1,581 @@ | |||
1 | /* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $ | ||
2 | * | ||
3 | * Driver for Eicon DIVA Server ISDN cards. | ||
4 | * User Mode IDI Interface | ||
5 | * | ||
6 | * Copyright 2000-2003 by Armin Schindler (mac@melware.de) | ||
7 | * Copyright 2000-2003 Cytronics & Melware (info@melware.de) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/poll.h> | ||
20 | #include <linux/proc_fs.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/devfs_fs_kernel.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | #include "platform.h" | ||
26 | #include "di_defs.h" | ||
27 | #include "divasync.h" | ||
28 | #include "um_xdi.h" | ||
29 | #include "um_idi.h" | ||
30 | |||
31 | static char *main_revision = "$Revision: 1.25.6.2 $"; | ||
32 | |||
33 | static int major; | ||
34 | |||
35 | MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards"); | ||
36 | MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); | ||
37 | MODULE_SUPPORTED_DEVICE("DIVA card driver"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | typedef struct _diva_um_idi_os_context { | ||
41 | wait_queue_head_t read_wait; | ||
42 | wait_queue_head_t close_wait; | ||
43 | struct timer_list diva_timer_id; | ||
44 | int aborted; | ||
45 | int adapter_nr; | ||
46 | } diva_um_idi_os_context_t; | ||
47 | |||
48 | static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)"; | ||
49 | static char *DRIVERLNAME = "diva_idi"; | ||
50 | static char *DEVNAME = "DivasIDI"; | ||
51 | char *DRIVERRELEASE_IDI = "2.0"; | ||
52 | |||
53 | extern int idifunc_init(void); | ||
54 | extern void idifunc_finit(void); | ||
55 | |||
56 | /* | ||
57 | * helper functions | ||
58 | */ | ||
59 | static char *getrev(const char *revision) | ||
60 | { | ||
61 | char *rev; | ||
62 | char *p; | ||
63 | if ((p = strchr(revision, ':'))) { | ||
64 | rev = p + 2; | ||
65 | p = strchr(rev, '$'); | ||
66 | *--p = 0; | ||
67 | } else | ||
68 | rev = "1.0"; | ||
69 | return rev; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * LOCALS | ||
74 | */ | ||
75 | static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count, | ||
76 | loff_t * offset); | ||
77 | static ssize_t um_idi_write(struct file *file, const char __user *buf, | ||
78 | size_t count, loff_t * offset); | ||
79 | static unsigned int um_idi_poll(struct file *file, poll_table * wait); | ||
80 | static int um_idi_open(struct inode *inode, struct file *file); | ||
81 | static int um_idi_release(struct inode *inode, struct file *file); | ||
82 | static int remove_entity(void *entity); | ||
83 | static void diva_um_timer_function(unsigned long data); | ||
84 | |||
85 | /* | ||
86 | * proc entry | ||
87 | */ | ||
88 | extern struct proc_dir_entry *proc_net_eicon; | ||
89 | static struct proc_dir_entry *um_idi_proc_entry = NULL; | ||
90 | |||
91 | static int | ||
92 | um_idi_proc_read(char *page, char **start, off_t off, int count, int *eof, | ||
93 | void *data) | ||
94 | { | ||
95 | int len = 0; | ||
96 | char tmprev[32]; | ||
97 | |||
98 | len += sprintf(page + len, "%s\n", DRIVERNAME); | ||
99 | len += sprintf(page + len, "name : %s\n", DRIVERLNAME); | ||
100 | len += sprintf(page + len, "release : %s\n", DRIVERRELEASE_IDI); | ||
101 | strcpy(tmprev, main_revision); | ||
102 | len += sprintf(page + len, "revision : %s\n", getrev(tmprev)); | ||
103 | len += sprintf(page + len, "build : %s\n", DIVA_BUILD); | ||
104 | len += sprintf(page + len, "major : %d\n", major); | ||
105 | |||
106 | if (off + count >= len) | ||
107 | *eof = 1; | ||
108 | if (len < off) | ||
109 | return 0; | ||
110 | *start = page + off; | ||
111 | return ((count < len - off) ? count : len - off); | ||
112 | } | ||
113 | |||
114 | static int DIVA_INIT_FUNCTION create_um_idi_proc(void) | ||
115 | { | ||
116 | um_idi_proc_entry = create_proc_entry(DRIVERLNAME, | ||
117 | S_IFREG | S_IRUGO | S_IWUSR, | ||
118 | proc_net_eicon); | ||
119 | if (!um_idi_proc_entry) | ||
120 | return (0); | ||
121 | |||
122 | um_idi_proc_entry->read_proc = um_idi_proc_read; | ||
123 | um_idi_proc_entry->owner = THIS_MODULE; | ||
124 | |||
125 | return (1); | ||
126 | } | ||
127 | |||
128 | static void remove_um_idi_proc(void) | ||
129 | { | ||
130 | if (um_idi_proc_entry) { | ||
131 | remove_proc_entry(DRIVERLNAME, proc_net_eicon); | ||
132 | um_idi_proc_entry = NULL; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static struct file_operations divas_idi_fops = { | ||
137 | .owner = THIS_MODULE, | ||
138 | .llseek = no_llseek, | ||
139 | .read = um_idi_read, | ||
140 | .write = um_idi_write, | ||
141 | .poll = um_idi_poll, | ||
142 | .open = um_idi_open, | ||
143 | .release = um_idi_release | ||
144 | }; | ||
145 | |||
146 | static void divas_idi_unregister_chrdev(void) | ||
147 | { | ||
148 | devfs_remove(DEVNAME); | ||
149 | unregister_chrdev(major, DEVNAME); | ||
150 | } | ||
151 | |||
152 | static int DIVA_INIT_FUNCTION divas_idi_register_chrdev(void) | ||
153 | { | ||
154 | if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0) | ||
155 | { | ||
156 | printk(KERN_ERR "%s: failed to create /dev entry.\n", | ||
157 | DRIVERLNAME); | ||
158 | return (0); | ||
159 | } | ||
160 | devfs_mk_cdev(MKDEV(major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVNAME); | ||
161 | |||
162 | return (1); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | ** Driver Load | ||
167 | */ | ||
168 | static int DIVA_INIT_FUNCTION divasi_init(void) | ||
169 | { | ||
170 | char tmprev[50]; | ||
171 | int ret = 0; | ||
172 | |||
173 | printk(KERN_INFO "%s\n", DRIVERNAME); | ||
174 | printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI); | ||
175 | strcpy(tmprev, main_revision); | ||
176 | printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD); | ||
177 | |||
178 | if (!divas_idi_register_chrdev()) { | ||
179 | ret = -EIO; | ||
180 | goto out; | ||
181 | } | ||
182 | |||
183 | if (!create_um_idi_proc()) { | ||
184 | divas_idi_unregister_chrdev(); | ||
185 | printk(KERN_ERR "%s: failed to create proc entry.\n", | ||
186 | DRIVERLNAME); | ||
187 | ret = -EIO; | ||
188 | goto out; | ||
189 | } | ||
190 | |||
191 | if (!(idifunc_init())) { | ||
192 | remove_um_idi_proc(); | ||
193 | divas_idi_unregister_chrdev(); | ||
194 | printk(KERN_ERR "%s: failed to connect to DIDD.\n", | ||
195 | DRIVERLNAME); | ||
196 | ret = -EIO; | ||
197 | goto out; | ||
198 | } | ||
199 | printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major); | ||
200 | |||
201 | out: | ||
202 | return (ret); | ||
203 | } | ||
204 | |||
205 | |||
206 | /* | ||
207 | ** Driver Unload | ||
208 | */ | ||
209 | static void DIVA_EXIT_FUNCTION divasi_exit(void) | ||
210 | { | ||
211 | idifunc_finit(); | ||
212 | remove_um_idi_proc(); | ||
213 | divas_idi_unregister_chrdev(); | ||
214 | |||
215 | printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME); | ||
216 | } | ||
217 | |||
218 | module_init(divasi_init); | ||
219 | module_exit(divasi_exit); | ||
220 | |||
221 | |||
222 | /* | ||
223 | * FILE OPERATIONS | ||
224 | */ | ||
225 | |||
226 | static int | ||
227 | divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src, | ||
228 | int length) | ||
229 | { | ||
230 | memcpy(dst, src, length); | ||
231 | return (length); | ||
232 | } | ||
233 | |||
234 | static ssize_t | ||
235 | um_idi_read(struct file *file, char __user *buf, size_t count, loff_t * offset) | ||
236 | { | ||
237 | diva_um_idi_os_context_t *p_os; | ||
238 | int ret = -EINVAL; | ||
239 | void *data; | ||
240 | |||
241 | if (!file->private_data) { | ||
242 | return (-ENODEV); | ||
243 | } | ||
244 | |||
245 | if (! | ||
246 | (p_os = | ||
247 | (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> | ||
248 | private_data))) | ||
249 | { | ||
250 | return (-ENODEV); | ||
251 | } | ||
252 | if (p_os->aborted) { | ||
253 | return (-ENODEV); | ||
254 | } | ||
255 | |||
256 | if (!(data = diva_os_malloc(0, count))) { | ||
257 | return (-ENOMEM); | ||
258 | } | ||
259 | |||
260 | ret = diva_um_idi_read(file->private_data, | ||
261 | file, data, count, | ||
262 | divas_um_idi_copy_to_user); | ||
263 | switch (ret) { | ||
264 | case 0: /* no message available */ | ||
265 | ret = (-EAGAIN); | ||
266 | break; | ||
267 | case (-1): /* adapter was removed */ | ||
268 | ret = (-ENODEV); | ||
269 | break; | ||
270 | case (-2): /* message_length > length of user buffer */ | ||
271 | ret = (-EFAULT); | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | if (ret > 0) { | ||
276 | if (copy_to_user(buf, data, ret)) { | ||
277 | ret = (-EFAULT); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | diva_os_free(0, data); | ||
282 | DBG_TRC(("read: ret %d", ret)); | ||
283 | return (ret); | ||
284 | } | ||
285 | |||
286 | |||
287 | static int | ||
288 | divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src, | ||
289 | int length) | ||
290 | { | ||
291 | memcpy(dst, src, length); | ||
292 | return (length); | ||
293 | } | ||
294 | |||
295 | static int um_idi_open_adapter(struct file *file, int adapter_nr) | ||
296 | { | ||
297 | diva_um_idi_os_context_t *p_os; | ||
298 | void *e = | ||
299 | divas_um_idi_create_entity((dword) adapter_nr, (void *) file); | ||
300 | |||
301 | if (!(file->private_data = e)) { | ||
302 | return (0); | ||
303 | } | ||
304 | p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e); | ||
305 | init_waitqueue_head(&p_os->read_wait); | ||
306 | init_waitqueue_head(&p_os->close_wait); | ||
307 | init_timer(&p_os->diva_timer_id); | ||
308 | p_os->diva_timer_id.function = (void *) diva_um_timer_function; | ||
309 | p_os->diva_timer_id.data = (unsigned long) p_os; | ||
310 | p_os->aborted = 0; | ||
311 | p_os->adapter_nr = adapter_nr; | ||
312 | return (1); | ||
313 | } | ||
314 | |||
315 | static ssize_t | ||
316 | um_idi_write(struct file *file, const char __user *buf, size_t count, | ||
317 | loff_t * offset) | ||
318 | { | ||
319 | diva_um_idi_os_context_t *p_os; | ||
320 | int ret = -EINVAL; | ||
321 | void *data; | ||
322 | int adapter_nr = 0; | ||
323 | |||
324 | if (!file->private_data) { | ||
325 | /* the first write() selects the adapter_nr */ | ||
326 | if (count == sizeof(int)) { | ||
327 | if (copy_from_user | ||
328 | ((void *) &adapter_nr, buf, | ||
329 | count)) return (-EFAULT); | ||
330 | if (!(um_idi_open_adapter(file, adapter_nr))) | ||
331 | return (-ENODEV); | ||
332 | return (count); | ||
333 | } else | ||
334 | return (-ENODEV); | ||
335 | } | ||
336 | |||
337 | if (!(p_os = | ||
338 | (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file-> | ||
339 | private_data))) | ||
340 | { | ||
341 | return (-ENODEV); | ||
342 | } | ||
343 | if (p_os->aborted) { | ||
344 | return (-ENODEV); | ||
345 | } | ||
346 | |||
347 | if (!(data = diva_os_malloc(0, count))) { | ||
348 | return (-ENOMEM); | ||
349 | } | ||
350 | |||
351 | if (copy_from_user(data, buf, count)) { | ||
352 | ret = -EFAULT; | ||
353 | } else { | ||
354 | ret = diva_um_idi_write(file->private_data, | ||
355 | file, data, count, | ||
356 | divas_um_idi_copy_from_user); | ||
357 | switch (ret) { | ||
358 | case 0: /* no space available */ | ||
359 | ret = (-EAGAIN); | ||
360 | break; | ||
361 | case (-1): /* adapter was removed */ | ||
362 | ret = (-ENODEV); | ||
363 | break; | ||
364 | case (-2): /* length of user buffer > max message_length */ | ||
365 | ret = (-EFAULT); | ||
366 | break; | ||
367 | } | ||
368 | } | ||
369 | diva_os_free(0, data); | ||
370 | DBG_TRC(("write: ret %d", ret)); | ||
371 | return (ret); | ||
372 | } | ||
373 | |||
374 | static unsigned int um_idi_poll(struct file *file, poll_table * wait) | ||
375 | { | ||
376 | diva_um_idi_os_context_t *p_os; | ||
377 | |||
378 | if (!file->private_data) { | ||
379 | return (POLLERR); | ||
380 | } | ||
381 | |||
382 | if ((!(p_os = | ||
383 | (diva_um_idi_os_context_t *) | ||
384 | diva_um_id_get_os_context(file->private_data))) | ||
385 | || p_os->aborted) { | ||
386 | return (POLLERR); | ||
387 | } | ||
388 | |||
389 | poll_wait(file, &p_os->read_wait, wait); | ||
390 | |||
391 | if (p_os->aborted) { | ||
392 | return (POLLERR); | ||
393 | } | ||
394 | |||
395 | switch (diva_user_mode_idi_ind_ready(file->private_data, file)) { | ||
396 | case (-1): | ||
397 | return (POLLERR); | ||
398 | |||
399 | case 0: | ||
400 | return (0); | ||
401 | } | ||
402 | |||
403 | return (POLLIN | POLLRDNORM); | ||
404 | } | ||
405 | |||
406 | static int um_idi_open(struct inode *inode, struct file *file) | ||
407 | { | ||
408 | return (0); | ||
409 | } | ||
410 | |||
411 | |||
412 | static int um_idi_release(struct inode *inode, struct file *file) | ||
413 | { | ||
414 | diva_um_idi_os_context_t *p_os; | ||
415 | unsigned int adapter_nr; | ||
416 | int ret = 0; | ||
417 | |||
418 | if (!(file->private_data)) { | ||
419 | ret = -ENODEV; | ||
420 | goto out; | ||
421 | } | ||
422 | |||
423 | if (!(p_os = | ||
424 | (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) { | ||
425 | ret = -ENODEV; | ||
426 | goto out; | ||
427 | } | ||
428 | |||
429 | adapter_nr = p_os->adapter_nr; | ||
430 | |||
431 | if ((ret = remove_entity(file->private_data))) { | ||
432 | goto out; | ||
433 | } | ||
434 | |||
435 | if (divas_um_idi_delete_entity | ||
436 | ((int) adapter_nr, file->private_data)) { | ||
437 | ret = -ENODEV; | ||
438 | goto out; | ||
439 | } | ||
440 | |||
441 | out: | ||
442 | return (ret); | ||
443 | } | ||
444 | |||
445 | int diva_os_get_context_size(void) | ||
446 | { | ||
447 | return (sizeof(diva_um_idi_os_context_t)); | ||
448 | } | ||
449 | |||
450 | void diva_os_wakeup_read(void *os_context) | ||
451 | { | ||
452 | diva_um_idi_os_context_t *p_os = | ||
453 | (diva_um_idi_os_context_t *) os_context; | ||
454 | wake_up_interruptible(&p_os->read_wait); | ||
455 | } | ||
456 | |||
457 | void diva_os_wakeup_close(void *os_context) | ||
458 | { | ||
459 | diva_um_idi_os_context_t *p_os = | ||
460 | (diva_um_idi_os_context_t *) os_context; | ||
461 | wake_up_interruptible(&p_os->close_wait); | ||
462 | } | ||
463 | |||
464 | static | ||
465 | void diva_um_timer_function(unsigned long data) | ||
466 | { | ||
467 | diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data; | ||
468 | |||
469 | p_os->aborted = 1; | ||
470 | wake_up_interruptible(&p_os->read_wait); | ||
471 | wake_up_interruptible(&p_os->close_wait); | ||
472 | DBG_ERR(("entity removal watchdog")) | ||
473 | } | ||
474 | |||
475 | /* | ||
476 | ** If application exits without entity removal this function will remove | ||
477 | ** entity and block until removal is complete | ||
478 | */ | ||
479 | static int remove_entity(void *entity) | ||
480 | { | ||
481 | struct task_struct *curtask = current; | ||
482 | diva_um_idi_os_context_t *p_os; | ||
483 | |||
484 | diva_um_idi_stop_wdog(entity); | ||
485 | |||
486 | if (!entity) { | ||
487 | DBG_FTL(("Zero entity on remove")) | ||
488 | return (0); | ||
489 | } | ||
490 | |||
491 | if (!(p_os = | ||
492 | (diva_um_idi_os_context_t *) | ||
493 | diva_um_id_get_os_context(entity))) { | ||
494 | DBG_FTL(("Zero entity os context on remove")) | ||
495 | return (0); | ||
496 | } | ||
497 | |||
498 | if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) { | ||
499 | /* | ||
500 | Entity is not assigned, also can be removed | ||
501 | */ | ||
502 | return (0); | ||
503 | } | ||
504 | |||
505 | DBG_TRC(("E(%08x) check remove", entity)) | ||
506 | |||
507 | /* | ||
508 | If adapter not answers on remove request inside of | ||
509 | 10 Sec, then adapter is dead | ||
510 | */ | ||
511 | diva_um_idi_start_wdog(entity); | ||
512 | |||
513 | { | ||
514 | DECLARE_WAITQUEUE(wait, curtask); | ||
515 | |||
516 | add_wait_queue(&p_os->close_wait, &wait); | ||
517 | for (;;) { | ||
518 | set_current_state(TASK_INTERRUPTIBLE); | ||
519 | if (!divas_um_idi_entity_start_remove(entity) | ||
520 | || p_os->aborted) { | ||
521 | break; | ||
522 | } | ||
523 | schedule(); | ||
524 | } | ||
525 | set_current_state(TASK_RUNNING); | ||
526 | remove_wait_queue(&p_os->close_wait, &wait); | ||
527 | } | ||
528 | |||
529 | DBG_TRC(("E(%08x) start remove", entity)) | ||
530 | { | ||
531 | DECLARE_WAITQUEUE(wait, curtask); | ||
532 | |||
533 | add_wait_queue(&p_os->close_wait, &wait); | ||
534 | for (;;) { | ||
535 | set_current_state(TASK_INTERRUPTIBLE); | ||
536 | if (!divas_um_idi_entity_assigned(entity) | ||
537 | || p_os->aborted) { | ||
538 | break; | ||
539 | } | ||
540 | schedule(); | ||
541 | } | ||
542 | set_current_state(TASK_RUNNING); | ||
543 | remove_wait_queue(&p_os->close_wait, &wait); | ||
544 | } | ||
545 | |||
546 | DBG_TRC(("E(%08x) remove complete, aborted:%d", entity, | ||
547 | p_os->aborted)) | ||
548 | |||
549 | diva_um_idi_stop_wdog(entity); | ||
550 | |||
551 | p_os->aborted = 0; | ||
552 | |||
553 | return (0); | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * timer watchdog | ||
558 | */ | ||
559 | void diva_um_idi_start_wdog(void *entity) | ||
560 | { | ||
561 | diva_um_idi_os_context_t *p_os; | ||
562 | |||
563 | if (entity && | ||
564 | ((p_os = | ||
565 | (diva_um_idi_os_context_t *) | ||
566 | diva_um_id_get_os_context(entity)))) { | ||
567 | mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ); | ||
568 | } | ||
569 | } | ||
570 | |||
571 | void diva_um_idi_stop_wdog(void *entity) | ||
572 | { | ||
573 | diva_um_idi_os_context_t *p_os; | ||
574 | |||
575 | if (entity && | ||
576 | ((p_os = | ||
577 | (diva_um_idi_os_context_t *) | ||
578 | diva_um_id_get_os_context(entity)))) { | ||
579 | del_timer(&p_os->diva_timer_id); | ||
580 | } | ||
581 | } | ||