[PATCH 01/15] staging: gasket: core: remove static function forward declarations

From: Todd Poynor
Date: Tue Jul 31 2018 - 16:25:19 EST


From: Todd Poynor <toddpoynor@xxxxxxxxxx>

Remove forward declarations of static functions, move code to avoid
forward references, for kernel style.

Signed-off-by: Todd Poynor <toddpoynor@xxxxxxxxxx>
---
drivers/staging/gasket/gasket_core.c | 1900 +++++++++++++-------------
1 file changed, 922 insertions(+), 978 deletions(-)

diff --git a/drivers/staging/gasket/gasket_core.c b/drivers/staging/gasket/gasket_core.c
index c00774059f9e..b5a7254fbfb3 100644
--- a/drivers/staging/gasket/gasket_core.c
+++ b/drivers/staging/gasket/gasket_core.c
@@ -67,61 +67,6 @@ enum do_map_region_status {
DO_MAP_REGION_INVALID,
};

-/* Function declarations; comments are with definitions. */
-static int __init gasket_init(void);
-static void __exit gasket_exit(void);
-
-static int gasket_pci_probe(
- struct pci_dev *pci_dev, const struct pci_device_id *id);
-static void gasket_pci_remove(struct pci_dev *pci_dev);
-
-static int gasket_setup_pci(struct pci_dev *pci_dev, struct gasket_dev *dev);
-static void gasket_cleanup_pci(struct gasket_dev *dev);
-
-static int gasket_map_pci_bar(struct gasket_dev *dev, int bar_num);
-static void gasket_unmap_pci_bar(struct gasket_dev *dev, int bar_num);
-
-static int gasket_alloc_dev(
- struct gasket_internal_desc *internal_desc, struct device *dev,
- struct gasket_dev **pdev, const char *kobj_name);
-static void gasket_free_dev(struct gasket_dev *dev);
-
-static int gasket_find_dev_slot(
- struct gasket_internal_desc *internal_desc, const char *kobj_name);
-
-static int gasket_add_cdev(
- struct gasket_cdev_info *dev_info,
- const struct file_operations *file_ops, struct module *owner);
-
-static int gasket_enable_dev(
- struct gasket_internal_desc *internal_desc,
- struct gasket_dev *gasket_dev);
-static void gasket_disable_dev(struct gasket_dev *gasket_dev);
-
-static struct gasket_internal_desc *lookup_internal_desc(
- struct pci_dev *pci_dev);
-
-static ssize_t gasket_sysfs_data_show(
- struct device *device, struct device_attribute *attr, char *buf);
-
-static int gasket_mmap(struct file *filp, struct vm_area_struct *vma);
-static int gasket_open(struct inode *inode, struct file *file);
-static int gasket_release(struct inode *inode, struct file *file);
-static long gasket_ioctl(struct file *filp, uint cmd, ulong arg);
-
-static int gasket_mm_vma_bar_offset(
- const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
- ulong *bar_offset);
-static bool gasket_mm_get_mapping_addrs(
- const struct gasket_mappable_region *region, ulong bar_offset,
- ulong requested_length, struct gasket_mappable_region *mappable_region,
- ulong *virt_offset);
-static enum do_map_region_status do_map_region(
- const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
- struct gasket_mappable_region *map_region);
-
-static int gasket_get_hw_status(struct gasket_dev *gasket_dev);
-
/* Global data definitions. */
/* Mutex - only for framework-wide data. Other data should be protected by
* finer-grained locks.
@@ -157,48 +102,6 @@ enum gasket_sysfs_attribute_type {
ATTR_USER_MEM_RANGES
};

-/* File operations for all Gasket devices. */
-static const struct file_operations gasket_file_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .mmap = gasket_mmap,
- .open = gasket_open,
- .release = gasket_release,
- .unlocked_ioctl = gasket_ioctl,
-};
-
-/* These attributes apply to all Gasket driver instances. */
-static const struct gasket_sysfs_attribute gasket_sysfs_generic_attrs[] = {
- GASKET_SYSFS_RO(bar_offsets, gasket_sysfs_data_show, ATTR_BAR_OFFSETS),
- GASKET_SYSFS_RO(bar_sizes, gasket_sysfs_data_show, ATTR_BAR_SIZES),
- GASKET_SYSFS_RO(driver_version, gasket_sysfs_data_show,
- ATTR_DRIVER_VERSION),
- GASKET_SYSFS_RO(framework_version, gasket_sysfs_data_show,
- ATTR_FRAMEWORK_VERSION),
- GASKET_SYSFS_RO(device_type, gasket_sysfs_data_show, ATTR_DEVICE_TYPE),
- GASKET_SYSFS_RO(revision, gasket_sysfs_data_show,
- ATTR_HARDWARE_REVISION),
- GASKET_SYSFS_RO(pci_address, gasket_sysfs_data_show, ATTR_PCI_ADDRESS),
- GASKET_SYSFS_RO(status, gasket_sysfs_data_show, ATTR_STATUS),
- GASKET_SYSFS_RO(is_device_owned, gasket_sysfs_data_show,
- ATTR_IS_DEVICE_OWNED),
- GASKET_SYSFS_RO(device_owner, gasket_sysfs_data_show,
- ATTR_DEVICE_OWNER),
- GASKET_SYSFS_RO(write_open_count, gasket_sysfs_data_show,
- ATTR_WRITE_OPEN_COUNT),
- GASKET_SYSFS_RO(reset_count, gasket_sysfs_data_show, ATTR_RESET_COUNT),
- GASKET_SYSFS_RO(user_mem_ranges, gasket_sysfs_data_show,
- ATTR_USER_MEM_RANGES),
- GASKET_END_OF_ATTR_ARRAY
-};
-
-MODULE_DESCRIPTION("Google Gasket driver framework");
-MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Rob Springer <rspringer@xxxxxxxxxx>");
-module_init(gasket_init);
-module_exit(gasket_exit);
-
/* Perform a standard Gasket callback. */
static inline int check_and_invoke_callback(
struct gasket_dev *gasket_dev, int (*cb_function)(struct gasket_dev *))
@@ -239,165 +142,43 @@ static int gasket_owned_by_current_tgid(struct gasket_cdev_info *info)
(info->ownership.owner == current->tgid));
}

-static int __init gasket_init(void)
+/*
+ * Find the next free gasket_internal_dev slot.
+ *
+ * Returns the located slot number on success or a negative number on failure.
+ */
+static int gasket_find_dev_slot(
+ struct gasket_internal_desc *internal_desc, const char *kobj_name)
{
int i;

- pr_info("Performing one-time init of the Gasket framework.\n");
- /* Check for duplicates and find a free slot. */
- mutex_lock(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- g_descs[i].driver_desc = NULL;
- mutex_init(&g_descs[i].mutex);
- }
-
- gasket_sysfs_init();
-
- mutex_unlock(&g_mutex);
- return 0;
-}
-
-static void __exit gasket_exit(void)
-{
- /* No deinit/dealloc needed at present. */
- pr_info("Removing Gasket framework module.\n");
-}
-
-/* See gasket_core.h for description. */
-int gasket_register_device(const struct gasket_driver_desc *driver_desc)
-{
- int i, ret;
- int desc_idx = -1;
- struct gasket_internal_desc *internal;
-
- pr_info("Initializing Gasket framework device\n");
- /* Check for duplicates and find a free slot. */
- mutex_lock(&g_mutex);
+ mutex_lock(&internal_desc->mutex);

- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc == driver_desc) {
- pr_err("%s driver already loaded/registered\n",
- driver_desc->name);
- mutex_unlock(&g_mutex);
+ /* Search for a previous instance of this device. */
+ for (i = 0; i < GASKET_DEV_MAX; i++) {
+ if (internal_desc->devs[i] &&
+ strcmp(internal_desc->devs[i]->kobj_name, kobj_name) == 0) {
+ pr_err("Duplicate device %s\n", kobj_name);
+ mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}
}

- /* This and the above loop could be combined, but this reads easier. */
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (!g_descs[i].driver_desc) {
- g_descs[i].driver_desc = driver_desc;
- desc_idx = i;
+ /* Find a free device slot. */
+ for (i = 0; i < GASKET_DEV_MAX; i++) {
+ if (!internal_desc->devs[i])
break;
- }
}
- mutex_unlock(&g_mutex);
-
- pr_info("Loaded %s driver, framework version %s\n",
- driver_desc->name, GASKET_FRAMEWORK_VERSION);

- if (desc_idx == -1) {
- pr_err("Too many Gasket drivers loaded: %d\n",
- GASKET_FRAMEWORK_DESC_MAX);
+ if (i == GASKET_DEV_MAX) {
+ pr_err("Too many registered devices; max %d\n", GASKET_DEV_MAX);
+ mutex_unlock(&internal_desc->mutex);
return -EBUSY;
}

- /* Internal structure setup. */
- pr_debug("Performing initial internal structure setup.\n");
- internal = &g_descs[desc_idx];
- mutex_init(&internal->mutex);
- memset(internal->devs, 0, sizeof(struct gasket_dev *) * GASKET_DEV_MAX);
- memset(&internal->pci, 0, sizeof(internal->pci));
- internal->pci.name = driver_desc->name;
- internal->pci.id_table = driver_desc->pci_id_table;
- internal->pci.probe = gasket_pci_probe;
- internal->pci.remove = gasket_pci_remove;
- internal->class =
- class_create(driver_desc->module, driver_desc->name);
-
- if (IS_ERR(internal->class)) {
- pr_err("Cannot register %s class [ret=%ld]\n",
- driver_desc->name, PTR_ERR(internal->class));
- ret = PTR_ERR(internal->class);
- goto unregister_gasket_driver;
- }
-
- /*
- * Not using pci_register_driver() (without underscores), as it
- * depends on KBUILD_MODNAME, and this is a shared file.
- */
- pr_debug("Registering PCI driver.\n");
- ret = __pci_register_driver(
- &internal->pci, driver_desc->module, driver_desc->name);
- if (ret) {
- pr_err("cannot register pci driver [ret=%d]\n", ret);
- goto fail1;
- }
-
- pr_debug("Registering char driver.\n");
- ret = register_chrdev_region(
- MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX,
- driver_desc->name);
- if (ret) {
- pr_err("cannot register char driver [ret=%d]\n", ret);
- goto fail2;
- }
-
- pr_info("Driver registered successfully.\n");
- return 0;
-
-fail2:
- pci_unregister_driver(&internal->pci);
-
-fail1:
- class_destroy(internal->class);
-
-unregister_gasket_driver:
- mutex_lock(&g_mutex);
- g_descs[desc_idx].driver_desc = NULL;
- mutex_unlock(&g_mutex);
- return ret;
-}
-EXPORT_SYMBOL(gasket_register_device);
-
-/* See gasket_core.h for description. */
-void gasket_unregister_device(const struct gasket_driver_desc *driver_desc)
-{
- int i, desc_idx;
- struct gasket_internal_desc *internal_desc = NULL;
-
- mutex_lock(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc == driver_desc) {
- internal_desc = &g_descs[i];
- desc_idx = i;
- break;
- }
- }
- mutex_unlock(&g_mutex);
-
- if (!internal_desc) {
- pr_err("request to unregister unknown desc: %s, %d:%d\n",
- driver_desc->name, driver_desc->major,
- driver_desc->minor);
- return;
- }
-
- unregister_chrdev_region(
- MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX);
-
- pci_unregister_driver(&internal_desc->pci);
-
- class_destroy(internal_desc->class);
-
- /* Finally, effectively "remove" the driver. */
- mutex_lock(&g_mutex);
- g_descs[desc_idx].driver_desc = NULL;
- mutex_unlock(&g_mutex);
-
- pr_info("removed %s driver\n", driver_desc->name);
+ mutex_unlock(&internal_desc->mutex);
+ return i;
}
-EXPORT_SYMBOL(gasket_unregister_device);

/*
* Allocate and initialize a Gasket device structure, add the device to the
@@ -474,265 +255,21 @@ static void gasket_free_dev(struct gasket_dev *gasket_dev)
}

/*
- * Find the next free gasket_internal_dev slot.
+ * Maps the specified bar into kernel space.
*
- * Returns the located slot number on success or a negative number on failure.
+ * Returns 0 on success, a negative error code otherwise.
+ * A zero-sized BAR will not be mapped, but is not an error.
*/
-static int gasket_find_dev_slot(
- struct gasket_internal_desc *internal_desc, const char *kobj_name)
+static int gasket_map_pci_bar(struct gasket_dev *gasket_dev, int bar_num)
{
- int i;
+ struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
+ const struct gasket_driver_desc *driver_desc =
+ internal_desc->driver_desc;
+ ulong desc_bytes = driver_desc->bar_descriptions[bar_num].size;
+ int ret;

- mutex_lock(&internal_desc->mutex);
-
- /* Search for a previous instance of this device. */
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (internal_desc->devs[i] &&
- strcmp(internal_desc->devs[i]->kobj_name, kobj_name) == 0) {
- pr_err("Duplicate device %s\n", kobj_name);
- mutex_unlock(&internal_desc->mutex);
- return -EBUSY;
- }
- }
-
- /* Find a free device slot. */
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (!internal_desc->devs[i])
- break;
- }
-
- if (i == GASKET_DEV_MAX) {
- pr_err("Too many registered devices; max %d\n", GASKET_DEV_MAX);
- mutex_unlock(&internal_desc->mutex);
- return -EBUSY;
- }
-
- mutex_unlock(&internal_desc->mutex);
- return i;
-}
-
-/*
- * PCI subsystem probe function.
- *
- * Called when a Gasket device is found. Allocates device metadata, maps device
- * memory, and calls gasket_enable_dev to prepare the device for active use.
- *
- * Returns 0 if successful and a negative value otherwise.
- */
-static int gasket_pci_probe(
- struct pci_dev *pci_dev, const struct pci_device_id *id)
-{
- int ret;
- const char *kobj_name = dev_name(&pci_dev->dev);
- struct gasket_internal_desc *internal_desc;
- struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc;
- struct device *parent;
-
- pr_info("Add Gasket device %s\n", kobj_name);
-
- mutex_lock(&g_mutex);
- internal_desc = lookup_internal_desc(pci_dev);
- mutex_unlock(&g_mutex);
- if (!internal_desc) {
- pr_err("PCI probe called for unknown driver type\n");
- return -ENODEV;
- }
-
- driver_desc = internal_desc->driver_desc;
-
- parent = &pci_dev->dev;
- ret = gasket_alloc_dev(internal_desc, parent, &gasket_dev, kobj_name);
- if (ret)
- return ret;
- gasket_dev->pci_dev = pci_dev_get(pci_dev);
- if (IS_ERR_OR_NULL(gasket_dev->dev_info.device)) {
- pr_err("Cannot create %s device %s [ret = %ld]\n",
- driver_desc->name, gasket_dev->dev_info.name,
- PTR_ERR(gasket_dev->dev_info.device));
- ret = -ENODEV;
- goto fail1;
- }
-
- ret = gasket_setup_pci(pci_dev, gasket_dev);
- if (ret)
- goto fail2;
-
- ret = check_and_invoke_callback(gasket_dev, driver_desc->add_dev_cb);
- if (ret) {
- dev_err(gasket_dev->dev, "Error in add device cb: %d\n", ret);
- goto fail2;
- }
-
- ret = gasket_sysfs_create_mapping(
- gasket_dev->dev_info.device, gasket_dev);
- if (ret)
- goto fail3;
-
- /*
- * Once we've created the mapping structures successfully, attempt to
- * create a symlink to the pci directory of this object.
- */
- ret = sysfs_create_link(&gasket_dev->dev_info.device->kobj,
- &pci_dev->dev.kobj, dev_name(&pci_dev->dev));
- if (ret) {
- dev_err(gasket_dev->dev,
- "Cannot create sysfs pci link: %d\n", ret);
- goto fail3;
- }
- ret = gasket_sysfs_create_entries(
- gasket_dev->dev_info.device, gasket_sysfs_generic_attrs);
- if (ret)
- goto fail4;
-
- ret = check_and_invoke_callback(
- gasket_dev, driver_desc->sysfs_setup_cb);
- if (ret) {
- dev_err(gasket_dev->dev, "Error in sysfs setup cb: %d\n", ret);
- goto fail5;
- }
-
- ret = gasket_enable_dev(internal_desc, gasket_dev);
- if (ret) {
- pr_err("cannot setup %s device\n", driver_desc->name);
- gasket_disable_dev(gasket_dev);
- goto fail5;
- }
-
- return 0;
-
-fail5:
- check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
-fail4:
-fail3:
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
-fail2:
- gasket_cleanup_pci(gasket_dev);
- check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
-fail1:
- gasket_free_dev(gasket_dev);
- return ret;
-}
-
-/*
- * PCI subsystem remove function.
- *
- * Called to remove a Gasket device. Finds the device in the device list and
- * cleans up metadata.
- */
-static void gasket_pci_remove(struct pci_dev *pci_dev)
-{
- int i;
- struct gasket_internal_desc *internal_desc;
- struct gasket_dev *gasket_dev = NULL;
- const struct gasket_driver_desc *driver_desc;
- /* Find the device desc. */
- mutex_lock(&g_mutex);
- internal_desc = lookup_internal_desc(pci_dev);
- if (!internal_desc) {
- mutex_unlock(&g_mutex);
- return;
- }
- mutex_unlock(&g_mutex);
-
- driver_desc = internal_desc->driver_desc;
-
- /* Now find the specific device */
- mutex_lock(&internal_desc->mutex);
- for (i = 0; i < GASKET_DEV_MAX; i++) {
- if (internal_desc->devs[i] &&
- internal_desc->devs[i]->pci_dev == pci_dev) {
- gasket_dev = internal_desc->devs[i];
- break;
- }
- }
- mutex_unlock(&internal_desc->mutex);
-
- if (!gasket_dev)
- return;
-
- pr_info("remove %s device %s\n", internal_desc->driver_desc->name,
- gasket_dev->kobj_name);
-
- gasket_disable_dev(gasket_dev);
- gasket_cleanup_pci(gasket_dev);
-
- check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
-
- check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
-
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
- gasket_free_dev(gasket_dev);
-}
-
-/*
- * Setup PCI & set up memory mapping for the specified device.
- *
- * Enables the PCI device, reads the BAR registers and sets up pointers to the
- * device's memory mapped IO space.
- *
- * Returns 0 on success and a negative value otherwise.
- */
-static int gasket_setup_pci(
- struct pci_dev *pci_dev, struct gasket_dev *gasket_dev)
-{
- int i, mapped_bars, ret;
-
- ret = pci_enable_device(pci_dev);
- if (ret) {
- dev_err(gasket_dev->dev, "cannot enable PCI device\n");
- return ret;
- }
-
- pci_set_master(pci_dev);
-
- for (i = 0; i < GASKET_NUM_BARS; i++) {
- ret = gasket_map_pci_bar(gasket_dev, i);
- if (ret) {
- mapped_bars = i;
- goto fail;
- }
- }
-
- return 0;
-
-fail:
- for (i = 0; i < mapped_bars; i++)
- gasket_unmap_pci_bar(gasket_dev, i);
-
- pci_disable_device(pci_dev);
- return -ENOMEM;
-}
-
-/* Unmaps memory and cleans up PCI for the specified device. */
-static void gasket_cleanup_pci(struct gasket_dev *gasket_dev)
-{
- int i;
-
- for (i = 0; i < GASKET_NUM_BARS; i++)
- gasket_unmap_pci_bar(gasket_dev, i);
-
- pci_disable_device(gasket_dev->pci_dev);
-}
-
-/*
- * Maps the specified bar into kernel space.
- *
- * Returns 0 on success, a negative error code otherwise.
- * A zero-sized BAR will not be mapped, but is not an error.
- */
-static int gasket_map_pci_bar(struct gasket_dev *gasket_dev, int bar_num)
-{
- struct gasket_internal_desc *internal_desc = gasket_dev->internal_desc;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
- ulong desc_bytes = driver_desc->bar_descriptions[bar_num].size;
- int ret;
-
- if (desc_bytes == 0)
- return 0;
+ if (desc_bytes == 0)
+ return 0;

if (driver_desc->bar_descriptions[bar_num].type != PCI_BAR) {
/* not PCI: skip this entry */
@@ -826,320 +363,329 @@ static void gasket_unmap_pci_bar(struct gasket_dev *dev, int bar_num)
release_mem_region(base, bytes);
}

-/* Add a char device and related info. */
-static int gasket_add_cdev(
- struct gasket_cdev_info *dev_info,
- const struct file_operations *file_ops, struct module *owner)
-{
- int ret;
-
- cdev_init(&dev_info->cdev, file_ops);
- dev_info->cdev.owner = owner;
- ret = cdev_add(&dev_info->cdev, dev_info->devt, 1);
- if (ret) {
- dev_err(dev_info->gasket_dev_ptr->dev,
- "cannot add char device [ret=%d]\n", ret);
- return ret;
- }
- dev_info->cdev_added = 1;
-
- return 0;
-}
-
-/* Perform final init and marks the device as active. */
-static int gasket_enable_dev(
- struct gasket_internal_desc *internal_desc,
- struct gasket_dev *gasket_dev)
+/*
+ * Setup PCI & set up memory mapping for the specified device.
+ *
+ * Enables the PCI device, reads the BAR registers and sets up pointers to the
+ * device's memory mapped IO space.
+ *
+ * Returns 0 on success and a negative value otherwise.
+ */
+static int gasket_setup_pci(
+ struct pci_dev *pci_dev, struct gasket_dev *gasket_dev)
{
- int tbl_idx;
- int ret;
- const struct gasket_driver_desc *driver_desc =
- internal_desc->driver_desc;
+ int i, mapped_bars, ret;

- ret = gasket_interrupt_init(
- gasket_dev, driver_desc->name,
- driver_desc->interrupt_type, driver_desc->interrupts,
- driver_desc->num_interrupts, driver_desc->interrupt_pack_width,
- driver_desc->interrupt_bar_index,
- driver_desc->wire_interrupt_offsets);
+ ret = pci_enable_device(pci_dev);
if (ret) {
- dev_err(gasket_dev->dev,
- "Critical failure to allocate interrupts: %d\n", ret);
- gasket_interrupt_cleanup(gasket_dev);
+ dev_err(gasket_dev->dev, "cannot enable PCI device\n");
return ret;
}

- for (tbl_idx = 0; tbl_idx < driver_desc->num_page_tables; tbl_idx++) {
- dev_dbg(gasket_dev->dev, "Initializing page table %d.\n",
- tbl_idx);
- ret = gasket_page_table_init(
- &gasket_dev->page_table[tbl_idx],
- &gasket_dev->bar_data[
- driver_desc->page_table_bar_index],
- &driver_desc->page_table_configs[tbl_idx],
- gasket_dev->dev, gasket_dev->pci_dev);
+ pci_set_master(pci_dev);
+
+ for (i = 0; i < GASKET_NUM_BARS; i++) {
+ ret = gasket_map_pci_bar(gasket_dev, i);
if (ret) {
- dev_err(gasket_dev->dev,
- "Couldn't init page table %d: %d\n",
- tbl_idx, ret);
- return ret;
+ mapped_bars = i;
+ goto fail;
}
- /*
- * Make sure that the page table is clear and set to simple
- * addresses.
- */
- gasket_page_table_reset(gasket_dev->page_table[tbl_idx]);
}

- /*
- * hardware_revision_cb returns a positive integer (the rev) if
- * successful.)
- */
- ret = check_and_invoke_callback(
- gasket_dev, driver_desc->hardware_revision_cb);
- if (ret < 0) {
- dev_err(gasket_dev->dev,
- "Error getting hardware revision: %d\n", ret);
- return ret;
- }
- gasket_dev->hardware_revision = ret;
+ return 0;

- ret = check_and_invoke_callback(gasket_dev, driver_desc->enable_dev_cb);
- if (ret) {
- dev_err(gasket_dev->dev, "Error in enable device cb: %d\n",
- ret);
- return ret;
- }
+fail:
+ for (i = 0; i < mapped_bars; i++)
+ gasket_unmap_pci_bar(gasket_dev, i);

- /* device_status_cb returns a device status, not an error code. */
- gasket_dev->status = gasket_get_hw_status(gasket_dev);
- if (gasket_dev->status == GASKET_STATUS_DEAD)
- dev_err(gasket_dev->dev, "Device reported as unhealthy.\n");
+ pci_disable_device(pci_dev);
+ return -ENOMEM;
+}

- ret = gasket_add_cdev(
- &gasket_dev->dev_info, &gasket_file_ops, driver_desc->module);
- if (ret)
- return ret;
+/* Unmaps memory and cleans up PCI for the specified device. */
+static void gasket_cleanup_pci(struct gasket_dev *gasket_dev)
+{
+ int i;

- return 0;
+ for (i = 0; i < GASKET_NUM_BARS; i++)
+ gasket_unmap_pci_bar(gasket_dev, i);
+
+ pci_disable_device(gasket_dev->pci_dev);
}

-/* Disable device operations. */
-static void gasket_disable_dev(struct gasket_dev *gasket_dev)
+/* Determine the health of the Gasket device. */
+static int gasket_get_hw_status(struct gasket_dev *gasket_dev)
{
+ int status;
+ int i;
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
- int i;
-
- /* Only delete the device if it has been successfully added. */
- if (gasket_dev->dev_info.cdev_added)
- cdev_del(&gasket_dev->dev_info.cdev);

- gasket_dev->status = GASKET_STATUS_DEAD;
+ status = gasket_check_and_invoke_callback_nolock(
+ gasket_dev, driver_desc->device_status_cb);
+ if (status != GASKET_STATUS_ALIVE) {
+ dev_dbg(gasket_dev->dev, "Hardware reported status %d.\n",
+ status);
+ return status;
+ }

- gasket_interrupt_cleanup(gasket_dev);
+ status = gasket_interrupt_system_status(gasket_dev);
+ if (status != GASKET_STATUS_ALIVE) {
+ dev_dbg(gasket_dev->dev,
+ "Interrupt system reported status %d.\n", status);
+ return status;
+ }

for (i = 0; i < driver_desc->num_page_tables; ++i) {
- if (gasket_dev->page_table[i]) {
- gasket_page_table_reset(gasket_dev->page_table[i]);
- gasket_page_table_cleanup(gasket_dev->page_table[i]);
+ status = gasket_page_table_system_status(
+ gasket_dev->page_table[i]);
+ if (status != GASKET_STATUS_ALIVE) {
+ dev_dbg(gasket_dev->dev,
+ "Page table %d reported status %d.\n",
+ i, status);
+ return status;
}
}

- check_and_invoke_callback(gasket_dev, driver_desc->disable_dev_cb);
+ return GASKET_STATUS_ALIVE;
}

-/*
- * Registered descriptor lookup.
- *
- * Precondition: Called with g_mutex held (to avoid a race on return).
- * Returns NULL if no matching device was found.
- */
-static struct gasket_internal_desc *lookup_internal_desc(
- struct pci_dev *pci_dev)
+static ssize_t gasket_write_mappable_regions(
+ char *buf, const struct gasket_driver_desc *driver_desc, int bar_index)
{
int i;
+ ssize_t written;
+ ssize_t total_written = 0;
+ ulong min_addr, max_addr;
+ struct gasket_bar_desc bar_desc =
+ driver_desc->bar_descriptions[bar_index];

- __must_hold(&g_mutex);
- for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
- if (g_descs[i].driver_desc &&
- g_descs[i].driver_desc->pci_id_table &&
- pci_match_id(g_descs[i].driver_desc->pci_id_table, pci_dev))
- return &g_descs[i];
+ if (bar_desc.permissions == GASKET_NOMAP)
+ return 0;
+ for (i = 0;
+ i < bar_desc.num_mappable_regions && total_written < PAGE_SIZE;
+ i++) {
+ min_addr = bar_desc.mappable_regions[i].start -
+ driver_desc->legacy_mmap_address_offset;
+ max_addr = bar_desc.mappable_regions[i].start -
+ driver_desc->legacy_mmap_address_offset +
+ bar_desc.mappable_regions[i].length_bytes;
+ written = scnprintf(buf, PAGE_SIZE - total_written,
+ "0x%08lx-0x%08lx\n", min_addr, max_addr);
+ total_written += written;
+ buf += written;
}
-
- return NULL;
+ return total_written;
}

-/**
- * Lookup a name by number in a num_name table.
- * @num: Number to lookup.
- * @table: Array of num_name structures, the table for the lookup.
- *
- * Description: Searches for num in the table. If found, the
- * corresponding name is returned; otherwise NULL
- * is returned.
- *
- * The table must have a NULL name pointer at the end.
- */
-const char *gasket_num_name_lookup(
- uint num, const struct gasket_num_name *table)
+static ssize_t gasket_sysfs_data_show(
+ struct device *device, struct device_attribute *attr, char *buf)
{
- uint i = 0;
+ int i, ret = 0;
+ ssize_t current_written = 0;
+ const struct gasket_driver_desc *driver_desc;
+ struct gasket_dev *gasket_dev;
+ struct gasket_sysfs_attribute *gasket_attr;
+ const struct gasket_bar_desc *bar_desc;
+ enum gasket_sysfs_attribute_type sysfs_type;

- while (table[i].snn_name) {
- if (num == table[i].snn_num)
- break;
- ++i;
+ gasket_dev = gasket_sysfs_get_device_data(device);
+ if (!gasket_dev) {
+ dev_err(device, "No sysfs mapping found for device\n");
+ return 0;
}

- return table[i].snn_name;
-}
-EXPORT_SYMBOL(gasket_num_name_lookup);
-
-/*
- * Open the char device file.
- *
- * If the open is for writing, and the device is not owned, this process becomes
- * the owner. If the open is for writing and the device is already owned by
- * some other process, it is an error. If this process is the owner, increment
- * the open count.
- *
- * Returns 0 if successful, a negative error number otherwise.
- */
-static int gasket_open(struct inode *inode, struct file *filp)
-{
- int ret;
- struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc;
- struct gasket_ownership *ownership;
- char task_name[TASK_COMM_LEN];
- struct gasket_cdev_info *dev_info =
- container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
- struct pid_namespace *pid_ns = task_active_pid_ns(current);
- int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
+ gasket_attr = gasket_sysfs_get_attr(device, attr);
+ if (!gasket_attr) {
+ dev_err(device, "No sysfs attr found for device\n");
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return 0;
+ }

- gasket_dev = dev_info->gasket_dev_ptr;
driver_desc = gasket_dev->internal_desc->driver_desc;
- ownership = &dev_info->ownership;
- get_task_comm(task_name, current);
- filp->private_data = gasket_dev;
- inode->i_size = 0;
-
- dev_dbg(gasket_dev->dev,
- "Attempting to open with tgid %u (%s) (f_mode: 0%03o, "
- "fmode_write: %d is_root: %u)\n",
- current->tgid, task_name, filp->f_mode,
- (filp->f_mode & FMODE_WRITE), is_root);

- /* Always allow non-writing accesses. */
- if (!(filp->f_mode & FMODE_WRITE)) {
- dev_dbg(gasket_dev->dev, "Allowing read-only opening.\n");
- return 0;
+ sysfs_type =
+ (enum gasket_sysfs_attribute_type)gasket_attr->data.attr_type;
+ switch (sysfs_type) {
+ case ATTR_BAR_OFFSETS:
+ for (i = 0; i < GASKET_NUM_BARS; i++) {
+ bar_desc = &driver_desc->bar_descriptions[i];
+ if (bar_desc->size == 0)
+ continue;
+ current_written =
+ snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
+ (ulong)bar_desc->base);
+ buf += current_written;
+ ret += current_written;
+ }
+ break;
+ case ATTR_BAR_SIZES:
+ for (i = 0; i < GASKET_NUM_BARS; i++) {
+ bar_desc = &driver_desc->bar_descriptions[i];
+ if (bar_desc->size == 0)
+ continue;
+ current_written =
+ snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
+ (ulong)bar_desc->size);
+ buf += current_written;
+ ret += current_written;
+ }
+ break;
+ case ATTR_DRIVER_VERSION:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%s\n",
+ gasket_dev->internal_desc->driver_desc->driver_version);
+ break;
+ case ATTR_FRAMEWORK_VERSION:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%s\n", GASKET_FRAMEWORK_VERSION);
+ break;
+ case ATTR_DEVICE_TYPE:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%s\n",
+ gasket_dev->internal_desc->driver_desc->name);
+ break;
+ case ATTR_HARDWARE_REVISION:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%d\n", gasket_dev->hardware_revision);
+ break;
+ case ATTR_PCI_ADDRESS:
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", gasket_dev->kobj_name);
+ break;
+ case ATTR_STATUS:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%s\n",
+ gasket_num_name_lookup(
+ gasket_dev->status, gasket_status_name_table));
+ break;
+ case ATTR_IS_DEVICE_OWNED:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%d\n",
+ gasket_dev->dev_info.ownership.is_owned);
+ break;
+ case ATTR_DEVICE_OWNER:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%d\n",
+ gasket_dev->dev_info.ownership.owner);
+ break;
+ case ATTR_WRITE_OPEN_COUNT:
+ ret = snprintf(
+ buf, PAGE_SIZE, "%d\n",
+ gasket_dev->dev_info.ownership.write_open_count);
+ break;
+ case ATTR_RESET_COUNT:
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", gasket_dev->reset_count);
+ break;
+ case ATTR_USER_MEM_RANGES:
+ for (i = 0; i < GASKET_NUM_BARS; ++i) {
+ current_written = gasket_write_mappable_regions(
+ buf, driver_desc, i);
+ buf += current_written;
+ ret += current_written;
+ }
+ break;
+ default:
+ dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
+ attr->attr.name);
+ ret = 0;
+ break;
}

- mutex_lock(&gasket_dev->mutex);
+ gasket_sysfs_put_attr(device, gasket_attr);
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return ret;
+}

- dev_dbg(gasket_dev->dev,
- "Current owner open count (owning tgid %u): %d.\n",
- ownership->owner, ownership->write_open_count);
+/* These attributes apply to all Gasket driver instances. */
+static const struct gasket_sysfs_attribute gasket_sysfs_generic_attrs[] = {
+ GASKET_SYSFS_RO(bar_offsets, gasket_sysfs_data_show, ATTR_BAR_OFFSETS),
+ GASKET_SYSFS_RO(bar_sizes, gasket_sysfs_data_show, ATTR_BAR_SIZES),
+ GASKET_SYSFS_RO(driver_version, gasket_sysfs_data_show,
+ ATTR_DRIVER_VERSION),
+ GASKET_SYSFS_RO(framework_version, gasket_sysfs_data_show,
+ ATTR_FRAMEWORK_VERSION),
+ GASKET_SYSFS_RO(device_type, gasket_sysfs_data_show, ATTR_DEVICE_TYPE),
+ GASKET_SYSFS_RO(revision, gasket_sysfs_data_show,
+ ATTR_HARDWARE_REVISION),
+ GASKET_SYSFS_RO(pci_address, gasket_sysfs_data_show, ATTR_PCI_ADDRESS),
+ GASKET_SYSFS_RO(status, gasket_sysfs_data_show, ATTR_STATUS),
+ GASKET_SYSFS_RO(is_device_owned, gasket_sysfs_data_show,
+ ATTR_IS_DEVICE_OWNED),
+ GASKET_SYSFS_RO(device_owner, gasket_sysfs_data_show,
+ ATTR_DEVICE_OWNER),
+ GASKET_SYSFS_RO(write_open_count, gasket_sysfs_data_show,
+ ATTR_WRITE_OPEN_COUNT),
+ GASKET_SYSFS_RO(reset_count, gasket_sysfs_data_show, ATTR_RESET_COUNT),
+ GASKET_SYSFS_RO(user_mem_ranges, gasket_sysfs_data_show,
+ ATTR_USER_MEM_RANGES),
+ GASKET_END_OF_ATTR_ARRAY
+};

- /* Opening a node owned by another TGID is an error (unless root) */
- if (ownership->is_owned && ownership->owner != current->tgid &&
- !is_root) {
- dev_err(gasket_dev->dev,
- "Process %u is opening a node held by %u.\n",
- current->tgid, ownership->owner);
- mutex_unlock(&gasket_dev->mutex);
- return -EPERM;
- }
+/* Add a char device and related info. */
+static int gasket_add_cdev(
+ struct gasket_cdev_info *dev_info,
+ const struct file_operations *file_ops, struct module *owner)
+{
+ int ret;

- /* If the node is not owned, assign it to the current TGID. */
- if (!ownership->is_owned) {
- ret = gasket_check_and_invoke_callback_nolock(
- gasket_dev, driver_desc->device_open_cb);
- if (ret) {
- dev_err(gasket_dev->dev,
- "Error in device open cb: %d\n", ret);
- mutex_unlock(&gasket_dev->mutex);
- return ret;
- }
- ownership->is_owned = 1;
- ownership->owner = current->tgid;
- dev_dbg(gasket_dev->dev, "Device owner is now tgid %u\n",
- ownership->owner);
+ cdev_init(&dev_info->cdev, file_ops);
+ dev_info->cdev.owner = owner;
+ ret = cdev_add(&dev_info->cdev, dev_info->devt, 1);
+ if (ret) {
+ dev_err(dev_info->gasket_dev_ptr->dev,
+ "cannot add char device [ret=%d]\n", ret);
+ return ret;
}
+ dev_info->cdev_added = 1;

- ownership->write_open_count++;
-
- dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
-
- mutex_unlock(&gasket_dev->mutex);
return 0;
}

-/*
- * Called on a close of the device file. If this process is the owner,
- * decrement the open count. On last close by the owner, free up buffers and
- * eventfd contexts, and release ownership.
- *
- * Returns 0 if successful, a negative error number otherwise.
- */
-static int gasket_release(struct inode *inode, struct file *file)
+/* Disable device operations. */
+static void gasket_disable_dev(struct gasket_dev *gasket_dev)
{
+ const struct gasket_driver_desc *driver_desc =
+ gasket_dev->internal_desc->driver_desc;
int i;
- struct gasket_dev *gasket_dev;
- struct gasket_ownership *ownership;
- const struct gasket_driver_desc *driver_desc;
- char task_name[TASK_COMM_LEN];
- struct gasket_cdev_info *dev_info =
- container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
- struct pid_namespace *pid_ns = task_active_pid_ns(current);
- int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);

- gasket_dev = dev_info->gasket_dev_ptr;
- driver_desc = gasket_dev->internal_desc->driver_desc;
- ownership = &dev_info->ownership;
- get_task_comm(task_name, current);
- mutex_lock(&gasket_dev->mutex);
+ /* Only delete the device if it has been successfully added. */
+ if (gasket_dev->dev_info.cdev_added)
+ cdev_del(&gasket_dev->dev_info.cdev);

- dev_dbg(gasket_dev->dev,
- "Releasing device node. Call origin: tgid %u (%s) "
- "(f_mode: 0%03o, fmode_write: %d, is_root: %u)\n",
- current->tgid, task_name, file->f_mode,
- (file->f_mode & FMODE_WRITE), is_root);
- dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
+ gasket_dev->status = GASKET_STATUS_DEAD;

- if (file->f_mode & FMODE_WRITE) {
- ownership->write_open_count--;
- if (ownership->write_open_count == 0) {
- dev_dbg(gasket_dev->dev, "Device is now free\n");
- ownership->is_owned = 0;
- ownership->owner = 0;
+ gasket_interrupt_cleanup(gasket_dev);

- /* Forces chip reset before we unmap the page tables. */
- driver_desc->device_reset_cb(gasket_dev, 0);
+ for (i = 0; i < driver_desc->num_page_tables; ++i) {
+ if (gasket_dev->page_table[i]) {
+ gasket_page_table_reset(gasket_dev->page_table[i]);
+ gasket_page_table_cleanup(gasket_dev->page_table[i]);
+ }
+ }

- for (i = 0; i < driver_desc->num_page_tables; ++i) {
- gasket_page_table_unmap_all(
- gasket_dev->page_table[i]);
- gasket_page_table_garbage_collect(
- gasket_dev->page_table[i]);
- gasket_free_coherent_memory_all(gasket_dev, i);
- }
+ check_and_invoke_callback(gasket_dev, driver_desc->disable_dev_cb);
+}

- /* Closes device, enters power save. */
- gasket_check_and_invoke_callback_nolock(
- gasket_dev, driver_desc->device_close_cb);
- }
+/*
+ * Registered descriptor lookup.
+ *
+ * Precondition: Called with g_mutex held (to avoid a race on return).
+ * Returns NULL if no matching device was found.
+ */
+static struct gasket_internal_desc *lookup_internal_desc(
+ struct pci_dev *pci_dev)
+{
+ int i;
+
+ __must_hold(&g_mutex);
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ if (g_descs[i].driver_desc &&
+ g_descs[i].driver_desc->pci_id_table &&
+ pci_match_id(g_descs[i].driver_desc->pci_id_table, pci_dev))
+ return &g_descs[i];
}

- dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
- ownership->owner, ownership->write_open_count);
- mutex_unlock(&gasket_dev->mutex);
- return 0;
+ return NULL;
}

/*
@@ -1301,12 +847,42 @@ static bool gasket_mm_get_mapping_addrs(
return false;
}

-int gasket_mm_unmap_region(
- const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
- const struct gasket_mappable_region *map_region)
-{
- ulong bar_offset;
- ulong virt_offset;
+/*
+ * Calculates the offset where the VMA range begins in its containing BAR.
+ * The offset is written into bar_offset on success.
+ * Returns zero on success, anything else on error.
+ */
+static int gasket_mm_vma_bar_offset(
+ const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
+ ulong *bar_offset)
+{
+ ulong raw_offset;
+ int bar_index;
+ const struct gasket_driver_desc *driver_desc =
+ gasket_dev->internal_desc->driver_desc;
+
+ raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
+ driver_desc->legacy_mmap_address_offset;
+ bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
+ if (bar_index < 0) {
+ dev_err(gasket_dev->dev,
+ "Unable to find matching bar for address 0x%lx\n",
+ raw_offset);
+ trace_gasket_mmap_exit(bar_index);
+ return bar_index;
+ }
+ *bar_offset =
+ raw_offset - driver_desc->bar_descriptions[bar_index].base;
+
+ return 0;
+}
+
+int gasket_mm_unmap_region(
+ const struct gasket_dev *gasket_dev, struct vm_area_struct *vma,
+ const struct gasket_mappable_region *map_region)
+{
+ ulong bar_offset;
+ ulong virt_offset;
struct gasket_mappable_region mappable_region;
int ret;

@@ -1407,36 +983,6 @@ static enum do_map_region_status do_map_region(
return DO_MAP_REGION_FAILURE;
}

-/*
- * Calculates the offset where the VMA range begins in its containing BAR.
- * The offset is written into bar_offset on success.
- * Returns zero on success, anything else on error.
- */
-static int gasket_mm_vma_bar_offset(
- const struct gasket_dev *gasket_dev, const struct vm_area_struct *vma,
- ulong *bar_offset)
-{
- ulong raw_offset;
- int bar_index;
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
-
- raw_offset = (vma->vm_pgoff << PAGE_SHIFT) +
- driver_desc->legacy_mmap_address_offset;
- bar_index = gasket_get_bar_index(gasket_dev, raw_offset);
- if (bar_index < 0) {
- dev_err(gasket_dev->dev,
- "Unable to find matching bar for address 0x%lx\n",
- raw_offset);
- trace_gasket_mmap_exit(bar_index);
- return bar_index;
- }
- *bar_offset =
- raw_offset - driver_desc->bar_descriptions[bar_index].base;
-
- return 0;
-}
-
/* Map a region of coherent memory. */
static int gasket_mmap_coherent(
struct gasket_dev *gasket_dev, struct vm_area_struct *vma)
@@ -1626,41 +1172,149 @@ static int gasket_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}

-/* Determine the health of the Gasket device. */
-static int gasket_get_hw_status(struct gasket_dev *gasket_dev)
+/*
+ * Open the char device file.
+ *
+ * If the open is for writing, and the device is not owned, this process becomes
+ * the owner. If the open is for writing and the device is already owned by
+ * some other process, it is an error. If this process is the owner, increment
+ * the open count.
+ *
+ * Returns 0 if successful, a negative error number otherwise.
+ */
+static int gasket_open(struct inode *inode, struct file *filp)
{
- int status;
- int i;
- const struct gasket_driver_desc *driver_desc =
- gasket_dev->internal_desc->driver_desc;
+ int ret;
+ struct gasket_dev *gasket_dev;
+ const struct gasket_driver_desc *driver_desc;
+ struct gasket_ownership *ownership;
+ char task_name[TASK_COMM_LEN];
+ struct gasket_cdev_info *dev_info =
+ container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
+ struct pid_namespace *pid_ns = task_active_pid_ns(current);
+ int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);

- status = gasket_check_and_invoke_callback_nolock(
- gasket_dev, driver_desc->device_status_cb);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev, "Hardware reported status %d.\n",
- status);
- return status;
+ gasket_dev = dev_info->gasket_dev_ptr;
+ driver_desc = gasket_dev->internal_desc->driver_desc;
+ ownership = &dev_info->ownership;
+ get_task_comm(task_name, current);
+ filp->private_data = gasket_dev;
+ inode->i_size = 0;
+
+ dev_dbg(gasket_dev->dev,
+ "Attempting to open with tgid %u (%s) (f_mode: 0%03o, "
+ "fmode_write: %d is_root: %u)\n",
+ current->tgid, task_name, filp->f_mode,
+ (filp->f_mode & FMODE_WRITE), is_root);
+
+ /* Always allow non-writing accesses. */
+ if (!(filp->f_mode & FMODE_WRITE)) {
+ dev_dbg(gasket_dev->dev, "Allowing read-only opening.\n");
+ return 0;
}

- status = gasket_interrupt_system_status(gasket_dev);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev,
- "Interrupt system reported status %d.\n", status);
- return status;
+ mutex_lock(&gasket_dev->mutex);
+
+ dev_dbg(gasket_dev->dev,
+ "Current owner open count (owning tgid %u): %d.\n",
+ ownership->owner, ownership->write_open_count);
+
+ /* Opening a node owned by another TGID is an error (unless root) */
+ if (ownership->is_owned && ownership->owner != current->tgid &&
+ !is_root) {
+ dev_err(gasket_dev->dev,
+ "Process %u is opening a node held by %u.\n",
+ current->tgid, ownership->owner);
+ mutex_unlock(&gasket_dev->mutex);
+ return -EPERM;
}

- for (i = 0; i < driver_desc->num_page_tables; ++i) {
- status = gasket_page_table_system_status(
- gasket_dev->page_table[i]);
- if (status != GASKET_STATUS_ALIVE) {
- dev_dbg(gasket_dev->dev,
- "Page table %d reported status %d.\n",
- i, status);
- return status;
+ /* If the node is not owned, assign it to the current TGID. */
+ if (!ownership->is_owned) {
+ ret = gasket_check_and_invoke_callback_nolock(
+ gasket_dev, driver_desc->device_open_cb);
+ if (ret) {
+ dev_err(gasket_dev->dev,
+ "Error in device open cb: %d\n", ret);
+ mutex_unlock(&gasket_dev->mutex);
+ return ret;
}
+ ownership->is_owned = 1;
+ ownership->owner = current->tgid;
+ dev_dbg(gasket_dev->dev, "Device owner is now tgid %u\n",
+ ownership->owner);
}

- return GASKET_STATUS_ALIVE;
+ ownership->write_open_count++;
+
+ dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
+ ownership->owner, ownership->write_open_count);
+
+ mutex_unlock(&gasket_dev->mutex);
+ return 0;
+}
+
+/*
+ * Called on a close of the device file. If this process is the owner,
+ * decrement the open count. On last close by the owner, free up buffers and
+ * eventfd contexts, and release ownership.
+ *
+ * Returns 0 if successful, a negative error number otherwise.
+ */
+static int gasket_release(struct inode *inode, struct file *file)
+{
+ int i;
+ struct gasket_dev *gasket_dev;
+ struct gasket_ownership *ownership;
+ const struct gasket_driver_desc *driver_desc;
+ char task_name[TASK_COMM_LEN];
+ struct gasket_cdev_info *dev_info =
+ container_of(inode->i_cdev, struct gasket_cdev_info, cdev);
+ struct pid_namespace *pid_ns = task_active_pid_ns(current);
+ int is_root = ns_capable(pid_ns->user_ns, CAP_SYS_ADMIN);
+
+ gasket_dev = dev_info->gasket_dev_ptr;
+ driver_desc = gasket_dev->internal_desc->driver_desc;
+ ownership = &dev_info->ownership;
+ get_task_comm(task_name, current);
+ mutex_lock(&gasket_dev->mutex);
+
+ dev_dbg(gasket_dev->dev,
+ "Releasing device node. Call origin: tgid %u (%s) "
+ "(f_mode: 0%03o, fmode_write: %d, is_root: %u)\n",
+ current->tgid, task_name, file->f_mode,
+ (file->f_mode & FMODE_WRITE), is_root);
+ dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n",
+ ownership->owner, ownership->write_open_count);
+
+ if (file->f_mode & FMODE_WRITE) {
+ ownership->write_open_count--;
+ if (ownership->write_open_count == 0) {
+ dev_dbg(gasket_dev->dev, "Device is now free\n");
+ ownership->is_owned = 0;
+ ownership->owner = 0;
+
+ /* Forces chip reset before we unmap the page tables. */
+ driver_desc->device_reset_cb(gasket_dev, 0);
+
+ for (i = 0; i < driver_desc->num_page_tables; ++i) {
+ gasket_page_table_unmap_all(
+ gasket_dev->page_table[i]);
+ gasket_page_table_garbage_collect(
+ gasket_dev->page_table[i]);
+ gasket_free_coherent_memory_all(gasket_dev, i);
+ }
+
+ /* Closes device, enters power save. */
+ gasket_check_and_invoke_callback_nolock(
+ gasket_dev, driver_desc->device_close_cb);
+ }
+ }
+
+ dev_dbg(gasket_dev->dev, "New open count (owning tgid %u): %d\n",
+ ownership->owner, ownership->write_open_count);
+ mutex_unlock(&gasket_dev->mutex);
+ return 0;
}

/*
@@ -1702,209 +1356,333 @@ static long gasket_ioctl(struct file *filp, uint cmd, ulong arg)
return gasket_handle_ioctl(filp, cmd, argp);
}

-int gasket_reset(struct gasket_dev *gasket_dev, uint reset_type)
-{
- int ret;
-
- mutex_lock(&gasket_dev->mutex);
- ret = gasket_reset_nolock(gasket_dev, reset_type);
- mutex_unlock(&gasket_dev->mutex);
- return ret;
-}
-EXPORT_SYMBOL(gasket_reset);
+/* File operations for all Gasket devices. */
+static const struct file_operations gasket_file_ops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .mmap = gasket_mmap,
+ .open = gasket_open,
+ .release = gasket_release,
+ .unlocked_ioctl = gasket_ioctl,
+};

-int gasket_reset_nolock(struct gasket_dev *gasket_dev, uint reset_type)
+/* Perform final init and marks the device as active. */
+static int gasket_enable_dev(
+ struct gasket_internal_desc *internal_desc,
+ struct gasket_dev *gasket_dev)
{
+ int tbl_idx;
int ret;
- int i;
- const struct gasket_driver_desc *driver_desc;
+ const struct gasket_driver_desc *driver_desc =
+ internal_desc->driver_desc;

- driver_desc = gasket_dev->internal_desc->driver_desc;
- if (!driver_desc->device_reset_cb)
- return 0;
-
- /* Perform a device reset of the requested type. */
- ret = driver_desc->device_reset_cb(gasket_dev, reset_type);
+ ret = gasket_interrupt_init(
+ gasket_dev, driver_desc->name,
+ driver_desc->interrupt_type, driver_desc->interrupts,
+ driver_desc->num_interrupts, driver_desc->interrupt_pack_width,
+ driver_desc->interrupt_bar_index,
+ driver_desc->wire_interrupt_offsets);
if (ret) {
- dev_dbg(gasket_dev->dev, "Device reset cb returned %d.\n",
- ret);
+ dev_err(gasket_dev->dev,
+ "Critical failure to allocate interrupts: %d\n", ret);
+ gasket_interrupt_cleanup(gasket_dev);
return ret;
}

- /* Reinitialize the page tables and interrupt framework. */
- for (i = 0; i < driver_desc->num_page_tables; ++i)
- gasket_page_table_reset(gasket_dev->page_table[i]);
+ for (tbl_idx = 0; tbl_idx < driver_desc->num_page_tables; tbl_idx++) {
+ dev_dbg(gasket_dev->dev, "Initializing page table %d.\n",
+ tbl_idx);
+ ret = gasket_page_table_init(
+ &gasket_dev->page_table[tbl_idx],
+ &gasket_dev->bar_data[
+ driver_desc->page_table_bar_index],
+ &driver_desc->page_table_configs[tbl_idx],
+ gasket_dev->dev, gasket_dev->pci_dev);
+ if (ret) {
+ dev_err(gasket_dev->dev,
+ "Couldn't init page table %d: %d\n",
+ tbl_idx, ret);
+ return ret;
+ }
+ /*
+ * Make sure that the page table is clear and set to simple
+ * addresses.
+ */
+ gasket_page_table_reset(gasket_dev->page_table[tbl_idx]);
+ }

- ret = gasket_interrupt_reinit(gasket_dev);
+ /*
+ * hardware_revision_cb returns a positive integer (the rev) if
+ * successful.)
+ */
+ ret = check_and_invoke_callback(
+ gasket_dev, driver_desc->hardware_revision_cb);
+ if (ret < 0) {
+ dev_err(gasket_dev->dev,
+ "Error getting hardware revision: %d\n", ret);
+ return ret;
+ }
+ gasket_dev->hardware_revision = ret;
+
+ ret = check_and_invoke_callback(gasket_dev, driver_desc->enable_dev_cb);
if (ret) {
- dev_dbg(gasket_dev->dev, "Unable to reinit interrupts: %d.\n",
+ dev_err(gasket_dev->dev, "Error in enable device cb: %d\n",
ret);
return ret;
}

- /* Get current device health. */
+ /* device_status_cb returns a device status, not an error code. */
gasket_dev->status = gasket_get_hw_status(gasket_dev);
- if (gasket_dev->status == GASKET_STATUS_DEAD) {
- dev_dbg(gasket_dev->dev, "Device reported as dead.\n");
- return -EINVAL;
- }
+ if (gasket_dev->status == GASKET_STATUS_DEAD)
+ dev_err(gasket_dev->dev, "Device reported as unhealthy.\n");
+
+ ret = gasket_add_cdev(
+ &gasket_dev->dev_info, &gasket_file_ops, driver_desc->module);
+ if (ret)
+ return ret;

return 0;
}
-EXPORT_SYMBOL(gasket_reset_nolock);

-gasket_ioctl_permissions_cb_t gasket_get_ioctl_permissions_cb(
- struct gasket_dev *gasket_dev)
+/*
+ * PCI subsystem probe function.
+ *
+ * Called when a Gasket device is found. Allocates device metadata, maps device
+ * memory, and calls gasket_enable_dev to prepare the device for active use.
+ *
+ * Returns 0 if successful and a negative value otherwise.
+ */
+static int gasket_pci_probe(
+ struct pci_dev *pci_dev, const struct pci_device_id *id)
{
- return gasket_dev->internal_desc->driver_desc->ioctl_permissions_cb;
-}
-EXPORT_SYMBOL(gasket_get_ioctl_permissions_cb);
+ int ret;
+ const char *kobj_name = dev_name(&pci_dev->dev);
+ struct gasket_internal_desc *internal_desc;
+ struct gasket_dev *gasket_dev;
+ const struct gasket_driver_desc *driver_desc;
+ struct device *parent;

-static ssize_t gasket_write_mappable_regions(
- char *buf, const struct gasket_driver_desc *driver_desc, int bar_index)
-{
- int i;
- ssize_t written;
- ssize_t total_written = 0;
- ulong min_addr, max_addr;
- struct gasket_bar_desc bar_desc =
- driver_desc->bar_descriptions[bar_index];
+ pr_info("Add Gasket device %s\n", kobj_name);

- if (bar_desc.permissions == GASKET_NOMAP)
- return 0;
- for (i = 0;
- i < bar_desc.num_mappable_regions && total_written < PAGE_SIZE;
- i++) {
- min_addr = bar_desc.mappable_regions[i].start -
- driver_desc->legacy_mmap_address_offset;
- max_addr = bar_desc.mappable_regions[i].start -
- driver_desc->legacy_mmap_address_offset +
- bar_desc.mappable_regions[i].length_bytes;
- written = scnprintf(buf, PAGE_SIZE - total_written,
- "0x%08lx-0x%08lx\n", min_addr, max_addr);
- total_written += written;
- buf += written;
+ mutex_lock(&g_mutex);
+ internal_desc = lookup_internal_desc(pci_dev);
+ mutex_unlock(&g_mutex);
+ if (!internal_desc) {
+ pr_err("PCI probe called for unknown driver type\n");
+ return -ENODEV;
}
- return total_written;
+
+ driver_desc = internal_desc->driver_desc;
+
+ parent = &pci_dev->dev;
+ ret = gasket_alloc_dev(internal_desc, parent, &gasket_dev, kobj_name);
+ if (ret)
+ return ret;
+ gasket_dev->pci_dev = pci_dev_get(pci_dev);
+ if (IS_ERR_OR_NULL(gasket_dev->dev_info.device)) {
+ pr_err("Cannot create %s device %s [ret = %ld]\n",
+ driver_desc->name, gasket_dev->dev_info.name,
+ PTR_ERR(gasket_dev->dev_info.device));
+ ret = -ENODEV;
+ goto fail1;
+ }
+
+ ret = gasket_setup_pci(pci_dev, gasket_dev);
+ if (ret)
+ goto fail2;
+
+ ret = check_and_invoke_callback(gasket_dev, driver_desc->add_dev_cb);
+ if (ret) {
+ dev_err(gasket_dev->dev, "Error in add device cb: %d\n", ret);
+ goto fail2;
+ }
+
+ ret = gasket_sysfs_create_mapping(
+ gasket_dev->dev_info.device, gasket_dev);
+ if (ret)
+ goto fail3;
+
+ /*
+ * Once we've created the mapping structures successfully, attempt to
+ * create a symlink to the pci directory of this object.
+ */
+ ret = sysfs_create_link(&gasket_dev->dev_info.device->kobj,
+ &pci_dev->dev.kobj, dev_name(&pci_dev->dev));
+ if (ret) {
+ dev_err(gasket_dev->dev,
+ "Cannot create sysfs pci link: %d\n", ret);
+ goto fail3;
+ }
+ ret = gasket_sysfs_create_entries(
+ gasket_dev->dev_info.device, gasket_sysfs_generic_attrs);
+ if (ret)
+ goto fail4;
+
+ ret = check_and_invoke_callback(
+ gasket_dev, driver_desc->sysfs_setup_cb);
+ if (ret) {
+ dev_err(gasket_dev->dev, "Error in sysfs setup cb: %d\n", ret);
+ goto fail5;
+ }
+
+ ret = gasket_enable_dev(internal_desc, gasket_dev);
+ if (ret) {
+ pr_err("cannot setup %s device\n", driver_desc->name);
+ gasket_disable_dev(gasket_dev);
+ goto fail5;
+ }
+
+ return 0;
+
+fail5:
+ check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
+fail4:
+fail3:
+ gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
+fail2:
+ gasket_cleanup_pci(gasket_dev);
+ check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
+ device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
+fail1:
+ gasket_free_dev(gasket_dev);
+ return ret;
}

-static ssize_t gasket_sysfs_data_show(
- struct device *device, struct device_attribute *attr, char *buf)
+/*
+ * PCI subsystem remove function.
+ *
+ * Called to remove a Gasket device. Finds the device in the device list and
+ * cleans up metadata.
+ */
+static void gasket_pci_remove(struct pci_dev *pci_dev)
{
- int i, ret = 0;
- ssize_t current_written = 0;
+ int i;
+ struct gasket_internal_desc *internal_desc;
+ struct gasket_dev *gasket_dev = NULL;
const struct gasket_driver_desc *driver_desc;
- struct gasket_dev *gasket_dev;
- struct gasket_sysfs_attribute *gasket_attr;
- const struct gasket_bar_desc *bar_desc;
- enum gasket_sysfs_attribute_type sysfs_type;
-
- gasket_dev = gasket_sysfs_get_device_data(device);
- if (!gasket_dev) {
- dev_err(device, "No sysfs mapping found for device\n");
- return 0;
+ /* Find the device desc. */
+ mutex_lock(&g_mutex);
+ internal_desc = lookup_internal_desc(pci_dev);
+ if (!internal_desc) {
+ mutex_unlock(&g_mutex);
+ return;
}
+ mutex_unlock(&g_mutex);

- gasket_attr = gasket_sysfs_get_attr(device, attr);
- if (!gasket_attr) {
- dev_err(device, "No sysfs attr found for device\n");
- gasket_sysfs_put_device_data(device, gasket_dev);
- return 0;
+ driver_desc = internal_desc->driver_desc;
+
+ /* Now find the specific device */
+ mutex_lock(&internal_desc->mutex);
+ for (i = 0; i < GASKET_DEV_MAX; i++) {
+ if (internal_desc->devs[i] &&
+ internal_desc->devs[i]->pci_dev == pci_dev) {
+ gasket_dev = internal_desc->devs[i];
+ break;
+ }
}
+ mutex_unlock(&internal_desc->mutex);

- driver_desc = gasket_dev->internal_desc->driver_desc;
+ if (!gasket_dev)
+ return;

- sysfs_type =
- (enum gasket_sysfs_attribute_type)gasket_attr->data.attr_type;
- switch (sysfs_type) {
- case ATTR_BAR_OFFSETS:
- for (i = 0; i < GASKET_NUM_BARS; i++) {
- bar_desc = &driver_desc->bar_descriptions[i];
- if (bar_desc->size == 0)
- continue;
- current_written =
- snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
- (ulong)bar_desc->base);
- buf += current_written;
- ret += current_written;
- }
- break;
- case ATTR_BAR_SIZES:
- for (i = 0; i < GASKET_NUM_BARS; i++) {
- bar_desc = &driver_desc->bar_descriptions[i];
- if (bar_desc->size == 0)
- continue;
- current_written =
- snprintf(buf, PAGE_SIZE - ret, "%d: 0x%lx\n", i,
- (ulong)bar_desc->size);
- buf += current_written;
- ret += current_written;
- }
- break;
- case ATTR_DRIVER_VERSION:
- ret = snprintf(
- buf, PAGE_SIZE, "%s\n",
- gasket_dev->internal_desc->driver_desc->driver_version);
- break;
- case ATTR_FRAMEWORK_VERSION:
- ret = snprintf(
- buf, PAGE_SIZE, "%s\n", GASKET_FRAMEWORK_VERSION);
- break;
- case ATTR_DEVICE_TYPE:
- ret = snprintf(
- buf, PAGE_SIZE, "%s\n",
- gasket_dev->internal_desc->driver_desc->name);
- break;
- case ATTR_HARDWARE_REVISION:
- ret = snprintf(
- buf, PAGE_SIZE, "%d\n", gasket_dev->hardware_revision);
- break;
- case ATTR_PCI_ADDRESS:
- ret = snprintf(buf, PAGE_SIZE, "%s\n", gasket_dev->kobj_name);
- break;
- case ATTR_STATUS:
- ret = snprintf(
- buf, PAGE_SIZE, "%s\n",
- gasket_num_name_lookup(
- gasket_dev->status, gasket_status_name_table));
- break;
- case ATTR_IS_DEVICE_OWNED:
- ret = snprintf(
- buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.is_owned);
- break;
- case ATTR_DEVICE_OWNER:
- ret = snprintf(
- buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.owner);
- break;
- case ATTR_WRITE_OPEN_COUNT:
- ret = snprintf(
- buf, PAGE_SIZE, "%d\n",
- gasket_dev->dev_info.ownership.write_open_count);
- break;
- case ATTR_RESET_COUNT:
- ret = snprintf(buf, PAGE_SIZE, "%d\n", gasket_dev->reset_count);
- break;
- case ATTR_USER_MEM_RANGES:
- for (i = 0; i < GASKET_NUM_BARS; ++i) {
- current_written = gasket_write_mappable_regions(
- buf, driver_desc, i);
- buf += current_written;
- ret += current_written;
- }
- break;
- default:
- dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
- attr->attr.name);
- ret = 0;
- break;
+ pr_info("remove %s device %s\n", internal_desc->driver_desc->name,
+ gasket_dev->kobj_name);
+
+ gasket_disable_dev(gasket_dev);
+ gasket_cleanup_pci(gasket_dev);
+
+ check_and_invoke_callback(gasket_dev, driver_desc->sysfs_cleanup_cb);
+ gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
+
+ check_and_invoke_callback(gasket_dev, driver_desc->remove_dev_cb);
+
+ device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
+ gasket_free_dev(gasket_dev);
+}
+
+/**
+ * Lookup a name by number in a num_name table.
+ * @num: Number to lookup.
+ * @table: Array of num_name structures, the table for the lookup.
+ *
+ * Description: Searches for num in the table. If found, the
+ * corresponding name is returned; otherwise NULL
+ * is returned.
+ *
+ * The table must have a NULL name pointer at the end.
+ */
+const char *gasket_num_name_lookup(
+ uint num, const struct gasket_num_name *table)
+{
+ uint i = 0;
+
+ while (table[i].snn_name) {
+ if (num == table[i].snn_num)
+ break;
+ ++i;
}

- gasket_sysfs_put_attr(device, gasket_attr);
- gasket_sysfs_put_device_data(device, gasket_dev);
+ return table[i].snn_name;
+}
+EXPORT_SYMBOL(gasket_num_name_lookup);
+
+int gasket_reset(struct gasket_dev *gasket_dev, uint reset_type)
+{
+ int ret;
+
+ mutex_lock(&gasket_dev->mutex);
+ ret = gasket_reset_nolock(gasket_dev, reset_type);
+ mutex_unlock(&gasket_dev->mutex);
return ret;
}
+EXPORT_SYMBOL(gasket_reset);
+
+int gasket_reset_nolock(struct gasket_dev *gasket_dev, uint reset_type)
+{
+ int ret;
+ int i;
+ const struct gasket_driver_desc *driver_desc;
+
+ driver_desc = gasket_dev->internal_desc->driver_desc;
+ if (!driver_desc->device_reset_cb)
+ return 0;
+
+ /* Perform a device reset of the requested type. */
+ ret = driver_desc->device_reset_cb(gasket_dev, reset_type);
+ if (ret) {
+ dev_dbg(gasket_dev->dev, "Device reset cb returned %d.\n",
+ ret);
+ return ret;
+ }
+
+ /* Reinitialize the page tables and interrupt framework. */
+ for (i = 0; i < driver_desc->num_page_tables; ++i)
+ gasket_page_table_reset(gasket_dev->page_table[i]);
+
+ ret = gasket_interrupt_reinit(gasket_dev);
+ if (ret) {
+ dev_dbg(gasket_dev->dev, "Unable to reinit interrupts: %d.\n",
+ ret);
+ return ret;
+ }
+
+ /* Get current device health. */
+ gasket_dev->status = gasket_get_hw_status(gasket_dev);
+ if (gasket_dev->status == GASKET_STATUS_DEAD) {
+ dev_dbg(gasket_dev->dev, "Device reported as dead.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(gasket_reset_nolock);
+
+gasket_ioctl_permissions_cb_t gasket_get_ioctl_permissions_cb(
+ struct gasket_dev *gasket_dev)
+{
+ return gasket_dev->internal_desc->driver_desc->ioctl_permissions_cb;
+}
+EXPORT_SYMBOL(gasket_get_ioctl_permissions_cb);

/* Get the driver structure for a given gasket_dev.
* @dev: pointer to gasket_dev, implementing the requested driver.
@@ -1954,3 +1732,169 @@ int gasket_wait_with_reschedule(
return -ETIMEDOUT;
}
EXPORT_SYMBOL(gasket_wait_with_reschedule);
+
+/* See gasket_core.h for description. */
+int gasket_register_device(const struct gasket_driver_desc *driver_desc)
+{
+ int i, ret;
+ int desc_idx = -1;
+ struct gasket_internal_desc *internal;
+
+ pr_info("Initializing Gasket framework device\n");
+ /* Check for duplicates and find a free slot. */
+ mutex_lock(&g_mutex);
+
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ if (g_descs[i].driver_desc == driver_desc) {
+ pr_err("%s driver already loaded/registered\n",
+ driver_desc->name);
+ mutex_unlock(&g_mutex);
+ return -EBUSY;
+ }
+ }
+
+ /* This and the above loop could be combined, but this reads easier. */
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ if (!g_descs[i].driver_desc) {
+ g_descs[i].driver_desc = driver_desc;
+ desc_idx = i;
+ break;
+ }
+ }
+ mutex_unlock(&g_mutex);
+
+ pr_info("Loaded %s driver, framework version %s\n",
+ driver_desc->name, GASKET_FRAMEWORK_VERSION);
+
+ if (desc_idx == -1) {
+ pr_err("Too many Gasket drivers loaded: %d\n",
+ GASKET_FRAMEWORK_DESC_MAX);
+ return -EBUSY;
+ }
+
+ /* Internal structure setup. */
+ pr_debug("Performing initial internal structure setup.\n");
+ internal = &g_descs[desc_idx];
+ mutex_init(&internal->mutex);
+ memset(internal->devs, 0, sizeof(struct gasket_dev *) * GASKET_DEV_MAX);
+ memset(&internal->pci, 0, sizeof(internal->pci));
+ internal->pci.name = driver_desc->name;
+ internal->pci.id_table = driver_desc->pci_id_table;
+ internal->pci.probe = gasket_pci_probe;
+ internal->pci.remove = gasket_pci_remove;
+ internal->class =
+ class_create(driver_desc->module, driver_desc->name);
+
+ if (IS_ERR(internal->class)) {
+ pr_err("Cannot register %s class [ret=%ld]\n",
+ driver_desc->name, PTR_ERR(internal->class));
+ ret = PTR_ERR(internal->class);
+ goto unregister_gasket_driver;
+ }
+
+ /*
+ * Not using pci_register_driver() (without underscores), as it
+ * depends on KBUILD_MODNAME, and this is a shared file.
+ */
+ pr_debug("Registering PCI driver.\n");
+ ret = __pci_register_driver(
+ &internal->pci, driver_desc->module, driver_desc->name);
+ if (ret) {
+ pr_err("cannot register pci driver [ret=%d]\n", ret);
+ goto fail1;
+ }
+
+ pr_debug("Registering char driver.\n");
+ ret = register_chrdev_region(
+ MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX,
+ driver_desc->name);
+ if (ret) {
+ pr_err("cannot register char driver [ret=%d]\n", ret);
+ goto fail2;
+ }
+
+ pr_info("Driver registered successfully.\n");
+ return 0;
+
+fail2:
+ pci_unregister_driver(&internal->pci);
+
+fail1:
+ class_destroy(internal->class);
+
+unregister_gasket_driver:
+ mutex_lock(&g_mutex);
+ g_descs[desc_idx].driver_desc = NULL;
+ mutex_unlock(&g_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(gasket_register_device);
+
+/* See gasket_core.h for description. */
+void gasket_unregister_device(const struct gasket_driver_desc *driver_desc)
+{
+ int i, desc_idx;
+ struct gasket_internal_desc *internal_desc = NULL;
+
+ mutex_lock(&g_mutex);
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ if (g_descs[i].driver_desc == driver_desc) {
+ internal_desc = &g_descs[i];
+ desc_idx = i;
+ break;
+ }
+ }
+ mutex_unlock(&g_mutex);
+
+ if (!internal_desc) {
+ pr_err("request to unregister unknown desc: %s, %d:%d\n",
+ driver_desc->name, driver_desc->major,
+ driver_desc->minor);
+ return;
+ }
+
+ unregister_chrdev_region(
+ MKDEV(driver_desc->major, driver_desc->minor), GASKET_DEV_MAX);
+
+ pci_unregister_driver(&internal_desc->pci);
+
+ class_destroy(internal_desc->class);
+
+ /* Finally, effectively "remove" the driver. */
+ mutex_lock(&g_mutex);
+ g_descs[desc_idx].driver_desc = NULL;
+ mutex_unlock(&g_mutex);
+
+ pr_info("removed %s driver\n", driver_desc->name);
+}
+EXPORT_SYMBOL(gasket_unregister_device);
+
+static int __init gasket_init(void)
+{
+ int i;
+
+ pr_info("Performing one-time init of the Gasket framework.\n");
+ /* Check for duplicates and find a free slot. */
+ mutex_lock(&g_mutex);
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ g_descs[i].driver_desc = NULL;
+ mutex_init(&g_descs[i].mutex);
+ }
+
+ gasket_sysfs_init();
+
+ mutex_unlock(&g_mutex);
+ return 0;
+}
+
+static void __exit gasket_exit(void)
+{
+ /* No deinit/dealloc needed at present. */
+ pr_info("Removing Gasket framework module.\n");
+}
+MODULE_DESCRIPTION("Google Gasket driver framework");
+MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rob Springer <rspringer@xxxxxxxxxx>");
+module_init(gasket_init);
+module_exit(gasket_exit);
--
2.18.0.345.g5c9ce644c3-goog