]> rtime.felk.cvut.cz Git - socketcan-devel.git/blob - kernel/2.6/net/can/proc.c
move some include files.
[socketcan-devel.git] / kernel / 2.6 / net / can / proc.c
1 /*
2  * af_can_proc.c
3  *
4  * Copyright (c) 2002-2005 Volkswagen Group Electronic Research
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, the following disclaimer and
12  *    the referenced file 'COPYING'.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of Volkswagen nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * Alternatively, provided that this notice is retained in full, this
21  * software may be distributed under the terms of the GNU General
22  * Public License ("GPL") version 2 as distributed in the 'COPYING'
23  * file from the main directory of the linux kernel source.
24  *
25  * The provided data structures and external interfaces from this code
26  * are not restricted to be used by modules with a GPL compatible license.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
39  * DAMAGE.
40  *
41  * Send feedback to <llcf@volkswagen.de>
42  *
43  */
44
45 #include <linux/module.h>
46 #include <linux/proc_fs.h>
47
48 #include <linux/can/af_can.h>
49
50 #include "version.h"
51
52 RCSID("$Id: proc.c,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
53
54 /* proc filenames */
55
56 #define CAN_PROC_VERSION     "version"
57 #define CAN_PROC_STATS       "stats"
58 #define CAN_PROC_RESET_STATS "reset_stats"
59 #define CAN_PROC_RCVLIST_ALL "rcvlist_all"
60 #define CAN_PROC_RCVLIST_FIL "rcvlist_fil"
61 #define CAN_PROC_RCVLIST_INV "rcvlist_inv"
62 #define CAN_PROC_RCVLIST_SFF "rcvlist_sff"
63 #define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
64
65 static void can_init_stats(int caller);
66 static void can_stat_update(unsigned long data);
67
68 static struct proc_dir_entry *can_create_proc_read_entry(const char *name, mode_t mode, read_proc_t* read_proc, void *data);
69 static void can_remove_proc_entry(const char *name);
70 static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, unsigned long count);
71
72 static int can_proc_read_version(char *page, char **start, off_t off, int count, int *eof, void *data);
73 static int can_proc_read_stats(char *page, char **start, off_t off, int count, int *eof, void *data);
74 static int can_proc_read_reset_stats(char *page, char **start, off_t off, int count, int *eof, void *data);
75 static int can_proc_read_rcvlist_all(char *page, char **start, off_t off, int count, int *eof, void *data);
76 static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int count, int *eof, void *data);
77 static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data);
78 static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data);
79 static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data);
80
81 static struct proc_dir_entry *can_dir         = NULL;
82 static struct proc_dir_entry *pde_version     = NULL;
83 static struct proc_dir_entry *pde_stats       = NULL;
84 static struct proc_dir_entry *pde_reset_stats = NULL;
85 static struct proc_dir_entry *pde_rcvlist_all = NULL;
86 static struct proc_dir_entry *pde_rcvlist_fil = NULL;
87 static struct proc_dir_entry *pde_rcvlist_inv = NULL;
88 static struct proc_dir_entry *pde_rcvlist_sff = NULL;
89 static struct proc_dir_entry *pde_rcvlist_eff = NULL;
90
91 struct timer_list stattimer; /* timer for statistics update */
92
93 struct s_stats  stats; /* statistics */
94 struct s_pstats pstats;
95
96 extern struct rcv_dev_list *rx_dev_list; /* rx dispatcher structures */
97 extern int stats_timer;                  /* module parameter. default: on */
98
99 /**************************************************/
100 /* procfs init / remove                           */
101 /**************************************************/
102
103 void can_init_proc(void)
104 {
105
106     /* procfs init */
107
108     /* create /proc/can directory */
109     can_dir = proc_mkdir(CAN_PROC_DIR, NULL);
110
111     if (can_dir) {
112
113         can_dir->owner = THIS_MODULE;
114
115         /* own procfs entries from the AF_CAN core */
116         pde_version     = can_create_proc_read_entry(CAN_PROC_VERSION, 0644, can_proc_read_version, NULL);
117         pde_stats       = can_create_proc_read_entry(CAN_PROC_STATS, 0644, can_proc_read_stats, NULL);
118         pde_reset_stats = can_create_proc_read_entry(CAN_PROC_RESET_STATS, 0644, can_proc_read_reset_stats, NULL);
119         pde_rcvlist_all = can_create_proc_read_entry(CAN_PROC_RCVLIST_ALL, 0644, can_proc_read_rcvlist_all, NULL);
120         pde_rcvlist_fil = can_create_proc_read_entry(CAN_PROC_RCVLIST_FIL, 0644, can_proc_read_rcvlist_fil, NULL);
121         pde_rcvlist_inv = can_create_proc_read_entry(CAN_PROC_RCVLIST_INV, 0644, can_proc_read_rcvlist_inv, NULL);
122         pde_rcvlist_sff = can_create_proc_read_entry(CAN_PROC_RCVLIST_SFF, 0644, can_proc_read_rcvlist_sff, NULL);
123         pde_rcvlist_eff = can_create_proc_read_entry(CAN_PROC_RCVLIST_EFF, 0644, can_proc_read_rcvlist_eff, NULL);
124
125         if (stats_timer) {
126             /* the statistics are updated every second (timer triggered) */
127             stattimer.function = can_stat_update;
128             stattimer.data = 0;
129             stattimer.expires = jiffies + HZ; /* every second */
130             add_timer(&stattimer); /* start statistics timer */
131         }
132     } else
133         printk(KERN_INFO "af_can: failed to create CAN_PROC_DIR. CONFIG_PROC_FS missing?\n");
134 }
135
136 void can_remove_proc(void)
137 {
138     /* procfs remove */
139     if (pde_version) {
140         can_remove_proc_entry(CAN_PROC_VERSION);
141     }
142
143     if (pde_stats) {
144         can_remove_proc_entry(CAN_PROC_STATS);
145     }
146
147     if (pde_reset_stats) {
148         can_remove_proc_entry(CAN_PROC_RESET_STATS);
149     }
150
151     if (pde_rcvlist_all) {
152         can_remove_proc_entry(CAN_PROC_RCVLIST_ALL);
153     }
154
155     if (pde_rcvlist_fil) {
156         can_remove_proc_entry(CAN_PROC_RCVLIST_FIL);
157     }
158
159     if (pde_rcvlist_inv) {
160         can_remove_proc_entry(CAN_PROC_RCVLIST_INV);
161     }
162
163     if (pde_rcvlist_sff) {
164         can_remove_proc_entry(CAN_PROC_RCVLIST_SFF);
165     }
166
167     if (pde_rcvlist_eff) {
168         can_remove_proc_entry(CAN_PROC_RCVLIST_EFF);
169     }
170
171     if (can_dir) {
172         remove_proc_entry(CAN_PROC_DIR, NULL);
173     }
174 }
175
176 /**************************************************/
177 /* proc read functions                            */
178 /**************************************************/
179
180 int can_print_recv_list(char *page, int len, struct rcv_list *rx_list, struct net_device *dev)
181 {
182     struct rcv_list *p;
183
184     if (rx_list) {
185         for (p = rx_list; p; p = p->next) {
186
187             /*                             can1.  00000000  00000000  00000000  .......0  tp20 */
188             if (p->can_id & CAN_EFF_FLAG) /* EFF & CAN_ID_ALL */
189                 len += snprintf(page + len, PAGE_SIZE - len, "   %-5s  %08X  %08x  %08x  %08x  %8ld  %s\n",
190                                dev->name, p->can_id, p->mask, (unsigned int)p->func,
191                                (unsigned int)p->data, p->matches, p->ident);
192             else
193                 len += snprintf(page + len, PAGE_SIZE - len, "   %-5s     %03X    %08x  %08x  %08x  %8ld  %s\n",
194                                dev->name, p->can_id, p->mask, (unsigned int)p->func,
195                                (unsigned int)p->data, p->matches, p->ident);
196
197             /* does a typical line fit into the current buffer? */
198             if (len > PAGE_SIZE - 100) { /* 100 Bytes before end of buffer */
199                 len += snprintf(page + len, PAGE_SIZE - len, "   (..)\n"); /* mark output cutted off */
200                 return len;
201             }
202         }
203     }
204
205     return len;
206 }
207
208 static int can_print_recv_banner(char *page, int len)
209 {
210     /*                  can1.  00000000  00000000  00000000  .......0  tp20 */
211     len += snprintf(page + len, PAGE_SIZE - len,
212                     "  device   can_id   can_mask  function  userdata   matches  ident\n");
213
214     return len;
215 }
216
217 int can_proc_read_stats(char *page, char **start, off_t off, int count, int *eof, void *data)
218 {
219     int len = 0;
220
221     len += snprintf(page + len, PAGE_SIZE - len, "\n");
222     len += snprintf(page + len, PAGE_SIZE - len, " %8ld transmitted frames (TXF)\n",
223                     stats.tx_frames);
224     len += snprintf(page + len, PAGE_SIZE - len, " %8ld received frames (RXF)\n",
225                     stats.rx_frames);
226     len += snprintf(page + len, PAGE_SIZE - len, " %8ld matched frames (RXMF)\n",
227                     stats.matches);
228
229     len += snprintf(page + len, PAGE_SIZE - len, "\n");
230
231     len += snprintf(page + len, PAGE_SIZE - len, " %8ld %% total match ratio (RXMR)\n",
232                     stats.total_rx_match_ratio);
233
234     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s total tx rate (TXR)\n",
235                     stats.total_tx_rate);
236     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s total rx rate (RXR)\n",
237                     stats.total_rx_rate);
238
239     len += snprintf(page + len, PAGE_SIZE - len, "\n");
240
241     len += snprintf(page + len, PAGE_SIZE - len, " %8ld %% current match ratio (CRXMR)\n",
242                     stats.current_rx_match_ratio);
243
244     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s current tx rate (CTXR)\n",
245                     stats.current_tx_rate);
246     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s current rx rate (CRXR)\n",
247                     stats.current_rx_rate);
248
249     len += snprintf(page + len, PAGE_SIZE - len, "\n");
250
251     len += snprintf(page + len, PAGE_SIZE - len, " %8ld %% max match ratio (MRXMR)\n",
252                     stats.max_rx_match_ratio);
253
254     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s max tx rate (MTXR)\n",
255                     stats.max_tx_rate);
256     len += snprintf(page + len, PAGE_SIZE - len, " %8ld frames/s max rx rate (MRXR)\n",
257                     stats.max_rx_rate);
258
259     len += snprintf(page + len, PAGE_SIZE - len, "\n");
260
261     len += snprintf(page + len, PAGE_SIZE - len, " %8ld current receive list entries (CRCV)\n", pstats.rcv_entries);
262     len += snprintf(page + len, PAGE_SIZE - len, " %8ld maximum receive list entries (MRCV)\n", pstats.rcv_entries_max);
263
264     if (pstats.stats_reset)
265         len += snprintf(page + len, PAGE_SIZE - len, "\n %8ld statistic resets (STR)\n", pstats.stats_reset);
266
267     len += snprintf(page + len, PAGE_SIZE - len, "\n");
268
269     *eof = 1;
270     return len;
271 }
272
273 int can_proc_read_reset_stats(char *page, char **start, off_t off, int count, int *eof, void *data)
274 {
275     int len = 0;
276
277     can_init_stats(1);
278
279     len += snprintf(page + len, PAGE_SIZE - len, "CAN statistic reset #%ld done.\n", pstats.stats_reset);
280
281     *eof = 1;
282     return len;
283 }
284
285 int can_proc_read_version(char *page, char **start, off_t off, int count, int *eof, void *data)
286 {
287     int len = 0;
288
289     len += snprintf(page + len, PAGE_SIZE - len,
290                     "%06X [ Volkswagen AG - Low Level CAN Framework (LLCF) v%s ]\n",
291                     LLCF_VERSION_CODE, VERSION);
292
293     *eof = 1;
294     return len;
295 }
296
297 int can_proc_read_rcvlist_all(char *page, char **start, off_t off, int count, int *eof, void *data)
298 {
299     int len = 0;
300     struct rcv_dev_list *p;
301
302     /* RX_ALL */
303     len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_all':\n");
304
305     /* find receive list for this device */
306     for (p = rx_dev_list; p; p = p->next) {
307
308         if (p->rx_all) {
309             len = can_print_recv_banner(page, len);
310             len = can_print_recv_list(page, len, p->rx_all, p->dev);
311         } else
312             if (p->dev)
313                 len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
314     }
315
316     len += snprintf(page + len, PAGE_SIZE - len, "\n");
317
318     *eof = 1;
319     return len;
320 }
321
322 int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int count, int *eof, void *data)
323 {
324     int len = 0;
325     struct rcv_dev_list *p;
326
327     /* RX_FIL */
328     len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_fil':\n");
329
330     /* find receive list for this device */
331     for (p = rx_dev_list; p; p = p->next) {
332
333         if (p->rx_fil) {
334             len = can_print_recv_banner(page, len);
335             len = can_print_recv_list(page, len, p->rx_fil, p->dev);
336         } else
337             if (p->dev)
338                 len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
339     }
340
341     len += snprintf(page + len, PAGE_SIZE - len, "\n");
342
343     *eof = 1;
344     return len;
345 }
346
347 int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data)
348 {
349     int len = 0;
350     struct rcv_dev_list *p;
351
352     /* RX_INV */
353     len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_inv':\n");
354
355     /* find receive list for this device */
356     for (p = rx_dev_list; p; p = p->next) {
357
358         if (p->rx_inv) {
359             len = can_print_recv_banner(page, len);
360             len = can_print_recv_list(page, len, p->rx_inv, p->dev);
361         } else
362             if (p->dev)
363                 len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
364     }
365
366     len += snprintf(page + len, PAGE_SIZE - len, "\n");
367
368     *eof = 1;
369     return len;
370 }
371
372 int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data)
373 {
374     int len = 0;
375     unsigned long id = 0;
376     int i;
377     struct rcv_dev_list *p;
378
379     /* RX_SFF */
380     len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_sff':\n");
381
382     /* find receive list for this device */
383     for (p = rx_dev_list; p; p = p->next) {
384
385         for(i=0; i<0x800; i++)
386             id |= (unsigned long) p->rx_sff[i]; /* check if any entry available */
387
388         if (id) {
389             len = can_print_recv_banner(page, len);
390             for(i=0; i<0x800; i++) {
391                 if ((p->rx_sff[i]) && (len < PAGE_SIZE - 100))
392                     len = can_print_recv_list(page, len, p->rx_sff[i], p->dev);
393             }
394         } else
395             if (p->dev)
396                 len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
397     }
398
399     len += snprintf(page + len, PAGE_SIZE - len, "\n");
400
401     *eof = 1;
402     return len;
403 }
404
405 int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data)
406 {
407     int len = 0;
408     struct rcv_dev_list *p;
409
410     /* RX_EFF */
411     len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_eff':\n");
412
413     /* find receive list for this device */
414     for (p = rx_dev_list; p; p = p->next) {
415
416         if (p->rx_eff) {
417             len = can_print_recv_banner(page, len);
418             len = can_print_recv_list(page, len, p->rx_eff, p->dev);
419         } else
420             if (p->dev)
421                 len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
422     }
423
424     len += snprintf(page + len, PAGE_SIZE - len, "\n");
425
426     *eof = 1;
427     return len;
428 }
429
430 /**************************************************/
431 /* proc utility functions                         */
432 /**************************************************/
433
434 static struct proc_dir_entry *can_create_proc_read_entry(const char *name, mode_t mode, read_proc_t* read_proc, void *data)
435 {
436     if (can_dir)
437         return create_proc_read_entry(name, mode, can_dir, read_proc, data);
438     else
439         return NULL;
440 }
441
442 static void can_remove_proc_entry(const char *name)
443 {
444     if (can_dir)
445         remove_proc_entry(name, can_dir);
446 }
447
448 static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, unsigned long count){
449
450     unsigned long ret = 0;
451
452     if (oldjif == newjif)
453         return 0;
454
455     if (count > (ULONG_MAX / HZ)) { /* see can_rcv() - this should NEVER happen! */
456         printk(KERN_ERR "af_can: calc_rate: count exceeded! %ld\n", count);
457         return 99999999;
458     }
459
460     ret = ((count * HZ) / (newjif - oldjif));
461
462     return ret;
463 };
464
465 /**************************************************/
466 /* af_can statistics stuff                        */
467 /**************************************************/
468
469 static void can_init_stats(int caller){
470
471     memset(&stats, 0, sizeof(stats));
472     stats.jiffies_init  = jiffies;
473     pstats.stats_reset++;
474 };
475
476 static void can_stat_update(unsigned long data){
477
478     unsigned long j = jiffies; /* snapshot */
479
480     //DBG("af_can: can_stat_update() jiffies = %ld\n", j);
481
482     if (j < stats.jiffies_init) /* jiffies overflow */
483         can_init_stats(2);
484
485     /* stats.rx_frames is the definitively max. statistic value */
486     if (stats.rx_frames > (ULONG_MAX / HZ)) /* prevent overflow in calc_rate() */
487         can_init_stats(3); /* restart */
488
489     if (stats.matches > (ULONG_MAX / 100)) /* matches overflow - very improbable */
490         can_init_stats(4);
491
492     /* calc total values */
493     if (stats.rx_frames)
494          stats.total_rx_match_ratio = (stats.matches * 100) / stats.rx_frames;
495
496     stats.total_tx_rate = calc_rate(stats.jiffies_init, j, stats.tx_frames);
497     stats.total_rx_rate = calc_rate(stats.jiffies_init, j, stats.rx_frames);
498
499     /* calc current values */
500     if (stats.rx_frames_delta)
501         stats.current_rx_match_ratio = (stats.matches_delta * 100) / stats.rx_frames_delta;
502
503     stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
504     stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
505
506     /* check / update maximum values */
507     if (stats.max_tx_rate < stats.current_tx_rate)
508         stats.max_tx_rate = stats.current_tx_rate;
509
510     if (stats.max_rx_rate < stats.current_rx_rate)
511         stats.max_rx_rate = stats.current_rx_rate;
512
513     if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
514         stats.max_rx_match_ratio = stats.current_rx_match_ratio;
515
516     /* clear values for 'current rate' calculation */
517     stats.tx_frames_delta = 0;
518     stats.rx_frames_delta = 0;
519     stats.matches_delta   = 0;
520
521     /* restart timer */
522     stattimer.expires = jiffies + HZ; /* every second */
523     add_timer(&stattimer);
524 };
525
526 /**************************************************/
527 /* EOF                                            */
528 /**************************************************/