/ addons / rtdm / device.c
device.c
  1  /**
  2   * @file
  3   * Real-Time Driver Model for RTAI, device management
  4   *
  5   * @note Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>
  6   * @note Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>
  7   *
  8   * with adaptions for RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
  9   *
 10   * RTAI is free software; you can redistribute it and/or modify it
 11   * under the terms of the GNU General Public License as published by
 12   * the Free Software Foundation; either version 2 of the License, or
 13   * (at your option) any later version.
 14   *
 15   * RTAI is distributed in the hope that it will be useful, but
 16   * WITHOUT ANY WARRANTY; without even the implied warranty of
 17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18   * General Public License for more details.
 19   *
 20   * You should have received a copy of the GNU General Public License
 21   * along with RTAI; if not, write to the Free Software Foundation,
 22   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 23   */
 24  
 25  /*!
 26   * @addtogroup driverapi
 27   * @{
 28   */
 29  
 30  #include <linux/module.h>
 31  #include <linux/delay.h>
 32  
 33  #include "rtdm/internal.h"
 34  
 35  #define SET_DEFAULT_OP(device, operation)				\
 36  	(device).operation##_rt  = (void *)rtdm_no_support;		\
 37  	(device).operation##_nrt = (void *)rtdm_no_support
 38  
 39  #define SET_DEFAULT_OP_IF_NULL(device, operation)			\
 40  	if (!(device).operation##_rt)					\
 41  		(device).operation##_rt = (void *)rtdm_no_support;	\
 42  	if (!(device).operation##_nrt)					\
 43  		(device).operation##_nrt = (void *)rtdm_no_support
 44  
 45  #define ANY_HANDLER(device, operation)					\
 46  	((device).operation##_rt || (device).operation##_nrt)
 47  
 48  unsigned int devname_hashtab_size = DEF_DEVNAME_HASHTAB_SIZE;
 49  unsigned int protocol_hashtab_size = DEF_PROTO_HASHTAB_SIZE;
 50  module_param(devname_hashtab_size, uint, 0400);
 51  module_param(protocol_hashtab_size, uint, 0400);
 52  MODULE_PARM_DESC(devname_hashtab_size,
 53  		 "Size of hash table for named devices (must be power of 2)");
 54  MODULE_PARM_DESC(protocol_hashtab_size,
 55  		 "Size of hash table for protocol devices "
 56  		 "(must be power of 2)");
 57  
 58  struct list_head *rtdm_named_devices;	/* hash table */
 59  struct list_head *rtdm_protocol_devices;	/* hash table */
 60  static int name_hashkey_mask;
 61  static int proto_hashkey_mask;
 62  
 63  int rtdm_apc;
 64  EXPORT_SYMBOL(rtdm_apc);
 65  
 66  struct semaphore nrt_dev_lock;
 67  DEFINE_XNLOCK(rt_dev_lock);
 68  
 69  #ifndef MODULE
 70  int rtdm_initialised = 0;
 71  #endif /* !MODULE */
 72  
 73  int rtdm_no_support(void)
 74  {
 75  	return -ENOSYS;
 76  }
 77  
 78  int rtdm_select_bind_no_support(struct rtdm_dev_context *context,
 79  				struct xnselector *selector,
 80  				unsigned type,
 81  				unsigned index)
 82  {
 83  	return -EBADF;
 84  }
 85  
 86  static inline int get_name_hash(const char *str, int limit, int hashkey_mask)
 87  {
 88  	int hash = 0;
 89  
 90  	while (*str != 0) {
 91  		hash += *str++;
 92  		if (--limit == 0)
 93  			break;
 94  	}
 95  	return hash & hashkey_mask;
 96  }
 97  
 98  static inline int get_proto_hash(int protocol_family, int socket_type)
 99  {
100  	return protocol_family & proto_hashkey_mask;
101  }
102  
103  static inline void rtdm_reference_device(struct rtdm_device *device)
104  {
105  	atomic_inc(&device->reserved.refcount);
106  }
107  
108  struct rtdm_device *get_named_device(const char *name)
109  {
110  	struct list_head *entry;
111  	struct rtdm_device *device;
112  	int hashkey;
113  	spl_t s;
114  
115  	hashkey = get_name_hash(name, RTDM_MAX_DEVNAME_LEN, name_hashkey_mask);
116  
117  	xnlock_get_irqsave(&rt_dev_lock, s);
118  
119  	list_for_each(entry, &rtdm_named_devices[hashkey]) {
120  		device = list_entry(entry, struct rtdm_device, reserved.entry);
121  
122  		if (strcmp(name, device->device_name) == 0) {
123  			rtdm_reference_device(device);
124  
125  			xnlock_put_irqrestore(&rt_dev_lock, s);
126  
127  			return device;
128  		}
129  	}
130  
131  	xnlock_put_irqrestore(&rt_dev_lock, s);
132  
133  	return NULL;
134  }
135  
136  struct rtdm_device *get_protocol_device(int protocol_family, int socket_type)
137  {
138  	struct list_head *entry;
139  	struct rtdm_device *device;
140  	int hashkey;
141  	spl_t s;
142  
143  	hashkey = get_proto_hash(protocol_family, socket_type);
144  
145  	xnlock_get_irqsave(&rt_dev_lock, s);
146  
147  	list_for_each(entry, &rtdm_protocol_devices[hashkey]) {
148  		device = list_entry(entry, struct rtdm_device, reserved.entry);
149  
150  		if ((device->protocol_family == protocol_family) &&
151  		    (device->socket_type == socket_type)) {
152  			rtdm_reference_device(device);
153  
154  			xnlock_put_irqrestore(&rt_dev_lock, s);
155  
156  			return device;
157  		}
158  	}
159  
160  	xnlock_put_irqrestore(&rt_dev_lock, s);
161  
162  	return NULL;
163  }
164  
165  /*!
166   * @ingroup driverapi
167   * @defgroup devregister Device Registration Services
168   * @{
169   */
170  
171  /**
172   * @brief Register a RTDM device
173   *
174   * @param[in] device Pointer to structure describing the new device.
175   *
176   * @return 0 is returned upon success. Otherwise:
177   *
178   * - -EINVAL is returned if the device structure contains invalid entries.
179   * Check kernel log in this case.
180   *
181   * - -ENOMEM is returned if the context for an exclusive device cannot be
182   * allocated.
183   *
184   * - -EEXIST is returned if the specified device name of protocol ID is
185   * already in use.
186   *
187   * - -EAGAIN is returned if some /proc entry cannot be created.
188   *
189   * Environments:
190   *
191   * This service can be called from:
192   *
193   * - Kernel module initialization/cleanup code
194   *
195   * Rescheduling: never.
196   */
197  int rtdm_dev_register(struct rtdm_device *device)
198  {
199  	int hashkey;
200  	spl_t s;
201  	struct list_head *entry;
202  	struct rtdm_device *existing_dev;
203  	int ret;
204  
205  	/* Catch unsuccessful initialisation */
206  	if (!rtdm_initialised)
207  		return -ENOSYS;
208  
209  	/* Sanity check: structure version */
210  	RTAI_ASSERT(RTDM, device->struct_version == RTDM_DEVICE_STRUCT_VER,
211  		    xnlogerr("RTDM: invalid rtdm_device version (%d, "
212  			     "required %d)\n", device->struct_version,
213  			     RTDM_DEVICE_STRUCT_VER);
214  		    return -EINVAL;);
215  
216  	/* Sanity check: proc_name specified? */
217  	RTAI_ASSERT(RTDM, device->proc_name,
218  		    xnlogerr("RTDM: no /proc entry name specified\n");
219  		    return -EINVAL;);
220  
221  	switch (device->device_flags & RTDM_DEVICE_TYPE_MASK) {
222  	case RTDM_NAMED_DEVICE:
223  		/* Sanity check: any open handler? */
224  		RTAI_ASSERT(RTDM, ANY_HANDLER(*device, open),
225  			    xnlogerr("RTDM: missing open handler\n");
226  			    return -EINVAL;);
227  		if (device->open_rt &&
228  		    device->socket_rt != (void *)rtdm_no_support)
229  		        xnlogerr("RTDM: RT open handler is deprecated, "
230  				 "driver requires update.\n");
231  		SET_DEFAULT_OP_IF_NULL(*device, open);
232  		SET_DEFAULT_OP(*device, socket);
233  		break;
234  
235  	case RTDM_PROTOCOL_DEVICE:
236  		/* Sanity check: any socket handler? */
237  		RTAI_ASSERT(RTDM, ANY_HANDLER(*device, socket),
238  			    xnlogerr("RTDM: missing socket handler\n");
239  			    return -EINVAL;);
240  		if (device->socket_rt &&
241  		    device->socket_rt != (void *)rtdm_no_support)
242  			xnlogerr("RTDM: RT socket creation handler is "
243  				 "deprecated, driver requires update.\n");
244  		SET_DEFAULT_OP_IF_NULL(*device, socket);
245  		SET_DEFAULT_OP(*device, open);
246  		break;
247  
248  	default:
249  		return -EINVAL;
250  	}
251  
252  	/* Sanity check: non-RT close handler?
253  	 * (Always required for forced cleanup) */
254  	if (!device->ops.close_nrt) {
255  		xnlogerr("RTDM: missing non-RT close handler\n");
256  		return -EINVAL;
257  	}
258  	if (device->ops.close_rt &&
259  	    device->ops.close_rt != (void *)rtdm_no_support)
260  		xnlogerr("RTDM: RT close handler is deprecated, driver "
261  			 "requires update.\n");
262  	else
263  		device->ops.close_rt = (void *)rtdm_no_support;
264  
265  	SET_DEFAULT_OP_IF_NULL(device->ops, ioctl);
266  	SET_DEFAULT_OP_IF_NULL(device->ops, read);
267  	SET_DEFAULT_OP_IF_NULL(device->ops, write);
268  	SET_DEFAULT_OP_IF_NULL(device->ops, recvmsg);
269  	SET_DEFAULT_OP_IF_NULL(device->ops, sendmsg);
270  	if (!device->ops.select_bind)
271  		device->ops.select_bind = rtdm_select_bind_no_support;
272  
273  	atomic_set(&device->reserved.refcount, 0);
274  	device->reserved.exclusive_context = NULL;
275  
276  	if (device->device_flags & RTDM_EXCLUSIVE) {
277  		device->reserved.exclusive_context =
278  		    kmalloc(sizeof(struct rtdm_dev_context) +
279  			    device->context_size, GFP_KERNEL);
280  		if (!device->reserved.exclusive_context) {
281  			xnlogerr("RTDM: no memory for exclusive context "
282  				 "(context size: %ld)\n",
283  				 (long)device->context_size);
284  			return -ENOMEM;
285  		}
286  		/* mark exclusive context as unused */
287  		device->reserved.exclusive_context->device = NULL;
288  	}
289  
290  	down(&nrt_dev_lock);
291  
292  	if ((device->device_flags & RTDM_DEVICE_TYPE_MASK) == RTDM_NAMED_DEVICE) {
293  		trace_mark(xn_rtdm, nameddev_register, "device %p name %s "
294  			   "flags %d class %d sub_class %d profile_version %d "
295  			   "driver_version %d", device, device->device_name,
296  			   device->device_flags, device->device_class,
297  			   device->device_sub_class, device->profile_version,
298  			   device->driver_version);
299  
300  		hashkey =
301  		    get_name_hash(device->device_name, RTDM_MAX_DEVNAME_LEN,
302  				  name_hashkey_mask);
303  
304  		list_for_each(entry, &rtdm_named_devices[hashkey]) {
305  			existing_dev =
306  			    list_entry(entry, struct rtdm_device,
307  				       reserved.entry);
308  			if (strcmp(device->device_name,
309  				   existing_dev->device_name) == 0) {
310  				ret = -EEXIST;
311  				goto err;
312  			}
313  		}
314  
315  		ret = rtdm_proc_register_device(device);
316  		if (ret)
317  			goto err;
318  
319  		xnlock_get_irqsave(&rt_dev_lock, s);
320  		list_add_tail(&device->reserved.entry,
321  			      &rtdm_named_devices[hashkey]);
322  		xnlock_put_irqrestore(&rt_dev_lock, s);
323  
324  		up(&nrt_dev_lock);
325  	} else {
326  		trace_mark(xn_rtdm, protocol_register, "device %p "
327  			   "protocol_family %d socket_type %d flags %d "
328  			   "class %d sub_class %d profile_version %d "
329  			   "driver_version %d", device,
330  			   device->protocol_family, device->socket_type,
331  			   device->device_flags, device->device_class,
332  			   device->device_sub_class, device->profile_version,
333  			   device->driver_version);
334  
335  		hashkey = get_proto_hash(device->protocol_family,
336  					 device->socket_type);
337  
338  		list_for_each(entry, &rtdm_protocol_devices[hashkey]) {
339  			existing_dev =
340  			    list_entry(entry, struct rtdm_device,
341  				       reserved.entry);
342  			if ((device->protocol_family ==
343  			     existing_dev->protocol_family)
344  			    && (device->socket_type ==
345  				existing_dev->socket_type)) {
346  				xnlogerr("RTDM: protocol %u:%u already "
347  					 "exists\n", device->protocol_family,
348  					 device->socket_type);
349  				ret = -EEXIST;
350  				goto err;
351  			}
352  		}
353  
354  		ret = rtdm_proc_register_device(device);
355  		if (ret)
356  			goto err;
357  
358  		xnlock_get_irqsave(&rt_dev_lock, s);
359  		list_add_tail(&device->reserved.entry,
360  			      &rtdm_protocol_devices[hashkey]);
361  		xnlock_put_irqrestore(&rt_dev_lock, s);
362  
363  		up(&nrt_dev_lock);
364  	}
365  	return 0;
366  
367  err:
368  	up(&nrt_dev_lock);
369  	if (device->reserved.exclusive_context)
370  		kfree(device->reserved.exclusive_context);
371  	return ret;
372  }
373  
374  EXPORT_SYMBOL(rtdm_dev_register);
375  
376  /**
377   * @brief Unregisters a RTDM device
378   *
379   * @param[in] device Pointer to structure describing the device to be
380   * unregistered.
381   * @param[in] poll_delay Polling delay in milliseconds to check repeatedly for
382   * open instances of @a device, or 0 for non-blocking mode.
383   *
384   * @return 0 is returned upon success. Otherwise:
385   *
386   * - -ENODEV is returned if the device was not registered.
387   *
388   * - -EAGAIN is returned if the device is busy with open instances and 0 has
389   * been passed for @a poll_delay.
390   *
391   * Environments:
392   *
393   * This service can be called from:
394   *
395   * - Kernel module initialization/cleanup code
396   *
397   * Rescheduling: never.
398   */
399  int rtdm_dev_unregister(struct rtdm_device *device, unsigned int poll_delay)
400  {
401  	spl_t s;
402  	struct rtdm_device *reg_dev;
403  	unsigned long warned = 0;
404  
405  	if (!rtdm_initialised)
406  		return -ENOSYS;
407  
408  	if ((device->device_flags & RTDM_DEVICE_TYPE_MASK) == RTDM_NAMED_DEVICE)
409  		reg_dev = get_named_device(device->device_name);
410  	else
411  		reg_dev = get_protocol_device(device->protocol_family,
412  					      device->socket_type);
413  	if (!reg_dev)
414  		return -ENODEV;
415  
416  	trace_mark(xn_rtdm, dev_unregister, "device %p poll_delay %u",
417  		   device, poll_delay);
418  
419  	down(&nrt_dev_lock);
420  	xnlock_get_irqsave(&rt_dev_lock, s);
421  
422  	while (atomic_read(&reg_dev->reserved.refcount) > 1) {
423  		xnlock_put_irqrestore(&rt_dev_lock, s);
424  		up(&nrt_dev_lock);
425  
426  		if (!poll_delay) {
427  			rtdm_dereference_device(reg_dev);
428  			trace_mark(xn_rtdm, dev_busy, "device %p", device);
429  			return -EAGAIN;
430  		}
431  
432  		if (!__test_and_set_bit(0, &warned))
433  			xnlogwarn("RTDM: device %s still in use - waiting for "
434  				  "release...\n", reg_dev->device_name);
435  		msleep(poll_delay);
436  		trace_mark(xn_rtdm, dev_poll, "device %p", device);
437  
438  		down(&nrt_dev_lock);
439  		xnlock_get_irqsave(&rt_dev_lock, s);
440  	}
441  
442  	list_del(&reg_dev->reserved.entry);
443  
444  	xnlock_put_irqrestore(&rt_dev_lock, s);
445  
446  	rtdm_proc_unregister_device(device);
447  
448  	up(&nrt_dev_lock);
449  
450  	if (reg_dev->reserved.exclusive_context)
451  		kfree(device->reserved.exclusive_context);
452  
453  	return 0;
454  }
455  
456  EXPORT_SYMBOL(rtdm_dev_unregister);
457  /** @} */
458  
459  int __init rtdm_dev_init(void)
460  {
461  	int err, i;
462  
463  	sema_init(&nrt_dev_lock, 1);
464  
465  	rtdm_apc = rthal_apc_alloc("deferred RTDM close", rtdm_apc_handler,
466  				   NULL);
467  	if (rtdm_apc < 0)
468  		return rtdm_apc;
469  
470  	name_hashkey_mask = devname_hashtab_size - 1;
471  	proto_hashkey_mask = protocol_hashtab_size - 1;
472  	if (((devname_hashtab_size & name_hashkey_mask) != 0) ||
473  	    ((protocol_hashtab_size & proto_hashkey_mask) != 0)) {
474  		err = -EINVAL;
475  		goto err_out1;
476  	}
477  
478  	rtdm_named_devices = (struct list_head *)
479  	    kmalloc(devname_hashtab_size * sizeof(struct list_head),
480  		    GFP_KERNEL);
481  	if (!rtdm_named_devices) {
482  		err = -ENOMEM;
483  		goto err_out1;
484  	}
485  
486  	for (i = 0; i < devname_hashtab_size; i++)
487  		INIT_LIST_HEAD(&rtdm_named_devices[i]);
488  
489  	rtdm_protocol_devices = (struct list_head *)
490  	    kmalloc(protocol_hashtab_size * sizeof(struct list_head),
491  		    GFP_KERNEL);
492  	if (!rtdm_protocol_devices) {
493  		err = -ENOMEM;
494  		goto err_out2;
495  	}
496  
497  	for (i = 0; i < protocol_hashtab_size; i++)
498  		INIT_LIST_HEAD(&rtdm_protocol_devices[i]);
499  
500  	return 0;
501  
502  err_out2:
503  	kfree(rtdm_named_devices);
504  
505  err_out1:
506  	rthal_apc_free(rtdm_apc);
507  
508  	return err;
509  }
510  
511  void rtdm_dev_cleanup(void)
512  {
513  	/*
514  	 * Note: no need to flush the cleanup_queue as no device is allowed
515  	 * to deregister as long as there are references.
516  	 */
517  	rthal_apc_free(rtdm_apc);
518  	kfree(rtdm_named_devices);
519  	kfree(rtdm_protocol_devices);
520  }
521  
522  /*@}*/