sir_dongle.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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/kmod.h>
  17. #include <linux/mutex.h>
  18. #include <net/irda/irda.h>
  19. #include "sir-dev.h"
  20. /**************************************************************************
  21. *
  22. * dongle registration and attachment
  23. *
  24. */
  25. static LIST_HEAD(dongle_list); /* list of registered dongle drivers */
  26. static DEFINE_MUTEX(dongle_list_lock); /* protects the list */
  27. int irda_register_dongle(struct dongle_driver *new)
  28. {
  29. struct list_head *entry;
  30. struct dongle_driver *drv;
  31. IRDA_DEBUG(0, "%s : registering dongle \"%s\" (%d).\n",
  32. __func__, new->driver_name, new->type);
  33. mutex_lock(&dongle_list_lock);
  34. list_for_each(entry, &dongle_list) {
  35. drv = list_entry(entry, struct dongle_driver, dongle_list);
  36. if (new->type == drv->type) {
  37. mutex_unlock(&dongle_list_lock);
  38. return -EEXIST;
  39. }
  40. }
  41. list_add(&new->dongle_list, &dongle_list);
  42. mutex_unlock(&dongle_list_lock);
  43. return 0;
  44. }
  45. EXPORT_SYMBOL(irda_register_dongle);
  46. int irda_unregister_dongle(struct dongle_driver *drv)
  47. {
  48. mutex_lock(&dongle_list_lock);
  49. list_del(&drv->dongle_list);
  50. mutex_unlock(&dongle_list_lock);
  51. return 0;
  52. }
  53. EXPORT_SYMBOL(irda_unregister_dongle);
  54. int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
  55. {
  56. struct list_head *entry;
  57. const struct dongle_driver *drv = NULL;
  58. int err = -EINVAL;
  59. request_module("irda-dongle-%d", type);
  60. if (dev->dongle_drv != NULL)
  61. return -EBUSY;
  62. /* serialize access to the list of registered dongles */
  63. mutex_lock(&dongle_list_lock);
  64. list_for_each(entry, &dongle_list) {
  65. drv = list_entry(entry, struct dongle_driver, dongle_list);
  66. if (drv->type == type)
  67. break;
  68. else
  69. drv = NULL;
  70. }
  71. if (!drv) {
  72. err = -ENODEV;
  73. goto out_unlock; /* no such dongle */
  74. }
  75. /* handling of SMP races with dongle module removal - three cases:
  76. * 1) dongle driver was already unregistered - then we haven't found the
  77. * requested dongle above and are already out here
  78. * 2) the module is already marked deleted but the driver is still
  79. * registered - then the try_module_get() below will fail
  80. * 3) the try_module_get() below succeeds before the module is marked
  81. * deleted - then sys_delete_module() fails and prevents the removal
  82. * because the module is in use.
  83. */
  84. if (!try_module_get(drv->owner)) {
  85. err = -ESTALE;
  86. goto out_unlock; /* rmmod already pending */
  87. }
  88. dev->dongle_drv = drv;
  89. if (!drv->open || (err=drv->open(dev))!=0)
  90. goto out_reject; /* failed to open driver */
  91. mutex_unlock(&dongle_list_lock);
  92. return 0;
  93. out_reject:
  94. dev->dongle_drv = NULL;
  95. module_put(drv->owner);
  96. out_unlock:
  97. mutex_unlock(&dongle_list_lock);
  98. return err;
  99. }
  100. int sirdev_put_dongle(struct sir_dev *dev)
  101. {
  102. const struct dongle_driver *drv = dev->dongle_drv;
  103. if (drv) {
  104. if (drv->close)
  105. drv->close(dev); /* close this dongle instance */
  106. dev->dongle_drv = NULL; /* unlink the dongle driver */
  107. module_put(drv->owner);/* decrement driver's module refcount */
  108. }
  109. return 0;
  110. }