2010-12-11 13:19:49 -06:00
|
|
|
/*
|
2010-12-14 16:55:31 -06:00
|
|
|
* main.c -- the bare skypopen char module
|
2010-12-11 13:19:49 -06:00
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Giovanni Maruzzelli
|
|
|
|
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
|
|
|
|
* Copyright (C) 2001 O'Reilly & Associates
|
|
|
|
*
|
|
|
|
* The source code in this file can be freely used, adapted,
|
|
|
|
* and redistributed in source or binary form, so long as an
|
|
|
|
* acknowledgment appears in derived source files. The citation
|
|
|
|
* should list that the code comes from the book "Linux Device
|
|
|
|
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
|
|
|
|
* by O'Reilly & Associates. No warranty is attached;
|
|
|
|
* we cannot take responsibility for errors or fitness for use.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
|
|
|
#include <linux/kernel.h> /* printk() */
|
|
|
|
#include <linux/slab.h> /* kmalloc() */
|
|
|
|
#include <linux/fs.h> /* everything... */
|
|
|
|
#include <linux/errno.h> /* error codes */
|
|
|
|
#include <linux/types.h> /* size_t */
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/fcntl.h> /* O_ACCMODE */
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/cdev.h>
|
|
|
|
|
|
|
|
#include <asm/system.h> /* cli(), *_flags */
|
|
|
|
#include <asm/uaccess.h> /* copy_*_user */
|
|
|
|
|
|
|
|
#include <linux/soundcard.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/hrtimer.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/jiffies.h>
|
|
|
|
#include <linux/hrtimer.h>
|
|
|
|
#include <linux/ktime.h>
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
#include "skypopen.h" /* local definitions */
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Our parameters which can be set at load time.
|
|
|
|
*/
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
int skypopen_major = SKYPOPEN_MAJOR;
|
|
|
|
int skypopen_minor = 3;
|
|
|
|
int skypopen_nr_devs = SKYPOPEN_NR_DEVS; /* number of bare skypopen devices */
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
module_param(skypopen_major, int, S_IRUGO);
|
|
|
|
module_param(skypopen_minor, int, S_IRUGO);
|
|
|
|
module_param(skypopen_nr_devs, int, S_IRUGO);
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
MODULE_AUTHOR("Original: Alessandro Rubini, Jonathan Corbet. Modified by: Giovanni Maruzzelli for FreeSWITCH skypopen");
|
2010-12-11 13:19:49 -06:00
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
static struct skypopen_dev *skypopen_devices; /* allocated in skypopen_init_module */
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
static int unload = 0;
|
|
|
|
#ifdef CENTOS
|
|
|
|
#define HRTIMER_MODE_REL HRTIMER_REL
|
|
|
|
#endif// CENTOS
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
#ifndef WANT_HRTIMER
|
2010-12-11 13:19:49 -06:00
|
|
|
void my_timer_callback_inq( unsigned long data )
|
|
|
|
{
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_dev *dev = (void *)data;
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
wake_up_interruptible(&dev->inq);
|
2010-12-17 08:44:20 -06:00
|
|
|
mod_timer( &dev->timer_inq, jiffies + msecs_to_jiffies(SKYPOPEN_SLEEP) );
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void my_timer_callback_outq( unsigned long data )
|
|
|
|
{
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_dev *dev = (void *)data;
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
wake_up_interruptible(&dev->outq);
|
2010-12-17 08:44:20 -06:00
|
|
|
mod_timer( &dev->timer_outq, jiffies + msecs_to_jiffies(SKYPOPEN_SLEEP) );
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
2010-12-17 08:44:20 -06:00
|
|
|
#else// WANT_HRTIMER
|
|
|
|
|
|
|
|
#ifndef CENTOS
|
|
|
|
static enum hrtimer_restart my_hrtimer_callback_inq( struct hrtimer *timer_inq )
|
|
|
|
{
|
|
|
|
struct skypopen_dev *dev = container_of(timer_inq, struct skypopen_dev, timer_inq);
|
|
|
|
ktime_t now;
|
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
|
|
|
|
now = ktime_get();
|
|
|
|
hrtimer_forward(&dev->timer_inq, now, ktime_set(0, SKYPOPEN_SLEEP * 1000000));
|
|
|
|
wake_up_interruptible(&dev->inq);
|
|
|
|
|
|
|
|
return HRTIMER_RESTART;
|
|
|
|
}
|
|
|
|
static enum hrtimer_restart my_hrtimer_callback_outq( struct hrtimer *timer_outq )
|
|
|
|
{
|
|
|
|
struct skypopen_dev *dev = container_of(timer_outq, struct skypopen_dev, timer_outq);
|
|
|
|
ktime_t now;
|
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
|
|
|
|
now = ktime_get();
|
|
|
|
hrtimer_forward(&dev->timer_outq, now, ktime_set(0, SKYPOPEN_SLEEP * 1000000));
|
|
|
|
wake_up_interruptible(&dev->outq);
|
|
|
|
|
|
|
|
return HRTIMER_RESTART;
|
|
|
|
}
|
|
|
|
#else// CENTOS
|
|
|
|
static int my_hrtimer_callback_inq( struct hrtimer *timer_inq )
|
|
|
|
{
|
|
|
|
struct skypopen_dev *dev = container_of(timer_inq, struct skypopen_dev, timer_inq);
|
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
|
|
|
|
hrtimer_forward(&dev->timer_inq, timer_inq->expires, ktime_set(0, SKYPOPEN_SLEEP * 1000000));
|
|
|
|
wake_up_interruptible(&dev->inq);
|
|
|
|
|
|
|
|
return HRTIMER_RESTART;
|
|
|
|
}
|
|
|
|
static int my_hrtimer_callback_outq( struct hrtimer *timer_outq )
|
|
|
|
{
|
|
|
|
struct skypopen_dev *dev = container_of(timer_outq, struct skypopen_dev, timer_outq);
|
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
|
|
|
|
hrtimer_forward(&dev->timer_outq, timer_outq->expires, ktime_set(0, SKYPOPEN_SLEEP * 1000000));
|
|
|
|
wake_up_interruptible(&dev->outq);
|
|
|
|
|
|
|
|
return HRTIMER_RESTART;
|
|
|
|
}
|
|
|
|
#endif// CENTOS
|
|
|
|
#endif// WANT_HRTIMER
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
/* The clone-specific data structure includes a key field */
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_listitem {
|
|
|
|
struct skypopen_dev device;
|
2010-12-11 13:19:49 -06:00
|
|
|
dev_t key;
|
|
|
|
struct list_head list;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The list of devices, and a lock to protect it */
|
2010-12-14 16:55:31 -06:00
|
|
|
static LIST_HEAD(skypopen_c_list);
|
|
|
|
static spinlock_t skypopen_c_lock = SPIN_LOCK_UNLOCKED;
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
/* Look for a device or create one if missing */
|
2010-12-14 16:55:31 -06:00
|
|
|
static struct skypopen_dev *skypopen_c_lookfor_device(dev_t key)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_listitem *lptr;
|
2010-12-17 08:44:20 -06:00
|
|
|
#ifdef WANT_HRTIMER
|
|
|
|
#if 0
|
|
|
|
ktime_t ktime_inq;
|
|
|
|
ktime_t ktime_outq;
|
|
|
|
#endif //0
|
|
|
|
#endif// WANT_HRTIMER
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
list_for_each_entry(lptr, &skypopen_c_list, list) {
|
2010-12-11 13:19:49 -06:00
|
|
|
if (lptr->key == key)
|
|
|
|
return &(lptr->device);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not found */
|
2010-12-14 16:55:31 -06:00
|
|
|
lptr = kmalloc(sizeof(struct skypopen_listitem), GFP_KERNEL);
|
2010-12-11 13:19:49 -06:00
|
|
|
if (!lptr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* initialize the device */
|
2010-12-14 16:55:31 -06:00
|
|
|
memset(lptr, 0, sizeof(struct skypopen_listitem));
|
2010-12-11 13:19:49 -06:00
|
|
|
lptr->key = key;
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
init_waitqueue_head(&lptr->device.inq);
|
|
|
|
init_waitqueue_head(&lptr->device.outq);
|
|
|
|
#ifndef WANT_HRTIMER
|
|
|
|
setup_timer( &lptr->device.timer_inq, my_timer_callback_inq, (long int)lptr );
|
|
|
|
setup_timer( &lptr->device.timer_outq, my_timer_callback_outq, (long int)lptr );
|
|
|
|
printk( "Starting skypopen OSS driver read timer (%dms) skype client:(%d)\n", SKYPOPEN_SLEEP, current->tgid );
|
|
|
|
mod_timer( &lptr->device.timer_inq, jiffies + msecs_to_jiffies(SKYPOPEN_SLEEP) );
|
|
|
|
printk( "Starting skypopen OSS driver write timer (%dms) skype client:(%d)\n", SKYPOPEN_SLEEP, current->tgid );
|
|
|
|
mod_timer( &lptr->device.timer_outq, jiffies + msecs_to_jiffies(SKYPOPEN_SLEEP) );
|
|
|
|
#else// WANT_HRTIMER
|
|
|
|
#if 0
|
|
|
|
ktime_inq = ktime_set( 0, SKYPOPEN_SLEEP * 1000000);
|
|
|
|
hrtimer_init( &lptr->device.timer_inq, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
|
|
|
|
lptr->device.timer_inq.function = &my_hrtimer_callback_inq;
|
|
|
|
hrtimer_start( &lptr->device.timer_inq, ktime_inq, HRTIMER_MODE_REL );
|
|
|
|
|
|
|
|
ktime_outq = ktime_set( 0, SKYPOPEN_SLEEP * 1000000);
|
|
|
|
hrtimer_init( &lptr->device.timer_outq, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
|
|
|
|
lptr->device.timer_outq.function = &my_hrtimer_callback_outq;
|
|
|
|
hrtimer_start( &lptr->device.timer_outq, ktime_outq, HRTIMER_MODE_REL );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif// WANT_HRTIMER
|
2010-12-14 16:55:31 -06:00
|
|
|
|
2010-12-11 13:19:49 -06:00
|
|
|
/* place it in the list */
|
2010-12-14 16:55:31 -06:00
|
|
|
list_add(&lptr->list, &skypopen_c_list);
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
return &(lptr->device);
|
|
|
|
}
|
2010-12-14 16:55:31 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Open and close
|
|
|
|
*/
|
|
|
|
static int skypopen_c_open(struct inode *inode, struct file *filp)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_dev *dev;
|
2010-12-11 13:19:49 -06:00
|
|
|
dev_t key;
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
key = current->tgid;
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
/* look for a skypopenc device in the list */
|
|
|
|
spin_lock(&skypopen_c_lock);
|
|
|
|
dev = skypopen_c_lookfor_device(key);
|
|
|
|
spin_unlock(&skypopen_c_lock);
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
/* then, everything else is copied from the bare skypopen device */
|
2010-12-11 13:19:49 -06:00
|
|
|
filp->private_data = dev;
|
|
|
|
return 0; /* success */
|
|
|
|
}
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
static int skypopen_c_release(struct inode *inode, struct file *filp)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Nothing to do, because the device is persistent.
|
|
|
|
* A `real' cloned device should be freed on last close
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
static ssize_t skypopen_read(struct file *filp, char __user *buf, size_t count,
|
2010-12-11 13:19:49 -06:00
|
|
|
loff_t *f_pos)
|
|
|
|
{
|
2010-12-14 08:38:57 -06:00
|
|
|
DEFINE_WAIT(wait);
|
2010-12-17 08:44:20 -06:00
|
|
|
struct skypopen_dev *dev = filp->private_data;
|
2010-12-19 13:40:27 -06:00
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return -1;
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
#ifdef WANT_HRTIMER
|
|
|
|
#if 1
|
|
|
|
if(dev->timer_inq_started == 0){
|
|
|
|
ktime_t ktime_inq;
|
|
|
|
|
|
|
|
ktime_inq = ktime_set( 0, SKYPOPEN_SLEEP * 1000000);
|
|
|
|
hrtimer_init( &dev->timer_inq, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
|
|
|
|
dev->timer_inq.function = &my_hrtimer_callback_inq;
|
|
|
|
hrtimer_start( &dev->timer_inq, ktime_inq, HRTIMER_MODE_REL );
|
|
|
|
dev->timer_inq_started = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif// WANT_HRTIMER
|
2010-12-14 16:55:31 -06:00
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
|
|
|
|
//printk("READ\n");
|
2010-12-14 08:38:57 -06:00
|
|
|
prepare_to_wait(&dev->inq, &wait, TASK_INTERRUPTIBLE);
|
|
|
|
schedule();
|
|
|
|
finish_wait(&dev->inq, &wait);
|
2010-12-11 13:19:49 -06:00
|
|
|
return count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
static ssize_t skypopen_write(struct file *filp, const char __user *buf, size_t count,
|
2010-12-11 13:19:49 -06:00
|
|
|
loff_t *f_pos)
|
|
|
|
{
|
2010-12-14 08:38:57 -06:00
|
|
|
DEFINE_WAIT(wait);
|
2010-12-17 08:44:20 -06:00
|
|
|
struct skypopen_dev *dev = filp->private_data;
|
2010-12-19 13:40:27 -06:00
|
|
|
|
|
|
|
if(unload)
|
|
|
|
return -1;
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
#ifdef WANT_HRTIMER
|
|
|
|
#if 1
|
|
|
|
if(dev->timer_outq_started == 0){
|
|
|
|
ktime_t ktime_outq;
|
|
|
|
|
|
|
|
ktime_outq = ktime_set( 0, SKYPOPEN_SLEEP * 1000000);
|
|
|
|
hrtimer_init( &dev->timer_outq, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
|
|
|
|
dev->timer_outq.function = &my_hrtimer_callback_outq;
|
|
|
|
hrtimer_start( &dev->timer_outq, ktime_outq, HRTIMER_MODE_REL );
|
|
|
|
dev->timer_outq_started = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif// WANT_HRTIMER
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
//printk("WRITE\n");
|
2010-12-14 08:38:57 -06:00
|
|
|
prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
|
|
|
|
schedule();
|
|
|
|
finish_wait(&dev->outq, &wait);
|
2010-12-11 13:19:49 -06:00
|
|
|
return count;
|
|
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* The ioctl() implementation
|
|
|
|
*/
|
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
static int skypopen_ioctl(struct inode *inode, struct file *filp,
|
2010-12-11 13:19:49 -06:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
int __user *p = argp;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case OSS_GETVERSION:
|
|
|
|
return put_user(SOUND_VERSION, p);
|
|
|
|
case SNDCTL_DSP_GETBLKSIZE:
|
2010-12-17 08:44:20 -06:00
|
|
|
return put_user(SKYPOPEN_BLK, p);
|
2010-12-11 13:19:49 -06:00
|
|
|
case SNDCTL_DSP_GETFMTS:
|
|
|
|
return put_user(28731, p);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
struct file_operations skypopen_fops = {
|
2010-12-11 13:19:49 -06:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.llseek = no_llseek,
|
2010-12-14 16:55:31 -06:00
|
|
|
.read = skypopen_read,
|
|
|
|
.write = skypopen_write,
|
|
|
|
.ioctl = skypopen_ioctl,
|
|
|
|
.open = skypopen_c_open,
|
|
|
|
.release = skypopen_c_release,
|
2010-12-11 13:19:49 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, the module stuff
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The cleanup function is used to handle initialization failures as well.
|
|
|
|
* Thefore, it must be careful to work correctly even if some of the items
|
|
|
|
* have not been initialized
|
|
|
|
*/
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
void skypopen_cleanup_module(void)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ret;
|
2010-12-14 16:55:31 -06:00
|
|
|
struct skypopen_listitem *lptr, *next;
|
|
|
|
dev_t devno = MKDEV(skypopen_major, skypopen_minor);
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-17 08:44:20 -06:00
|
|
|
|
|
|
|
unload = 1;
|
|
|
|
|
|
|
|
msleep(100);
|
|
|
|
|
2010-12-11 13:19:49 -06:00
|
|
|
/* Get rid of our char dev entries */
|
2010-12-14 16:55:31 -06:00
|
|
|
if (skypopen_devices) {
|
|
|
|
for (i = 0; i < skypopen_nr_devs; i++) {
|
|
|
|
cdev_del(&skypopen_devices[i].cdev);
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
2010-12-14 16:55:31 -06:00
|
|
|
kfree(skypopen_devices);
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
|
|
|
|
2010-12-13 07:33:37 -06:00
|
|
|
/* And all the cloned devices */
|
2010-12-14 16:55:31 -06:00
|
|
|
list_for_each_entry_safe(lptr, next, &skypopen_c_list, list) {
|
2010-12-17 08:44:20 -06:00
|
|
|
#ifndef WANT_HRTIMER
|
2010-12-11 13:19:49 -06:00
|
|
|
ret= del_timer( &lptr->device.timer_inq );
|
2010-12-17 08:44:20 -06:00
|
|
|
printk( "Stopped skypopen OSS driver read timer\n");
|
2010-12-11 13:19:49 -06:00
|
|
|
ret= del_timer( &lptr->device.timer_outq );
|
2010-12-17 08:44:20 -06:00
|
|
|
printk( "Stopped skypopen OSS driver write timer\n");
|
|
|
|
#else// WANT_HRTIMER
|
2010-12-19 13:40:27 -06:00
|
|
|
if(lptr->device.timer_inq_started){
|
|
|
|
ret = hrtimer_cancel( &lptr->device.timer_inq );
|
|
|
|
printk( "Stopped skypopen OSS driver read HRtimer\n");
|
|
|
|
}
|
|
|
|
if(lptr->device.timer_outq_started){
|
|
|
|
ret = hrtimer_cancel( &lptr->device.timer_outq );
|
|
|
|
printk( "Stopped skypopen OSS driver write HRtimer\n");
|
|
|
|
}
|
2010-12-17 08:44:20 -06:00
|
|
|
|
|
|
|
#endif// WANT_HRTIMER
|
2010-12-11 13:19:49 -06:00
|
|
|
list_del(&lptr->list);
|
|
|
|
kfree(lptr);
|
|
|
|
}
|
|
|
|
/* cleanup_module is never called if registering failed */
|
2010-12-14 16:55:31 -06:00
|
|
|
unregister_chrdev_region(devno, skypopen_nr_devs);
|
|
|
|
printk("skypopen OSS driver unloaded\n");
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the char_dev structure for this device.
|
|
|
|
*/
|
2010-12-14 16:55:31 -06:00
|
|
|
static void skypopen_setup_cdev(struct skypopen_dev *dev, int index)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
2010-12-14 16:55:31 -06:00
|
|
|
int err, devno = MKDEV(skypopen_major, skypopen_minor + index);
|
2010-12-11 13:19:49 -06:00
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
cdev_init(&dev->cdev, &skypopen_fops);
|
2010-12-11 13:19:49 -06:00
|
|
|
dev->cdev.owner = THIS_MODULE;
|
2010-12-14 16:55:31 -06:00
|
|
|
dev->cdev.ops = &skypopen_fops;
|
2010-12-11 13:19:49 -06:00
|
|
|
err = cdev_add (&dev->cdev, devno, 1);
|
|
|
|
/* Fail gracefully if need be */
|
|
|
|
if (err)
|
2010-12-14 16:55:31 -06:00
|
|
|
printk(KERN_NOTICE "Error %d adding skypopen%d", err, index);
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
int skypopen_init_module(void)
|
2010-12-11 13:19:49 -06:00
|
|
|
{
|
|
|
|
int result, i;
|
|
|
|
dev_t dev = 0;
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
printk("skypopen OSS driver loading (www.freeswitch.org)\n");
|
2010-12-11 13:19:49 -06:00
|
|
|
/*
|
|
|
|
* Get a range of minor numbers to work with, asking for a dynamic
|
|
|
|
* major unless directed otherwise at load time.
|
|
|
|
*/
|
2010-12-14 16:55:31 -06:00
|
|
|
if (skypopen_major) {
|
|
|
|
dev = MKDEV(skypopen_major, skypopen_minor);
|
|
|
|
result = register_chrdev_region(dev, skypopen_nr_devs, "dsp");
|
2010-12-11 13:19:49 -06:00
|
|
|
} else {
|
2010-12-14 16:55:31 -06:00
|
|
|
result = alloc_chrdev_region(&dev, skypopen_minor, skypopen_nr_devs,
|
2010-12-11 13:19:49 -06:00
|
|
|
"dsp");
|
2010-12-14 16:55:31 -06:00
|
|
|
skypopen_major = MAJOR(dev);
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
|
|
|
if (result < 0) {
|
2010-12-14 16:55:31 -06:00
|
|
|
printk(KERN_WARNING "skypopen OSS driver: can't get major %d\n", skypopen_major);
|
2010-12-11 13:19:49 -06:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate the devices -- we can't have them static, as the number
|
|
|
|
* can be specified at load time
|
|
|
|
*/
|
2010-12-14 16:55:31 -06:00
|
|
|
skypopen_devices = kmalloc(skypopen_nr_devs * sizeof(struct skypopen_dev), GFP_KERNEL);
|
|
|
|
if (!skypopen_devices) {
|
2010-12-11 13:19:49 -06:00
|
|
|
result = -ENOMEM;
|
|
|
|
goto fail; /* Make this more graceful */
|
|
|
|
}
|
2010-12-14 16:55:31 -06:00
|
|
|
memset(skypopen_devices, 0, skypopen_nr_devs * sizeof(struct skypopen_dev));
|
2010-12-11 13:19:49 -06:00
|
|
|
|
|
|
|
/* Initialize each device. */
|
2010-12-14 16:55:31 -06:00
|
|
|
for (i = 0; i < skypopen_nr_devs; i++) {
|
|
|
|
skypopen_setup_cdev(&skypopen_devices[i], i);
|
2010-12-11 13:19:49 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* At this point call the init function for any friend device */
|
2010-12-14 16:55:31 -06:00
|
|
|
dev = MKDEV(skypopen_major, skypopen_minor + skypopen_nr_devs);
|
2010-12-11 13:19:49 -06:00
|
|
|
return 0; /* succeed */
|
|
|
|
|
|
|
|
fail:
|
2010-12-14 16:55:31 -06:00
|
|
|
skypopen_cleanup_module();
|
2010-12-11 13:19:49 -06:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-12-14 16:55:31 -06:00
|
|
|
module_init(skypopen_init_module);
|
|
|
|
module_exit(skypopen_cleanup_module);
|