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