summaryrefslogtreecommitdiffstats
path: root/drivers/char/radio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/radio.c')
-rw-r--r--drivers/char/radio.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/char/radio.c b/drivers/char/radio.c
new file mode 100644
index 000000000..2b79e9872
--- /dev/null
+++ b/drivers/char/radio.c
@@ -0,0 +1,234 @@
+/*
+ * Radio Card Device Driver for Linux
+ *
+ * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+
+#include <linux/radio.h>
+#ifdef CONFIG_RADIO_RTRACK
+#include "rtrack.h"
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+#include "winradio.h"
+#endif
+
+int radio_open(struct inode *inode, struct file *file);
+int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+/* /dev/radio interface */
+static struct file_operations radio_fops = {
+ NULL, /* seek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ &radio_ioctl, /* ioctl */
+ NULL, /* mmap */
+ &radio_open, /* we're not allowed NULL, it seems... */
+ NULL /* release */
+};
+
+static struct miscdevice radio_miscdevice = {
+ RADIO_MINOR, /* minor device number */
+ "radio", /* title */
+ &radio_fops, /* file operations */
+ NULL, NULL /* previous and next (not our business) */
+};
+
+
+static struct radio_device *firstdevice, *lastdevice;
+static int numdevices;
+
+__initfunc(void radio_init(void))
+{
+ /* register the handler for the device number... */
+ if(misc_register(&radio_miscdevice)) {
+ printk("radio: couldn't register misc device\n");
+ return;
+ }
+
+ /* do some general initialisation stuff */
+ lastdevice = firstdevice = NULL;
+ numdevices = 0;
+
+#ifdef CONFIG_RADIO_RTRACK
+ radiotrack_init();
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+ printk("oooops. no winradio support yet... :-(\n");
+#endif
+/* etc.... */
+
+ printk("radio: registered %d devices\n", numdevices);
+}
+
+
+/* according to drivers/char/misc.c, the "open" call must not be NULL.
+ * I'm not sure if I approve, but I didn't write it, so...
+ */
+int radio_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+
+/* append a device to the linked list... */
+int radio_add_device(struct radio_device *newdev)
+{
+ if(firstdevice == NULL) {
+ firstdevice = newdev;
+ } else {
+ lastdevice->next = newdev;
+ }
+ lastdevice = newdev; numdevices++;
+ newdev->cap->dev_num=numdevices;
+ newdev->next = NULL; /* don't need, but... */
+ return(numdevices);
+}
+
+struct radio_device *getdev(int index)
+{
+struct radio_device *retval;
+
+ if(index > numdevices)
+ return NULL; /* let's have a bit less of that */
+
+ retval = firstdevice;
+ for(;index;index--)
+ retval = retval->next;
+
+ return retval;
+}
+
+
+/* interface routine */
+int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+struct radio_device *dev;
+struct radio_ctl *ctl_arg = (struct radio_ctl*)arg;
+int nowrite;
+int val, ret;
+
+ if((void*)arg == NULL)
+ return -EFAULT; /* XXX - check errnos are OK */
+
+
+ switch(cmd) {
+ case RADIO_NUMDEVS:
+ return (put_user(numdevices,(int*)arg) ? -EFAULT : 0);
+
+
+ case RADIO_GETCAPS:
+ /* p'raps I should verify for read then write ?? */
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap)))
+ return -EFAULT;
+ if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+ copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap));
+ return 0;
+
+
+ case RADIO_GETBNDCAP:
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band)))
+ return -EFAULT;
+
+ if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+
+ val = ((struct radio_band*)arg)->index;
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL; /* XXX errno */
+
+ copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)),
+ sizeof(struct radio_band));
+ return 0;
+ }
+
+
+/* now, we know that arg points to a struct radio_ctl */
+ /* get the requested device */
+ if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl)))
+ return -EFAULT;
+
+ if((dev = getdev(ctl_arg->dev_num)) == NULL)
+ return -EINVAL;
+
+ nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl));
+
+ val = ctl_arg->value;
+
+ switch(cmd) {
+ case RADIO_SETVOL:
+ if((val < dev->cap->volmin) || (val > dev->cap->volmax))
+ return -EINVAL;
+ if((ret = (*(dev->setvol))(dev, val)))
+ return ret;
+ dev->curvol = val;
+ return 0;
+
+ case RADIO_GETVOL:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curvol;
+ return 0;
+
+
+ case RADIO_SETBAND:
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL;
+ if((ret = (*(dev->setband))(dev, val)))
+ return ret;
+ dev->curband = val;
+ return 0;
+
+ case RADIO_GETBAND:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curband;
+ return 0;
+
+
+ case RADIO_SETFREQ: {
+ struct radio_band *bp;
+
+ bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band));
+ if((val < bp->freqmin) || (val > bp->freqmax))
+ return -EINVAL;
+ if((ret = (*(dev->setfreq))(dev, val)))
+ return ret;
+ dev->curfreq = val;
+ return 0;
+ }
+
+ case RADIO_GETFREQ:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curfreq;
+ return 0;
+
+
+ case RADIO_GETSIGSTR:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = (*(dev->getsigstr))(dev);
+ return 0;
+
+
+ default:
+ return -ENOSYS;
+ }
+}