/* * p5.c -- debuging module of the target mode * * Sends and receives data using the sn and snh module, respectively. It * uses a device to write and read data. * * (C) Copyright 2001 Pedro Semeano and Luis Sismeiro * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sn.h" /* debuging code */ #if 0 #ifdef ENTER #undef ENTER #endif #ifdef EXIT #undef EXIT #endif #ifdef PKT #undef PKT #endif #ifdef DEBUG #undef DEBUG #endif #define ENTER(x) printk("p5: entering %s \n",x); #define EXIT(x) printk("p5: exit %s\n",x); #define DEBUG(x) x #define PKT printk("P5: %s(%u)\n",__FUNCTION__,__LINE__); #else #ifdef ENTER #undef ENTER #endif #ifdef EXIT #undef EXIT #endif #define ENTER(x) #define EXIT(x) #define DEBUG(x) #endif /* * Char device characteristics. */ #define P5_MAJOR 240 #define P5_NAME "pfive" #define P5_MAX_DEVICES 32 #define P5_CAN_USE 1 #define P5_CANT_USE 0 #define P5_BUFFERS 128 /* * VFS file function definitions */ ssize_t p5_read(struct file *,char *,size_t ,loff_t *); ssize_t p5_write(struct file *,const char *,size_t ,loff_t *); int p5_ioctl(struct inode *,struct file *,unsigned int, unsigned long); int p5_open(struct inode *,struct file *); int p5_release(struct inode *,struct file *); /* * SCSI function definitions. */ int p5_init(void); int p5_exit(void); int p5_attach(int device); int p5_detach(int device); int p5_rx_packet(int len,unsigned char *data); void p5_tx_finish(unsigned char *); /* * Read data structure. */ struct data_buffers { struct list_head list; struct semaphore sem; char *data; int len; int pos; }; struct list_head data_list; LIST_HEAD(data_list); /* * Char device structure. */ struct struct_devices { char open; char usage; int curbuf; int pos; devfs_handle_t handle; }; static struct struct_devices *devices; static spinlock_t p5_devices_lock = SPIN_LOCK_UNLOCKED; static int num_opened; int curpos; devfs_handle_t dfdir; /* * This structure has the available functions in to operate the char device. */ struct file_operations p5_fops = { owner:THIS_MODULE, read:p5_read, write:p5_write, ioctl:p5_ioctl, open:p5_open, release:p5_release, }; /* * This structure has the available functions to the sn module. */ struct sn_upper p5_template = { name:P5_NAME, init:p5_init, exit:p5_exit, attach:p5_attach, detach:p5_detach, rx_packet:p5_rx_packet, tx_finish:p5_tx_finish, }; /* * p5_read(file,data,size,offset) * * This function reads data from the char device. */ ssize_t p5_read(struct file *fi,char *data,size_t size,loff_t *offset) { /* int dev=MINOR(fi->f_dentry->d_inode->i_rdev); */ ssize_t s=0; int diff; struct data_buffers *aux; again: if (list_empty(&data_list) || size==0 ) { return s; } aux=list_entry(data_list.next,struct data_buffers,list); diff=aux->len-aux->pos; /* * If several data packets are in the buffer copy all until reach * size, else copy only the available data. */ if (diff>size) { copy_to_user(data,&aux->data[aux->pos],size); s+=size; aux->pos+=size; } else { copy_to_user(data,&aux->data[aux->pos],diff); s+=diff; size-=diff; aux->pos+=diff; if (aux->pos>=aux->len) { list_del(data_list.next); kfree(aux->data); kfree(aux); } goto again; } return s; } void p5_tx_finish(unsigned char *data) { ENTER("p5_tx_finish"); kfree(data); EXIT("p5_tx_finish"); } /* * p5_write(file,data,size,offset) * * This function sends a data packet to the sn module */ /*ssize_t p5_write(struct file *fi,const char *data,size_t size,loff_t *offset) { int dev=MINOR(fi->f_dentry->d_inode->i_rdev); char *pkt; int ret; ENTER("p5_write"); if (size%2==0) pkt=(char *)kmalloc(size,GFP_KERNEL); else pkt=(char *)kmalloc(size+1,GFP_KERNEL); if (pkt==NULL) { printk("P5: No more memory... Loosing packet\n"); return -ENOMEM; } copy_from_user(pkt,data,size); printk("P5: sending to device: %u\n",dev); printk("P5: write string %s len: %u\n",data,size); if (size%2==0) { ret=sn_tx_packet(dev,size,pkt); } else { pkt[size+1]=0; ret=sn_tx_packet(dev,size+1,pkt); } if (ret!=0) { printk("P5: Error transmiting packet...\n"); return 0; } EXIT("p5_write"); return size; } */ int p5_send(int dev,int size,const char *data) { char *pkt; int ret; if (size%2==0) pkt=(char *)kmalloc(size,GFP_KERNEL); else pkt=(char *)kmalloc(size+1,GFP_KERNEL); if (pkt==NULL) { printk("P5: No more memory... Loosing packet\n"); return -ENOMEM; } copy_from_user(pkt,data,size); if (size%2==0) { ret=sn_tx_packet(dev,size,pkt); } else { pkt[size+1]=0; ret=sn_tx_packet(dev,size+1,pkt); } if (ret!=0) { printk("P5: Error transmiting packet...\n"); return 0; } return size; } ssize_t p5_write(struct file *fi,const char *data,size_t size,loff_t *offset) { int dev=MINOR(fi->f_dentry->d_inode->i_rdev); int count=0; int pos=0; ENTER("p5_new_write"); do { if (size>4096) count=4096; else count=size; p5_send(dev,count,&data[pos]); pos+=count; size-=count; } while (size>0); EXIT("p5_new_write"); return pos; } /* * p5_ioctl() * * To remove or not to remove? It isn't used right now. */ int p5_ioctl(struct inode *in,struct file *fi,unsigned int i, unsigned long l) { ENTER("p5_ioctl"); EXIT("p5_ioctl"); return 0; } /* * p5_open (inode,file) * * Opens the char device for writing */ int p5_open(struct inode *in,struct file *fi) { int dev=MINOR(in->i_rdev); ENTER("p5_open"); if (dev>P5_MAX_DEVICES) { DEBUG(printk("P5: invalid device\n")); return -EFAULT; } if (devices[dev].open==1) { return -EBUSY; } devices[dev].open=1; devices[dev].curbuf=curpos; num_opened+=1; MOD_INC_USE_COUNT; EXIT("p5_open"); return 0; } int p5_release(struct inode *in,struct file *fi) { int dev=MINOR(in->i_rdev); struct data_buffers *aux; ENTER("p5_release"); if (dev>P5_MAX_DEVICES) { DEBUG(printk("P5: invalid device\n")); return -EFAULT; } if (devices[dev].open!=1) { DEBUG(printk("P5: trying to close a closed file????\n")); return -EINVAL; } devices[dev].open=0; num_opened-=1; if (num_opened==0) { do { if (list_empty(&data_list) ) { goto release_end; } aux=list_entry(data_list.next,struct data_buffers,list); list_del(data_list.next); kfree(aux->data); kfree(aux); } while (!list_empty(&data_list)); } release_end: EXIT("p5_release"); MOD_DEC_USE_COUNT; return 0; } /* SCSI */ /* * Spinlock p5_devices_lock MUST NOT be held before any of this functions */ int p5_init(void) { ENTER("p5_init"); /* spin_lock(&p5_devices_lock); */ memset(devices,0,sizeof(struct struct_devices)*P5_MAX_DEVICES); /* spin_unlock(&p5_devices_lock); */ EXIT("p5_init"); return 0; } int p5_exit(void) { ENTER("p5_exit"); EXIT("p5_exit"); return 0; } int p5_attach(int dev) { char buffer[16]; ENTER("p5_attach"); /* spin_lock(&p5_devices_lock); */ sprintf(buffer,"pfive%u",dev); devices[dev].usage=P5_CAN_USE; printk("buffer: %s Device: %u dfdir: %p \n",buffer,dev,&dfdir); devices[dev].handle=devfs_register(dfdir,buffer,DEVFS_FL_NONE, P5_MAJOR,dev, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, &p5_fops,NULL); if (devices[dev].handle==NULL) { /* spin_unlock(&p5_devices_lock); */ printk("P5: Failure to register device\n"); return -ENODEV; } /* spin_unlock(&p5_devices_lock); */ EXIT("p5_attach"); return 0; } int p5_detach(int dev) { ENTER("p5_detach"); /* spin_lock(&p5_devices_lock); */ devices[dev].usage=P5_CANT_USE; devfs_unregister(devices[dev].handle); /* spin_unlock(&p5_devices_lock); */ EXIT("p5_detach"); return 0; } int p5_rx_packet(int len,unsigned char *data) { struct data_buffers *aux; ENTER("p5_rx_packet"); if (num_opened==0) { printk("P5: No device was open\n"); EXIT("p5_rx_packet"); return 0; } aux=kmalloc(sizeof(struct data_buffers),GFP_ATOMIC); if (aux==NULL) goto drop; aux->data=kmalloc(len,GFP_ATOMIC); if (aux->data==NULL) goto drop; memcpy(aux->data,data,len); aux->len=len; aux->pos=0; list_add_tail(&aux->list,&data_list); EXIT("p5_rx_packet"); return 0; drop: printk("P5: Dropping packet. No more memory...\n"); return 0; } static int init_p5(void) { ENTER("init_p5"); if (devfs_register_chrdev(P5_MAJOR,P5_NAME,&p5_fops)) { printk("P5: Error registering devices...\n"); return -ENODEV; } dfdir=devfs_mk_dir(NULL,"pfive",NULL); printk("dfdir: %p\n",&dfdir); /* spin_lock(&p5_devices_lock); */ devices=(struct struct_devices *)kmalloc(sizeof(struct struct_devices)*P5_MAX_DEVICES,GFP_KERNEL); if (devices==NULL) { printk("P5: Error allocating memory for devices\n"); goto devices; } printk("P5: VFS registered!!\n"); sn_register_upper(&p5_template); num_opened=0; printk("SN: SN registered!!\n"); EXIT("init_p5"); return 0; devices: devfs_unregister_chrdev(P5_MAJOR,P5_NAME); return -ENODEV; } static void exit_p5(void) { ENTER("exit_p5"); sn_unregister_upper(&p5_template); DEBUG(printk("P5: SN unregister\n")); devfs_unregister_chrdev(P5_MAJOR,P5_NAME); DEBUG(printk("P5: VFS unregisterd. Exiting...\n")); EXIT("exit_p5"); } module_init(init_p5); module_exit(exit_p5);