linux/arch/mips/kernel/rtlx.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
   7 * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
   8 * Copyright (C) 2013 Imagination Technologies Ltd.
   9 */
  10#include <linux/kernel.h>
  11#include <linux/fs.h>
  12#include <linux/syscalls.h>
  13#include <linux/moduleloader.h>
  14#include <linux/atomic.h>
  15#include <linux/sched/signal.h>
  16
  17#include <asm/mipsmtregs.h>
  18#include <asm/mips_mt.h>
  19#include <asm/processor.h>
  20#include <asm/rtlx.h>
  21#include <asm/setup.h>
  22#include <asm/vpe.h>
  23
  24static int sp_stopping;
  25struct rtlx_info *rtlx;
  26struct chan_waitqueues channel_wqs[RTLX_CHANNELS];
  27struct vpe_notifications rtlx_notify;
  28void (*aprp_hook)(void) = NULL;
  29EXPORT_SYMBOL(aprp_hook);
  30
  31static void __used dump_rtlx(void)
  32{
  33        int i;
  34
  35        pr_info("id 0x%lx state %d\n", rtlx->id, rtlx->state);
  36
  37        for (i = 0; i < RTLX_CHANNELS; i++) {
  38                struct rtlx_channel *chan = &rtlx->channel[i];
  39
  40                pr_info(" rt_state %d lx_state %d buffer_size %d\n",
  41                        chan->rt_state, chan->lx_state, chan->buffer_size);
  42
  43                pr_info(" rt_read %d rt_write %d\n",
  44                        chan->rt_read, chan->rt_write);
  45
  46                pr_info(" lx_read %d lx_write %d\n",
  47                        chan->lx_read, chan->lx_write);
  48
  49                pr_info(" rt_buffer <%s>\n", chan->rt_buffer);
  50                pr_info(" lx_buffer <%s>\n", chan->lx_buffer);
  51        }
  52}
  53
  54/* call when we have the address of the shared structure from the SP side. */
  55static int rtlx_init(struct rtlx_info *rtlxi)
  56{
  57        if (rtlxi->id != RTLX_ID) {
  58                pr_err("no valid RTLX id at 0x%p 0x%lx\n", rtlxi, rtlxi->id);
  59                return -ENOEXEC;
  60        }
  61
  62        rtlx = rtlxi;
  63
  64        return 0;
  65}
  66
  67/* notifications */
  68void rtlx_starting(int vpe)
  69{
  70        int i;
  71        sp_stopping = 0;
  72
  73        /* force a reload of rtlx */
  74        rtlx = NULL;
  75
  76        /* wake up any sleeping rtlx_open's */
  77        for (i = 0; i < RTLX_CHANNELS; i++)
  78                wake_up_interruptible(&channel_wqs[i].lx_queue);
  79}
  80
  81void rtlx_stopping(int vpe)
  82{
  83        int i;
  84
  85        sp_stopping = 1;
  86        for (i = 0; i < RTLX_CHANNELS; i++)
  87                wake_up_interruptible(&channel_wqs[i].lx_queue);
  88}
  89
  90
  91int rtlx_open(int index, int can_sleep)
  92{
  93        struct rtlx_info **p;
  94        struct rtlx_channel *chan;
  95        enum rtlx_state state;
  96        int ret = 0;
  97
  98        if (index >= RTLX_CHANNELS) {
  99                pr_debug("rtlx_open index out of range\n");
 100                return -ENOSYS;
 101        }
 102
 103        if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
 104                pr_debug("rtlx_open channel %d already opened\n", index);
 105                ret = -EBUSY;
 106                goto out_fail;
 107        }
 108
 109        if (rtlx == NULL) {
 110                p = vpe_get_shared(aprp_cpu_index());
 111                if (p == NULL) {
 112                        if (can_sleep) {
 113                                ret = __wait_event_interruptible(
 114                                        channel_wqs[index].lx_queue,
 115                                        (p = vpe_get_shared(aprp_cpu_index())));
 116                                if (ret)
 117                                        goto out_fail;
 118                        } else {
 119                                pr_debug("No SP program loaded, and device opened with O_NONBLOCK\n");
 120                                ret = -ENOSYS;
 121                                goto out_fail;
 122                        }
 123                }
 124
 125                smp_rmb();
 126                if (*p == NULL) {
 127                        if (can_sleep) {
 128                                DEFINE_WAIT(wait);
 129
 130                                for (;;) {
 131                                        prepare_to_wait(
 132                                                &channel_wqs[index].lx_queue,
 133                                                &wait, TASK_INTERRUPTIBLE);
 134                                        smp_rmb();
 135                                        if (*p != NULL)
 136                                                break;
 137                                        if (!signal_pending(current)) {
 138                                                schedule();
 139                                                continue;
 140                                        }
 141                                        ret = -ERESTARTSYS;
 142                                        goto out_fail;
 143                                }
 144                                finish_wait(&channel_wqs[index].lx_queue,
 145                                            &wait);
 146                        } else {
 147                                pr_err(" *vpe_get_shared is NULL. Has an SP program been loaded?\n");
 148                                ret = -ENOSYS;
 149                                goto out_fail;
 150                        }
 151                }
 152
 153                if ((unsigned int)*p < KSEG0) {
 154                        pr_warn("vpe_get_shared returned an invalid pointer maybe an error code %d\n",
 155                                (int)*p);
 156                        ret = -ENOSYS;
 157                        goto out_fail;
 158                }
 159
 160                ret = rtlx_init(*p);
 161                if (ret < 0)
 162                        goto out_ret;
 163        }
 164
 165        chan = &rtlx->channel[index];
 166
 167        state = xchg(&chan->lx_state, RTLX_STATE_OPENED);
 168        if (state == RTLX_STATE_OPENED) {
 169                ret = -EBUSY;
 170                goto out_fail;
 171        }
 172
 173out_fail:
 174        smp_mb();
 175        atomic_dec(&channel_wqs[index].in_open);
 176        smp_mb();
 177
 178out_ret:
 179        return ret;
 180}
 181
 182int rtlx_release(int index)
 183{
 184        if (rtlx == NULL) {
 185                pr_err("rtlx_release() with null rtlx\n");
 186                return 0;
 187        }
 188        rtlx->channel[index].lx_state = RTLX_STATE_UNUSED;
 189        return 0;
 190}
 191
 192unsigned int rtlx_read_poll(int index, int can_sleep)
 193{
 194        struct rtlx_channel *chan;
 195
 196        if (rtlx == NULL)
 197                return 0;
 198
 199        chan = &rtlx->channel[index];
 200
 201        /* data available to read? */
 202        if (chan->lx_read == chan->lx_write) {
 203                if (can_sleep) {
 204                        int ret = __wait_event_interruptible(
 205                                channel_wqs[index].lx_queue,
 206                                (chan->lx_read != chan->lx_write) ||
 207                                sp_stopping);
 208                        if (ret)
 209                                return ret;
 210
 211                        if (sp_stopping)
 212                                return 0;
 213                } else
 214                        return 0;
 215        }
 216
 217        return (chan->lx_write + chan->buffer_size - chan->lx_read)
 218               % chan->buffer_size;
 219}
 220
 221static inline int write_spacefree(int read, int write, int size)
 222{
 223        if (read == write) {
 224                /*
 225                 * Never fill the buffer completely, so indexes are always
 226                 * equal if empty and only empty, or !equal if data available
 227                 */
 228                return size - 1;
 229        }
 230
 231        return ((read + size - write) % size) - 1;
 232}
 233
 234unsigned int rtlx_write_poll(int index)
 235{
 236        struct rtlx_channel *chan = &rtlx->channel[index];
 237
 238        return write_spacefree(chan->rt_read, chan->rt_write,
 239                                chan->buffer_size);
 240}
 241
 242ssize_t rtlx_read(int index, void __user *buff, size_t count)
 243{
 244        size_t lx_write, fl = 0L;
 245        struct rtlx_channel *lx;
 246        unsigned long failed;
 247
 248        if (rtlx == NULL)
 249                return -ENOSYS;
 250
 251        lx = &rtlx->channel[index];
 252
 253        mutex_lock(&channel_wqs[index].mutex);
 254        smp_rmb();
 255        lx_write = lx->lx_write;
 256
 257        /* find out how much in total */
 258        count = min(count,
 259                     (size_t)(lx_write + lx->buffer_size - lx->lx_read)
 260                     % lx->buffer_size);
 261
 262        /* then how much from the read pointer onwards */
 263        fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
 264
 265        failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
 266        if (failed)
 267                goto out;
 268
 269        /* and if there is anything left at the beginning of the buffer */
 270        if (count - fl)
 271                failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
 272
 273out:
 274        count -= failed;
 275
 276        smp_wmb();
 277        lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
 278        smp_wmb();
 279        mutex_unlock(&channel_wqs[index].mutex);
 280
 281        return count;
 282}
 283
 284ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
 285{
 286        struct rtlx_channel *rt;
 287        unsigned long failed;
 288        size_t rt_read;
 289        size_t fl;
 290
 291        if (rtlx == NULL)
 292                return -ENOSYS;
 293
 294        rt = &rtlx->channel[index];
 295
 296        mutex_lock(&channel_wqs[index].mutex);
 297        smp_rmb();
 298        rt_read = rt->rt_read;
 299
 300        /* total number of bytes to copy */
 301        count = min_t(size_t, count, write_spacefree(rt_read, rt->rt_write,
 302                                                     rt->buffer_size));
 303
 304        /* first bit from write pointer to the end of the buffer, or count */
 305        fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
 306
 307        failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
 308        if (failed)
 309                goto out;
 310
 311        /* if there's any left copy to the beginning of the buffer */
 312        if (count - fl)
 313                failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
 314
 315out:
 316        count -= failed;
 317
 318        smp_wmb();
 319        rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
 320        smp_wmb();
 321        mutex_unlock(&channel_wqs[index].mutex);
 322
 323        _interrupt_sp();
 324
 325        return count;
 326}
 327
 328
 329static int file_open(struct inode *inode, struct file *filp)
 330{
 331        return rtlx_open(iminor(inode), (filp->f_flags & O_NONBLOCK) ? 0 : 1);
 332}
 333
 334static int file_release(struct inode *inode, struct file *filp)
 335{
 336        return rtlx_release(iminor(inode));
 337}
 338
 339static __poll_t file_poll(struct file *file, poll_table *wait)
 340{
 341        int minor = iminor(file_inode(file));
 342        __poll_t mask = 0;
 343
 344        poll_wait(file, &channel_wqs[minor].rt_queue, wait);
 345        poll_wait(file, &channel_wqs[minor].lx_queue, wait);
 346
 347        if (rtlx == NULL)
 348                return 0;
 349
 350        /* data available to read? */
 351        if (rtlx_read_poll(minor, 0))
 352                mask |= EPOLLIN | EPOLLRDNORM;
 353
 354        /* space to write */
 355        if (rtlx_write_poll(minor))
 356                mask |= EPOLLOUT | EPOLLWRNORM;
 357
 358        return mask;
 359}
 360
 361static ssize_t file_read(struct file *file, char __user *buffer, size_t count,
 362                         loff_t *ppos)
 363{
 364        int minor = iminor(file_inode(file));
 365
 366        /* data available? */
 367        if (!rtlx_read_poll(minor, (file->f_flags & O_NONBLOCK) ? 0 : 1))
 368                return 0;       /* -EAGAIN makes 'cat' whine */
 369
 370        return rtlx_read(minor, buffer, count);
 371}
 372
 373static ssize_t file_write(struct file *file, const char __user *buffer,
 374                          size_t count, loff_t *ppos)
 375{
 376        int minor = iminor(file_inode(file));
 377
 378        /* any space left... */
 379        if (!rtlx_write_poll(minor)) {
 380                int ret;
 381
 382                if (file->f_flags & O_NONBLOCK)
 383                        return -EAGAIN;
 384
 385                ret = __wait_event_interruptible(channel_wqs[minor].rt_queue,
 386                                           rtlx_write_poll(minor));
 387                if (ret)
 388                        return ret;
 389        }
 390
 391        return rtlx_write(minor, buffer, count);
 392}
 393
 394const struct file_operations rtlx_fops = {
 395        .owner =   THIS_MODULE,
 396        .open =    file_open,
 397        .release = file_release,
 398        .write =   file_write,
 399        .read =    file_read,
 400        .poll =    file_poll,
 401        .llseek =  noop_llseek,
 402};
 403
 404module_init(rtlx_module_init);
 405module_exit(rtlx_module_exit);
 406
 407MODULE_DESCRIPTION("MIPS RTLX");
 408MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
 409MODULE_LICENSE("GPL");
 410