blob: 205d80ef44558a0c26468e3b4a4d4c34ce50e786 [file] [log] [blame]
Bill Pemberton0b52b742012-09-20 16:55:27 -04001/*
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>
David Howells0d01ff22013-04-11 23:51:01 +010040#include <linux/slab.h>
Bill Pemberton0b52b742012-09-20 16:55:27 -040041#include <linux/ctype.h>
42#include <linux/seq_file.h>
Geert Uytterhoevenee985812012-09-27 20:38:26 +020043#include <linux/uaccess.h>
Stephen Rothwell4dac2112012-09-25 00:29:20 +100044#include <linux/vmalloc.h>
Bill Pemberton0b52b742012-09-20 16:55:27 -040045
46#include "dgrp_common.h"
47
Bill Pemberton0b52b742012-09-20 16:55:27 -040048static 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);
Al Viroaf064cd2013-03-30 01:03:53 -040052static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
Bill Pemberton0b52b742012-09-20 16:55:27 -040053 struct proc_dir_entry *root,
Al Viroaf064cd2013-03-30 01:03:53 -040054 const struct file_operations *fops);
Bill Pemberton0b52b742012-09-20 16:55:27 -040055
56/* File operation declarations */
Bill Pemberton0b52b742012-09-20 16:55:27 -040057static int parse_write_config(char *);
58
Tommi Rantala900a72a2013-02-08 13:41:26 +020059static ssize_t dgrp_config_proc_write(struct file *file,
60 const char __user *buffer,
61 size_t count, loff_t *pos);
Bill Pemberton0b52b742012-09-20 16:55:27 -040062
Tommi Rantala900a72a2013-02-08 13:41:26 +020063static 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);
Bill Pemberton0b52b742012-09-20 16:55:27 -040066
Al Viro75ef9de2013-04-04 19:09:41 -040067static const struct file_operations config_proc_file_ops = {
Bill Pemberton0b52b742012-09-20 16:55:27 -040068 .owner = THIS_MODULE,
Tommi Rantala900a72a2013-02-08 13:41:26 +020069 .open = dgrp_config_proc_open,
Bill Pemberton0b52b742012-09-20 16:55:27 -040070 .read = seq_read,
71 .llseek = seq_lseek,
72 .release = seq_release,
Tommi Rantala900a72a2013-02-08 13:41:26 +020073 .write = dgrp_config_proc_write,
Bill Pemberton0b52b742012-09-20 16:55:27 -040074};
75
Al Viro75ef9de2013-04-04 19:09:41 -040076static const struct file_operations info_proc_file_ops = {
Bill Pemberton0b52b742012-09-20 16:55:27 -040077 .owner = THIS_MODULE,
Tommi Rantala900a72a2013-02-08 13:41:26 +020078 .open = dgrp_info_proc_open,
Bill Pemberton0b52b742012-09-20 16:55:27 -040079 .read = seq_read,
80 .llseek = seq_lseek,
Tommi Rantala69a7c5032013-02-08 13:41:25 +020081 .release = single_release,
Bill Pemberton0b52b742012-09-20 16:55:27 -040082};
83
Al Viro75ef9de2013-04-04 19:09:41 -040084static const struct file_operations nodeinfo_proc_file_ops = {
Bill Pemberton0b52b742012-09-20 16:55:27 -040085 .owner = THIS_MODULE,
Tommi Rantala900a72a2013-02-08 13:41:26 +020086 .open = dgrp_nodeinfo_proc_open,
Bill Pemberton0b52b742012-09-20 16:55:27 -040087 .read = seq_read,
88 .llseek = seq_lseek,
89 .release = seq_release,
90};
91
Bill Pemberton0b52b742012-09-20 16:55:27 -040092static 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
Al Viroaf064cd2013-03-30 01:03:53 -040097static 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
Bill Pemberton0b52b742012-09-20 16:55:27 -0400112void dgrp_unregister_proc(void)
113{
Bill Pemberton0b52b742012-09-20 16:55:27 -0400114 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) {
Al Viro08f3d072013-03-30 00:36:23 -0400120 struct nd_struct *nd;
Al Viroaf064cd2013-03-30 01:03:53 -0400121 list_for_each_entry(nd, &nd_struct_list, list)
122 remove_files(nd);
Al Viro08f3d072013-03-30 00:36:23 -0400123 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);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400131 dgrp_proc_dir_entry = NULL;
132 }
Bill Pemberton0b52b742012-09-20 16:55:27 -0400133}
134
135void dgrp_register_proc(void)
136{
137 /*
138 * Register /proc/dgrp
139 */
Al Viroaa66d7b2013-03-29 20:39:17 -0400140 dgrp_proc_dir_entry = proc_mkdir("dgrp", NULL);
Al Viro08f3d072013-03-30 00:36:23 -0400141 if (!dgrp_proc_dir_entry)
Bill Pembertond7c46602012-10-09 14:18:19 -0400142 return;
Al Viro895b5592013-03-30 01:07:56 -0400143 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);
Al Viro08f3d072013-03-30 00:36:23 -0400146 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);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400150}
151
Tommi Rantala900a72a2013-02-08 13:41:26 +0200152static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400153{
154 return seq_list_start_head(&nd_struct_list, *pos);
155}
156
Tommi Rantala900a72a2013-02-08 13:41:26 +0200157static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400158{
159 return seq_list_next(v, &nd_struct_list, pos);
160}
161
Tommi Rantala900a72a2013-02-08 13:41:26 +0200162static void dgrp_config_proc_stop(struct seq_file *m, void *v)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400163{
164}
165
Tommi Rantala900a72a2013-02-08 13:41:26 +0200166static int dgrp_config_proc_show(struct seq_file *m, void *v)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400167{
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 = {
Tommi Rantala900a72a2013-02-08 13:41:26 +0200192 .start = dgrp_config_proc_start,
193 .next = dgrp_config_proc_next,
194 .stop = dgrp_config_proc_stop,
195 .show = dgrp_config_proc_show,
Bill Pemberton0b52b742012-09-20 16:55:27 -0400196};
197
Tommi Rantala900a72a2013-02-08 13:41:26 +0200198static int dgrp_config_proc_open(struct inode *inode, struct file *file)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400199{
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 */
Tommi Rantala900a72a2013-02-08 13:41:26 +0200209static ssize_t dgrp_config_proc_write(struct file *file,
210 const char __user *buffer,
211 size_t count, loff_t *pos)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400212{
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
Tommi Rantala900a72a2013-02-08 13:41:26 +0200375static int dgrp_info_proc_show(struct seq_file *m, void *v)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400376{
377 seq_printf(m, "version: %s\n", DIGI_VERSION);
378 seq_puts(m, "register_with_sysfs: 1\n");
Bill Pemberton0b52b742012-09-20 16:55:27 -0400379 seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
380 dgrp_poll_tick, dgrp_poll_tick);
381
382 return 0;
383}
384
Tommi Rantala900a72a2013-02-08 13:41:26 +0200385static int dgrp_info_proc_open(struct inode *inode, struct file *file)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400386{
Tommi Rantala900a72a2013-02-08 13:41:26 +0200387 return single_open(file, dgrp_info_proc_show, NULL);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400388}
389
390
Tommi Rantala900a72a2013-02-08 13:41:26 +0200391static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400392{
393 return seq_list_start_head(&nd_struct_list, *pos);
394}
395
Tommi Rantala900a72a2013-02-08 13:41:26 +0200396static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400397{
398 return seq_list_next(v, &nd_struct_list, pos);
399}
400
Tommi Rantala900a72a2013-02-08 13:41:26 +0200401static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400402{
403}
404
Tommi Rantala900a72a2013-02-08 13:41:26 +0200405static int dgrp_nodeinfo_show(struct seq_file *m, void *v)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400406{
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 = {
Tommi Rantala900a72a2013-02-08 13:41:26 +0200447 .start = dgrp_nodeinfo_start,
448 .next = dgrp_nodeinfo_next,
449 .stop = dgrp_nodeinfo_stop,
450 .show = dgrp_nodeinfo_show,
Bill Pemberton0b52b742012-09-20 16:55:27 -0400451};
452
Tommi Rantala900a72a2013-02-08 13:41:26 +0200453static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400454{
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);
Al Viroaf064cd2013-03-30 01:03:53 -0400480 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;
Bill Pemberton0b52b742012-09-20 16:55:27 -0400484 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
Al Viroaf064cd2013-03-30 01:03:53 -0400498 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);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400504 return 0;
505
Jiri Slaby191c5f12012-11-15 09:49:56 +0100506 /* FIXME this guy should free the tty driver stored in nd and destroy
507 * all channel ports */
Bill Pemberton0b52b742012-09-20 16:55:27 -0400508error_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
Al Viroaf064cd2013-03-30 01:03:53 -0400522 remove_files(nd);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400523
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
Al Viroaf064cd2013-03-30 01:03:53 -0400534static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
Bill Pemberton0b52b742012-09-20 16:55:27 -0400535 struct proc_dir_entry *root,
Al Viroaf064cd2013-03-30 01:03:53 -0400536 const struct file_operations *fops)
Bill Pemberton0b52b742012-09-20 16:55:27 -0400537{
538 char buf[3];
Bill Pemberton0b52b742012-09-20 16:55:27 -0400539 ID_TO_CHAR(node->nd_ID, buf);
Al Viro895b5592013-03-30 01:07:56 -0400540 return proc_create_data(buf, 0600, root, fops, node);
Bill Pemberton0b52b742012-09-20 16:55:27 -0400541}