]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/dgrp/dgrp_specproc.c
Merge branch 'vfree' into for-next
[karo-tx-linux.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/slab.h>
41 #include <linux/ctype.h>
42 #include <linux/seq_file.h>
43 #include <linux/uaccess.h>
44 #include <linux/vmalloc.h>
45
46 #include "dgrp_common.h"
47
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 struct proc_dir_entry *add_proc_file(struct nd_struct *node,
53                                  struct proc_dir_entry *root,
54                                  const struct file_operations *fops);
55
56 /* File operation declarations */
57 static int parse_write_config(char *);
58
59 static ssize_t dgrp_config_proc_write(struct file *file,
60                                       const char __user *buffer,
61                                       size_t count, loff_t *pos);
62
63 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file);
64 static int dgrp_info_proc_open(struct inode *inode, struct file *file);
65 static int dgrp_config_proc_open(struct inode *inode, struct file *file);
66
67 static const struct file_operations config_proc_file_ops = {
68         .owner   = THIS_MODULE,
69         .open    = dgrp_config_proc_open,
70         .read    = seq_read,
71         .llseek  = seq_lseek,
72         .release = seq_release,
73         .write   = dgrp_config_proc_write,
74 };
75
76 static const struct file_operations info_proc_file_ops = {
77         .owner   = THIS_MODULE,
78         .open    = dgrp_info_proc_open,
79         .read    = seq_read,
80         .llseek  = seq_lseek,
81         .release = single_release,
82 };
83
84 static const struct file_operations nodeinfo_proc_file_ops = {
85         .owner   = THIS_MODULE,
86         .open    = dgrp_nodeinfo_proc_open,
87         .read    = seq_read,
88         .llseek  = seq_lseek,
89         .release = seq_release,
90 };
91
92 static struct proc_dir_entry *net_entry_pointer;
93 static struct proc_dir_entry *mon_entry_pointer;
94 static struct proc_dir_entry *dpa_entry_pointer;
95 static struct proc_dir_entry *ports_entry_pointer;
96
97 static 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
112 void dgrp_unregister_proc(void)
113 {
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) {
120                 struct nd_struct *nd;
121                 list_for_each_entry(nd, &nd_struct_list, list)
122                         remove_files(nd);
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);
131                 dgrp_proc_dir_entry = NULL;
132         }
133 }
134
135 void dgrp_register_proc(void)
136 {
137         /*
138          *      Register /proc/dgrp
139          */
140         dgrp_proc_dir_entry = proc_mkdir("dgrp", NULL);
141         if (!dgrp_proc_dir_entry)
142                 return;
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);
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);
150 }
151
152 static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
153 {
154         return seq_list_start_head(&nd_struct_list, *pos);
155 }
156
157 static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
158 {
159         return seq_list_next(v, &nd_struct_list, pos);
160 }
161
162 static void dgrp_config_proc_stop(struct seq_file *m, void *v)
163 {
164 }
165
166 static int dgrp_config_proc_show(struct seq_file *m, void *v)
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
191 static const struct seq_operations proc_config_ops = {
192         .start = dgrp_config_proc_start,
193         .next  = dgrp_config_proc_next,
194         .stop  = dgrp_config_proc_stop,
195         .show  = dgrp_config_proc_show,
196 };
197
198 static int dgrp_config_proc_open(struct inode *inode, struct file *file)
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  */
209 static ssize_t dgrp_config_proc_write(struct file *file,
210                                       const char __user *buffer,
211                                       size_t count, loff_t *pos)
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;
245 done:
246         vfree(inbuf);
247         return retval;
248 }
249
250 /*
251  *  ------------------------------------------------------------------------
252  *
253  *  The following are the functions to parse input
254  *
255  *  ------------------------------------------------------------------------
256  */
257 static inline char *skip_past_ws(const char *str)
258 {
259         while ((*str) && !isspace(*str))
260                 ++str;
261
262         return skip_spaces(str);
263 }
264
265 static 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
285 static 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
305 static 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
338 static 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  */
354 static 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
375 static int dgrp_info_proc_show(struct seq_file *m, void *v)
376 {
377         seq_printf(m, "version: %s\n", DIGI_VERSION);
378         seq_puts(m, "register_with_sysfs: 1\n");
379         seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
380                    dgrp_poll_tick, dgrp_poll_tick);
381
382         return 0;
383 }
384
385 static int dgrp_info_proc_open(struct inode *inode, struct file *file)
386 {
387         return single_open(file, dgrp_info_proc_show, NULL);
388 }
389
390
391 static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
392 {
393         return seq_list_start_head(&nd_struct_list, *pos);
394 }
395
396 static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
397 {
398         return seq_list_next(v, &nd_struct_list, pos);
399 }
400
401 static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
402 {
403 }
404
405 static int dgrp_nodeinfo_show(struct seq_file *m, void *v)
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
446 static const struct seq_operations nodeinfo_ops = {
447         .start = dgrp_nodeinfo_start,
448         .next  = dgrp_nodeinfo_next,
449         .stop  = dgrp_nodeinfo_stop,
450         .show  = dgrp_nodeinfo_show,
451 };
452
453 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file)
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  */
462 static 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);
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;
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
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);
504         return 0;
505
506         /* FIXME this guy should free the tty driver stored in nd and destroy
507          * all channel ports */
508 error_out:
509         kfree(nd);
510         return ret;
511
512 }
513
514 static 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
522         remove_files(nd);
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
534 static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
535                                  struct proc_dir_entry *root,
536                                  const struct file_operations *fops)
537 {
538         char buf[3];
539         ID_TO_CHAR(node->nd_ID, buf);
540         return proc_create_data(buf, 0600, root, fops, node);
541 }