skypopen: added a proof of concept standalone OSS audio driver (for Skype-oss clients)
This commit is contained in:
parent
009c41d460
commit
577d7b5538
|
@ -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
|
|
@ -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);
|
|
@ -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_ */
|
|
@ -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
|
Loading…
Reference in New Issue