]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
skypopen: added a proof of concept standalone OSS audio driver (for Skype-oss clients)
authorGiovanni Maruzzelli <gmaruzz@gmail.com>
Sat, 11 Dec 2010 19:19:49 +0000 (13:19 -0600)
committerGiovanni Maruzzelli <gmaruzz@gmail.com>
Sat, 11 Dec 2010 19:21:45 +0000 (13:21 -0600)
src/mod/endpoints/mod_skypopen/oss/Makefile [new file with mode: 0644]
src/mod/endpoints/mod_skypopen/oss/main.c [new file with mode: 0644]
src/mod/endpoints/mod_skypopen/oss/scull.h [new file with mode: 0644]
src/mod/endpoints/mod_skypopen/oss/scull.init [new file with mode: 0644]

diff --git a/src/mod/endpoints/mod_skypopen/oss/Makefile b/src/mod/endpoints/mod_skypopen/oss/Makefile
new file mode 100644 (file)
index 0000000..91cc61b
--- /dev/null
@@ -0,0 +1,43 @@
+# Comment/uncomment the following line to disable/enable debugging
+#DEBUG = y
+LDDINC=/usr/src/linux-headers-2.6.32-26-server/include
+
+# Add your debugging flag (or not) to CFLAGS
+ifeq ($(DEBUG),y)
+  DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
+else
+  DEBFLAGS = -O2 -Wall
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS)
+EXTRA_CFLAGS += -I$(LDDINC)
+
+ifneq ($(KERNELRELEASE),)
+# call from kernel build system
+
+scull-objs := main.o
+
+obj-m  := scull.o
+
+else
+
+KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+PWD       := $(shell pwd)
+
+modules:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
+
+endif
+
+
+
+clean:
+       rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
+
+depend .depend dep:
+       $(CC) $(EXTRA_CFLAGS) -M *.c > .depend
+
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/src/mod/endpoints/mod_skypopen/oss/main.c b/src/mod/endpoints/mod_skypopen/oss/main.c
new file mode 100644 (file)
index 0000000..4d36ac0
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * main.c -- the bare scull char module
+ *
+ * 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>
+
+#include "scull.h"             /* local definitions */
+
+/*
+ * Our parameters which can be set at load time.
+ */
+
+int scull_major =   SCULL_MAJOR;
+int scull_minor =   3;
+int scull_nr_devs = SCULL_NR_DEVS;     /* number of bare scull devices */
+
+module_param(scull_major, int, S_IRUGO);
+module_param(scull_minor, int, S_IRUGO);
+module_param(scull_nr_devs, int, S_IRUGO);
+
+MODULE_AUTHOR("Original: Alessandro Rubini, Jonathan Corbet. Heavy modified by: Giovanni Maruzzelli");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct scull_dev *scull_devices;        /* allocated in scull_init_module */
+
+#define GIOVA_BLK 3840
+#define GIOVA_SLEEP 40000
+
+void my_timer_callback_inq( unsigned long data )
+{
+       struct scull_dev *dev = (void *)data;
+
+       wake_up_interruptible(&dev->inq);
+       mod_timer( &dev->timer_inq, jiffies + msecs_to_jiffies(GIOVA_SLEEP/1000) );
+
+}
+
+void my_timer_callback_outq( unsigned long data )
+{
+       struct scull_dev *dev = (void *)data;
+
+       wake_up_interruptible(&dev->outq);
+       mod_timer( &dev->timer_outq, jiffies + msecs_to_jiffies(GIOVA_SLEEP/1000) );
+}
+
+/* The clone-specific data structure includes a key field */
+
+struct scull_listitem {
+       struct scull_dev device;
+       dev_t key;
+       struct list_head list;
+
+};
+
+/* The list of devices, and a lock to protect it */
+static LIST_HEAD(scull_c_list);
+static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED;
+
+/* Look for a device or create one if missing */
+static struct scull_dev *scull_c_lookfor_device(dev_t key)
+{
+       struct scull_listitem *lptr;
+
+       list_for_each_entry(lptr, &scull_c_list, list) {
+               if (lptr->key == key)
+                       return &(lptr->device);
+       }
+
+       /* not found */
+       lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL);
+       if (!lptr)
+               return NULL;
+
+       /* initialize the device */
+       memset(lptr, 0, sizeof(struct scull_listitem));
+       lptr->key = key;
+
+       init_waitqueue_head(&lptr->device.inq);
+       init_waitqueue_head(&lptr->device.outq);
+       printk(" Timer installing\n");
+       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 timer to fire in %dms (%ld)\n", GIOVA_SLEEP/1000, jiffies );
+       mod_timer( &lptr->device.timer_inq, jiffies + msecs_to_jiffies(GIOVA_SLEEP/1000) );
+       mod_timer( &lptr->device.timer_outq, jiffies + msecs_to_jiffies(GIOVA_SLEEP/1000) );
+       /* place it in the list */
+       list_add(&lptr->list, &scull_c_list);
+
+       return &(lptr->device);
+}
+static int scull_c_open(struct inode *inode, struct file *filp)
+{
+       struct scull_dev *dev;
+       dev_t key;
+
+       if (!current->pid) { 
+               printk("Process \"%s\" has no pid\n", current->comm);
+               return -EINVAL;
+       }
+       key = current->pid;
+
+       /* look for a scullc device in the list */
+       spin_lock(&scull_c_lock);
+       dev = scull_c_lookfor_device(key);
+       spin_unlock(&scull_c_lock);
+
+       if (!dev)
+               return -ENOMEM;
+
+       /* then, everything else is copied from the bare scull device */
+       filp->private_data = dev;
+       return 0;          /* success */
+}
+
+static int scull_c_release(struct inode *inode, struct file *filp)
+{
+       /*
+        * Nothing to do, because the device is persistent.
+        * A `real' cloned device should be freed on last close
+        */
+       return 0;
+}
+
+
+
+/*************************************************************/
+/*
+ * Open and close
+ */
+
+ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
+               loff_t *f_pos)
+{
+       struct scull_dev *dev = filp->private_data;
+
+               DEFINE_WAIT(wait);
+               prepare_to_wait(&dev->inq, &wait, TASK_INTERRUPTIBLE);
+                       schedule();
+               finish_wait(&dev->inq, &wait);
+       return count;
+
+}
+
+ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
+               loff_t *f_pos)
+{
+       struct scull_dev *dev = filp->private_data;
+               DEFINE_WAIT(wait);
+               prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
+                       schedule();
+               finish_wait(&dev->outq, &wait);
+
+       return count;
+
+}
+/*
+ * The ioctl() implementation
+ */
+
+int scull_ioctl(struct inode *inode, struct file *filp,
+               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:
+                       return put_user(GIOVA_BLK, p);
+               case SNDCTL_DSP_GETFMTS:
+                       return put_user(28731, p);
+
+               default:
+                       return 0;
+       }
+
+}
+
+struct file_operations scull_fops = {
+       .owner =    THIS_MODULE,
+       .llseek =   no_llseek,
+       .read =     scull_read,
+       .write =    scull_write,
+       .ioctl =    scull_ioctl,
+       .open =     scull_c_open,
+       .release =  scull_c_release,
+};
+
+/*
+ * 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
+ */
+
+void scull_cleanup_module(void)
+{
+       int i;
+       int ret;
+       struct scull_listitem *lptr, *next;
+       dev_t devno = MKDEV(scull_major, scull_minor);
+
+       /* Get rid of our char dev entries */
+       if (scull_devices) {
+               for (i = 0; i < scull_nr_devs; i++) {
+                       cdev_del(&scull_devices[i].cdev);
+               }
+               kfree(scull_devices);
+       }
+
+
+       /* And all the cloned devices */
+       list_for_each_entry_safe(lptr, next, &scull_c_list, list) {
+               ret= del_timer( &lptr->device.timer_inq );
+               if (ret) printk("The inq timer was still in use...\n");
+               ret= del_timer( &lptr->device.timer_outq );
+               if (ret) printk("The outq timer was still in use...\n");
+               list_del(&lptr->list);
+               kfree(lptr);
+       }
+               printk("Timer uninstalling\n");
+       /* cleanup_module is never called if registering failed */
+       unregister_chrdev_region(devno, scull_nr_devs);
+
+}
+
+
+/*
+ * Set up the char_dev structure for this device.
+ */
+static void scull_setup_cdev(struct scull_dev *dev, int index)
+{
+       int err, devno = MKDEV(scull_major, scull_minor + index);
+
+       cdev_init(&dev->cdev, &scull_fops);
+       dev->cdev.owner = THIS_MODULE;
+       dev->cdev.ops = &scull_fops;
+       err = cdev_add (&dev->cdev, devno, 1);
+       /* Fail gracefully if need be */
+       if (err)
+               printk(KERN_NOTICE "Error %d adding scull%d", err, index);
+}
+
+
+
+int scull_init_module(void)
+{
+       int result, i;
+       dev_t dev = 0;
+
+       /*
+        * Get a range of minor numbers to work with, asking for a dynamic
+        * major unless directed otherwise at load time.
+        */
+       if (scull_major) {
+               dev = MKDEV(scull_major, scull_minor);
+               result = register_chrdev_region(dev, scull_nr_devs, "dsp");
+       } else {
+               result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
+                               "dsp");
+               scull_major = MAJOR(dev);
+       }
+       if (result < 0) {
+               printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
+               return result;
+       }
+
+       /* 
+        * allocate the devices -- we can't have them static, as the number
+        * can be specified at load time
+        */
+       scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
+       if (!scull_devices) {
+               result = -ENOMEM;
+               goto fail;  /* Make this more graceful */
+       }
+       memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
+
+       /* Initialize each device. */
+       for (i = 0; i < scull_nr_devs; i++) {
+               scull_setup_cdev(&scull_devices[i], i);
+       }
+
+       /* At this point call the init function for any friend device */
+       dev = MKDEV(scull_major, scull_minor + scull_nr_devs);
+       return 0; /* succeed */
+
+fail:
+       scull_cleanup_module();
+       return result;
+}
+
+module_init(scull_init_module);
+module_exit(scull_cleanup_module);
diff --git a/src/mod/endpoints/mod_skypopen/oss/scull.h b/src/mod/endpoints/mod_skypopen/oss/scull.h
new file mode 100644 (file)
index 0000000..e86281b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * scull.h -- definitions for the char module
+ *
+ * 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.
+ *
+ * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $
+ */
+
+#ifndef _SCULL_H_
+#define _SCULL_H_
+
+#include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
+
+#ifndef SCULL_MAJOR
+#define SCULL_MAJOR 14   /* dynamic major by default */
+#endif
+
+#ifndef SCULL_NR_DEVS
+#define SCULL_NR_DEVS 1    /* scull0 through scull3 */
+#endif
+
+struct scull_dev {
+       struct cdev cdev;         /* Char device structure              */
+       wait_queue_head_t inq; /* read and write queues */
+       wait_queue_head_t outq; /* read and write queues */
+       struct timer_list timer_inq;
+       struct timer_list timer_outq;
+       //unsigned long read_howmany;
+       //unsigned long write_howmany;
+       //unsigned long read_sleeped_acc;
+       //unsigned long write_sleeped_acc;
+       //double read_delay; /* how much delay last time */
+       //double write_delay; /* how much delay last time */
+};
+
+
+/*
+ * The different configurable parameters
+ */
+extern int scull_major;     /* main.c */
+extern int scull_nr_devs;
+
+
+/*
+ * Prototypes for shared functions
+ */
+
+ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
+                   loff_t *f_pos);
+ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
+                    loff_t *f_pos);
+int     scull_ioctl(struct inode *inode, struct file *filp,
+                    unsigned int cmd, unsigned long arg);
+
+#endif /* _SCULL_H_ */
diff --git a/src/mod/endpoints/mod_skypopen/oss/scull.init b/src/mod/endpoints/mod_skypopen/oss/scull.init
new file mode 100644 (file)
index 0000000..e0523ce
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/bash
+# Sample init script for the a driver module <rubini@linux.it>
+
+DEVICE="scull"
+SECTION="misc"
+
+# The list of filenames and minor numbers: $PREFIX is prefixed to all names
+PREFIX="scull"
+FILES="     0 0         1 1         2 2        3 3    priv 16 
+        pipe0 32    pipe1 33    pipe2 34   pipe3 35
+       single 48      uid 64     wuid 80"
+
+INSMOD=/sbin/insmod; # use /sbin/modprobe if you prefer
+
+function device_specific_post_load () {
+    true; # fill at will
+}
+function device_specific_pre_unload () {
+    true; # fill at will
+}
+
+# Everything below this line should work unchanged for any char device.
+# Obviously, however, no options on the command line: either in
+# /etc/${DEVICE}.conf or /etc/modules.conf (if modprobe is used)
+
+# Optional configuration file: format is
+#    owner  <ownername>
+#    group  <groupname>
+#    mode   <modename>
+#    options <insmod options>
+CFG=/etc/${DEVICE}.conf
+
+# kernel version, used to look for modules
+KERNEL=`uname -r`
+
+#FIXME: it looks like there is no misc section. Where should it be?
+MODDIR="/lib/modules/${KERNEL}/kernel/drivers/${SECTION}"
+if [ ! -d $MODDIR ]; then MODDIR="/lib/modules/${KERNEL}/${SECTION}"; fi
+
+# Root or die
+if [ "$(id -u)" != "0" ]
+then
+  echo "You must be root to load or unload kernel modules"
+  exit 1
+fi
+
+# Read configuration file
+if [ -r $CFG ]; then
+    OWNER=`awk "\\$1==\"owner\" {print \\$2}" $CFG`
+    GROUP=`awk "\\$1==\"group\" {print \\$2}" $CFG`
+    MODE=`awk "\\$1==\"mode\" {print \\$2}" $CFG`
+    # The options string may include extra blanks or only blanks
+    OPTIONS=`sed -n '/^options / s/options //p' $CFG`
+fi
+
+
+# Create device files
+function create_files () {
+    cd /dev
+    local devlist=""
+    local file
+    while true; do
+       if [ $# -lt 2 ]; then break; fi
+       file="${DEVICE}$1"
+       mknod $file c $MAJOR $2
+       devlist="$devlist $file"
+       shift 2
+    done
+    if [ -n "$OWNER" ]; then chown $OWNER $devlist; fi
+    if [ -n "$GROUP" ]; then chgrp $GROUP $devlist; fi
+    if [ -n "$MODE"  ]; then chmod $MODE  $devlist; fi
+}
+
+# Remove device files
+function remove_files () {
+    cd /dev
+    local devlist=""
+    local file
+    while true; do
+       if [ $# -lt 2 ]; then break; fi
+       file="${DEVICE}$1"
+       devlist="$devlist $file"
+       shift 2
+    done
+    rm -f $devlist
+}
+
+# Load and create files
+function load_device () {
+    
+    if [ -f $MODDIR/$DEVICE.o ]; then
+       devpath=$MODDIR/$DEVICE.o
+    else if [ -f ./$DEVICE.o ]; then
+       devpath=./$DEVICE.o
+    else
+       devpath=$DEVICE; # let insmod/modprobe guess
+    fi; fi
+    if [ "$devpath" != "$DEVICE" ]; then
+       echo -n " (loading file $devpath)"
+    fi
+
+    if $INSMOD $devpath $OPTIONS; then
+       MAJOR=`awk "\\$2==\"$DEVICE\" {print \\$1}" /proc/devices`
+       remove_files $FILES
+       create_files $FILES
+       device_specific_post_load
+    else
+       echo " FAILED!"
+     fi
+}
+
+# Unload and remove files
+function unload_device () {
+    device_specific_pre_unload 
+    /sbin/rmmod $DEVICE
+    remove_files $FILES
+}
+
+
+case "$1" in
+  start)
+     echo -n "Loading $DEVICE"
+     load_device
+     echo "."
+     ;;
+  stop)
+     echo -n "Unloading $DEVICE"
+     unload_device
+     echo "."
+     ;;
+  force-reload|restart)
+     echo -n "Reloading $DEVICE"
+     unload_device
+     load_device
+     echo "."
+     ;;
+  *)
+     echo "Usage: $0 {start|stop|restart|force-reload}"
+     exit 1
+esac
+
+exit 0