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