]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/proc.c
7cb69c3a1da75073aafa7de0fe77fda365b32cb3
[lincan.git] / lincan / src / proc.c
1 /**************************************************************************/
2 /* File: proc.c - proc filesystem entries for CAN driver                  */
3 /*                                                                        */
4 /* LinCAN - (Not only) Linux CAN bus driver                               */
5 /* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz>   */
6 /* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
7 /* Funded by OCERA and FRESCOR IST projects                               */
8 /* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
9 /*                                                                        */
10 /* LinCAN is free software; you can redistribute it and/or modify it      */
11 /* under terms of the GNU General Public License as published by the      */
12 /* Free Software Foundation; either version 2, or (at your option) any    */
13 /* later version.  LinCAN is distributed in the hope that it will be      */
14 /* useful, but WITHOUT ANY WARRANTY; without even the implied warranty    */
15 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU    */
16 /* General Public License for more details. You should have received a    */
17 /* copy of the GNU General Public License along with LinCAN; see file     */
18 /* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
19 /* Cambridge, MA 02139, USA.                                              */
20 /*                                                                        */
21 /* To allow use of LinCAN in the compact embedded systems firmware        */
22 /* and RT-executives (RTEMS for example), main authors agree with next    */
23 /* special exception:                                                     */
24 /*                                                                        */
25 /* Including LinCAN header files in a file, instantiating LinCAN generics */
26 /* or templates, or linking other files with LinCAN objects to produce    */
27 /* an application image/executable, does not by itself cause the          */
28 /* resulting application image/executable to be covered by                */
29 /* the GNU General Public License.                                        */
30 /* This exception does not however invalidate any other reasons           */
31 /* why the executable file might be covered by the GNU Public License.    */
32 /* Publication of enhanced or derived LinCAN files is required although.  */
33 /**************************************************************************/
34
35 #include "../include/can.h"
36 #include "../include/can_sysdep.h"
37 #include "../include/main.h"
38 #include "../include/proc.h"
39 #include "../include/setup.h"
40
41 #define __NO_VERSION__
42 #include <linux/module.h>
43
44 int add_channel_to_procdir(struct candevice_t *candev);
45 int remove_channel_from_procdir(void);
46 int add_object_to_procdir(int chip_nr);
47 int remove_object_from_procdir(int chip_nr);
48
49 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0))
50 static int can_proc_readlink(struct proc_dir_entry *ent, char *page);
51 #endif
52
53 #if  LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,25)
54 #define CAN_PROC_ROOT (&proc_root)
55 #else /* >= 2.6.26 */
56 #define CAN_PROC_ROOT (NULL)
57 #endif /* >= 2.6.26 */
58
59 static int cc=0; /* static counter for each CAN chip */
60
61 static struct canproc_t can_proc_base;
62 static struct canproc_t *base=&can_proc_base;
63
64 /* The following functions are needed only for kernel version 2.2. Kernel
65  * version 2.4 already defines them for us.
66  */
67 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0))
68 static void can_fill_inode(struct inode *inode, int fill)
69 {
70         if (fill)
71                 MOD_INC_USE_COUNT;
72         else
73                 MOD_DEC_USE_COUNT;
74 }
75
76 static struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mode,
77                                         struct proc_dir_entry *parent)
78 {
79         struct proc_dir_entry *new_entry = NULL;
80         char *namestore;
81         int namelen;
82
83         if(!name || !parent)
84                 return NULL;
85         namelen=strlen(name);
86         if(!namelen)
87                 return NULL;
88
89         new_entry = (struct proc_dir_entry *)
90                         can_checked_malloc(sizeof(struct proc_dir_entry)+namelen+1);
91
92         if (new_entry == NULL)
93                 return NULL;
94
95         memset(new_entry, 0, sizeof(struct proc_dir_entry));
96
97         /* Store copy of the proc entry name */
98         namestore = ((char *) new_entry) + sizeof(struct proc_dir_entry);
99         memcpy(namestore, name, namelen + 1);
100
101         new_entry->low_ino = 0;
102         new_entry->namelen = namelen;
103         new_entry->name = namestore;
104         new_entry->mode = mode;
105         new_entry->nlink = 0;
106         new_entry->fill_inode = can_fill_inode;
107         new_entry->parent = parent;
108
109         proc_register(parent, new_entry);
110
111         return new_entry;
112 }
113
114 static int can_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_entry *parent)
115 {
116         if (del != NULL) {
117                 proc_unregister(parent, del->low_ino);
118                 can_checked_free(del);
119                 return 0;
120         }
121         else return -ENODEV;
122 }
123
124
125 static int can_proc_readlink(struct proc_dir_entry *ent, char *page)
126 {
127         char *link_dest = (char*)ent->data;
128
129         strcpy(page, link_dest);
130         return strlen(link_dest);
131 }
132
133
134
135 /* This compatibility version of proc_symlink does not store local copy of destination */
136 static inline struct proc_dir_entry *can_proc_symlink(const char *name,
137                 struct proc_dir_entry *parent, const char *dest)
138 {
139         struct proc_dir_entry *entry;
140
141
142         entry = can_create_proc_entry(name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, parent);
143         if (entry == NULL)
144                 return NULL;
145         entry->readlink_proc = can_proc_readlink;
146         entry->data = dest;
147         return entry;
148 }
149
150 #else /* Functions forwarded for kernel 2.4 and above */
151
152 static inline struct proc_dir_entry * can_create_proc_entry(const char *name, mode_t mode,
153                                         struct proc_dir_entry *parent)
154 {
155         return create_proc_entry(name, mode, parent);
156 }
157
158
159 /* This does not fully follow linux 2.4 and 2.6 prototype to simplify 2.2.x compatibility */
160 /* The newer kernels use entry name instead of pointer to the entry */
161 static int can_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_entry *parent)
162 {
163         if(!del) return -ENODEV;
164         remove_proc_entry(del->name,parent);
165         return 0;
166 }
167
168 static inline struct proc_dir_entry *can_proc_symlink(const char *name,
169                 struct proc_dir_entry *parent, const char *dest)
170 {
171         return proc_symlink(name, parent, dest);
172 }
173
174 #endif /* Functions required for kernel 2.2 */
175
176 /* can_init_procdir registers the entire CAN directory tree recursively at
177  * the proc system.
178  */
179 int can_init_procdir(void)
180 {
181         int board;
182         struct candevice_t *candev;
183         base->can_proc_entry = can_create_proc_entry("can", S_IFDIR | S_IRUGO |
184                                         S_IXUGO, CAN_PROC_ROOT);
185         if (base->can_proc_entry == NULL)
186                 return -ENODEV;
187
188         for (board=0; board<hardware_p->nr_boards; board++) {
189                 candev=hardware_p->candevice[board];
190                 if(candev) add_channel_to_procdir(candev);
191         }
192
193         return 0;
194 }
195
196 /* can_delete_procdir removes the entire CAN tree from the proc system */
197 int can_delete_procdir(void)
198 {
199         if (remove_channel_from_procdir())
200                 return -ENODEV;
201         /* name: "can" */
202         if (can_remove_proc_entry(base->can_proc_entry, CAN_PROC_ROOT))
203                 return -ENODEV;
204
205         return 0;
206 }
207
208 static int can_chip_procinfo(char *buf, char **start, off_t offset,
209                  int count, int *eof, void *data)
210 {
211         struct canchip_t *chip=data;
212         int len=0;
213
214         /* Generic chip info */
215         len += sprintf(buf+len,"type    : %s\n",chip->chip_type);
216         len += sprintf(buf+len,"index   : %d\n",chip->chip_idx);
217         len += sprintf(buf+len,"irq     : %d\n",chip->chip_irq);
218         len += sprintf(buf+len,"addr    : %lu\n",
219                         can_ioptr2ulong(chip->chip_base_addr));
220         len += sprintf(buf+len,"config  : %s\n",
221                         (chip->flags & CHIP_CONFIGURED) ? "yes":"no");
222         len += sprintf(buf+len,"clock   : %ld Hz\n",chip->clock);
223         len += sprintf(buf+len,"baud    : %ld\n",chip->baudrate);
224         len += sprintf(buf+len,"num obj : %d\n",chip->max_objects);
225
226
227 #if 0
228         /* Chip specific info if available */
229         if(chip->chipspecops->get_info)
230                 len += (chip->chipspecops->get_info)(chip,buf+len);
231 #endif
232
233         *eof = 1;
234         return len;
235 }
236
237
238 int add_channel_to_procdir(struct candevice_t *candev)
239 {
240         int i=0;
241
242         for (i=0; i < candev->nr_all_chips; i++) {
243
244                 base->channel[cc] = (struct channelproc_t *)
245                         can_checked_malloc(sizeof(struct channelproc_t));
246                 if (base->channel[cc] == NULL)
247                         return -ENOMEM;
248
249                 sprintf(base->channel[cc]->ch_name, "channel%d",cc);
250
251                 base->channel[cc]->ch_entry = can_create_proc_entry(
252                                                 base->channel[cc]->ch_name,
253                                                 S_IFDIR | S_IRUGO |S_IXUGO,
254                                                 base->can_proc_entry);
255
256                 if (base->channel[cc]->ch_entry == NULL)
257                         return -ENODEV;
258
259                 add_object_to_procdir(cc);
260
261                 create_proc_read_entry("chip_info",        /* proc entry name */
262                                        0,                  /* protection mask, 0->default */
263                                        base->channel[cc]->ch_entry,  /* parent dir, NULL->/proc */
264                                        can_chip_procinfo,
265                                        candev->chip[i]);
266
267                 cc++;
268         }
269
270         return 0;
271 }
272
273 int remove_channel_from_procdir(void)
274 {
275
276         while (cc != 0) {
277                 cc--;
278
279                 if(!base->channel[cc]) continue;
280
281                 remove_proc_entry("chip_info", base->channel[cc]->ch_entry);
282
283                 if (remove_object_from_procdir(cc))
284                         return -ENODEV;
285
286                 /* name: base->channel[cc]->ch_name */
287                 if (can_remove_proc_entry(base->channel[cc]->ch_entry,
288                                                         base->can_proc_entry))
289                         return -ENODEV;
290
291                 can_checked_free(base->channel[cc]);
292                 base->channel[cc] = NULL;
293         }
294
295         return 0;
296 }
297
298
299 int add_object_to_procdir(int chip_nr)
300 {
301         int i, max_objects;
302
303         max_objects=chips_p[chip_nr]->max_objects;
304
305         for (i=0; i<max_objects; i++) {
306                 base->channel[chip_nr]->object[i] = (struct objectproc_t *)
307                         can_checked_malloc(sizeof(struct objectproc_t));
308
309                 if (base->channel[chip_nr]->object[i] == NULL)
310                         return -ENOMEM;
311
312                 sprintf(base->channel[chip_nr]->object[i]->obj_name,"object%d",i);
313                 sprintf(base->channel[chip_nr]->object[i]->lnk_name,"dev");
314
315                 base->channel[chip_nr]->object[i]->obj_entry = can_create_proc_entry(
316                                 base->channel[chip_nr]->object[i]->obj_name,
317                                 S_IFDIR | S_IRUGO | S_IXUGO,
318                                 base->channel[chip_nr]->ch_entry);
319                 if (base->channel[chip_nr]->object[i]->obj_entry == NULL)
320                         return -ENODEV;
321
322                 sprintf(base->channel[chip_nr]->object[i]->lnk_dev,"/dev/can%d",
323                         chips_p[chip_nr]->msgobj[i]->minor);
324
325                 base->channel[chip_nr]->object[i]->lnk = can_proc_symlink(
326                                 base->channel[chip_nr]->object[i]->lnk_name,
327                                 base->channel[chip_nr]->object[i]->obj_entry,
328                                 base->channel[chip_nr]->object[i]->lnk_dev);
329                 if (base->channel[chip_nr]->object[i]->lnk == NULL)
330                         return -ENODEV;
331
332         }
333         return 0;
334 }
335
336 int remove_object_from_procdir(int chip_nr)
337 {
338         int i=0, obj=0;
339
340         obj=chips_p[chip_nr]->max_objects;
341
342         for (i=0; i<obj; i++) {
343                 if(!base->channel[chip_nr]->object[i]) continue;
344
345                 /* name: base->channel[chip_nr]->object[i]->lnk_name */
346                 if (can_remove_proc_entry( base->channel[chip_nr]->object[i]->lnk,
347                                 base->channel[chip_nr]->object[i]->obj_entry))
348                         return -ENODEV;
349                 /* name: base->channel[chip_nr]->object[i]->obj_name */
350                 if (can_remove_proc_entry(
351                                 base->channel[chip_nr]->object[i]->obj_entry,
352                                 base->channel[chip_nr]->ch_entry))
353                         return -ENODEV;
354
355                 can_checked_free(base->channel[chip_nr]->object[i]);
356
357                 base->channel[chip_nr]->object[i]=NULL;
358         }
359         return 0;
360 }
361