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 <rmeyerriecks@digium.com> Signed-off-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9998 a0bf4364-ded3-4de4-8d8a-66a801d63aff
This commit is contained in:
parent
1714113c17
commit
f2c0bcd0f2
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) = {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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...) \
|
||||
|
Loading…
Reference in New Issue
Block a user