/ addons / rtdm / proc.c
proc.c
  1  /*
  2   * Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>.
  3   * Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>.
  4   *
  5   * adapted to RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
  6   *
  7   * RTAI is free software; you can redistribute it and/or modify it
  8   * under the terms of the GNU General Public License as published by
  9   * the Free Software Foundation; either version 2 of the License, or
 10   * (at your option) any later version.
 11   *
 12   * RTAI is distributed in the hope that it will be useful, but
 13   * WITHOUT ANY WARRANTY; without even the implied warranty of
 14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 15   * General Public License for more details.
 16   *
 17   * You should have received a copy of the GNU General Public License
 18   * along with RTAI; if not, write to the Free Software Foundation,
 19   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 20   */
 21  
 22  
 23  #include "rtdm/internal.h"
 24  #include <rtdm/vfile.h>
 25  
 26  struct xnvfile_directory rtdm_vfroot;	/* /proc/rtai/rtdm */
 27  
 28  struct vfile_device_data {
 29  	int h;
 30  	int hmax;
 31  	struct list_head *devmap;
 32  	struct list_head *curr;
 33  };
 34  
 35  static int get_nrt_lock(struct xnvfile *vfile)
 36  {
 37  	return down_interruptible(&nrt_dev_lock) ? -ERESTARTSYS : 0;
 38  }
 39  
 40  static void put_nrt_lock(struct xnvfile *vfile)
 41  {
 42  	up(&nrt_dev_lock);
 43  }
 44  
 45  static struct xnvfile_lock_ops lockops = {
 46  	.get = get_nrt_lock,
 47  	.put = put_nrt_lock,
 48  };
 49  
 50  static struct list_head *next_devlist(struct vfile_device_data *priv)
 51  {
 52  	struct list_head *head;
 53  
 54  	while (priv->h < priv->hmax) {
 55  		head = priv->devmap + priv->h;
 56  		if (!list_empty(head))
 57  			return head;
 58  		priv->h++;
 59  	}
 60  
 61  	return NULL;
 62  }
 63  
 64  static void *next_dev(struct xnvfile_regular_iterator *it)
 65  {
 66  	struct vfile_device_data *priv = xnvfile_iterator_priv(it);
 67  	struct list_head *next;
 68  
 69  	next = priv->curr->next;
 70  seek:
 71  	if (next == priv->devmap + priv->h) {
 72  		/* Done with the current hash slot, let's progress. */
 73  		if (priv->h >= priv->hmax) {
 74  			next = NULL; /* all done. */
 75  			goto out;
 76  		}
 77  
 78  		priv->h++;
 79  		next = next_devlist(priv);
 80  		if (next) {
 81  			next = next->next; /* skip head. */
 82  			goto seek;
 83  		}
 84  	}
 85  out:
 86  	priv->curr = next;
 87  
 88  	return next;
 89  }
 90  
 91  static void *named_begin(struct xnvfile_regular_iterator *it)
 92  {
 93  	struct vfile_device_data *priv = xnvfile_iterator_priv(it);
 94  	struct list_head *devlist;
 95  	loff_t pos = 0;
 96  
 97  	priv->devmap = rtdm_named_devices;
 98  	priv->hmax = devname_hashtab_size;
 99  	priv->h = 0;
100  
101  	devlist = next_devlist(priv);
102  	if (devlist == NULL)
103  		return NULL;	/* All devlists empty. */
104  
105  	priv->curr = devlist->next;	/* Skip head. */
106  
107  	/*
108  	 * priv->curr now points to the first device; advance to the requested
109  	 * position from there.
110  	 */
111  	while (priv->curr && pos++ < it->pos)
112  		priv->curr = next_dev(it);
113  
114  	if (pos == 1)
115  		/* Output the header once, only if some device follows. */
116  		xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n");
117  
118  	return priv->curr;
119  }
120  
121  static int named_show(struct xnvfile_regular_iterator *it, void *data)
122  {
123  	struct vfile_device_data *priv = xnvfile_iterator_priv(it);
124  	struct list_head *curr = data;
125  	struct rtdm_device *device;
126  
127  	device = list_entry(curr, struct rtdm_device, reserved.entry);
128  	xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n",
129  		       priv->h, device->device_name,
130  		       device->driver_name,
131  		       device->proc_name);
132  
133  	return 0;
134  }
135  
136  static struct xnvfile_regular_ops named_vfile_ops = {
137  	.begin = named_begin,
138  	.next = next_dev,
139  	.show = named_show,
140  };
141  
142  static struct xnvfile_regular named_vfile = {
143  	.privsz = sizeof(struct vfile_device_data),
144  	.ops = &named_vfile_ops,
145  	.entry = { .lockops = &lockops }
146  };
147  
148  static void *proto_begin(struct xnvfile_regular_iterator *it)
149  {
150  
151  	struct vfile_device_data *priv = xnvfile_iterator_priv(it);
152  	struct list_head *devlist;
153  	loff_t pos = 0;
154  
155  	priv->devmap = rtdm_protocol_devices;
156  	priv->hmax = protocol_hashtab_size;
157  	priv->h = 0;
158  
159  	devlist = next_devlist(priv);
160  	if (devlist == NULL)
161  		return NULL;	/* All devlists empty. */
162  
163  	priv->curr = devlist->next;	/* Skip head. */
164  
165  	/*
166  	 * priv->curr now points to the first device; advance to the requested
167  	 * position from there.
168  	 */
169  	while (priv->curr && pos++ < it->pos)
170  		priv->curr = next_dev(it);
171  
172  	if (pos == 1)
173  		/* Output the header once, only if some device follows. */
174  		xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n");
175  
176  	return priv->curr;
177  }
178  
179  static int proto_show(struct xnvfile_regular_iterator *it, void *data)
180  {
181  	struct vfile_device_data *priv = xnvfile_iterator_priv(it);
182  	struct list_head *curr = data;
183  	struct rtdm_device *device;
184  	char pnum[32];
185  
186  	device = list_entry(curr, struct rtdm_device, reserved.entry);
187  
188  	snprintf(pnum, sizeof(pnum), "%u:%u",
189  		 device->protocol_family, device->socket_type);
190  
191  	xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n",
192  		       priv->h,
193  		       pnum, device->driver_name,
194  		       device->proc_name);
195  	return 0;
196  }
197  
198  static struct xnvfile_regular_ops proto_vfile_ops = {
199  	.begin = proto_begin,
200  	.next = next_dev,
201  	.show = proto_show,
202  };
203  
204  static struct xnvfile_regular proto_vfile = {
205  	.privsz = sizeof(struct vfile_device_data),
206  	.ops = &proto_vfile_ops,
207  	.entry = { .lockops = &lockops }
208  };
209  
210  static void *openfd_begin(struct xnvfile_regular_iterator *it)
211  {
212  	if (it->pos == 0)
213  		return VFILE_SEQ_START;
214  
215  	return it->pos <= RTDM_FD_MAX ? it : NULL;
216  }
217  
218  static void *openfd_next(struct xnvfile_regular_iterator *it)
219  {
220  	if (it->pos > RTDM_FD_MAX)
221  		return NULL;
222  
223  	return it;
224  }
225  
226  static int openfd_show(struct xnvfile_regular_iterator *it, void *data)
227  {
228  	struct rtdm_dev_context *context;
229  	struct rtdm_device *device;
230  	struct rtdm_process owner;
231  	int close_lock_count, fd;
232  	spl_t s;
233  
234  	if (data == NULL) {
235  		xnvfile_puts(it, "Index\tLocked\tDevice\t\t\t\tOwner [PID]\n");
236  		return 0;
237  	}
238  
239  	fd = (int)it->pos - 1;
240  
241  	xnlock_get_irqsave(&rt_fildes_lock, s);
242  
243  	context = fildes_table[fd].context;
244  	if (context == NULL) {
245  		xnlock_put_irqrestore(&rt_fildes_lock, s);
246  		return VFILE_SEQ_SKIP;
247  	}
248  
249  	close_lock_count = atomic_read(&context->close_lock_count);
250  	device = context->device;
251  	if (context->reserved.owner)
252  		memcpy(&owner, context->reserved.owner, sizeof(owner));
253  	else {
254  		strcpy(owner.name, "<kernel>");
255  		owner.pid = -1;
256  	}
257  
258  	xnlock_put_irqrestore(&rt_fildes_lock, s);
259  
260  	xnvfile_printf(it, "%d\t%d\t%-31s %s [%d]\n", fd,
261  		       close_lock_count,
262  		       (device->device_flags & RTDM_NAMED_DEVICE) ?
263  		       device->device_name : device->proc_name,
264  		       owner.name, owner.pid);
265  	return 0;
266  }
267  
268  static ssize_t openfd_store(struct xnvfile_input *input)
269  {
270  	ssize_t ret, cret;
271  	long val;
272  
273  	ret = xnvfile_get_integer(input, &val);
274  	if (ret < 0)
275  		return ret;
276  
277  	cret = __rt_dev_close(current, (int)val);
278  	if (cret < 0)
279  		return cret;
280  
281  	return ret;
282  }
283  
284  static struct xnvfile_regular_ops openfd_vfile_ops = {
285  	.begin = openfd_begin,
286  	.next = openfd_next,
287  	.show = openfd_show,
288  	.store = openfd_store,
289  };
290  
291  static struct xnvfile_regular openfd_vfile = {
292  	.ops = &openfd_vfile_ops,
293  	.entry = { .lockops = &lockops }
294  };
295  
296  static int allfd_vfile_show(struct xnvfile_regular_iterator *it, void *data)
297  {
298  	xnvfile_printf(it, "total=%d:open=%d:free=%d\n", RTDM_FD_MAX,
299  		       open_fildes, RTDM_FD_MAX - open_fildes);
300  	return 0;
301  }
302  
303  static struct xnvfile_regular_ops allfd_vfile_ops = {
304  	.show = allfd_vfile_show,
305  };
306  
307  static struct xnvfile_regular allfd_vfile = {
308  	.ops = &allfd_vfile_ops,
309  };
310  
311  static int devinfo_vfile_show(struct xnvfile_regular_iterator *it, void *data)
312  {
313  	struct rtdm_device *device;
314  	int i;
315  
316  	if (down_interruptible(&nrt_dev_lock))
317  		return -ERESTARTSYS;
318  
319  	/*
320  	 * As the device may have disappeared while the handler was called,
321  	 * first match the pointer against registered devices.
322  	 */
323  	for (i = 0; i < devname_hashtab_size; i++)
324  		list_for_each_entry(device, &rtdm_named_devices[i],
325  				    reserved.entry)
326  			if (device == xnvfile_priv(it->vfile))
327  				goto found;
328  
329  	for (i = 0; i < protocol_hashtab_size; i++)
330  		list_for_each_entry(device, &rtdm_protocol_devices[i],
331  				    reserved.entry)
332  			if (device == xnvfile_priv(it->vfile))
333  				goto found;
334  
335  	up(&nrt_dev_lock);
336  	return -ENODEV;
337  
338  found:
339  	xnvfile_printf(it, "driver:\t\t%s\nversion:\t%d.%d.%d\n",
340  		       device->driver_name,
341  		       RTDM_DRIVER_MAJOR_VER(device->driver_version),
342  		       RTDM_DRIVER_MINOR_VER(device->driver_version),
343  		       RTDM_DRIVER_PATCH_VER(device->driver_version));
344  
345  	xnvfile_printf(it, "peripheral:\t%s\nprovider:\t%s\n",
346  		       device->peripheral_name, device->provider_name);
347  
348  	xnvfile_printf(it, "class:\t\t%d\nsub-class:\t%d\n",
349  		       device->device_class, device->device_sub_class);
350  
351  	xnvfile_printf(it, "flags:\t\t%s%s%s\n",
352  		       (device->device_flags & RTDM_EXCLUSIVE) ?
353  		       "EXCLUSIVE  " : "",
354  		       (device->device_flags & RTDM_NAMED_DEVICE) ?
355  		       "NAMED_DEVICE  " : "",
356  		       (device->device_flags & RTDM_PROTOCOL_DEVICE) ?
357  		       "PROTOCOL_DEVICE  " : "");
358  
359  	xnvfile_printf(it, "lock count:\t%d\n",
360  		       atomic_read(&device->reserved.refcount));
361  
362  	up(&nrt_dev_lock);
363  	return 0;
364  }
365  
366  static struct xnvfile_regular_ops devinfo_vfile_ops = {
367  	.show = devinfo_vfile_show,
368  };
369  
370  int rtdm_proc_register_device(struct rtdm_device *device)
371  {
372  	int ret;
373  
374  	ret = xnvfile_init_dir(device->proc_name,
375  			       &device->vfroot, &rtdm_vfroot);
376  	if (ret)
377  		goto err_out;
378  
379  	memset(&device->info_vfile, 0, sizeof(device->info_vfile));
380  	device->info_vfile.ops = &devinfo_vfile_ops;
381  
382  	ret = xnvfile_init_regular("information", &device->info_vfile,
383  				   &device->vfroot);
384  	if (ret) {
385  		xnvfile_destroy_dir(&device->vfroot);
386  		goto err_out;
387  	}
388  
389  	xnvfile_priv(&device->info_vfile) = device;
390  
391  	return 0;
392  
393        err_out:
394  	xnlogerr("RTDM: error while creating device vfile\n");
395  	return ret;
396  }
397  
398  void rtdm_proc_unregister_device(struct rtdm_device *device)
399  {
400  	xnvfile_destroy_regular(&device->info_vfile);
401  	xnvfile_destroy_dir(&device->vfroot);
402  }
403  
404  int __init rtdm_proc_init(void)
405  {
406  	int ret;
407  
408  	/* Initialise vfiles */
409  //	ret = xnvfile_init_root(); /proc/rtai is initted elsewhere
410  	ret = xnvfile_init_dir("rtai/rtdm", &rtdm_vfroot, &nkvfroot);
411  	if (ret)
412  		goto error;
413  
414  	ret = xnvfile_init_regular("named_devices", &named_vfile, &rtdm_vfroot);
415  	if (ret)
416  		goto error;
417  
418  	ret = xnvfile_init_regular("protocol_devices", &proto_vfile, &rtdm_vfroot);
419  	if (ret)
420  		goto error;
421  
422  	ret = xnvfile_init_regular("open_fildes", &openfd_vfile, &rtdm_vfroot);
423  	if (ret)
424  		goto error;
425  
426  	ret = xnvfile_init_regular("fildes", &allfd_vfile, &rtdm_vfroot);
427  	if (ret)
428  		goto error;
429  
430  	return 0;
431  
432  error:
433  	rtdm_proc_cleanup();
434  	return ret;
435  }
436  
437  void rtdm_proc_cleanup(void)
438  {
439  	xnvfile_destroy_regular(&allfd_vfile);
440  	xnvfile_destroy_regular(&openfd_vfile);
441  	xnvfile_destroy_regular(&proto_vfile);
442  	xnvfile_destroy_regular(&named_vfile);
443  	xnvfile_destroy_dir(&rtdm_vfroot);
444  //	xnvfile_destroy_root(); /proc/rtai is destroyed elsewhere
445  }