]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/staging/dgrp/dgrp_specproc.c
dgrp procfs fixes, part 4: get rid of sysctl-like machinery
[mirror_ubuntu-artful-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 proc_dir_entry *dgrp_proc_dir_entry;
48
49 static int dgrp_add_id(long id);
50 static int dgrp_remove_nd(struct nd_struct *nd);
51 static void unregister_dgrp_device(struct proc_dir_entry *de);
52 static void register_dgrp_device(struct nd_struct *node,
53 struct proc_dir_entry *root,
54 void (*register_hook)(struct proc_dir_entry *de));
55
56 /* File operation declarations */
57 static int parse_write_config(char *);
58
59
60 static struct inode_operations proc_inode_ops = {
61 .permission = dgrp_inode_permission
62 };
63
64
65 static ssize_t dgrp_config_proc_write(struct file *file,
66 const char __user *buffer,
67 size_t count, loff_t *pos);
68
69 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file);
70 static int dgrp_info_proc_open(struct inode *inode, struct file *file);
71 static int dgrp_config_proc_open(struct inode *inode, struct file *file);
72
73 static struct file_operations config_proc_file_ops = {
74 .owner = THIS_MODULE,
75 .open = dgrp_config_proc_open,
76 .read = seq_read,
77 .llseek = seq_lseek,
78 .release = seq_release,
79 .write = dgrp_config_proc_write,
80 };
81
82 static struct file_operations info_proc_file_ops = {
83 .owner = THIS_MODULE,
84 .open = dgrp_info_proc_open,
85 .read = seq_read,
86 .llseek = seq_lseek,
87 .release = single_release,
88 };
89
90 static struct file_operations nodeinfo_proc_file_ops = {
91 .owner = THIS_MODULE,
92 .open = dgrp_nodeinfo_proc_open,
93 .read = seq_read,
94 .llseek = seq_lseek,
95 .release = seq_release,
96 };
97
98 static struct proc_dir_entry *net_entry_pointer;
99 static struct proc_dir_entry *mon_entry_pointer;
100 static struct proc_dir_entry *dpa_entry_pointer;
101 static struct proc_dir_entry *ports_entry_pointer;
102
103 void dgrp_unregister_proc(void)
104 {
105 net_entry_pointer = NULL;
106 mon_entry_pointer = NULL;
107 dpa_entry_pointer = NULL;
108 ports_entry_pointer = NULL;
109
110 if (dgrp_proc_dir_entry) {
111 struct nd_struct *nd;
112 list_for_each_entry(nd, &nd_struct_list, list) {
113 if (nd->nd_net_de) {
114 unregister_dgrp_device(nd->nd_net_de);
115 dgrp_remove_node_class_sysfs_files(nd);
116 }
117
118 if (nd->nd_mon_de)
119 unregister_dgrp_device(nd->nd_mon_de);
120
121 if (nd->nd_dpa_de)
122 unregister_dgrp_device(nd->nd_dpa_de);
123
124 if (nd->nd_ports_de)
125 unregister_dgrp_device(nd->nd_ports_de);
126 }
127 remove_proc_entry("dgrp/config", NULL);
128 remove_proc_entry("dgrp/info", NULL);
129 remove_proc_entry("dgrp/nodeinfo", NULL);
130 remove_proc_entry("dgrp/net", NULL);
131 remove_proc_entry("dgrp/mon", NULL);
132 remove_proc_entry("dgrp/dpa", NULL);
133 remove_proc_entry("dgrp/ports", NULL);
134 remove_proc_entry("dgrp", NULL);
135 dgrp_proc_dir_entry = NULL;
136 }
137 }
138
139 void dgrp_register_proc(void)
140 {
141 struct proc_dir_entry *de;
142 /*
143 * Register /proc/dgrp
144 */
145 dgrp_proc_dir_entry = proc_mkdir("dgrp", NULL);
146 if (!dgrp_proc_dir_entry)
147 return;
148 de = create_proc_entry("dgrp/config", 0644, NULL);
149 if (de) {
150 de->proc_fops = &config_proc_file_ops;
151 de->proc_iops = &proc_inode_ops;
152 }
153 de = create_proc_entry("dgrp/info", 0644, NULL);
154 if (de) {
155 de->proc_fops = &info_proc_file_ops;
156 de->proc_iops = &proc_inode_ops;
157 }
158 de = create_proc_entry("dgrp/nodeinfo", 0644, NULL);
159 if (de) {
160 de->proc_fops = &nodeinfo_proc_file_ops;
161 de->proc_iops = &proc_inode_ops;
162 }
163 net_entry_pointer = proc_mkdir_mode("dgrp/net", 0500, NULL);
164 mon_entry_pointer = proc_mkdir_mode("dgrp/mon", 0500, NULL);
165 dpa_entry_pointer = proc_mkdir_mode("dgrp/dpa", 0500, NULL);
166 ports_entry_pointer = proc_mkdir_mode("dgrp/ports", 0500, NULL);
167 }
168
169 static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
170 {
171 return seq_list_start_head(&nd_struct_list, *pos);
172 }
173
174 static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
175 {
176 return seq_list_next(v, &nd_struct_list, pos);
177 }
178
179 static void dgrp_config_proc_stop(struct seq_file *m, void *v)
180 {
181 }
182
183 static int dgrp_config_proc_show(struct seq_file *m, void *v)
184 {
185 struct nd_struct *nd;
186 char tmp_id[4];
187
188 if (v == &nd_struct_list) {
189 seq_puts(m, "#-----------------------------------------------------------------------------\n");
190 seq_puts(m, "# Avail\n");
191 seq_puts(m, "# ID Major State Ports\n");
192 return 0;
193 }
194
195 nd = list_entry(v, struct nd_struct, list);
196
197 ID_TO_CHAR(nd->nd_ID, tmp_id);
198
199 seq_printf(m, " %-2.2s %-5ld %-10.10s %-5d\n",
200 tmp_id,
201 nd->nd_major,
202 ND_STATE_STR(nd->nd_state),
203 nd->nd_chan_count);
204
205 return 0;
206 }
207
208 static const struct seq_operations proc_config_ops = {
209 .start = dgrp_config_proc_start,
210 .next = dgrp_config_proc_next,
211 .stop = dgrp_config_proc_stop,
212 .show = dgrp_config_proc_show,
213 };
214
215 static int dgrp_config_proc_open(struct inode *inode, struct file *file)
216 {
217 return seq_open(file, &proc_config_ops);
218 }
219
220
221 /*
222 * When writing configuration information, each "record" (i.e. each
223 * write) is treated as an independent request. See the "parse"
224 * description for more details.
225 */
226 static ssize_t dgrp_config_proc_write(struct file *file,
227 const char __user *buffer,
228 size_t count, loff_t *pos)
229 {
230 ssize_t retval;
231 char *inbuf, *sp;
232 char *line, *ldelim;
233
234 if (count > 32768)
235 return -EINVAL;
236
237 inbuf = sp = vzalloc(count + 1);
238 if (!inbuf)
239 return -ENOMEM;
240
241 if (copy_from_user(inbuf, buffer, count)) {
242 retval = -EFAULT;
243 goto done;
244 }
245
246 inbuf[count] = 0;
247
248 ldelim = "\n";
249
250 line = strpbrk(sp, ldelim);
251 while (line) {
252 *line = 0;
253 retval = parse_write_config(sp);
254 if (retval)
255 goto done;
256
257 sp = line + 1;
258 line = strpbrk(sp, ldelim);
259 }
260
261 retval = count;
262 done:
263 vfree(inbuf);
264 return retval;
265 }
266
267 /*
268 * ------------------------------------------------------------------------
269 *
270 * The following are the functions to parse input
271 *
272 * ------------------------------------------------------------------------
273 */
274 static inline char *skip_past_ws(const char *str)
275 {
276 while ((*str) && !isspace(*str))
277 ++str;
278
279 return skip_spaces(str);
280 }
281
282 static int parse_id(char **c, char *cID)
283 {
284 int tmp = **c;
285
286 if (isalnum(tmp) || (tmp == '_'))
287 cID[0] = tmp;
288 else
289 return -EINVAL;
290
291 (*c)++; tmp = **c;
292
293 if (isalnum(tmp) || (tmp == '_')) {
294 cID[1] = tmp;
295 (*c)++;
296 } else
297 cID[1] = 0;
298
299 return 0;
300 }
301
302 static int parse_add_config(char *buf)
303 {
304 char *c = buf;
305 int retval;
306 char cID[2];
307 long ID;
308
309 c = skip_past_ws(c);
310
311 retval = parse_id(&c, cID);
312 if (retval < 0)
313 return retval;
314
315 ID = CHAR_TO_ID(cID);
316
317 c = skip_past_ws(c);
318
319 return dgrp_add_id(ID);
320 }
321
322 static int parse_del_config(char *buf)
323 {
324 char *c = buf;
325 int retval;
326 struct nd_struct *nd;
327 char cID[2];
328 long ID;
329 long major;
330
331 c = skip_past_ws(c);
332
333 retval = parse_id(&c, cID);
334 if (retval < 0)
335 return retval;
336
337 ID = CHAR_TO_ID(cID);
338
339 c = skip_past_ws(c);
340
341 retval = kstrtol(c, 10, &major);
342 if (retval)
343 return retval;
344
345 nd = nd_struct_get(major);
346 if (!nd)
347 return -EINVAL;
348
349 if ((nd->nd_major != major) || (nd->nd_ID != ID))
350 return -EINVAL;
351
352 return dgrp_remove_nd(nd);
353 }
354
355 static int parse_chg_config(char *buf)
356 {
357 return -EINVAL;
358 }
359
360 /*
361 * The passed character buffer represents a single configuration request.
362 * If the first character is a "+", it is parsed as a request to add a
363 * PortServer
364 * If the first character is a "-", it is parsed as a request to delete a
365 * PortServer
366 * If the first character is a "*", it is parsed as a request to change a
367 * PortServer
368 * Any other character (including whitespace) causes the record to be
369 * ignored.
370 */
371 static int parse_write_config(char *buf)
372 {
373 int retval;
374
375 switch (buf[0]) {
376 case '+':
377 retval = parse_add_config(buf);
378 break;
379 case '-':
380 retval = parse_del_config(buf);
381 break;
382 case '*':
383 retval = parse_chg_config(buf);
384 break;
385 default:
386 retval = -EINVAL;
387 }
388
389 return retval;
390 }
391
392 static int dgrp_info_proc_show(struct seq_file *m, void *v)
393 {
394 seq_printf(m, "version: %s\n", DIGI_VERSION);
395 seq_puts(m, "register_with_sysfs: 1\n");
396 seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
397 dgrp_poll_tick, dgrp_poll_tick);
398
399 return 0;
400 }
401
402 static int dgrp_info_proc_open(struct inode *inode, struct file *file)
403 {
404 return single_open(file, dgrp_info_proc_show, NULL);
405 }
406
407
408 static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
409 {
410 return seq_list_start_head(&nd_struct_list, *pos);
411 }
412
413 static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
414 {
415 return seq_list_next(v, &nd_struct_list, pos);
416 }
417
418 static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
419 {
420 }
421
422 static int dgrp_nodeinfo_show(struct seq_file *m, void *v)
423 {
424 struct nd_struct *nd;
425 char hwver[8];
426 char swver[8];
427 char tmp_id[4];
428
429 if (v == &nd_struct_list) {
430 seq_puts(m, "#-----------------------------------------------------------------------------\n");
431 seq_puts(m, "# HW HW SW\n");
432 seq_puts(m, "# ID State Version ID Version Description\n");
433 return 0;
434 }
435
436 nd = list_entry(v, struct nd_struct, list);
437
438 ID_TO_CHAR(nd->nd_ID, tmp_id);
439
440 if (nd->nd_state == NS_READY) {
441 sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
442 nd->nd_hw_ver & 0xff);
443 sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
444 nd->nd_sw_ver & 0xff);
445 seq_printf(m, " %-2.2s %-10.10s %-7.7s %-3d %-7.7s %-35.35s\n",
446 tmp_id,
447 ND_STATE_STR(nd->nd_state),
448 hwver,
449 nd->nd_hw_id,
450 swver,
451 nd->nd_ps_desc);
452
453 } else {
454 seq_printf(m, " %-2.2s %-10.10s\n",
455 tmp_id,
456 ND_STATE_STR(nd->nd_state));
457 }
458
459 return 0;
460 }
461
462
463 static const struct seq_operations nodeinfo_ops = {
464 .start = dgrp_nodeinfo_start,
465 .next = dgrp_nodeinfo_next,
466 .stop = dgrp_nodeinfo_stop,
467 .show = dgrp_nodeinfo_show,
468 };
469
470 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file)
471 {
472 return seq_open(file, &nodeinfo_ops);
473 }
474
475 /**
476 * dgrp_add_id() -- creates new nd struct and adds it to list
477 * @id: id of device to add
478 */
479 static int dgrp_add_id(long id)
480 {
481 struct nd_struct *nd;
482 int ret;
483 int i;
484
485 nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
486 if (!nd)
487 return -ENOMEM;
488
489 nd->nd_major = 0;
490 nd->nd_ID = id;
491
492 spin_lock_init(&nd->nd_lock);
493
494 init_waitqueue_head(&nd->nd_tx_waitq);
495 init_waitqueue_head(&nd->nd_mon_wqueue);
496 init_waitqueue_head(&nd->nd_dpa_wqueue);
497 for (i = 0; i < SEQ_MAX; i++)
498 init_waitqueue_head(&nd->nd_seq_wque[i]);
499
500 /* setup the structures to get the major number */
501 ret = dgrp_tty_init(nd);
502 if (ret)
503 goto error_out;
504
505 nd->nd_major = nd->nd_serial_ttdriver->major;
506
507 ret = nd_struct_add(nd);
508 if (ret)
509 goto error_out;
510
511 register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
512 register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
513 register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
514 register_dgrp_device(nd, ports_entry_pointer,
515 dgrp_register_ports_hook);
516
517 return 0;
518
519 /* FIXME this guy should free the tty driver stored in nd and destroy
520 * all channel ports */
521 error_out:
522 kfree(nd);
523 return ret;
524
525 }
526
527 static int dgrp_remove_nd(struct nd_struct *nd)
528 {
529 int ret;
530
531 /* Check to see if the selected structure is in use */
532 if (nd->nd_tty_ref_cnt)
533 return -EBUSY;
534
535 if (nd->nd_net_de) {
536 unregister_dgrp_device(nd->nd_net_de);
537 dgrp_remove_node_class_sysfs_files(nd);
538 }
539
540 unregister_dgrp_device(nd->nd_mon_de);
541
542 unregister_dgrp_device(nd->nd_ports_de);
543
544 unregister_dgrp_device(nd->nd_dpa_de);
545
546 dgrp_tty_uninit(nd);
547
548 ret = nd_struct_del(nd);
549 if (ret)
550 return ret;
551
552 kfree(nd);
553 return 0;
554 }
555
556 static void register_dgrp_device(struct nd_struct *node,
557 struct proc_dir_entry *root,
558 void (*register_hook)(struct proc_dir_entry *de))
559 {
560 char buf[3];
561 struct proc_dir_entry *de;
562
563 ID_TO_CHAR(node->nd_ID, buf);
564
565 de = create_proc_entry(buf, 0600 | S_IFREG, root);
566 if (!de)
567 return;
568
569 de->data = (void *) node;
570
571 if (register_hook)
572 register_hook(de);
573
574 }
575
576 static void unregister_dgrp_device(struct proc_dir_entry *de)
577 {
578 if (!de)
579 return;
580
581 /* Don't unregister proc entries that are still being used.. */
582 if ((atomic_read(&de->count)) != 1) {
583 pr_alert("%s - proc entry %s in use. Not removing.\n",
584 __func__, de->name);
585 return;
586 }
587
588 remove_proc_entry(de->name, de->parent);
589 de = NULL;
590 }