sir_dongle.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*********************************************************************
  2. *
  3. * sir_dongle.c: manager for serial dongle protocol drivers
  4. *
  5. * Copyright (c) 2002 Martin Diehl
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. ********************************************************************/
  13. #include <linux/module.h>
  14. #include <linux/kernel.h>
  15. #include <linux/init.h>
  16. #include <linux/smp_lock.h>
  17. #include <linux/kmod.h>
  18. #include <linux/mutex.h>
  19. #include <net/irda/irda.h>
  20. #include "sir-dev.h"
  21. /**************************************************************************
  22. *
  23. * dongle registration and attachment
  24. *
  25. */
  26. static LIST_HEAD(dongle_list); /* list of registered dongle drivers */
  27. static DEFINE_MUTEX(dongle_list_lock); /* protects the list */
  28. int irda_register_dongle(struct dongle_driver *new)
  29. {
  30. struct list_head *entry;
  31. struct dongle_driver *drv;
  32. IRDA_DEBUG(0, "%s : registering dongle \"%s\" (%d).\n",
  33. __FUNCTION__, new->driver_name, new->type);
  34. mutex_lock(&dongle_list_lock);
  35. list_for_each(entry, &dongle_list) {
  36. drv = list_entry(entry, struct dongle_driver, dongle_list);
  37. if (new->type == drv->type) {
  38. mutex_unlock(&dongle_list_lock);
  39. return -EEXIST;
  40. }
  41. }
  42. list_add(&new->dongle_list, &dongle_list);
  43. mutex_unlock(&dongle_list_lock);
  44. return 0;
  45. }
  46. EXPORT_SYMBOL(irda_register_dongle);
  47. int irda_unregister_dongle(struct dongle_driver *drv)
  48. {
  49. mutex_lock(&dongle_list_lock);
  50. list_del(&drv->dongle_list);
  51. mutex_unlock(&dongle_list_lock);
  52. return 0;
  53. }
  54. EXPORT_SYMBOL(irda_unregister_dongle);
  55. int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
  56. {
  57. struct list_head *entry;
  58. const struct dongle_driver *drv = NULL;
  59. int err = -EINVAL;
  60. #ifdef CONFIG_KMOD
  61. request_module("irda-dongle-%d", type);
  62. #endif
  63. if (dev->dongle_drv != NULL)
  64. return -EBUSY;
  65. /* serialize access to the list of registered dongles */
  66. mutex_lock(&dongle_list_lock);
  67. list_for_each(entry, &dongle_list) {
  68. drv = list_entry(entry, struct dongle_driver, dongle_list);
  69. if (drv->type == type)
  70. break;
  71. else
  72. drv = NULL;
  73. }
  74. if (!drv) {
  75. err = -ENODEV;
  76. goto out_unlock; /* no such dongle */
  77. }
  78. /* handling of SMP races with dongle module removal - three cases:
  79. * 1) dongle driver was already unregistered - then we haven't found the
  80. * requested dongle above and are already out here
  81. * 2) the module is already marked deleted but the driver is still
  82. * registered - then the try_module_get() below will fail
  83. * 3) the try_module_get() below succeeds before the module is marked
  84. * deleted - then sys_delete_module() fails and prevents the removal
  85. * because the module is in use.
  86. */
  87. if (!try_module_get(drv->owner)) {
  88. err = -ESTALE;
  89. goto out_unlock; /* rmmod already pending */
  90. }
  91. dev->dongle_drv = drv;
  92. if (!drv->open || (err=drv->open(dev))!=0)
  93. goto out_reject; /* failed to open driver */
  94. mutex_unlock(&dongle_list_lock);
  95. return 0;
  96. out_reject:
  97. dev->dongle_drv = NULL;
  98. module_put(drv->owner);
  99. out_unlock:
  100. mutex_unlock(&dongle_list_lock);
  101. return err;
  102. }
  103. int sirdev_put_dongle(struct sir_dev *dev)
  104. {
  105. const struct dongle_driver *drv = dev->dongle_drv;
  106. if (drv) {
  107. if (drv->close)
  108. drv->close(dev); /* close this dongle instance */
  109. dev->dongle_drv = NULL; /* unlink the dongle driver */
  110. module_put(drv->owner);/* decrement driver's module refcount */
  111. }
  112. return 0;
  113. }