/* ***************************************************************** * (C) Copyright 2007 Benjamin Krill * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * *****************************************************************/ #include #include #include #include #include #include #include "fpgafs.h" #define FPGAFS_MAX_LLDRV 3 static struct fpgafs_lldrv *lldrv[FPGAFS_MAX_LLDRV]; static struct fpgafs_lldrv *lldrv_cur; static int lldrv_count = 0x0; static DEFINE_SPINLOCK(fpgafs_lldrv_lock); struct fpga_context* alloc_fpga_context(void) { struct fpga_context *ctx; ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) goto out; /* initialize the struct*/ ctx->lldrv = -1; out: return ctx; } void free_fpga_context(struct fpga_context *ctx) { if (ctx->load_buf) kfree(ctx->load_buf); kfree(ctx); return; } ssize_t fpgafs_send_data(struct file *file, const char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->send(fcur, buf, len) : -EBUSY; } //EXPORT_SYMBOL_GPL(fpgafs_send_data); ssize_t fpgafs_recv_data(struct file *file, char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->recv(fcur, buf, len) : -EBUSY; } //EXPORT_SYMBOL_GPL(fpgafs_recv_data); ssize_t fpgafs_write_load(struct file *file, const char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->write_load(fcur, buf, len) : -EBUSY; } //EXPORT_SYMBOL_GPL(fpgafs_write_load); ssize_t fpgafs_read_load(struct file *file, char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->read_load(fcur, buf, len) : -EBUSY; } //EXPORT_SYMBOL_GPL(fpgafs_read_load); /* set/get current low level driver */ ssize_t fpgafs_read_lldrv(struct file *file, char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; size_t l = (len > 5)?5:len; if (fcur->lldrv > -1) { if (copy_to_user(buf, lldrv[fcur->lldrv]->name, l) < 0) return -EFAULT; } else { return -EBUSY; } return l; } //EXPORT_SYMBOL_GPL(fpgafs_read_lldrv); ssize_t fpgafs_write_lldrv(struct file *file, const char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; u32 cp = 0, i; u8 __user *usr; unsigned char tmp[5]; size_t l = (len > 5)?5:len; /* get name */ while (cp < l) { usr = (u8*)&buf[cp]; if (__get_user(tmp[cp], usr)) return -EFAULT; cp++; } for(i=0; i < FPGAFS_MAX_LLDRV; i++) if (lldrv[i] != NULL) { for (cp = 0; cp < l; cp++) { printk("%d: %c %c\n", cp, lldrv[i]->name[cp], tmp[cp]); if (lldrv[i]->name[cp] != tmp[cp]) break; } if (cp == l) { fcur->lldrv = i; printk("lldrv = %d\n", i); return l; } } fcur->lldrv = -1; return -1; } //EXPORT_SYMBOL_GPL(fpgafs_write_lldrv); ssize_t fpgafs_read_stat(struct file *file, char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->stat(fcur, buf, len) : -EBUSY; } ssize_t fpgafs_write_cmd(struct file *file, const char __user *buf, size_t len, loff_t *pos) { struct fpga_context *fcur = (struct fpga_context*)file->private_data; return (fcur->lldrv > -1) ? lldrv[fcur->lldrv]->cmd(fcur, buf, len) : -EBUSY; } /* low level un-/register functions */ int fpgafs_register_lldrv(struct fpgafs_lldrv *drv) { int i; spin_lock(&fpgafs_lldrv_lock); if (lldrv_count == FPGAFS_MAX_LLDRV) return -EBUSY; /* find free space */ for(i=0; i < FPGAFS_MAX_LLDRV; i++) if (lldrv[i] == NULL) { lldrv[i] = drv; break; } if (lldrv[i]->init) lldrv[i]->init(); lldrv_cur = lldrv[i]; lldrv_count++; spin_unlock(&fpgafs_lldrv_lock); return 0; } EXPORT_SYMBOL_GPL(fpgafs_register_lldrv); int fpgafs_unregister_lldrv(struct fpgafs_lldrv *drv) { unsigned long flags; int i,k; spin_lock_irqsave(&fpgafs_lldrv_lock, flags); for(i=0; i < FPGAFS_MAX_LLDRV; i++) { if (lldrv[i] == drv) { /* call the exit function */ if (lldrv[i]->exit) lldrv[i]->exit(); lldrv[i] = NULL; /* if current, search another low level driver */ if (lldrv_cur == drv) { lldrv_cur = NULL; for(k=0; k < FPGAFS_MAX_LLDRV; k++) if (lldrv[k]) { lldrv_cur = lldrv[k]; break; } } break; } } lldrv_count--; spin_unlock_irqrestore(&fpgafs_lldrv_lock, flags); return 0; } EXPORT_SYMBOL_GPL(fpgafs_unregister_lldrv);