new helper: file_inode(file)
[pandora-kernel.git] / drivers / staging / dgrp / dgrp_specproc.c
1 /*
2  *
3  * Copyright 1999 Digi International (www.digi.com)
4  *     James Puzzo  <jamesp at digi dot com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
13  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  * PURPOSE.  See the GNU General Public License for more details.
15  *
16  */
17
18 /*
19  *
20  *  Filename:
21  *
22  *     dgrp_specproc.c
23  *
24  *  Description:
25  *
26  *     Handle the "config" proc entry for the linux realport device driver
27  *     and provide slots for the "net" and "mon" devices
28  *
29  *  Author:
30  *
31  *     James A. Puzzo
32  *
33  */
34
35 #include <linux/module.h>
36 #include <linux/tty.h>
37 #include <linux/sched.h>
38 #include <linux/cred.h>
39 #include <linux/proc_fs.h>
40 #include <linux/ctype.h>
41 #include <linux/seq_file.h>
42 #include <linux/uaccess.h>
43 #include <linux/vmalloc.h>
44
45 #include "dgrp_common.h"
46
47 static struct dgrp_proc_entry dgrp_table[];
48 static struct proc_dir_entry *dgrp_proc_dir_entry;
49
50 static int dgrp_add_id(long id);
51 static int dgrp_remove_nd(struct nd_struct *nd);
52 static void unregister_dgrp_device(struct proc_dir_entry *de);
53 static void register_dgrp_device(struct nd_struct *node,
54                                  struct proc_dir_entry *root,
55                                  void (*register_hook)(struct proc_dir_entry *de));
56
57 /* File operation declarations */
58 static int dgrp_gen_proc_open(struct inode *, struct file *);
59 static int dgrp_gen_proc_close(struct inode *, struct file *);
60 static int parse_write_config(char *);
61
62
63 static const struct file_operations dgrp_proc_file_ops = {
64         .owner   = THIS_MODULE,
65         .open    = dgrp_gen_proc_open,
66         .release = dgrp_gen_proc_close,
67 };
68
69 static struct inode_operations proc_inode_ops = {
70         .permission = dgrp_inode_permission
71 };
72
73
74 static void register_proc_table(struct dgrp_proc_entry *,
75                                 struct proc_dir_entry *);
76 static void unregister_proc_table(struct dgrp_proc_entry *,
77                                   struct proc_dir_entry *);
78
79 static struct dgrp_proc_entry dgrp_net_table[];
80 static struct dgrp_proc_entry dgrp_mon_table[];
81 static struct dgrp_proc_entry dgrp_ports_table[];
82 static struct dgrp_proc_entry dgrp_dpa_table[];
83
84 static ssize_t config_proc_write(struct file *file, const char __user *buffer,
85                                  size_t count, loff_t *pos);
86
87 static int nodeinfo_proc_open(struct inode *inode, struct file *file);
88 static int info_proc_open(struct inode *inode, struct file *file);
89 static int config_proc_open(struct inode *inode, struct file *file);
90
91 static struct file_operations config_proc_file_ops = {
92         .owner   = THIS_MODULE,
93         .open    = config_proc_open,
94         .read    = seq_read,
95         .llseek  = seq_lseek,
96         .release = seq_release,
97         .write   = config_proc_write
98 };
99
100 static struct file_operations info_proc_file_ops = {
101         .owner   = THIS_MODULE,
102         .open    = info_proc_open,
103         .read    = seq_read,
104         .llseek  = seq_lseek,
105         .release = seq_release,
106 };
107
108 static struct file_operations nodeinfo_proc_file_ops = {
109         .owner   = THIS_MODULE,
110         .open    = nodeinfo_proc_open,
111         .read    = seq_read,
112         .llseek  = seq_lseek,
113         .release = seq_release,
114 };
115
116 static struct dgrp_proc_entry dgrp_table[] = {
117         {
118                 .id = DGRP_CONFIG,
119                 .name = "config",
120                 .mode = 0644,
121                 .proc_file_ops = &config_proc_file_ops,
122         },
123         {
124                 .id = DGRP_INFO,
125                 .name = "info",
126                 .mode = 0644,
127                 .proc_file_ops = &info_proc_file_ops,
128         },
129         {
130                 .id = DGRP_NODEINFO,
131                 .name = "nodeinfo",
132                 .mode = 0644,
133                 .proc_file_ops = &nodeinfo_proc_file_ops,
134         },
135         {
136                 .id = DGRP_NETDIR,
137                 .name = "net",
138                 .mode = 0500,
139                 .child = dgrp_net_table
140         },
141         {
142                 .id = DGRP_MONDIR,
143                 .name = "mon",
144                 .mode = 0500,
145                 .child = dgrp_mon_table
146         },
147         {
148                 .id = DGRP_PORTSDIR,
149                 .name = "ports",
150                 .mode = 0500,
151                 .child = dgrp_ports_table
152         },
153         {
154                 .id = DGRP_DPADIR,
155                 .name = "dpa",
156                 .mode = 0500,
157                 .child = dgrp_dpa_table
158         }
159 };
160
161 static struct proc_dir_entry *net_entry_pointer;
162 static struct proc_dir_entry *mon_entry_pointer;
163 static struct proc_dir_entry *dpa_entry_pointer;
164 static struct proc_dir_entry *ports_entry_pointer;
165
166 static struct dgrp_proc_entry dgrp_net_table[] = {
167         {0}
168 };
169
170 static struct dgrp_proc_entry dgrp_mon_table[] = {
171         {0}
172 };
173
174 static struct dgrp_proc_entry dgrp_ports_table[] = {
175         {0}
176 };
177
178 static struct dgrp_proc_entry dgrp_dpa_table[] = {
179         {0}
180 };
181
182 void dgrp_unregister_proc(void)
183 {
184         unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
185         net_entry_pointer = NULL;
186         mon_entry_pointer = NULL;
187         dpa_entry_pointer = NULL;
188         ports_entry_pointer = NULL;
189
190         if (dgrp_proc_dir_entry) {
191                 remove_proc_entry(dgrp_proc_dir_entry->name,
192                                   dgrp_proc_dir_entry->parent);
193                 dgrp_proc_dir_entry = NULL;
194         }
195
196 }
197
198 void dgrp_register_proc(void)
199 {
200         /*
201          *      Register /proc/dgrp
202          */
203         dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
204                                           &dgrp_proc_file_ops);
205         register_proc_table(dgrp_table, dgrp_proc_dir_entry);
206 }
207
208 /*
209  * /proc/sys support
210  */
211 static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
212 {
213         if (!de || !de->low_ino)
214                 return 0;
215         if (de->namelen != len)
216                 return 0;
217         return !memcmp(name, de->name, len);
218 }
219
220
221 /*
222  *  Scan the entries in table and add them all to /proc at the position
223  *  referred to by "root"
224  */
225 static void register_proc_table(struct dgrp_proc_entry *table,
226                                 struct proc_dir_entry *root)
227 {
228         struct proc_dir_entry *de;
229         int len;
230         mode_t mode;
231
232         if (table == NULL)
233                 return;
234
235         for (; table->id; table++) {
236                 /* Can't do anything without a proc name. */
237                 if (!table->name)
238                         continue;
239
240                 /* Maybe we can't do anything with it... */
241                 if (!table->proc_file_ops &&
242                     !table->child) {
243                         pr_warn("dgrp: Can't register %s\n",
244                                 table->name);
245                         continue;
246                 }
247
248                 len = strlen(table->name);
249                 mode = table->mode;
250
251                 de = NULL;
252                 if (!table->child)
253                         mode |= S_IFREG;
254                 else {
255                         mode |= S_IFDIR;
256                         for (de = root->subdir; de; de = de->next) {
257                                 if (dgrp_proc_match(len, table->name, de))
258                                         break;
259                         }
260                         /* If the subdir exists already, de is non-NULL */
261                 }
262
263                 if (!de) {
264                         de = create_proc_entry(table->name, mode, root);
265                         if (!de)
266                                 continue;
267                         de->data = (void *) table;
268                         if (!table->child) {
269                                 de->proc_iops = &proc_inode_ops;
270                                 if (table->proc_file_ops)
271                                         de->proc_fops = table->proc_file_ops;
272                                 else
273                                         de->proc_fops = &dgrp_proc_file_ops;
274                         }
275                 }
276                 table->de = de;
277                 if (de->mode & S_IFDIR)
278                         register_proc_table(table->child, de);
279
280                 if (table->id == DGRP_NETDIR)
281                         net_entry_pointer = de;
282
283                 if (table->id == DGRP_MONDIR)
284                         mon_entry_pointer = de;
285
286                 if (table->id == DGRP_DPADIR)
287                         dpa_entry_pointer = de;
288
289                 if (table->id == DGRP_PORTSDIR)
290                         ports_entry_pointer = de;
291         }
292 }
293
294 /*
295  * Unregister a /proc sysctl table and any subdirectories.
296  */
297 static void unregister_proc_table(struct dgrp_proc_entry *table,
298                                   struct proc_dir_entry *root)
299 {
300         struct proc_dir_entry *de;
301         struct nd_struct *tmp;
302
303         if (table == NULL)
304                 return;
305
306         list_for_each_entry(tmp, &nd_struct_list, list) {
307                 if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
308                         unregister_dgrp_device(tmp->nd_net_de);
309                         dgrp_remove_node_class_sysfs_files(tmp);
310                 }
311
312                 if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
313                         unregister_dgrp_device(tmp->nd_mon_de);
314
315                 if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
316                         unregister_dgrp_device(tmp->nd_dpa_de);
317
318                 if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
319                         unregister_dgrp_device(tmp->nd_ports_de);
320         }
321
322         for (; table->id; table++) {
323                 de = table->de;
324
325                 if (!de)
326                         continue;
327                 if (de->mode & S_IFDIR) {
328                         if (!table->child) {
329                                 pr_alert("dgrp: malformed sysctl tree on free\n");
330                                 continue;
331                         }
332                         unregister_proc_table(table->child, de);
333
334         /* Don't unregister directories which still have entries */
335                         if (de->subdir)
336                                 continue;
337                 }
338
339                 /* Don't unregister proc entries that are still being used.. */
340                 if ((atomic_read(&de->count)) != 1) {
341                         pr_alert("proc entry %s in use, not removing\n",
342                                 de->name);
343                         continue;
344                 }
345
346                 remove_proc_entry(de->name, de->parent);
347                 table->de = NULL;
348         }
349 }
350
351 static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
352 {
353         struct proc_dir_entry *de;
354         struct dgrp_proc_entry *entry;
355         int ret = 0;
356
357         de = (struct proc_dir_entry *) PDE(file_inode(file));
358         if (!de || !de->data) {
359                 ret = -ENXIO;
360                 goto done;
361         }
362
363         entry = (struct dgrp_proc_entry *) de->data;
364         if (!entry) {
365                 ret = -ENXIO;
366                 goto done;
367         }
368
369         down(&entry->excl_sem);
370
371         if (entry->excl_cnt)
372                 ret = -EBUSY;
373         else
374                 entry->excl_cnt++;
375
376         up(&entry->excl_sem);
377
378 done:
379         return ret;
380 }
381
382 static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
383 {
384         struct proc_dir_entry *de;
385         struct dgrp_proc_entry *entry;
386
387         de = (struct proc_dir_entry *) PDE(file_inode(file));
388         if (!de || !de->data)
389                 goto done;
390
391         entry = (struct dgrp_proc_entry *) de->data;
392         if (!entry)
393                 goto done;
394
395         down(&entry->excl_sem);
396
397         if (entry->excl_cnt)
398                 entry->excl_cnt = 0;
399
400         up(&entry->excl_sem);
401
402 done:
403         return 0;
404 }
405
406 static void *config_proc_start(struct seq_file *m, loff_t *pos)
407 {
408         return seq_list_start_head(&nd_struct_list, *pos);
409 }
410
411 static void *config_proc_next(struct seq_file *p, void *v, loff_t *pos)
412 {
413         return seq_list_next(v, &nd_struct_list, pos);
414 }
415
416 static void config_proc_stop(struct seq_file *m, void *v)
417 {
418 }
419
420 static int config_proc_show(struct seq_file *m, void *v)
421 {
422         struct nd_struct *nd;
423         char tmp_id[4];
424
425         if (v == &nd_struct_list) {
426                 seq_puts(m, "#-----------------------------------------------------------------------------\n");
427                 seq_puts(m, "#                        Avail\n");
428                 seq_puts(m, "# ID  Major  State       Ports\n");
429                 return 0;
430         }
431
432         nd = list_entry(v, struct nd_struct, list);
433
434         ID_TO_CHAR(nd->nd_ID, tmp_id);
435
436         seq_printf(m, "  %-2.2s  %-5ld  %-10.10s  %-5d\n",
437                    tmp_id,
438                    nd->nd_major,
439                    ND_STATE_STR(nd->nd_state),
440                    nd->nd_chan_count);
441
442         return 0;
443 }
444
445 static const struct seq_operations proc_config_ops = {
446         .start = config_proc_start,
447         .next  = config_proc_next,
448         .stop  = config_proc_stop,
449         .show  = config_proc_show
450 };
451
452 static int config_proc_open(struct inode *inode, struct file *file)
453 {
454         return seq_open(file, &proc_config_ops);
455 }
456
457
458 /*
459  *  When writing configuration information, each "record" (i.e. each
460  *  write) is treated as an independent request.  See the "parse"
461  *  description for more details.
462  */
463 static ssize_t config_proc_write(struct file *file, const char __user *buffer,
464                                  size_t count, loff_t *pos)
465 {
466         ssize_t retval;
467         char *inbuf, *sp;
468         char *line, *ldelim;
469
470         if (count > 32768)
471                 return -EINVAL;
472
473         inbuf = sp = vzalloc(count + 1);
474         if (!inbuf)
475                 return -ENOMEM;
476
477         if (copy_from_user(inbuf, buffer, count)) {
478                 retval = -EFAULT;
479                 goto done;
480         }
481
482         inbuf[count] = 0;
483
484         ldelim = "\n";
485
486         line = strpbrk(sp, ldelim);
487         while (line) {
488                 *line = 0;
489                 retval = parse_write_config(sp);
490                 if (retval)
491                         goto done;
492
493                 sp = line + 1;
494                 line = strpbrk(sp, ldelim);
495         }
496
497         retval = count;
498 done:
499         vfree(inbuf);
500         return retval;
501 }
502
503 /*
504  *  ------------------------------------------------------------------------
505  *
506  *  The following are the functions to parse input
507  *
508  *  ------------------------------------------------------------------------
509  */
510 static inline char *skip_past_ws(const char *str)
511 {
512         while ((*str) && !isspace(*str))
513                 ++str;
514
515         return skip_spaces(str);
516 }
517
518 static int parse_id(char **c, char *cID)
519 {
520         int tmp = **c;
521
522         if (isalnum(tmp) || (tmp == '_'))
523                 cID[0] = tmp;
524         else
525                 return -EINVAL;
526
527         (*c)++; tmp = **c;
528
529         if (isalnum(tmp) || (tmp == '_')) {
530                 cID[1] = tmp;
531                 (*c)++;
532         } else
533                 cID[1] = 0;
534
535         return 0;
536 }
537
538 static int parse_add_config(char *buf)
539 {
540         char *c = buf;
541         int  retval;
542         char cID[2];
543         long ID;
544
545         c = skip_past_ws(c);
546
547         retval = parse_id(&c, cID);
548         if (retval < 0)
549                 return retval;
550
551         ID = CHAR_TO_ID(cID);
552
553         c = skip_past_ws(c);
554
555         return dgrp_add_id(ID);
556 }
557
558 static int parse_del_config(char *buf)
559 {
560         char *c = buf;
561         int  retval;
562         struct nd_struct *nd;
563         char cID[2];
564         long ID;
565         long major;
566
567         c = skip_past_ws(c);
568
569         retval = parse_id(&c, cID);
570         if (retval < 0)
571                 return retval;
572
573         ID = CHAR_TO_ID(cID);
574
575         c = skip_past_ws(c);
576
577         retval = kstrtol(c, 10, &major);
578         if (retval)
579                 return retval;
580
581         nd = nd_struct_get(major);
582         if (!nd)
583                 return -EINVAL;
584
585         if ((nd->nd_major != major) || (nd->nd_ID != ID))
586                 return -EINVAL;
587
588         return dgrp_remove_nd(nd);
589 }
590
591 static int parse_chg_config(char *buf)
592 {
593         return -EINVAL;
594 }
595
596 /*
597  *  The passed character buffer represents a single configuration request.
598  *  If the first character is a "+", it is parsed as a request to add a
599  *     PortServer
600  *  If the first character is a "-", it is parsed as a request to delete a
601  *     PortServer
602  *  If the first character is a "*", it is parsed as a request to change a
603  *     PortServer
604  *  Any other character (including whitespace) causes the record to be
605  *     ignored.
606  */
607 static int parse_write_config(char *buf)
608 {
609         int retval;
610
611         switch (buf[0]) {
612         case '+':
613                 retval = parse_add_config(buf);
614                 break;
615         case '-':
616                 retval = parse_del_config(buf);
617                 break;
618         case '*':
619                 retval = parse_chg_config(buf);
620                 break;
621         default:
622                 retval = -EINVAL;
623         }
624
625         return retval;
626 }
627
628 static int info_proc_show(struct seq_file *m, void *v)
629 {
630         seq_printf(m, "version: %s\n", DIGI_VERSION);
631         seq_puts(m, "register_with_sysfs: 1\n");
632         seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
633                    dgrp_poll_tick, dgrp_poll_tick);
634
635         return 0;
636 }
637
638 static int info_proc_open(struct inode *inode, struct file *file)
639 {
640         return single_open(file, info_proc_show, NULL);
641 }
642
643
644 static void *nodeinfo_start(struct seq_file *m, loff_t *pos)
645 {
646         return seq_list_start_head(&nd_struct_list, *pos);
647 }
648
649 static void *nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
650 {
651         return seq_list_next(v, &nd_struct_list, pos);
652 }
653
654 static void nodeinfo_stop(struct seq_file *m, void *v)
655 {
656 }
657
658 static int nodeinfo_show(struct seq_file *m, void *v)
659 {
660         struct nd_struct *nd;
661         char hwver[8];
662         char swver[8];
663         char tmp_id[4];
664
665         if (v == &nd_struct_list) {
666                 seq_puts(m, "#-----------------------------------------------------------------------------\n");
667                 seq_puts(m, "#                 HW       HW   SW\n");
668                 seq_puts(m, "# ID  State       Version  ID   Version  Description\n");
669                 return 0;
670         }
671
672         nd = list_entry(v, struct nd_struct, list);
673
674         ID_TO_CHAR(nd->nd_ID, tmp_id);
675
676         if (nd->nd_state == NS_READY) {
677                 sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
678                         nd->nd_hw_ver & 0xff);
679                 sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
680                         nd->nd_sw_ver & 0xff);
681                 seq_printf(m, "  %-2.2s  %-10.10s  %-7.7s  %-3d  %-7.7s  %-35.35s\n",
682                            tmp_id,
683                            ND_STATE_STR(nd->nd_state),
684                            hwver,
685                            nd->nd_hw_id,
686                            swver,
687                            nd->nd_ps_desc);
688
689         } else {
690                 seq_printf(m, "  %-2.2s  %-10.10s\n",
691                            tmp_id,
692                            ND_STATE_STR(nd->nd_state));
693         }
694
695         return 0;
696 }
697
698
699 static const struct seq_operations nodeinfo_ops = {
700         .start = nodeinfo_start,
701         .next  = nodeinfo_next,
702         .stop  = nodeinfo_stop,
703         .show  = nodeinfo_show
704 };
705
706 static int nodeinfo_proc_open(struct inode *inode, struct file *file)
707 {
708         return seq_open(file, &nodeinfo_ops);
709 }
710
711 /**
712  * dgrp_add_id() -- creates new nd struct and adds it to list
713  * @id: id of device to add
714  */
715 static int dgrp_add_id(long id)
716 {
717         struct nd_struct *nd;
718         int ret;
719         int i;
720
721         nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
722         if (!nd)
723                 return -ENOMEM;
724
725         nd->nd_major = 0;
726         nd->nd_ID = id;
727
728         spin_lock_init(&nd->nd_lock);
729
730         init_waitqueue_head(&nd->nd_tx_waitq);
731         init_waitqueue_head(&nd->nd_mon_wqueue);
732         init_waitqueue_head(&nd->nd_dpa_wqueue);
733         for (i = 0; i < SEQ_MAX; i++)
734                 init_waitqueue_head(&nd->nd_seq_wque[i]);
735
736         /* setup the structures to get the major number */
737         ret = dgrp_tty_init(nd);
738         if (ret)
739                 goto error_out;
740
741         nd->nd_major = nd->nd_serial_ttdriver->major;
742
743         ret = nd_struct_add(nd);
744         if (ret)
745                 goto error_out;
746
747         register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
748         register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
749         register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
750         register_dgrp_device(nd, ports_entry_pointer,
751                               dgrp_register_ports_hook);
752
753         return 0;
754
755         /* FIXME this guy should free the tty driver stored in nd and destroy
756          * all channel ports */
757 error_out:
758         kfree(nd);
759         return ret;
760
761 }
762
763 static int dgrp_remove_nd(struct nd_struct *nd)
764 {
765         int ret;
766
767         /* Check to see if the selected structure is in use */
768         if (nd->nd_tty_ref_cnt)
769                 return -EBUSY;
770
771         if (nd->nd_net_de) {
772                 unregister_dgrp_device(nd->nd_net_de);
773                 dgrp_remove_node_class_sysfs_files(nd);
774         }
775
776         if (nd->nd_mon_de)
777                 unregister_dgrp_device(nd->nd_mon_de);
778
779         if (nd->nd_ports_de)
780                 unregister_dgrp_device(nd->nd_ports_de);
781
782         if (nd->nd_dpa_de)
783                 unregister_dgrp_device(nd->nd_dpa_de);
784
785         dgrp_tty_uninit(nd);
786
787         ret = nd_struct_del(nd);
788         if (ret)
789                 return ret;
790
791         kfree(nd);
792         return 0;
793 }
794
795 static void register_dgrp_device(struct nd_struct *node,
796                                  struct proc_dir_entry *root,
797                                  void (*register_hook)(struct proc_dir_entry *de))
798 {
799         char buf[3];
800         struct proc_dir_entry *de;
801
802         ID_TO_CHAR(node->nd_ID, buf);
803
804         de = create_proc_entry(buf, 0600 | S_IFREG, root);
805         if (!de)
806                 return;
807
808         de->data = (void *) node;
809
810         if (register_hook)
811                 register_hook(de);
812
813 }
814
815 static void unregister_dgrp_device(struct proc_dir_entry *de)
816 {
817         if (!de)
818                 return;
819
820         /* Don't unregister proc entries that are still being used.. */
821         if ((atomic_read(&de->count)) != 1) {
822                 pr_alert("%s - proc entry %s in use. Not removing.\n",
823                          __func__, de->name);
824                 return;
825         }
826
827         remove_proc_entry(de->name, de->parent);
828         de = NULL;
829 }