From f2c0bcd0f254c5347b6638d2bc9dce119d8355b6 Mon Sep 17 00:00:00 2001 From: Russ Meyerriecks Date: Tue, 28 Jun 2011 22:29:00 +0000 Subject: [PATCH] wcte12xp, wctdm24xxp: Load VPMOCT032 firmware in background. The firmware load has been moved into a workqueue to prevent the module load from blocking for the duration of the firmware upload. This could be up to 40 seconds. Driver prevents configuration until firmware load is finished and is_initialized() returns true. Signed-off-by: Russ Meyerriecks Signed-off-by: Shaun Ruffell git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9998 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- drivers/dahdi/voicebus/vpmoct.c | 273 +++++++++++++++++++++----- drivers/dahdi/voicebus/vpmoct.h | 8 +- drivers/dahdi/wctdm24xxp/base.c | 118 ++++++----- drivers/dahdi/wctdm24xxp/wctdm24xxp.h | 8 +- drivers/dahdi/wctdm24xxp/xhfc.c | 10 +- drivers/dahdi/wcte12xp/base.c | 177 +++++++++-------- drivers/dahdi/wcte12xp/wcte12xp.h | 2 +- 7 files changed, 406 insertions(+), 190 deletions(-) diff --git a/drivers/dahdi/voicebus/vpmoct.c b/drivers/dahdi/voicebus/vpmoct.c index 26db816..0a31c97 100644 --- a/drivers/dahdi/voicebus/vpmoct.c +++ b/drivers/dahdi/voicebus/vpmoct.c @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -291,9 +292,36 @@ error: return -1; } +/** + * vpmoct_get_mode - Return the current operating mode of the VPMOCT032. + * @vpm: The vpm to query. + * + * Will be either BOOTLOADER, APPLICATION, or UNKNOWN. + * + */ +static enum vpmoct_mode vpmoct_get_mode(struct vpmoct *vpm) +{ + int i; + enum vpmoct_mode ret = UNKNOWN; + char identifier[11] = {0}; + + for (i = 0; i < ARRAY_SIZE(identifier) - 1; i++) + identifier[i] = vpmoct_read_byte(vpm, VPMOCT_IDENT+i); + + if (!memcmp(identifier, "bootloader", sizeof(identifier) - 1)) + ret = BOOTLOADER; + else if (!memcmp(identifier, "VPMOCT032\0", sizeof(identifier) - 1)) + ret = APPLICATION; + + dev_dbg(vpm->dev, "vpmoct identifier: %s\n", identifier); + return ret; +} + + static inline short vpmoct_check_firmware_crc(struct vpmoct *vpm, size_t size, u8 major, u8 minor) { + short ret = 0; u8 status; /* Load firmware size */ @@ -311,15 +339,29 @@ vpmoct_check_firmware_crc(struct vpmoct *vpm, size_t size, u8 major, u8 minor) dev_info(vpm->dev, "vpmoct firmware CRC check failed: %x\n", status); /* TODO: Try the load again */ - return -1; + ret = -1; } else { - dev_info(vpm->dev, "vpmoct firmware uploaded successfully\n"); + /* Switch to application code */ vpmoct_write_dword(vpm, VPMOCT_BOOT_ADDRESS2, 0xDEADBEEF); - /* Soft reset the processor */ vpmoct_write_byte(vpm, VPMOCT_BOOT_CMD, VPMOCT_BOOT_REBOOT); - return 0; + + msleep(250); + status = vpmoct_resync(vpm); + + if (APPLICATION != vpmoct_get_mode(vpm)) { + dev_info(vpm->dev, + "vpmoct firmware failed to switch to " + "application. (%x)\n", status); + ret = -1; + } else { + vpm->mode = APPLICATION; + dev_info(vpm->dev, + "vpmoct firmware uploaded successfully\n"); + } } + + return ret; } static inline short vpmoct_switch_to_boot(struct vpmoct *vpm) @@ -330,10 +372,62 @@ static inline short vpmoct_switch_to_boot(struct vpmoct *vpm) dev_info(vpm->dev, "Failed to switch to bootloader\n"); return -1; } - vpm->mode = VPMOCT_MODE_BOOTLOADER; + vpm->mode = BOOTLOADER; return 0; } +struct vpmoct_load_work { + struct vpmoct *vpm; + struct work_struct work; + struct workqueue_struct *wq; + load_complete_func_t load_complete; + bool operational; +}; + +/** + * vpmoct_load_complete_fn - + * + * This function should run in the context of one of the system workqueues so + * that it can destroy any workqueues that may have been created to setup a + * long running firmware load. + * + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void vpmoct_load_complete_fn(void *data) +{ + struct vpmoct_load_work *work = data; +#else +static void vpmoct_load_complete_fn(struct work_struct *data) +{ + struct vpmoct_load_work *work = + container_of(data, struct vpmoct_load_work, work); +#endif + /* Do not touch work->vpm after calling load complete. It may have + * been freed in the function by the board driver. */ + work->load_complete(work->vpm->dev, work->operational); + destroy_workqueue(work->wq); + kfree(work); +} + +/** + * vpmoct_load_complete - Call the load_complete function in a system workqueue. + * @work: + * @operational: Whether the VPM is functioning or not. + * + */ +static void +vpmoct_load_complete(struct vpmoct_load_work *work, bool operational) +{ + work->operational = operational; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&work->work, vpmoct_load_complete_fn, work); +#else + INIT_WORK(&work->work, vpmoct_load_complete_fn); +#endif + schedule_work(&work->work); +} + static bool is_valid_vpmoct_firmware(const struct firmware *fw) { const struct vpmoct_header *header = @@ -353,38 +447,38 @@ static void vpmoct_set_defaults(struct vpmoct *vpm) * vpmoct_load_flash - Check the current flash version and possibly load. * @vpm: The VPMOCT032 module to check / load. * - * Returns 0 on success, otherwise an error message. - * - * Must be called in process context. - * */ -static int vpmoct_load_flash(struct vpmoct *vpm) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void vpmoct_load_flash(void *data) { - int firm; + struct vpmoct_load_work *work = data; +#else +static void vpmoct_load_flash(struct work_struct *data) +{ + struct vpmoct_load_work *work = + container_of(data, struct vpmoct_load_work, work); +#endif + int res; + struct vpmoct *const vpm = work->vpm; const struct firmware *fw; const struct vpmoct_header *header; char serial[VPMOCT_SERIAL_SIZE+1]; const char *const FIRMWARE_NAME = "dahdi-fw-vpmoct032.bin"; int i; - /* Load the firmware */ - firm = request_firmware(&fw, FIRMWARE_NAME, vpm->dev); - if (firm) { - dev_info(vpm->dev, "vpmoct: Failed to load firmware from"\ - " userspace!, %d\n", firm); - return -ENOMEM; - } - - if (!is_valid_vpmoct_firmware(fw)) { + res = request_firmware(&fw, FIRMWARE_NAME, vpm->dev); + if (res) { dev_warn(vpm->dev, - "%s is invalid. Please reinstall.\n", FIRMWARE_NAME); - release_firmware(fw); - return -EINVAL; + "vpmoct: Failed to load firmware from userspace! %d\n", + res); + header = NULL; + fw = NULL; + } else { + header = (const struct vpmoct_header *)fw->data; } - header = (const struct vpmoct_header *)fw->data; + if (vpm->mode == APPLICATION) { - if (vpm->mode == VPMOCT_MODE_APPLICATION) { /* Check the running application firmware * for the proper version */ vpm->major = vpmoct_read_byte(vpm, VPMOCT_MAJOR); @@ -393,16 +487,37 @@ static int vpmoct_load_flash(struct vpmoct *vpm) serial[i] = vpmoct_read_byte(vpm, VPMOCT_SERIAL+i); serial[VPMOCT_SERIAL_SIZE] = '\0'; - dev_info(vpm->dev, "vpmoct: Detected firmware v%d.%d\n", - vpm->major, vpm->minor); - dev_info(vpm->dev, "vpmoct: Serial %s\n", serial); + dev_info(vpm->dev, + "vpmoct: Detected firmware v%d.%d Serial: %s\n", + vpm->major, vpm->minor, + (serial[0] != -1) ? serial : "(None)"); + + if (!fw) { + /* Again, we'll use the existing loaded firmware. */ + vpmoct_set_defaults(vpm); + vpmoct_load_complete(work, true); + return; + } + + if (!is_valid_vpmoct_firmware(fw)) { + dev_warn(vpm->dev, + "%s is invalid. Please reinstall.\n", + FIRMWARE_NAME); + + /* Just use the old version of the fimware. */ + release_firmware(fw); + vpmoct_set_defaults(vpm); + vpmoct_load_complete(work, true); + return; + } if (vpm->minor == header->minor && vpm->major == header->major) { /* Proper version is running */ release_firmware(fw); vpmoct_set_defaults(vpm); - return 0; + vpmoct_load_complete(work, true); + return; } else { /* Incorrect version of application code is @@ -412,6 +527,15 @@ static int vpmoct_load_flash(struct vpmoct *vpm) } } + if (!fw) { + vpmoct_load_complete(work, false); + return; + } else if (!is_valid_vpmoct_firmware(fw)) { + dev_warn(vpm->dev, + "%s is invalid. Please reinstall.\n", FIRMWARE_NAME); + goto error; + } + dev_info(vpm->dev, "vpmoct: Uploading firmware, v%d.%d. This can "\ "take up to 1 minute\n", header->major, header->minor); @@ -426,12 +550,15 @@ static int vpmoct_load_flash(struct vpmoct *vpm) goto error; release_firmware(fw); vpmoct_set_defaults(vpm); - return 0; + vpmoct_load_complete(work, true); + return; error: dev_info(vpm->dev, "Unable to load firmware\n"); release_firmware(fw); - return -1; + /* TODO: Should we disable module if the firmware doesn't load? */ + vpmoct_load_complete(work, false); + return; } struct vpmoct *vpmoct_alloc(void) @@ -452,44 +579,82 @@ EXPORT_SYMBOL(vpmoct_alloc); void vpmoct_free(struct vpmoct *vpm) { + unsigned long flags; + struct vpmoct_cmd *cmd; + LIST_HEAD(list); + + if (!vpm) + return; + + spin_lock_irqsave(&vpm->list_lock, flags); + list_splice(&vpm->active_list, &list); + list_splice(&vpm->pending_list, &list); + spin_unlock_irqrestore(&vpm->list_lock, flags); + + while (!list_empty(&list)) { + cmd = list_entry(list.next, struct vpmoct_cmd, node); + list_del(&cmd->node); + kfree(cmd); + } + kfree(vpm); } EXPORT_SYMBOL(vpmoct_free); /** * vpmoct_init - Check for / initialize VPMOCT032 module. - * @vpm: struct vpmoct allocated with vpmoct_alloc + * @vpm: struct vpmoct allocated with vpmoct_alloc + * @load_complete_fn: Function to call when the load is complete. * - * Returns 0 on success or an error code. + * Check to see if there is a VPMOCT module installed. If there appears to be + * one return 0 and perform any necessary setup in the background. The + * load_complete function will be called in a system global workqueue when the + * initialization is complete. * * Must be called in process context. */ -int vpmoct_init(struct vpmoct *vpm) +int vpmoct_init(struct vpmoct *vpm, load_complete_func_t load_complete) { - unsigned int i; - char identifier[10]; + struct vpmoct_load_work *work; - if (vpmoct_resync(vpm)) - return -ENODEV; + if (!vpm || !vpm->dev || !load_complete) + return -EINVAL; - /* Probe for vpmoct ident string */ - for (i = 0; i < ARRAY_SIZE(identifier); i++) - identifier[i] = vpmoct_read_byte(vpm, VPMOCT_IDENT+i); - - if (!memcmp(identifier, "bootloader", sizeof(identifier))) { - /* vpmoct is in bootloader mode */ - dev_info(vpm->dev, "Detected vpmoct bootloader, attempting "\ - "to load firmware\n"); - vpm->mode = VPMOCT_MODE_BOOTLOADER; - return vpmoct_load_flash(vpm); - } else if (!memcmp(identifier, "VPMOCT032\0", sizeof(identifier))) { - /* vpmoct is in application mode */ - vpm->mode = VPMOCT_MODE_APPLICATION; - return vpmoct_load_flash(vpm); - } else { - /* No vpmoct is installed */ + if (vpmoct_resync(vpm)) { + load_complete(vpm->dev, false); return -ENODEV; } + + vpm->mode = vpmoct_get_mode(vpm); + + if (UNKNOWN == vpm->mode) { + load_complete(vpm->dev, false); + return -ENODEV; + } + + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) { + load_complete(vpm->dev, false); + return -ENOMEM; + } + + work->wq = create_singlethread_workqueue("vpmoct"); + if (!work->wq) { + kfree(work); + load_complete(vpm->dev, false); + return -ENOMEM; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&work->work, vpmoct_load_flash, work); +#else + INIT_WORK(&work->work, vpmoct_load_flash); +#endif + + work->vpm = vpm; + work->load_complete = load_complete; + queue_work(work->wq, &work->work); + return 0; } EXPORT_SYMBOL(vpmoct_init); diff --git a/drivers/dahdi/voicebus/vpmoct.h b/drivers/dahdi/voicebus/vpmoct.h index 486a80d..cb55826 100644 --- a/drivers/dahdi/voicebus/vpmoct.h +++ b/drivers/dahdi/voicebus/vpmoct.h @@ -61,15 +61,14 @@ #define VPMOCT_BOOT_ADDRESS2 0x1c #define VPMOCT_BOOT_RAM 0x20 -#define VPMOCT_MODE_BOOTLOADER 0 -#define VPMOCT_MODE_APPLICATION 1 +enum vpmoct_mode { UNKNOWN = 0, APPLICATION, BOOTLOADER }; struct vpmoct { struct list_head pending_list; struct list_head active_list; spinlock_t list_lock; struct mutex mutex; - unsigned short int mode; + enum vpmoct_mode mode; struct device *dev; u32 companding; u32 echo; @@ -99,7 +98,8 @@ static inline bool is_vpmoct_cmd_read(const struct vpmoct_cmd *cmd) struct vpmoct *vpmoct_alloc(void); void vpmoct_free(struct vpmoct *vpm); -int vpmoct_init(struct vpmoct *vpm); +typedef void (*load_complete_func_t)(struct device *dev, bool operational); +int vpmoct_init(struct vpmoct *vpm, load_complete_func_t load_complete); int vpmoct_echocan_create(struct vpmoct *vpm, int channo, int companding); diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index 73bcae6..98c0f0e 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -309,12 +309,6 @@ static inline __attribute_const__ int VPM_CMD_BYTE(int timeslot, int bit) return ((((timeslot) & 0x3) * 3 + (bit)) * 7) + ((timeslot) >> 2); } -static inline bool is_initialized(struct wctdm *wc) -{ - WARN_ON(wc->initialized < 0); - return (wc->initialized == 0); -} - static void setchanconfig_from_state(struct vpmadt032 *vpm, int channel, GpakChannelConfig_t *chanconfig) @@ -2131,6 +2125,7 @@ static const char *wctdm_echocan_name(const struct dahdi_chan *chan) return vpmadt032_name; else if (wc->vpmoct) return vpmoct_name; + return NULL; } @@ -4374,12 +4369,37 @@ static int wctdm_initialize_vpmadt032(struct wctdm *wc) return 0; } +static void wctdm_vpm_load_complete(struct device *dev, bool operational) +{ + unsigned long flags; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct wctdm *wc = pci_get_drvdata(pdev); + struct vpmoct *vpm = NULL; + + WARN_ON(!wc || !wc->not_ready); + if (!wc || !wc->not_ready) + return; + + spin_lock_irqsave(&wc->reglock, flags); + wc->not_ready--; + if (operational) { + wc->ctlreg |= 0x10; + } else { + vpm = wc->vpmoct; + wc->vpmoct = NULL; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + if (vpm) + vpmoct_free(vpm); +} + static void wctdm_initialize_vpm(struct wctdm *wc) { int res = 0; if (!vpmsupport) - goto cleanup; + return; res = wctdm_initialize_vpmadt032(wc); if (!res) { @@ -4393,29 +4413,26 @@ static void wctdm_initialize_vpm(struct wctdm *wc) if (!vpm) { dev_info(&wc->vb.pdev->dev, "Unable to allocate memory for struct vpmoct\n"); - goto cleanup; + return; } vpm->dev = &wc->vb.pdev->dev; spin_lock_irqsave(&wc->reglock, flags); wc->vpmoct = vpm; + wc->not_ready++; spin_unlock_irqrestore(&wc->reglock, flags); - if (!vpmoct_init(vpm)) { - wc->ctlreg |= 0x10; - return; - } else { + res = vpmoct_init(vpm, wctdm_vpm_load_complete); + if (-EINVAL == res) { spin_lock_irqsave(&wc->reglock, flags); wc->vpmoct = NULL; + wc->not_ready--; spin_unlock_irqrestore(&wc->reglock, flags); vpmoct_free(vpm); - goto cleanup; } } - -cleanup: - dev_info(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); + return; } static void wctdm_identify_modules(struct wctdm *wc) @@ -5214,7 +5231,7 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!wc) return -ENOMEM; - wc->initialized = 1; + wc->not_ready = 1; down(&ifacelock); /* \todo this is a candidate for removal... */ @@ -5460,7 +5477,7 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - wc->initialized--; + wc->not_ready--; dev_info(&wc->vb.pdev->dev, "Found a %s: %s (%d BRI spans, %d analog %s)\n", @@ -5531,38 +5548,51 @@ static void wctdm_release(struct wctdm *wc) static void __devexit wctdm_remove_one(struct pci_dev *pdev) { - struct wctdm *wc = pci_get_drvdata(pdev); - struct vpmadt032 *vpm = wc->vpmadt032; int i; + unsigned long flags; + struct wctdm *wc = pci_get_drvdata(pdev); + struct vpmadt032 *vpmadt032; + struct vpmoct *vpmoct; + if (!wc) + return; - if (wc) { + vpmadt032 = wc->vpmadt032; + vpmoct = wc->vpmoct; - remove_sysfs_files(wc); + remove_sysfs_files(wc); - if (vpm) { - clear_bit(VPM150M_ACTIVE, &vpm->control); - flush_scheduled_work(); - } - - /* shut down any BRI modules */ - for (i = 0; i < wc->mods_per_board; i += 4) { - if (wc->mods[i].type == BRI) - wctdm_unload_b400m(wc, i); - } - - voicebus_stop(&wc->vb); - - if (vpm) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - } - - dev_info(&wc->vb.pdev->dev, "Freed a %s\n", - (is_hx8(wc)) ? "Hybrid card" : "Wildcard"); - /* Release span */ - wctdm_release(wc); + if (vpmadt032) { + clear_bit(VPM150M_ACTIVE, &vpmadt032->control); + flush_scheduled_work(); + } else if (vpmoct) { + while (wctdm_wait_for_ready(wc)) + schedule(); } + + /* shut down any BRI modules */ + for (i = 0; i < wc->mods_per_board; i += 4) { + if (wc->mods[i].type == BRI) + wctdm_unload_b400m(wc, i); + } + + voicebus_stop(&wc->vb); + + if (vpmadt032) { + vpmadt032_free(vpmadt032); + wc->vpmadt032 = NULL; + } else if (vpmoct) { + spin_lock_irqsave(&wc->reglock, flags); + wc->vpmoct = NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + vpmoct_free(vpmoct); + } + + dev_info(&wc->vb.pdev->dev, "Freed a %s\n", + (is_hx8(wc)) ? "Hybrid card" : "Wildcard"); + + /* Release span */ + wctdm_release(wc); } static DEFINE_PCI_DEVICE_TABLE(wctdm_pci_tbl) = { diff --git a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h index 0f497aa..4ab313d 100644 --- a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h +++ b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h @@ -273,11 +273,17 @@ struct wctdm { struct semaphore syncsem; int oldsync; - int initialized; /* 0 when the entire card is ready to go */ + int not_ready; /* 0 when the entire card is ready to go */ unsigned long checkflag; /* Internal state flags and task bits */ int companding; }; +static inline bool is_initialized(struct wctdm *wc) +{ + WARN_ON(wc->not_ready < 0); + return (wc->not_ready == 0); +} + /* Atomic flag bits for checkflag field */ #define WCTDM_CHECK_TIMING 0 diff --git a/drivers/dahdi/wctdm24xxp/xhfc.c b/drivers/dahdi/wctdm24xxp/xhfc.c index de6ad89..c5eb5a6 100644 --- a/drivers/dahdi/wctdm24xxp/xhfc.c +++ b/drivers/dahdi/wctdm24xxp/xhfc.c @@ -1160,7 +1160,7 @@ static int xhfc_find_sync_with_timingcable(struct b400m *b4) } for (j = 0; j < WC_MAX_IFACES && ifaces[j]; j++) { - if (!ifaces[j]->initialized) { + if (is_initialized(ifaces[j])) { set_bit(WCTDM_CHECK_TIMING, &wc->checkflag); osrc = -2; goto out; @@ -2194,7 +2194,7 @@ int b400m_spanconfig(struct file *file, struct dahdi_span *span, b4 = bspan->parent; wc = b4->wc; - if ((file->f_flags & O_NONBLOCK) && !wc->initialized) + if ((file->f_flags & O_NONBLOCK) && !is_initialized(wc)) return -EAGAIN; res = wctdm_wait_for_ready(wc); @@ -2271,7 +2271,7 @@ int b400m_chanconfig(struct file *file, struct dahdi_chan *chan, int sigtype) struct b400m *b4 = bspan->parent; int res; - if ((file->f_flags & O_NONBLOCK) && !b4->wc->initialized) + if ((file->f_flags & O_NONBLOCK) && !is_initialized(b4->wc)) return -EAGAIN; res = wctdm_wait_for_ready(b4->wc); @@ -2395,7 +2395,7 @@ static void xhfc_work(struct work_struct *work) int i, j, k, fifo; unsigned char b, b2; - if (b4->shutdown || !b4->wc->initialized) + if (b4->shutdown || !is_initialized(b4->wc)) return; b4->irq_oview = b400m_getreg(b4, R_IRQ_OVIEW); @@ -2518,7 +2518,7 @@ void wctdm_bri_checkisr(struct wctdm *wc, struct wctdm_module *const mod, return; /* DEFINITELY don't do anything if our structures aren't ready! */ - if (!wc->initialized || !b4 || !b4->inited) + if (!is_initialized(wc) || !b4 || !b4->inited) return; if (offset == 0) { diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c index 28afdf6..68fcd96 100644 --- a/drivers/dahdi/wcte12xp/base.c +++ b/drivers/dahdi/wcte12xp/base.c @@ -1054,8 +1054,24 @@ static int t1xxp_startup(struct file *file, struct dahdi_span *span) static inline bool is_initialized(struct t1 *wc) { - WARN_ON(wc->initialized < 0); - return (wc->initialized == 0); + WARN_ON(wc->not_ready < 0); + return (wc->not_ready == 0); +} + +/** + * t1_wait_for_ready + * + * Check if the board has finished any setup and is ready to start processing + * calls. + */ +static int t1_wait_for_ready(struct t1 *wc) +{ + while (!is_initialized(wc)) { + if (fatal_signal_pending(current)) + return -EIO; + msleep_interruptible(250); + } + return 0; } static int t1xxp_chanconfig(struct file *file, @@ -1066,11 +1082,7 @@ static int t1xxp_chanconfig(struct file *file, if (file->f_flags & O_NONBLOCK && !is_initialized(wc)) { return -EAGAIN; } else { - while (!is_initialized(wc)) { - if (fatal_signal_pending(current)) - return -EIO; - msleep_interruptible(250); - } + t1_wait_for_ready(wc); } if (test_bit(DAHDI_FLAGBIT_RUNNING, &chan->span->flags) && @@ -1539,7 +1551,7 @@ static void vpm_load_func(struct work_struct *work) set_bit(0, &wc->ctlreg); } - wc->initialized--; + wc->not_ready--; kfree(w); } @@ -1561,26 +1573,42 @@ static int vpm_start_load(struct t1 *wc) return 0; } -static int check_and_load_vpm(struct t1 *wc) +static void t1_vpm_load_complete(struct device *dev, bool operational) { - int res; unsigned long flags; - struct vpmadt032_options options; - struct vpmadt032 *vpm = NULL; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct t1 *wc = pci_get_drvdata(pdev); + struct vpmoct *vpm = NULL; - if (!vpmsupport) { - t1_info(wc, "VPM Support Disabled\n"); - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return 0; + if (!wc || is_initialized(wc)) { + WARN_ON(!wc); + return; } - /* The firmware may already be loaded. */ - if (wc->vpmadt032) { - u16 version; - res = gpakPingDsp(wc->vpmadt032->dspid, &version); - if (!res) - return 0; + spin_lock_irqsave(&wc->reglock, flags); + wc->not_ready--; + if (operational) { + set_bit(VPM150M_ACTIVE, &wc->ctlreg); + } else { + clear_bit(VPM150M_ACTIVE, &wc->ctlreg); + vpm = wc->vpmoct; + wc->vpmoct = NULL; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + if (vpm) + vpmoct_free(vpm); +} + +static void check_and_load_vpm(struct t1 *wc) +{ + unsigned long flags; + struct vpmadt032_options options; + struct vpmadt032 *vpmadt = NULL; + + if (!vpmsupport) { + t1_info(wc, "VPM Support Disabled via module parameter\n"); + return; } memset(&options, 0, sizeof(options)); @@ -1594,69 +1622,49 @@ static int check_and_load_vpm(struct t1 *wc) * done setting it up here, an hour should cover it... */ wc->vpm_check = jiffies + HZ*3600; - vpm = vpmadt032_alloc(&options); - if (!vpm) - return -ENOMEM; + vpmadt = vpmadt032_alloc(&options); + if (!vpmadt) + return; - vpm->setchanconfig_from_state = setchanconfig_from_state; + vpmadt->setchanconfig_from_state = setchanconfig_from_state; spin_lock_irqsave(&wc->reglock, flags); - wc->vpmadt032 = vpm; + wc->vpmadt032 = vpmadt; spin_unlock_irqrestore(&wc->reglock, flags); - res = vpmadt032_test(vpm, &wc->vb); - if (-ENODEV == res) { - struct vpmoct *vpmoct; - + /* Probe for and attempt to load a vpmadt032 module */ + if (vpmadt032_test(vpmadt, &wc->vb) || vpm_start_load(wc)) { /* There does not appear to be a VPMADT032 installed. */ clear_bit(VPM150M_ACTIVE, &wc->ctlreg); spin_lock_irqsave(&wc->reglock, flags); wc->vpmadt032 = NULL; spin_unlock_irqrestore(&wc->reglock, flags); - vpmadt032_free(vpm); + vpmadt032_free(vpmadt); + } + + /* Probe for and attempt to load a vpmoct032 module */ + if (NULL == wc->vpmadt032) { + struct vpmoct *vpmoct; /* Check for vpmoct */ vpmoct = vpmoct_alloc(); if (!vpmoct) - return -ENOMEM; + return; vpmoct->dev = &wc->vb.pdev->dev; spin_lock_irqsave(&wc->reglock, flags); wc->vpmoct = vpmoct; + wc->not_ready++; spin_unlock_irqrestore(&wc->reglock, flags); - res = vpmoct_init(wc->vpmoct); - if (res) { - dev_info(&wc->vb.pdev->dev, - "Unable to initialize vpmoct module\n"); - spin_lock_irqsave(&wc->reglock, flags); - wc->vpmoct = NULL; - spin_unlock_irqrestore(&wc->reglock, flags); - vpmoct_free(vpmoct); - } else { - set_bit(VPM150M_ACTIVE, &wc->ctlreg); - } - - return res; + vpmoct_init(vpmoct, t1_vpm_load_complete); } - - res = vpm_start_load(wc); - if (res) { - /* There does not appear to be a VPMADT032 installed. */ - clear_bit(VPM150M_ACTIVE, &wc->ctlreg); - spin_lock_irqsave(&wc->reglock, flags); - wc->vpmadt032 = NULL; - spin_unlock_irqrestore(&wc->reglock, flags); - vpmadt032_free(vpm); - return res; - } - return res; } #else -static inline int check_and_load_vpm(const struct t1 *wc) +static inline void check_and_load_vpm(const struct t1 *wc) { - return 0; + return; } #endif @@ -1706,11 +1714,7 @@ t1xxp_spanconfig(struct file *file, struct dahdi_span *span, if (!is_initialized(wc)) return -EAGAIN; } else { - while (!is_initialized(wc)) { - if (fatal_signal_pending(current)) - return -EIO; - msleep_interruptible(250); - } + t1_wait_for_ready(wc); } /* Do we want to SYNC on receive or not */ @@ -2340,7 +2344,7 @@ static void vpm_check_func(struct work_struct *work) /* If there is a failed VPM module, do not block dahdi_cfg * indefinitely. */ if (++wc->vpm_check_count > MAX_CHECKS) { - wc->initialized--; + wc->not_ready--; wc->vpm_check = MAX_JIFFY_OFFSET; t1_info(wc, "Disabling VPMADT032 Checking.\n"); return; @@ -2395,7 +2399,7 @@ static void vpm_check_func(struct work_struct *work) set_bit(VPM150M_ACTIVE, &wc->ctlreg); t1_info(wc, "VPMADT032 is reenabled.\n"); wc->vpm_check = jiffies + HZ*5; - wc->initialized--; + wc->not_ready--; return; } @@ -2544,7 +2548,7 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi if (!wc) return -ENOMEM; - wc->initialized = 1; + wc->not_ready = 1; ifaces[index] = wc; @@ -2659,11 +2663,10 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi t1_info(wc, "Found a %s\n", wc->variety); voicebus_unlock_latency(&wc->vb); - /* If there is VPMADT032 or VPMOCT032 module attached to this device, - * it will signal ready after the channels are configured and ready - * for use. */ - if (!wc->vpmadt032 && !wc->vpmoct) - wc->initialized--; + /* If there is VPMADT032 module attached to this device, it will + * signal ready after the channels are configured and ready for use. */ + if (!wc->vpmadt032) + wc->not_ready--; return 0; } @@ -2671,7 +2674,9 @@ static void __devexit te12xp_remove_one(struct pci_dev *pdev) { struct t1 *wc = pci_get_drvdata(pdev); #ifdef VPM_SUPPORT - struct vpmadt032 *vpm = wc->vpmadt032; + unsigned long flags; + struct vpmadt032 *vpmadt = wc->vpmadt032; + struct vpmoct *vpmoct = wc->vpmoct; #endif if (!wc) return; @@ -2685,20 +2690,30 @@ static void __devexit te12xp_remove_one(struct pci_dev *pdev) del_timer_sync(&wc->timer); flush_workqueue(wc->wq); #ifdef VPM_SUPPORT - if (vpm) - flush_workqueue(vpm->wq); + if (vpmadt) { + clear_bit(VPM150M_ACTIVE, &vpmadt->control); + flush_workqueue(vpmadt->wq); + } else if (vpmoct) { + while (t1_wait_for_ready(wc)) + schedule(); + } #endif del_timer_sync(&wc->timer); voicebus_release(&wc->vb); #ifdef VPM_SUPPORT - if(vpm) { + if (vpmadt) { + spin_lock_irqsave(&wc->reglock, flags); wc->vpmadt032 = NULL; - clear_bit(VPM150M_ACTIVE, &vpm->control); - vpmadt032_free(vpm); + spin_unlock_irqrestore(&wc->reglock, flags); + vpmadt032_free(vpmadt); + } else if (vpmoct) { + spin_lock_irqsave(&wc->reglock, flags); + wc->vpmoct = NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + vpmoct_free(vpmoct); } - #endif t1_info(wc, "Freed a Wildcard TE12xP.\n"); diff --git a/drivers/dahdi/wcte12xp/wcte12xp.h b/drivers/dahdi/wcte12xp/wcte12xp.h index a927e07..507a43e 100644 --- a/drivers/dahdi/wcte12xp/wcte12xp.h +++ b/drivers/dahdi/wcte12xp/wcte12xp.h @@ -139,7 +139,7 @@ struct t1 { struct timer_list timer; struct work_struct timer_work; struct workqueue_struct *wq; - bool initialized; /* 0 when entire card is ready to go */ + unsigned int not_ready; /* 0 when entire card is ready to go */ }; #define t1_info(t1, format, arg...) \