From 577d7b5538f3ce2017092cfec54532a10d3382b9 Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli Date: Sat, 11 Dec 2010 13:19:49 -0600 Subject: [PATCH] skypopen: added a proof of concept standalone OSS audio driver (for Skype-oss clients) --- src/mod/endpoints/mod_skypopen/oss/Makefile | 43 +++ src/mod/endpoints/mod_skypopen/oss/main.c | 335 ++++++++++++++++++ src/mod/endpoints/mod_skypopen/oss/scull.h | 64 ++++ src/mod/endpoints/mod_skypopen/oss/scull.init | 142 ++++++++ 4 files changed, 584 insertions(+) create mode 100644 src/mod/endpoints/mod_skypopen/oss/Makefile create mode 100644 src/mod/endpoints/mod_skypopen/oss/main.c create mode 100644 src/mod/endpoints/mod_skypopen/oss/scull.h create mode 100644 src/mod/endpoints/mod_skypopen/oss/scull.init diff --git a/src/mod/endpoints/mod_skypopen/oss/Makefile b/src/mod/endpoints/mod_skypopen/oss/Makefile new file mode 100644 index 0000000000..91cc61ba1e --- /dev/null +++ b/src/mod/endpoints/mod_skypopen/oss/Makefile @@ -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 index 0000000000..4d36ac00d5 --- /dev/null +++ b/src/mod/endpoints/mod_skypopen/oss/main.c @@ -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 +#include +#include + +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include + +#include /* cli(), *_flags */ +#include /* copy_*_user */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..e86281bd94 --- /dev/null +++ b/src/mod/endpoints/mod_skypopen/oss/scull.h @@ -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 /* 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 index 0000000000..e0523ce456 --- /dev/null +++ b/src/mod/endpoints/mod_skypopen/oss/scull.init @@ -0,0 +1,142 @@ +#!/bin/bash +# Sample init script for the a driver module + +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 +# group +# mode +# 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