/* * cdev.c * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: cdev.c,v 1.1 2004/03/11 03:59:31 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #define __KERNEL__ #include "includes.h" #include "adapter.h" #include "message.h" #include "cdev.h" #include "mvip_if.h" #include #define HAVEDMA 1 /* Global Data (yuck!) */ extern adapter_t *cards[]; extern int cinst; extern int devclass_id; extern void scm_lock(void); extern void scm_unlock(void); /* File operations callbacks */ int scm_lseek(struct inode *i, struct file *f, off_t o, int l) { return -ESPIPE; } int scm_read(struct inode *i, struct file *f, char *b, int l); int scm_write(struct inode *i, struct file *f, const char *b, int l); int scm_ioctl(struct inode *, struct file *, unsigned int, unsigned long); int scm_open(struct inode *, struct file *); void scm_release(struct inode *, struct file *); /* functions located elsewhere */ extern int boot_board(adapter_t *a); extern int stop_board(adapter_t *a); extern int scm_sndpkt(channel_t *, __u8 *, int); extern void scm_connect(channel_t *, char *); extern int scm_hangup(channel_t *); /* If we're using an older Linux release... */ #if (LINUX_VERSION_CODE < 0x020100) static unsigned long copy_from_user(void *t, const void *f, unsigned long n); static unsigned long copy_to_user(void *t, const void *f, unsigned long n); #endif static struct file_operations fops = { scm_lseek, scm_read, scm_write, 0, 0, scm_ioctl, 0, scm_open, scm_release, 0, 0, 0, 0 }; static char dev_name[] = "scm"; #define SCM_MAJOR 63 /* Pending a /proc replacement */ /* Register our user interface */ int cdev_init() { return register_chrdev(SCM_MAJOR, dev_name, &fops); } /* Destroy our user interface */ void cdev_cleanup() { unregister_chrdev(SCM_MAJOR, dev_name); } /* Maintain state information about each inode */ static bool locked[MAX_CARDS]; static char *stat_buf[MAX_CARDS]; static unsigned int stat_len[MAX_CARDS]; static off_t stat_offset[MAX_CARDS]; /* inode has opened, collect state info and lock file */ int scm_open(struct inode *i, struct file *f) { int nra = MINOR(i->i_rdev); char *buf; unsigned int *len; adapter_t *a; char state_buf[5]; if(nra >= cinst) return -ENODEV; if (locked[nra]) return -EBUSY; scm_lock(); locked[nra] = true; /* Create the status buffer on open */ stat_buf[nra] = (char *)get_free_page(GFP_KERNEL); if(!stat_buf[nra]) { locked[nra] = false; scm_unlock(); return -ENOMEM; } buf = stat_buf[nra]; len = &stat_len[nra]; a = cards[nra]; memset(state_buf, '\0', 5); memcpy(state_buf, (void *)a->r->state, 4); *len = sprintf(buf, "%-4s %6s %6s %6s %6s %5s\n", "dev", "cmd wr", "cmd rd", "rsp wr", "rsp rd", "state"); *len += sprintf(buf + *len, "%4s %0#6x %0#6x %0#6x %0#6x %5s\n", a->devicename, (unsigned short) a->r->cmd_write_ptr, (unsigned short) a->r->cmd_read_ptr, (unsigned short) a->r->rsp_write_ptr, (unsigned short) a->r->rsp_read_ptr, state_buf); return 0; } /* Inode has closed, free state info and unlock */ void scm_release(struct inode *i, struct file *f) { int nra = MINOR(i->i_rdev); stat_len[nra] = 0; stat_offset[nra] = 0; free_page((unsigned long)stat_buf[nra]); stat_buf[nra] = NULL; locked[nra] = false; scm_unlock(); } /* file is being read return state info from open */ int scm_read(struct inode *i, struct file *f, char *b, int l) { int nra = MINOR(i->i_rdev); int nl; /* EOF + Wrap */ if(stat_offset[nra] == stat_len[nra]) { stat_offset[nra] = 0; return 0; } nl = stat_len[nra] - stat_offset[nra]; if(nl > l) nl = l; copy_to_user(b, stat_buf[nra] + stat_offset[nra], nl); stat_offset[nra] += nl; return nl; } /* Image upload state information */ static bool uploading = false; static int img_size = 0; static int t_size = 0; /* An image is being uploaded */ int scm_write(struct inode *i, struct file *f, const char *b, int l) { int nra = MINOR(i->i_rdev); int nl = l, ii; char *blk = NULL; struct cmd_upload *cb; unsigned char csum = 0; response_t *rsp; bool failed = false; if(!uploading) { printk(KERN_ERR "write: not uploading!\n"); return -EPERM; } if(l > BLK_SIZE) nl = BLK_SIZE; #ifdef HAVEDMA blk = kmalloc(BLK_SIZE, GFP_KERNEL); if(!blk) { printk(KERN_ERR "write: no memory\n"); return -ENOMEM; } #else blk = cards[nra]->vrambase + 0x9000; #endif if(nl != 0) { copy_from_user(blk, b, nl); for(ii = 0 ; ii < nl ; ii++) csum += *(blk + ii); } cb = kmalloc(sizeof(struct cmd_upload), GFP_KERNEL); memset(cb, 0, sizeof(struct cmd_upload)); cb->size = nl; cb->addr = (__u32) blk; cb->cont = UPLOAD_CONTINUE; cb->mod_size = img_size; if(t_size - nl == 0 || nl == 0) { /* Last block */ uploading = false; cb->cont = UPLOAD_END; printk(KERN_INFO "Image load complete\n"); } rsp = msg_osload(cards[nra], cb); if(rsp->u.upload.checksum != csum) { printk(KERN_ERR "%s: Code upload checksum failure!! pos = %d, %#x\n", cards[nra]->devicename, t_size, rsp->u.upload.checksum); failed = true; } kfree(cb); t_size -= nl; #ifdef HAVEDMA kfree(blk); #endif return failed ? -EIO : nl; } int scm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) { int nra = MINOR(i->i_rdev); adapter_t *a = cards[nra]; response_t *rsp; switch(cmd) { case SCMIOC_RESET: printk(KERN_NOTICE "%s: Resetting device\n", a->devicename); a->r->sft_reset = 0x80000000; return 0; case SCMIOC_OSLOAD: if(!uploading && a->r->state[1] == '2') { printk(KERN_INFO "%s: Receiving OS image %ld bytes\n", a->devicename, arg); uploading = true; img_size = t_size = arg; return 0; } else return -EPERM; case SCMIOC_OSSTART: return boot_board(a); case SCMIOC_OSSTOP: return stop_board(a); case SCMIOC_ABORT: if(uploading) { /* Spoof an empty block */ scm_write(i, f, NULL, 0); return 0; } return -1; case SCMIOC_OSVER: rsp = msg_osversion(a); if(rsp == NULL) return -1; copy_to_user((void *)arg, rsp->u.raw, 8); return 0; } return -EINVAL; } #ifndef LINUX_VERSION_CODE #include #endif #if (LINUX_VERSION_CODE < 0x020100) #include static unsigned long copy_from_user(void *t, const void *f, unsigned long n) { int ret = 0; if((ret = verify_area(VERIFY_READ, f, n)) == 0) memcpy_fromfs(t, f, n); return ret; } static unsigned long copy_to_user(void *t, const void *f, unsigned long n) { int ret = 0; if((ret = verify_area(VERIFY_WRITE, t, n)) == 0) memcpy_tofs(t, f, n); return ret; } #else #include #endif