sir_dongle.c 3.4 KB

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