rtlx.c (8924B)
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");