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