linux/drivers/mcst/mgpm/mgpm.c

1431 lines
34 KiB
C

/*
*
*
*
*/
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/namei.h>
#include <linux/fs_struct.h>
#include <linux/mount.h>
#include <linux/security.h>
// for mknod
#include <linux/err.h>
#include <linux/namei.h>
#include <linux/audit.h>
// end for mknod
/*
#ifndef CONFIG_PCI
# error "This driver needs PCI support to be available"
#endif
*/
#include <linux/mcst/mgpm_io.h>
#include <linux/mcst/mgpm.h>
#include <linux/mcst/mcst_selftest.h>
#define MAX_DRV_NM_SZ 64
// /proc/sys/debug/mgpm_debug trigger
int mgpm_debug = 0;
#define DBGMGPM_MODE
#undef DBGMGPM_MODE
#if defined(DBGMGPM_MODE)
#define dbgmgpm printk
#else
#define dbgmgpm if ( mgpm_debug ) printk
#endif
extern struct dentry *lookup_hash(struct nameidata *nd);
int drv_unlink(char *dir, char *filename);
static int drv_mknod(char *filename, int mode, dev_t dev);
static int drv_mkdir(char *pathname, int mode);
static int drv_create_dir(char *dir);
int drv_create_minor(char *dir, char *name, int type, dev_t dev);
static int may_mknod(mode_t mode);
#if defined(VER_2614)
int mk_mknod(char *filename, int mode, dev_t dev);
int mk_mkdir(char *pathname, int mode);
int mk_rm_dir(char *dir);
int mk_unlink(char *filename);
#endif
#if defined(CONFIG_SYSCTL)
#include <linux/sysctl.h>
static ctl_table mgpm_table[] = {
{
.procname = "mgpm_debug",
.data = &mgpm_debug,
.maxlen = sizeof(mgpm_debug),
.mode = 0666,
.proc_handler = proc_dointvec,
},
{ }
};
static ctl_table mgpm_root_table[] = {
{
.procname = "debug",
.maxlen = 0,
.mode = 0555,
.child = mgpm_table,
},
{ }
};
static struct ctl_table_header *mgpm_sysctl_header;
static void __init mgpm_sysctl_register(void)
{
mgpm_sysctl_header = register_sysctl_table(mgpm_root_table);
}
static void mgpm_sysctl_unregister(void)
{
if ( mgpm_sysctl_header )
unregister_sysctl_table(mgpm_sysctl_header);
}
#else /* CONFIG_SYSCTL */
static void __init mgpm_sysctl_register(void)
{
}
static void mgpm_sysctl_unregister(void)
{
}
#endif
/* Regs acces to mgpm regs */
void put_mgpm_reg(mgpm_dev_t *dev,int reg,int val) {
char *addr = (char *)dev->device_mem_start;
u32 *reg_adr = (u32 *)(addr + reg);
*reg_adr = val;
wmb();
//printk("put_mgpm_reg reg 0x%x val 0x%x\n",(int)reg_adr,val);
}
int get_mgpm_reg(mgpm_dev_t *dev,int reg) {
char *addr = (char *)dev->device_mem_start;
int value;
addr += reg;
value = *(u32*)addr;
//printk("get_mgpm_reg reg 0x%x val 0x%x\n",(int)addr,value);
return (value);
}
/**/
irqreturn_t mgpm_intr_handler(int irq, void *arg);
static ssize_t mgpm_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
//static char dev_name[] = "MCST,mgpm";
#define MGPM_NAME "MCST,mgpm"
#define MGPM_DIR "mgpm"
static int mgpm_nr_devs;
mgpm_dev_t *mgpm_devices[MAX_MGPM];
static int major = 0; /* default to dynamic major */
module_param(major, int, 0);
MODULE_PARM_DESC(major, "Major device number");
/********************/
/*typedef long long hrtime_t;*/
hrtime_t gethrtime(int p)
{
struct timeval tv;
hrtime_t val;
do_gettimeofday(&tv);
val = tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL;
return (val);
}
/******************/
static int mgpm_open(struct inode *inode, struct file *filp)
{
mgpm_dev_t *dev;
int minor = MINOR(inode->i_rdev);
dbgmgpm("Open\n");
dev = (mgpm_dev_t *)filp->private_data;
if (!dev) {
if ( minor >= mgpm_nr_devs )
return -ENODEV;
dev = mgpm_devices[minor];
if ( dev->opened ) {
printk("WARNING:open:\tre-open device\n");
return -EBUSY;
} else {
dev->opened = 1;
}
filp->private_data = dev;
}
dbgmgpm("Open. Ok.%s %s\n",__TIME__,__DATE__);
return 0;
}
static int mgpm_release(struct inode *inode, struct file *filp)
{
mgpm_dev_t *dev;
int minor = MINOR(inode->i_rdev);
dbgmgpm("Close\n");
if ( minor >= mgpm_nr_devs )
return -ENODEV;
dev = mgpm_devices[minor];
dev->opened = 0;
filp->private_data = NULL;
put_mgpm_reg(dev, MGPM_INTR_FEQ_REG,0);
put_mgpm_reg(dev, MGPM_CLR_INTR_MASK,0);
put_mgpm_reg(dev, MGPM_CLEAR_INTR_CNT,0);
put_mgpm_reg(dev, MGPM_CLEAR_INTR_DMA,0);
put_mgpm_reg(dev, MGPM_RESET,1);
dbgmgpm("Close. Ok.\n");
return 0;
}
static long mgpm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = filp->f_path.dentry->d_inode;
mgpm_dev_t *dev;
long ret;
lock_kernel();
dev = (mgpm_dev_t *)filp->private_data;
switch (cmd) {
case MCST_SELFTEST_MAGIC:
{
int rval;
selftest_t st;
selftest_pci_t *st_pci = &st.info.pci;
st.bus_type = BUS_PCI;
struct pci_dev *pdev = dev->pdev;
st_pci->vendor = pdev->vendor;
st_pci->device = pdev->device;
st_pci->major = MAJOR(inode->i_rdev);
st_pci->minor = MINOR(inode->i_rdev);
st_pci->bus = pdev->bus->number;
st_pci->slot = PCI_SLOT(pdev->devfn);
st_pci->func = PCI_FUNC(pdev->devfn);
st_pci->class = pdev->class;
strcpy(st_pci->name, MGPM_NAME);
// printk("%s: name [%s]. vendor = %#x, device = %#x. major = %d, minor = %d. bus = %d, slot = %d, func = %d, class = %#x\n", __func__,
// st_pci->name, st_pci->vendor, st_pci->device, st_pci->major, st_pci->minor, st_pci->bus, st_pci->slot, st_pci->func, st_pci->class);
rval = copy_to_user((void *)arg, (void *)&st, sizeof(selftest_t));
if ( rval != 0 ) {
printk( "%s: MCST_SELFTEST_MAGIC: copy_to_user() failed\n", __func__);
ret = -EFAULT;
goto out;
}
ret = 0;
goto out;
}
case MGPM_IOC_RESET_DEVICE:
put_mgpm_reg(dev, U0KMKP_REG_ADR,1);
ret = 0;
goto out;
case MGPM_IOC_WAIT_MODE: {
int mode;
dbgmgpm("MGPM_IOC_WAIT_MODE\n");
if ( copy_from_user( (void *)&mode, (const void __user *)arg, sizeof (int)) ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_WAIT_MODE "
"drv_copyin failure\n");
ret = -EINVAL;
goto out;
}
/* TODO: only ioctl wait mode is implemented now.*/
if(mode == 1) {
mode = 0;
} else {
mode = 0;
}
if (copy_to_user((int __user *)arg, &mode, sizeof(int))) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_WAIT_MODE "
"drv_copyout failure\n");
ret = -EFAULT;
goto out;
}
ret = 0;
goto out;
}
case MGPM_IOC_IOCTL_WAIT: {
long time_out;
dbgmgpm("MGPM_IOC_IOCTL_WAIT\n");
if ( copy_from_user( (void *)&time_out, (const void __user *)arg, sizeof (long)) ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_IOCTL_WAIT drv_copyoin failure\n");
ret = -EINVAL;
goto out;
}
dbgmgpm("MGPM_IOC_IOCTL_WAIT set_timeout %ld\n",time_out);
while (dev->intr_in_completed == 0) {
#if 0
int timed_out;
timed_out = interruptible_sleep_on_timeout(&dev->wait_intr_fin_queue,time_out*HZ*10);
if (timed_out) {
printk("MGPM_IOC_IOCTL_WAIT ERROR - TIME OUT\n");
return -ERESTARTSYS;
}
#else
interruptible_sleep_on(&dev->wait_intr_fin_queue);
#endif
}
dev->times[dev->stat_size].ptime = gethrtime(0);
//printk("r = %lld\n", get_usec_from_start_sync());
// printk("int : %lld\n",dev->times[dev->stat_size].btime);
// if (dev->stat_size < MAX_NUM_EL)
// dev->stat_size++;
dev->intr_in_completed = 0;
dbgmgpm("MGPM_IOC_IOCTL_WAIT OK CPU %d\n",smp_processor_id());
ret = 0;
goto out;
}
case 0x111111: {
printk("!!!!!! MGPM_CLEAR_INTR_CNT %x, MGPM_INTR_REG %x MGPM_INTR_FEQ_REG %x\n",
get_mgpm_reg(dev, MGPM_CLEAR_INTR_CNT),
get_mgpm_reg(dev, MGPM_INTR_REG),
get_mgpm_reg(dev,MGPM_INTR_FEQ_REG));
ret = 0;
goto out;
}
case MGPM_IOC_START_TR: {
dev->stat_size = 0;
ret = 0;
goto out;
}
case MGPM_IOC_GET_TR_SZ: {
int size = dev->stat_size;
if (copy_to_user((int __user *)arg, &size, sizeof(int))) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_GET_TR_SZ "
"drv_copyout failure\n");
ret = -EFAULT;
goto out;
}
ret = 0;
goto out;
}
case MGPM_IOC_SET_FREQUENCY: {
/* Set freq and start generate interrupts */
int freq;
dbgmgpm("MGPM_IOC_SET_FREQUENCY\n");
if ( copy_from_user( (void *)&freq, (const void __user *)arg, sizeof (int)) ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_SET_FREQUENCY "
"drv_copyoin failure\n");
ret = -EINVAL;
goto out;
}
if ( freq <= 0 ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_SET_FREQUENCY: "
"Frequency must be >= 0\n");
ret = -EINVAL;
goto out;
}
dbgmgpm("Freq = %d Hz\n",freq);
/**XXX: This is hardware adjust hack */
freq = (int)(20000/freq);
if ( freq <= 0 ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_SET_FREQUENCY: "
"Frequency must be >= 0\n");
ret = -EINVAL;
goto out;
}
dbgmgpm("Freq = %d for mgpm\n",freq);
put_mgpm_reg(dev, MGPM_INTR_FEQ_REG,freq);
put_mgpm_reg(dev, MGPM_CLR_INTR_MASK,1);
dbgmgpm("MGPM_INTR_FEQ_REG 0x%x\n", get_mgpm_reg(dev,MGPM_INTR_FEQ_REG));
ret = 0;
goto out;
}
case MGPM_IOC_SET_DEBUG_MODE: {
int debug_mode;
dbgmgpm("WARNING:mgpm_ioctl :MGPM_IOC_SET_DEBUG_MODE \n");
if ( copy_from_user( (void *)&debug_mode, (const void __user *)arg, sizeof (int)) ) {
printk("WARNING:mgpm_ioctl :MGPM_IOC_SET_DEBUG_MODE "
"drv_copyoin failure\n");
ret = -EINVAL;
goto out;
}
if ( debug_mode >= 0 ) {
dbgmgpm("WARNING:mgpm_ioctl :MGPM_IOC_SET_DEBUG_MODE set debug mode 0x%x\n",debug_mode);
mgpm_debug = debug_mode;
} else {
mgpm_debug = 0;
dbgmgpm("WARNING:mgpm_ioctl :MGPM_IOC_SET_DEBUG_MODE "
"invalid debug mode. %d\n",debug_mode);
}
ret = 0;
goto out;
}
case MGPM_IOC_HALT_DEVICE:
dev->device_type = NONE_TYPE;
put_mgpm_reg(dev, MGPM_RESET,1);
ret = 0;
default:
ret = -ENOTTY;
}
out:
unlock_kernel();
return ret;
}
static ssize_t mgpm_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int i;
int size_or_code;
mgpm_dev_t *dev = (mgpm_dev_t *)filp->private_data;
// int timed_out;
dbgmgpm("mgpm_read start\n");
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
size_or_code = (count<DEVICE_BUF_BYTE_SIZE)?count:DEVICE_BUF_BYTE_SIZE;
count = size_or_code;
/* translate bytes to words */
if ( size_or_code % sizeof(int) != 0 || size_or_code < sizeof(int)) {
size_or_code = size_or_code / sizeof(int);
size_or_code++;
} else {
size_or_code = size_or_code / sizeof(int);
}
/*local_irq_disable();*/
while (dev->trans_completed == 0) {
up(&dev->sem);
// interruptible_sleep_on_timeout(&dev->wait_trans_fin_queue,100*HZ);
interruptible_sleep_on(&dev->wait_trans_fin_queue);
/* if (dev->trans_completed == 0) {
printk("DMA READ ERROR - TIME OUT\n");
return -ERESTARTSYS;
}
*/
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
dev->trans_completed = 0;
for(i = 0;i < (int)size_or_code; i++) {
int *point = (int*)(dev->buf_dma_adr[1]);
dbgmgpm("READ 0x%x \n",point[i]);
}
if (copy_to_user(buf, dev->buf_dma_adr[1], count)) {
up(&dev->sem);
return -EFAULT;
}
up(&dev->sem);
dbgmgpm("mgpm_read finish\n");
return count;
}
static ssize_t mgpm_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
int i;
int size_or_code;
mgpm_dev_t *dev = (mgpm_dev_t *)filp->private_data;
dbgmgpm("mgpm_write start\n");
if (down_interruptible(&dev->sem)) {
return -ERESTARTSYS;
}wmb();
if (copy_from_user(dev->buf_dma_adr[0], buf, count)) {
up(&dev->sem);
return -EFAULT;
}
size_or_code = (count<DEVICE_BUF_BYTE_SIZE)?count:DEVICE_BUF_BYTE_SIZE;
count = size_or_code;
/* translate bytes to words */
if ( size_or_code % sizeof(int) != 0 || size_or_code < sizeof(int)) {
size_or_code = size_or_code / sizeof(int);
size_or_code++;
} else {
size_or_code = size_or_code / sizeof(int);
}
if(mgpm_debug) {
printk("mgpm_write: size_or_code %d\n",size_or_code);
for(i = 0; i < size_or_code; i++) {
printk("%d: wrb 0x%x\n",i,dev->buf_dma_adr[0][i]);
}
for(i = 0;i < (int)size_or_code; i++) {
int *point = (int*)(dev->buf_dma_adr[1]);
point[i] = 0xfefefefe;
printk("rd 0x%x \n",point[i]);
}
}
dev->batch_dma_adr[0] = 0x40000000 | size_or_code;
dev->batch_dma_adr[1] = 0xa0000000 | size_or_code;
put_mgpm_reg(dev, BATCH_CMD_ADR_REG_ADR,dev->batch_bus_addr);
put_mgpm_reg(dev, MGPM_ADDR_WRITE,dev->bus_addr[0]);
put_mgpm_reg(dev, MGPM_ADDR_READ,dev->bus_addr[1]);
local_irq_disable();
put_mgpm_reg(dev, BATCH_CMD_QUANTITY_REG_ADR,1);
dev->times[dev->stat_size].s_dma_t = gethrtime(0);// gethrtime();
if(mgpm_debug) {
printk("\n#####mgpm_write BK (0x%x)0: 0x%x 1: 0x%x\n",
get_mgpm_reg(dev,BATCH_CMD_ADR_REG_ADR),
get_mgpm_reg(dev,0x1000),get_mgpm_reg(dev,0x1004));
printk("mgpm_write START TRANSFER\n");
}
up(&dev->sem);
if(mgpm_debug) {
printk("mgpm_write BK 0: 0x%x 1: 0x%x\n",
get_mgpm_reg(dev, 0x1000),get_mgpm_reg(dev, 0x1004));
printk("W 0x%x R 0x%x\n", get_mgpm_reg(dev, 0x1600),get_mgpm_reg(dev, 0x1604));
printk("mgpm_write finish\n");
}
local_irq_enable();
return count;
}
static unsigned int mgpm_poll(struct file *filp, struct poll_table_struct *wait)
{
mgpm_dev_t *dev;
mgpm_io_word_t command_word;
unsigned int mask = 0;
dev = (mgpm_dev_t *)filp->private_data;
poll_wait(filp, &dev->wait_trans_fin_queue, wait);
if ( dev->trans_completed == 1 ) {
dev->trans_completed = 0;
command_word = (dev->batch_dma_adr[0] & 0xffff0000) >> 16; /**** REMEMBER: FOR ONE COMMAND ****/
dev->term_dev_adress = (command_word & TERM_DEV_ADR_POS) >> 11;
dev->subadress = (command_word & SUBADRESS_POS) >> 5;
if (dev->subadress == MIN_SUBADRESS || dev->subadress == MAX_SUBADRESS)
dev->size_or_code.cntrl_com_code = command_word & SIZE_CODE_POS;
else
dev->size_or_code.byte_msg_size = 2*((command_word & SIZE_CODE_POS) == 0 ? MAX_CHANNEL_MSG_SIZE : command_word & SIZE_CODE_POS);
if ( (command_word & COMMAND_BIT_POS) == 0 )
dev->term_trans_direction = TERM_DEV_READ;
else
dev->term_trans_direction = TERM_DEV_WRITE;
mask |= POLLIN;
}
return mask;
}
static int mgpm_mmap(struct file *filp, struct vm_area_struct *vma)
{
mgpm_dev_t *dev;
unsigned long mem_start;
unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT);
unsigned long vm_start;
dev = (mgpm_dev_t *)filp->private_data;
dbgmgpm("mgpm mmap: start\n");
mem_start = virt_to_phys((char *)dev->times);
mem_start += (vma->vm_pgoff << PAGE_SHIFT);
vm_start = vma->vm_start;
vma->vm_flags |= (VM_READ | VM_WRITE | VM_IO | VM_RESERVED);
dbgmgpm("mgpm mmap:\tmem: %#x; off: %#x; off_r: %#x; st: %#x; en: %#x\n"
"\t\t\tmof: %#x; sof: %#x; st_en: %#x; sz: %#x; \n",
(u_int)mem_start,
(u_int)vma->vm_pgoff,
(u_int)offset,
(u_int)vma->vm_start,
(u_int)vma->vm_end,
(u_int)(mem_start + offset),
(u_int)(vma->vm_start + offset),
(u_int)(vma->vm_end - vma->vm_start),
(u_int) MGPM_ST_TIMES_SIZE);
dbgmgpm("pol time %lld\nbeg time %lld\n endint %lld (%d)(%x)\n",
dev->times[dev->stat_size].ptime,
dev->times[dev->stat_size].btime,
dev->times[dev->stat_size].time,
(int)(dev->times[dev->stat_size].time - dev->times[dev->stat_size].btime),(int)(dev->times[dev->stat_size].ptime));
if ( (vma->vm_start + offset) > vma->vm_end ) {
printk("mmap:\terror offset more than size\n");
return -ENXIO;
}
if (remap_pfn_range(vma, vm_start, (mem_start >> PAGE_SHIFT),
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
printk("mgpm mmap:\terror remap memory to user\n");
return -EAGAIN;
}
return 0;
}
/*static void*/
irqreturn_t
mgpm_intr_handler(int irq, void *arg)
{
mgpm_dev_t *dev = (mgpm_dev_t *) arg;
u_int reg,timereg;
hrtime_t t_beg = gethrtime(1);
if ( dev == NULL )
return (IRQ_NONE);
/* Get current time and check for interrupt. */
timereg = get_mgpm_reg(dev, MGPM_CLEAR_INTR_CNT);
reg = get_mgpm_reg(dev, MGPM_INTR_REG);
//dbgmgpm("mgpm_intr_handler MGPM_INTR_REG 0x%8x \n",get_mgpm_reg(dev, MGPM_INTR_REG));
if ( (reg & 0xC0) == 0 ) {
/* Must be not for us */
return (IRQ_NONE);
}
if ( reg & 0x80 /*CNTR*/) {
/* Get time in nano sec */
dev->times[dev->stat_size].intr_time = ((timereg >> 8) & 0x7FFF);
dev->times[dev->stat_size].intr_counter = (( timereg >> 24 ) & 0x7f);
dev->times[dev->stat_size].intr_num++;
/* Clear interrupt */
put_mgpm_reg(dev, MGPM_CLEAR_INTR_CNT,0);
dev->times[dev->stat_size].time = gethrtime(1);
dev->times[dev->stat_size].btime = t_beg;
//dbgmgpm("MGPM_INTR_FEQ_REG 0x%x timereg 0x%x (time %lld)\n", get_mgpm_reg(dev, MGPM_INTR_FEQ_REG),timereg,dev->times[dev->stat_size].time);
/** TODO: do mutex here */
dev->intr_in_completed = 1;
/** */
wake_up_interruptible(&dev->wait_intr_fin_queue);
}
if ( reg & 0x40 /* DMA */) {
dbgmgpm("DMA Interrupt.\n");
dev->times[dev->stat_size].e_dma_t = 0;//gethrtime(1);
put_mgpm_reg(dev, MGPM_CLEAR_INTR_DMA,0);
/** TODO: do mutex here */
dev->trans_completed = 1;
/** */
wake_up_interruptible(&dev->wait_trans_fin_queue);
}
return IRQ_HANDLED;
}
void __you_cannot_kmalloc_that_much(void)
{
/* If one will compile this driver with -O2 optimisation
* then remove this declaration. This one is usefull only when
* optimisationless compile in use.
*/
}
static void * mgpm_rvmalloc(unsigned long size)
{
void * mem;
struct page *map, *mapend;
unsigned long order;
size=PAGE_ALIGN(size);
order = get_order(size);
size = PAGE_SIZE << order;
mem = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
mapend = virt_to_page (mem + (PAGE_SIZE << order) - 1);
for (map = virt_to_page(mem); map <= mapend; map++) {
SetPageReserved(map);
}
return mem;
}
static void mgpm_rvfree(void * mem, unsigned long size)
{
unsigned long order;
struct page *map, *mapend;
if (!mem)
return;
size=PAGE_ALIGN(size);
order = get_order(size);
size = PAGE_SIZE << order;
order = get_order(size);
mapend = virt_to_page(mem + (PAGE_SIZE << order) - 1);
for (map = virt_to_page(mem); map <= mapend; map++) {
ClearPageReserved(map);
}
free_pages((unsigned long)mem, order);
}
static struct file_operations mgpm_fops = {
owner: THIS_MODULE,
open: mgpm_open,
release: mgpm_release,
read: mgpm_read,
write: mgpm_write,
unlocked_ioctl: mgpm_ioctl,
poll: mgpm_poll,
mmap: mgpm_mmap,
};
static int __init mgpm_init(void)
{
struct pci_dev *pdev = NULL;
mgpm_dev_t *dev;
dev_t dev_mn;
int result = 0;
int cur_word;
int cur_buf_num;
int i;
mgpm_sysctl_register();
dbgmgpm("\t\tINSTALLATION MGPM DEVICE DRIVER\n");
/* if (!pci_present())
return -ENODEV;*/
result = alloc_chrdev_region(&dev_mn, 0, MAX_MGPM, MGPM_NAME);
if (result < 0) {
printk(KERN_WARNING "mgpm: can't get major %d\n", major);
return result;
}
if (!major) {
major = MAJOR(dev_mn);
printk(KERN_DEBUG pci_dev_name ": got dynamic major %d\n", major);
}
mgpm_nr_devs = 0;
while ((pdev = pci_get_device(VENDOR_MCST, MGPM_DEVICE_ID, pdev)) != NULL) {
#ifdef __sparc__
pci_write_config_dword(pdev, 0x40, 1);
#endif
if ( pci_set_dma_mask(pdev, 0xffffffff) != 0 ) {
printk("!!! pci_set_dma_mask cannot set mask 0xffffffff.\n");
continue;
}
if ( (dev = kmalloc(sizeof(mgpm_dev_t), GFP_KERNEL)) < 0 ) {
printk("!!! Cannot allocate memory for mgpm_dev_t.\n");
return -ENOMEM;
}
memset(dev, 0, sizeof(mgpm_dev_t));
dev->pdev = pdev;
dev->dev = dev_mn;
dev->device_type = NONE_TYPE;
dev->opened = 0;
//dev->irq = pdev->irq | 0x20;
dev->irq = pdev->irq;
dev->trans_completed = 0;
dbgmgpm("%d DEV IRQ = x%x PDEV = 0x%x.\n", mgpm_nr_devs,dev->irq ,pdev->irq);
/* allocate memory for time results */
dev->times = mgpm_rvmalloc((unsigned long)MGPM_ST_TIMES_SIZE*MAX_NUM_EL);
if (dev->times == NULL) {
printk(KERN_WARNING "MGPM: Can't alloc memory for stat.\n");
return 0;
}
dev->stat_size = 0;
/* No Junk for user */
memset(dev->times, 0x0, MGPM_ST_TIMES_SIZE*MAX_NUM_EL);
/* Semafor init */
init_waitqueue_head(&dev->wait_trans_fin_queue);
init_waitqueue_head(&dev->wait_intr_fin_queue);
sema_init(&dev->sem, 1);
/*Regs map*/
dev->device_mem_start = (u32*) ioremap( pci_resource_start(pdev, 0), pci_resource_len(pdev, 0) );
dev->batch_dma_adr = (u32*) pci_alloc_consistent(pdev, BATCH_BUF_BYTE_SIZE, &dev->batch_bus_addr);
if ( dev->batch_dma_adr == NULL )
printk("!!! Cannot pci_alloc_consistent for command batch buffer, device #%d\n", mgpm_nr_devs);
dev->buf_dma_adr[0] = (u32*) pci_alloc_consistent(pdev,
DEVICE_BUF_BYTE_SIZE*DEVICE_BUF_QUANTITY, &dev->bus_addr[0]);
if ( dev->buf_dma_adr[0] == NULL )
printk("!!! Cannot pci_alloc_consistent for device buffer, device #%d, buffer #%d\n", mgpm_nr_devs, 0);
for ( cur_buf_num = 1; cur_buf_num < DEVICE_BUF_QUANTITY; cur_buf_num++ ) {
dev->buf_dma_adr[cur_buf_num] = dev->buf_dma_adr[0] + DEVICE_BUF_BYTE_SIZE / sizeof(u32) * cur_buf_num;
dev->bus_addr[cur_buf_num] = dev->bus_addr[0] + DEVICE_BUF_BYTE_SIZE * cur_buf_num;
}
put_mgpm_reg(dev, U0KMKP_REG_ADR, 1);
for ( cur_word = 0; cur_word < DEVICE_MEM_WORD_SIZE; cur_word++ )
put_mgpm_reg(dev, cur_word,DEVICE_MEM_CLEAR);
{
int devno = MKDEV(major, mgpm_nr_devs);
cdev_init(&dev->cdev, &mgpm_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &mgpm_fops;
result = cdev_add (&dev->cdev, devno, 1);
if ( result != 0 ) {
printk(KERN_WARNING "mgpm: cannot add device to the system \n");
return result;
}
}
mgpm_devices[mgpm_nr_devs] = dev;
/* INCREMENT DEVICE NUMBER */
mgpm_nr_devs++;
}
dbgmgpm("%d MGPM DEVICES ARE AVAILABLE.\n", mgpm_nr_devs);
if ( mgpm_nr_devs > 0 ) {
if ( major == 0 )
major = result;
dbgmgpm("MGPM MAJOR NUMBER IS %d.\n", major);
}
else {
printk(KERN_WARNING "MGPM: REGISTER_CHRDEV DID NOT EXECUTE.\n");
return 0;
}
for ( i = 0; i < mgpm_nr_devs; i++ )
{
if ( request_threaded_irq(mgpm_devices[i]->irq, &mgpm_intr_handler, NULL, IRQF_SHARED, MGPM_NAME, (void *)mgpm_devices[i]) ) {
printk("Cannot register interrupt handler %s.\n", MGPM_NAME);
return -EAGAIN;
}
if(1){ // Create nodes
int mode;
dev_t devt;
char nod[128];
mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
devt = (MAJOR(dev_mn) << 8) | i;
#if defined(VER_2614)
sprintf(nod,"/dev/mgpm%d", i);
mode |= S_IFCHR;
if (mk_mknod(nod, mode, devt) == -EEXIST) {
// printk("mknod: node %s exist, removing in and then creating again\n", nod);
mk_unlink(nod);
if (mk_mknod(nod, mode, devt) != 0) {
printk("mk_create_minor: creating node %s failed\n", nod);
return -1;
}
}
#else
sprintf(nod, "mgpm_%d", i);
if (drv_create_minor(MGPM_DIR, nod, S_IFCHR, devt)) {
printk(KERN_ERR "INST %d. "
"%s(): mod_create_minor() failed\n",
mgpm_nr_devs, __func__);
return -1;
}
#endif
}
}
return 0;
}
static void __exit mgpm_exit(void)
{
int i;
for (i = 0; i < mgpm_nr_devs; i++)
unregister_chrdev_region(mgpm_devices[0]->dev, MAX_MGPM);
for (i = 0; i < mgpm_nr_devs; i++) {
free_irq(mgpm_devices[i]->irq, mgpm_devices[i]);
pci_free_consistent(mgpm_devices[i]->pdev, BATCH_BUF_BYTE_SIZE, (void *)mgpm_devices[i]->batch_dma_adr, mgpm_devices[i]->batch_bus_addr);
pci_free_consistent(mgpm_devices[i]->pdev, DEVICE_BUF_BYTE_SIZE*DEVICE_BUF_QUANTITY, (void *)mgpm_devices[i]->buf_dma_adr[0], mgpm_devices[i]->bus_addr[0]);
/* clear pages reserved & free mem */
mgpm_rvfree(mgpm_devices[i]->times, (unsigned long)MGPM_ST_TIMES_SIZE*MAX_NUM_EL);
kfree(mgpm_devices[i]);
}
mgpm_sysctl_unregister();
return;
}
/****************************** !!!!!!!!!!! *******************/
#if defined(VER_2614)
int mk_unlink(char *filename)
{
int error = 0;
char *name;
struct dentry *dentry;
struct nameidata nd;
struct inode *inode = NULL;
name = __getname();
audit_getname(name);
if (!name){
name = ERR_PTR(-ENOMEM);
error = PTR_ERR(name);
}
if(IS_ERR(name))
return PTR_ERR(name);
sprintf(name, "%s", filename);
error = path_lookup(name, LOOKUP_PARENT, &nd);
if (error) {
printk("mk_unlink: path_lookup() ret error %s %d\n", name, error);
goto exit;
}
error = -EISDIR;
if (nd.last_type != LAST_NORM)
goto exit1;
down(&nd.dentry->d_inode->i_sem);
dentry = lookup_hash(&nd.last, nd.dentry);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
/* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len])
goto slashes;
inode = dentry->d_inode;
if (inode)
atomic_inc(&inode->i_count);
error = vfs_unlink(nd.dentry->d_inode, dentry);
exit2:
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
exit1:
path_release(&nd);
exit:
putname(name);
if (inode)
iput(inode); /* truncate the inode here */
return error;
slashes:
error = !dentry->d_inode ? -ENOENT :
S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
goto exit2;
}
int mk_rm_dir(char *dir)
{
int error = 0;
char * name;
struct dentry *dentry;
struct nameidata nd;
if (dir == NULL) {
printk("mk_rm_dir: dir == NULL\n");
return -EFAULT;
}
name = __getname();
audit_getname(name);
if (!name){
name = ERR_PTR(-ENOMEM);
error = PTR_ERR(name);
}
if(IS_ERR(name))
return PTR_ERR(name);
sprintf(name, "%s", dir);
error = path_lookup(name, LOOKUP_PARENT, &nd);
if (error) {
printk("mk_rm_dir: path_lookup() ret error %s %d\n", name, error);
goto exit;
}
switch(nd.last_type) {
case LAST_DOTDOT:
error = -ENOTEMPTY;
goto exit1;
case LAST_DOT:
error = -EINVAL;
goto exit1;
case LAST_ROOT:
error = -EBUSY;
goto exit1;
}
down(&nd.dentry->d_inode->i_sem);
dentry = lookup_hash(&nd.last, nd.dentry);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
error = vfs_rmdir(nd.dentry->d_inode, dentry);
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
exit1:
path_release(&nd);
exit:
putname(name);
return error;
}
int mk_mkdir(char *pathname, int mode)
{
int error = 0;
char *tmp;
struct dentry *dentry;
struct nameidata nd;
if (pathname == NULL) {
printk("mk_mkdir: pathname == NULL ret -EFAULT\n");
return -EFAULT;
}
tmp = __getname();
audit_getname(tmp);
if (!tmp) {
tmp = ERR_PTR(-ENOMEM);
error = PTR_ERR(tmp);
}
if (!IS_ERR(tmp)) {
sprintf(tmp, "%s", pathname);
error = path_lookup(tmp, LOOKUP_PARENT, &nd);
if (error) {
printk("mk_mkdir: path_lookup() ret error %d\n", error);
goto out;
}
dentry = lookup_create(&nd, 1);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
if (!IS_POSIXACL(nd.dentry->d_inode))
mode &= ~current->fs->umask;
error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
path_release(&nd);
out:
putname(tmp);
}
return error;
}
extern asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev);
int mk_mknod(char *filename, int mode, dev_t dev)
{
int error = 0;
char *tmp;
struct dentry * dentry;
struct nameidata nd;
if (filename == NULL) {
printk("mk_mknod: filename == NULL\n");
return -EINVAL;
}
if (S_ISDIR(mode)) {
printk("mk_mknod: S_ISDIR\n");
return -EPERM;
}
tmp = __getname();
audit_getname(tmp);
if (!tmp){
tmp = ERR_PTR(-ENOMEM);
error = PTR_ERR(tmp);
}
if (IS_ERR(tmp))
return PTR_ERR(tmp);
sprintf(tmp, "%s", filename);
error = path_lookup(tmp, LOOKUP_PARENT, &nd);
if (error){
printk("mk_mknod: path_lookup() ret error %s %d\n", tmp, error);
goto out;
}
dentry = lookup_create(&nd, 0);
error = PTR_ERR(dentry);
if (!IS_POSIXACL(nd.dentry->d_inode))
mode &= ~current->fs->umask;
if (!IS_ERR(dentry)) {
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd);
break;
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(nd.dentry->d_inode,dentry,mode,new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0);
break;
case S_IFDIR:
error = -EPERM;
break;
default:
error = -EINVAL;
}
dput(dentry);
}
up(&nd.dentry->d_inode->i_sem);
path_release(&nd);
out:
putname(tmp);
return error;
}
#endif
//------------------------------------------------------------------------------------------------------------------------------------//
int drv_create_minor(char *dir, char *name, int type, dev_t dev)
{
int rval;
char node[MAX_DRV_NM_SZ];
int mode;
if ( strlen(dir) + strlen(name) + 2 > MAX_DRV_NM_SZ) {
printk("len_name > MAX_DRV_NM_SZ\n");
return -EFAULT;
}
rval = drv_create_dir(dir);
if (rval) {
printk("drv_create_minor: drv_create_dir failed rval %d\n",
rval);
return (rval);
}
sprintf(node, "/dev/%s/%s", dir, name);
mode = type | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
rval = drv_mknod(node, mode, dev);
if (rval) {
if (rval == -EEXIST) {
printk("drv_create_minor: node %s exist, removing then creating again\n", node);
rval = drv_unlink(dir, name);
if (rval) {
printk("drv_create_minor: removing node %s failed\n", node);
}
rval = drv_mknod(node, mode, dev);
if (rval) {
printk("drv_create_minor: creating node %s failed\n", node);
}
return rval;
}
}
return rval;
}
static int drv_create_dir(char *dir)
{
int rval;
mode_t mode;
char str[MAX_DRV_NM_SZ];
if (dir == NULL) {
printk("drv_create_dir: dir == NULL\n");
return -EFAULT;
}
mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
sprintf(str, "/dev/%s", dir);
rval = drv_mkdir(str, mode);
if (rval) {
if (rval == -EEXIST) {
printk("drv_create_dir: directory %s exist\n", str);
return 0;
}
printk("drv_create_dir for %s rval %d\n", str, rval);
return (rval);
}
return 0;
}
static int drv_mkdir(char *pathname, int mode)
{
int error = 0;
char *tmp;
struct dentry *dentry;
struct nameidata nd;
if (pathname == NULL) {
printk("drv_mkdir: pathname == NULL ret -EFAULT\n");
return -EFAULT;
}
/* getname */
tmp = __getname();
if (!tmp) {
tmp = ERR_PTR(-ENOMEM);
error = PTR_ERR(tmp);
}
audit_getname(tmp);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
/**/
sprintf(tmp, "%s", pathname);
error = path_lookup(tmp, LOOKUP_PARENT, &nd);
if (error) {
printk("drv_mkdir: path_lookup() ret error %d\n", error);
goto out;
}
dentry = lookup_create(&nd, 1);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_unlock;
if (!IS_POSIXACL(nd.path.dentry->d_inode))
mode &= ~current_umask();
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
error = security_path_mkdir(&nd.path, dentry, mode);
if (error)
goto out_drop_write;
error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
out_drop_write:
mnt_drop_write(nd.path.mnt);
out_dput:
dput(dentry);
out_unlock:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
out:
putname(tmp);
return error;
}
static int drv_mknod(char *filename, int mode, dev_t dev)
{
int error = 0;
char *tmp;
struct dentry * dentry;
struct nameidata nd;
if (filename == NULL) {
printk("drv_mknod: filename == NULL\n");
return -EINVAL;
}
if (S_ISDIR(mode)) {
printk("drv_mknod: S_ISDIR\n");
return -EPERM;
}
/* getname */
tmp = __getname();
if (!tmp){
tmp = ERR_PTR(-ENOMEM);
error = PTR_ERR(tmp);
}
audit_getname(tmp);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
/**/
sprintf(tmp, "%s", filename);
error = path_lookup(tmp, LOOKUP_PARENT, &nd);
if (error){
printk("drv_mknod: path_lookup() ret error %d\n", error);
goto out;
}
dentry = lookup_create(&nd, 0);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out_unlock;
}
if (!IS_POSIXACL(nd.path.dentry->d_inode))
mode &= ~current_umask();
error = may_mknod(mode);
if (error)
goto out_dput;
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
error = security_path_mknod(&nd.path, dentry, mode, dev);
if (error)
goto out_drop_write;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
break;
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
break;
case S_IFDIR:
error = -EPERM;
break;
default:
error = -EINVAL;
}
out_drop_write:
mnt_drop_write(nd.path.mnt);
out_dput:
dput(dentry);
out_unlock:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
out:
putname(tmp);
return error;
}
int drv_unlink(char *dir, char *filename)
{
int error = 0;
char *name;
struct dentry *dentry;
struct nameidata nd;
struct inode *inode = NULL;
name = __getname();
audit_getname(name);
if (!name){
name = ERR_PTR(-ENOMEM);
error = PTR_ERR(name);
}
if(IS_ERR(name))
return PTR_ERR(name);
sprintf(name, "/dev/%s/%s", dir, filename);
error = path_lookup(name, LOOKUP_PARENT, &nd);
if (error) {
printk("drv_unlink: path_lookup() ret error %d\n", error);
goto exit;
}
error = -EISDIR;
if (nd.last_type != LAST_NORM)
goto exit1;
mutex_lock(&nd.path.dentry->d_inode->i_mutex);
dentry = lookup_hash(&nd);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
/* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len])
goto slashes;
inode = dentry->d_inode;
if (inode)
atomic_inc(&inode->i_count);
error = vfs_unlink(nd.path.dentry->d_inode, dentry);
exit2:
dput(dentry);
}
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
exit1:
path_put(&nd.path);
exit:
putname(name);
if (inode)
iput(inode); /* truncate the inode here */
return error;
slashes:
error = !dentry->d_inode ? -ENOENT :
S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
goto exit2;
}
static int may_mknod(mode_t mode)
{
switch (mode & S_IFMT) {
case S_IFREG:
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
case 0: /* zero mode translates to S_IFREG */
return 0;
case S_IFDIR:
return -EPERM;
default:
return -EINVAL;
}
}
/****************************** !!!!!!!!!!! *******************/
module_init(mgpm_init);
module_exit(mgpm_exit);
MODULE_PARM_DESC (major, "i");
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Denis Fedotov");
MODULE_DESCRIPTION("MGPM device driver");