123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- /*********************************************************************
- *
- * sir_dongle.c: manager for serial dongle protocol drivers
- *
- * Copyright (c) 2002 Martin Diehl
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- ********************************************************************/
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/kmod.h>
- #include <linux/mutex.h>
- #include <net/irda/irda.h>
- #include "sir-dev.h"
- /**************************************************************************
- *
- * dongle registration and attachment
- *
- */
- static LIST_HEAD(dongle_list); /* list of registered dongle drivers */
- static DEFINE_MUTEX(dongle_list_lock); /* protects the list */
- int irda_register_dongle(struct dongle_driver *new)
- {
- struct list_head *entry;
- struct dongle_driver *drv;
- pr_debug("%s : registering dongle \"%s\" (%d).\n",
- __func__, new->driver_name, new->type);
- mutex_lock(&dongle_list_lock);
- list_for_each(entry, &dongle_list) {
- drv = list_entry(entry, struct dongle_driver, dongle_list);
- if (new->type == drv->type) {
- mutex_unlock(&dongle_list_lock);
- return -EEXIST;
- }
- }
- list_add(&new->dongle_list, &dongle_list);
- mutex_unlock(&dongle_list_lock);
- return 0;
- }
- EXPORT_SYMBOL(irda_register_dongle);
- int irda_unregister_dongle(struct dongle_driver *drv)
- {
- mutex_lock(&dongle_list_lock);
- list_del(&drv->dongle_list);
- mutex_unlock(&dongle_list_lock);
- return 0;
- }
- EXPORT_SYMBOL(irda_unregister_dongle);
- int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type)
- {
- struct list_head *entry;
- const struct dongle_driver *drv = NULL;
- int err = -EINVAL;
- request_module("irda-dongle-%d", type);
- if (dev->dongle_drv != NULL)
- return -EBUSY;
-
- /* serialize access to the list of registered dongles */
- mutex_lock(&dongle_list_lock);
- list_for_each(entry, &dongle_list) {
- drv = list_entry(entry, struct dongle_driver, dongle_list);
- if (drv->type == type)
- break;
- else
- drv = NULL;
- }
- if (!drv) {
- err = -ENODEV;
- goto out_unlock; /* no such dongle */
- }
- /* handling of SMP races with dongle module removal - three cases:
- * 1) dongle driver was already unregistered - then we haven't found the
- * requested dongle above and are already out here
- * 2) the module is already marked deleted but the driver is still
- * registered - then the try_module_get() below will fail
- * 3) the try_module_get() below succeeds before the module is marked
- * deleted - then sys_delete_module() fails and prevents the removal
- * because the module is in use.
- */
- if (!try_module_get(drv->owner)) {
- err = -ESTALE;
- goto out_unlock; /* rmmod already pending */
- }
- dev->dongle_drv = drv;
- if (!drv->open || (err=drv->open(dev))!=0)
- goto out_reject; /* failed to open driver */
- mutex_unlock(&dongle_list_lock);
- return 0;
- out_reject:
- dev->dongle_drv = NULL;
- module_put(drv->owner);
- out_unlock:
- mutex_unlock(&dongle_list_lock);
- return err;
- }
- int sirdev_put_dongle(struct sir_dev *dev)
- {
- const struct dongle_driver *drv = dev->dongle_drv;
- if (drv) {
- if (drv->close)
- drv->close(dev); /* close this dongle instance */
- dev->dongle_drv = NULL; /* unlink the dongle driver */
- module_put(drv->owner);/* decrement driver's module refcount */
- }
- return 0;
- }
|