#include <linux/vmalloc.h>
#include <linux/elf.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/syscalls.h>
#include <linux/moduleloader.h>
#include <linux/interrupt.h>
#include <asm/vpe.h>
#include <asm/rtlx.h>
-#define RTLX_TARG_VPE 1
-
static struct rtlx_info *rtlx;
static int major;
static char module_name[] = "rtlx";
wait_queue_head_t rt_queue;
wait_queue_head_t lx_queue;
atomic_t in_open;
+ struct mutex mutex;
} channel_wqs[RTLX_CHANNELS];
-static struct irqaction irq;
-static int irq_num;
static struct vpe_notifications notify;
static int sp_stopping = 0;
static irqreturn_t rtlx_interrupt(int irq, void *dev_id)
{
int i;
+ unsigned int flags, vpeflags;
+
+ /* Ought not to be strictly necessary for SMTC builds */
+ local_irq_save(flags);
+ vpeflags = dvpe();
+ set_c0_status(0x100 << MIPS_CPU_RTLX_IRQ);
+ irq_enable_hazard();
+ evpe(vpeflags);
+ local_irq_restore(flags);
for (i = 0; i < RTLX_CHANNELS; i++) {
wake_up(&channel_wqs[i].lx_queue);
return IRQ_HANDLED;
}
-static __attribute_used__ void dump_rtlx(void)
+static void __used dump_rtlx(void)
{
int i;
static int rtlx_init(struct rtlx_info *rtlxi)
{
if (rtlxi->id != RTLX_ID) {
- printk(KERN_ERR "no valid RTLX id at 0x%p 0x%x\n", rtlxi, rtlxi->id);
+ printk(KERN_ERR "no valid RTLX id at 0x%p 0x%lx\n",
+ rtlxi, rtlxi->id);
return -ENOEXEC;
}
int rtlx_open(int index, int can_sleep)
{
- volatile struct rtlx_info **p;
+ struct rtlx_info **p;
struct rtlx_channel *chan;
enum rtlx_state state;
int ret = 0;
}
if (rtlx == NULL) {
- if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
- if (can_sleep) {
- __wait_event_interruptible(channel_wqs[index].lx_queue,
- (p = vpe_get_shared(RTLX_TARG_VPE)),
- ret);
- if (ret)
- goto out_fail;
- } else {
- printk(KERN_DEBUG "No SP program loaded, and device "
- "opened with O_NONBLOCK\n");
- ret = -ENOSYS;
+ if( (p = vpe_get_shared(tclimit)) == NULL) {
+ if (can_sleep) {
+ __wait_event_interruptible(channel_wqs[index].lx_queue,
+ (p = vpe_get_shared(tclimit)), ret);
+ if (ret)
goto out_fail;
- }
+ } else {
+ printk(KERN_DEBUG "No SP program loaded, and device "
+ "opened with O_NONBLOCK\n");
+ ret = -ENOSYS;
+ goto out_fail;
+ }
}
+ smp_rmb();
if (*p == NULL) {
if (can_sleep) {
- __wait_event_interruptible(channel_wqs[index].lx_queue,
- *p != NULL,
- ret);
- if (ret)
+ DEFINE_WAIT(wait);
+
+ for (;;) {
+ prepare_to_wait(
+ &channel_wqs[index].lx_queue,
+ &wait, TASK_INTERRUPTIBLE);
+ smp_rmb();
+ if (*p != NULL)
+ break;
+ if (!signal_pending(current)) {
+ schedule();
+ continue;
+ }
+ ret = -ERESTARTSYS;
goto out_fail;
+ }
+ finish_wait(&channel_wqs[index].lx_queue, &wait);
} else {
- printk(" *vpe_get_shared is NULL. "
+ pr_err(" *vpe_get_shared is NULL. "
"Has an SP program been loaded?\n");
ret = -ENOSYS;
goto out_fail;
}
if ((unsigned int)*p < KSEG0) {
- printk(KERN_WARNING "vpe_get_shared returned an invalid pointer "
- "maybe an error code %d\n", (int)*p);
+ printk(KERN_WARNING "vpe_get_shared returned an "
+ "invalid pointer maybe an error code %d\n",
+ (int)*p);
ret = -ENOSYS;
goto out_fail;
}
int rtlx_release(int index)
{
+ if (rtlx == NULL) {
+ pr_err("rtlx_release() with null rtlx\n");
+ return 0;
+ }
rtlx->channel[index].lx_state = RTLX_STATE_UNUSED;
return 0;
}
int ret = 0;
__wait_event_interruptible(channel_wqs[index].lx_queue,
- chan->lx_read != chan->lx_write || sp_stopping,
- ret);
+ (chan->lx_read != chan->lx_write) ||
+ sp_stopping, ret);
if (ret)
return ret;
unsigned int rtlx_write_poll(int index)
{
struct rtlx_channel *chan = &rtlx->channel[index];
- return write_spacefree(chan->rt_read, chan->rt_write, chan->buffer_size);
-}
-static inline void copy_to(void *dst, void *src, size_t count, int user)
-{
- if (user)
- copy_to_user(dst, src, count);
- else
- memcpy(dst, src, count);
+ return write_spacefree(chan->rt_read, chan->rt_write,
+ chan->buffer_size);
}
-static inline void copy_from(void *dst, void *src, size_t count, int user)
+ssize_t rtlx_read(int index, void __user *buff, size_t count)
{
- if (user)
- copy_from_user(dst, src, count);
- else
- memcpy(dst, src, count);
-}
-
-ssize_t rtlx_read(int index, void *buff, size_t count, int user)
-{
- size_t fl = 0L;
+ size_t lx_write, fl = 0L;
struct rtlx_channel *lx;
+ unsigned long failed;
if (rtlx == NULL)
return -ENOSYS;
lx = &rtlx->channel[index];
+ mutex_lock(&channel_wqs[index].mutex);
+ smp_rmb();
+ lx_write = lx->lx_write;
+
/* find out how much in total */
count = min(count,
- (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read)
+ (size_t)(lx_write + lx->buffer_size - lx->lx_read)
% lx->buffer_size);
/* then how much from the read pointer onwards */
- fl = min( count, (size_t)lx->buffer_size - lx->lx_read);
+ fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
- copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user);
+ failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
+ if (failed)
+ goto out;
/* and if there is anything left at the beginning of the buffer */
- if ( count - fl )
- copy_to (buff + fl, lx->lx_buffer, count - fl, user);
+ if (count - fl)
+ failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
+
+out:
+ count -= failed;
- /* update the index */
- lx->lx_read += count;
- lx->lx_read %= lx->buffer_size;
+ smp_wmb();
+ lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
+ smp_wmb();
+ mutex_unlock(&channel_wqs[index].mutex);
return count;
}
-ssize_t rtlx_write(int index, void *buffer, size_t count, int user)
+ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
{
struct rtlx_channel *rt;
+ unsigned long failed;
+ size_t rt_read;
size_t fl;
if (rtlx == NULL)
rt = &rtlx->channel[index];
+ mutex_lock(&channel_wqs[index].mutex);
+ smp_rmb();
+ rt_read = rt->rt_read;
+
/* total number of bytes to copy */
- count = min(count,
- (size_t)write_spacefree(rt->rt_read, rt->rt_write,
- rt->buffer_size));
+ count = min(count, (size_t)write_spacefree(rt_read, rt->rt_write,
+ rt->buffer_size));
/* first bit from write pointer to the end of the buffer, or count */
fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
- copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user);
+ failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
+ if (failed)
+ goto out;
/* if there's any left copy to the beginning of the buffer */
- if( count - fl )
- copy_from (rt->rt_buffer, buffer + fl, count - fl, user);
+ if (count - fl) {
+ failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
+ }
+
+out:
+ count -= failed;
- rt->rt_write += count;
- rt->rt_write %= rt->buffer_size;
+ smp_wmb();
+ rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
+ smp_wmb();
+ mutex_unlock(&channel_wqs[index].mutex);
- return(count);
+ return count;
}
static int file_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
+ int err;
- return rtlx_open(minor, (filp->f_flags & O_NONBLOCK) ? 0 : 1);
+ lock_kernel();
+ err = rtlx_open(minor, (filp->f_flags & O_NONBLOCK) ? 0 : 1);
+ unlock_kernel();
+ return err;
}
static int file_release(struct inode *inode, struct file *filp)
return 0; // -EAGAIN makes cat whinge
}
- return rtlx_read(minor, buffer, count, 1);
+ return rtlx_read(minor, buffer, count);
}
static ssize_t file_write(struct file *file, const char __user * buffer,
return ret;
}
- return rtlx_write(minor, (void *)buffer, count, 1);
+ return rtlx_write(minor, buffer, count);
}
static const struct file_operations rtlx_fops = {
static char register_chrdev_failed[] __initdata =
KERN_ERR "rtlx_module_init: unable to register device\n";
-static int rtlx_module_init(void)
+static int __init rtlx_module_init(void)
{
struct device *dev;
int i, err;
+ if (!cpu_has_mipsmt) {
+ printk("VPE loader: not a MIPS MT capable processor\n");
+ return -ENODEV;
+ }
+
+ if (tclimit == 0) {
+ printk(KERN_WARNING "No TCs reserved for AP/SP, not "
+ "initializing RTLX.\nPass maxtcs=<n> argument as kernel "
+ "argument\n");
+
+ return -ENODEV;
+ }
+
major = register_chrdev(0, module_name, &rtlx_fops);
if (major < 0) {
printk(register_chrdev_failed);
init_waitqueue_head(&channel_wqs[i].rt_queue);
init_waitqueue_head(&channel_wqs[i].lx_queue);
atomic_set(&channel_wqs[i].in_open, 0);
+ mutex_init(&channel_wqs[i].mutex);
dev = device_create(mt_class, NULL, MKDEV(major, i),
"%s%d", module_name, i);
/* set up notifiers */
notify.start = starting;
notify.stop = stopping;
- vpe_notify(RTLX_TARG_VPE, ¬ify);
+ vpe_notify(tclimit, ¬ify);
if (cpu_has_vint)
set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
+ else {
+ pr_err("APRP RTLX init on non-vectored-interrupt processor\n");
+ err = -ENODEV;
+ goto out_chrdev;
+ }
rtlx_irq.dev_id = rtlx;
setup_irq(rtlx_irq_num, &rtlx_irq);