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