Messages in this thread | | | From | Drew Bertola <> | Date | Tue, 16 Nov 1999 17:51:27 -0800 (PST) | Subject | Drivers in 2.2.x: Best way to get minor number from within xxx_read() |
| |
I have an ISA I/O board called tsio and based on 4 82C55s. It has 16 8-bit ports, 12 for I/O and 4 for "control words". I want to create 16 device nodes, 1 for each port, so that I can read/write to each independently. I set the nodes up so that each device has it's minor number the same as it's offset from the base I/O port address (0-15).
The problem I have is that I don't want to grab the offset from tsio_open() and store it in a static int as that would not allow simultaneous opens of different ports.
I am now using filp->private_data to hold the minor and them accessing it to find the offset within each tsio_read() / tsio_write(), but that seems akward. Does anyone have a suggestion as to a better way to do this or an explanation as to why I can't MINOR(inode->i_rdev) within the read / write operations themselves?
If anyone has the time, I would like a review of the code for suggestions. It's only 250 lines w/ comments, so I've included it below.
Thanks, Drew
D->|
-----------------------------tsio.c-------------------------------- /* tsio.c (for Linux kernel version 2.2.x or higher. * * This is the Linux driver for the Ecosys Monitoring Systems * (TeloSense) I/O board. The driver requires that the base I/O * address be passed to it at load time. * * A node (e.g. /dev/tsio0, * /dev/tsio1, ...) must be created for * each 8-bit port. This can be done at the * invocation of the * module (or thereafter) by using mknod(1). The minor number for * each node will be used to determine the offset of the port from the * base I/O address. * * */
#define __KERNEL__ #define MODULE
#if 0 #define EXPORT_SYMTAB /* We don't export any syms (yet). */ #endif
/* Our own definitions for this device. */ #define DEVICE_NAME "tsio" #define RANGE 16 /* This is the number of 8-bit ports on the board. */
/* * We need to determine the offset (from io_base) for a read or write * operation. This is determined from the minor number of the device. * The only time we can do that is in tsio_open(), but we need to * secure the offset for a device for tsio_read() and tsio_write(). * To do that, we place the offset into *filp->*private_data. * `private_data' is a `void *', so we need to use a macro to help * conceal the unclean assignment: */ #define MINOR_FROM_FILEP(F) (unsigned int)((F)->private_data)
#include <linux/module.h> #include <linux/ioport.h> #include <linux/errno.h> #include <linux/fs.h> #include <asm/uaccess.h> /* For put_user, get_user, etc. */ #include <linux/config.h> #include <linux/sched.h> #include <linux/kdev_t.h> /* For MINOR macro, etc. */ #include <asm/page.h> #include <asm/io.h>
static unsigned int io_base = 0; /* Default set to generate error. */ static unsigned int tsio_major = 0; /* Set to zero (dynamic allocation. */
MODULE_PARM(io_base, "i"); /* Define at load time. */
static int tsio_request_port (unsigned int port, unsigned int range) { int err; if ((err = check_region (port, range)) < 0 ) return err; /* busy */ request_region (port, range, DEVICE_NAME); return 0; }
static void tsio_release_port (unsigned int port, unsigned int range) { release_region (port, range); }
/*************** READ *****************/ /* The offset (difference between port to be written and base port) is * determined from the minor number of the device. We use the * private_data component of *filp to store the offset. This is set * in tsio_open() which has access to *inode. * */
ssize_t tsio_read (struct file *filp, char *buf, size_t count, loff_t *loff) { unsigned byte_read;
int offset = filp->private_data;
if (verify_area (VERIFY_WRITE, buf, count) == -EFAULT) { return -EFAULT; }
put_user (byte_read = inb (io_base + offset), buf);
#ifdef DEBUG printk ("put user byte %c.\n", byte_read); printk ("offset = %d\n", offset); #endif
return 1; }
/*************** WRITE *****************/ /* The offset (difference between port to be written and base port) is * determined from the minor number of the device. We use the * private_data component of *filp to store the offset. This is set * in tsio_open() which has access to *inode. * */
ssize_t tsio_write (struct file *filp, const char *buf, size_t count, loff_t *loff) { unsigned char c = buf [0]; int offset = filp->private_data;
get_user (c, buf); outb (c, io_base + offset);
#ifdef DEBUG printk ("getting user byte %-#12x\n", c); printk ("tsio write %-#12x\n", c); printk ("offset = %d\n", offset); #endif
return 1; }
/*************** OPEN *****************/ int tsio_open (struct inode *inode, struct file *filp) { /* * Here, we use the macro defined above: * * MINOR_FROM_FILEP(F) (unsigned int)((F)->private_data) * * to store the minor number in the *filp structure. This will * allow us to retrieve the minor number (used as 'offset') from * within tsio_read() and tsio_write(). */ MINOR_FROM_FILEP (filp) = MINOR (inode->i_rdev); MOD_INC_USE_COUNT; return 0; }
/*************** CLOSE *****************/ int tsio_release (struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; }
/*************** FOPS ******************/ struct file_operations tsio_fops = { NULL, /* loff_t tsio_llseek(struct file *, loff_t, int) */ tsio_read, /* tsio_read - read from device. */ tsio_write, /* tsio_write - write to device. */ NULL, /* tsio_readdir - only for directories (NULL for devices). */ NULL, /* tsio_poll - check if device is readable or writable. */ NULL, /* tsio_ioctl - issue commands to device from user space. */ NULL, /* tsio_mmap - map device memory to a process's memory. */ tsio_open, /* tsio_open - open device. */ NULL, /* tsio_flush - flush device? New to 2.2.x kernel. */ tsio_release, /* tsio_release - close device.*/ NULL, /* tsio_fsync - flush device. */ NULL, /* tsio_fasync - check fasync flag. */ NULL, /* tsio_check_media_change - only used with block devices. */ NULL, /* tsio_revalidate - only meaningful for block drivers. */ };
int init_module (void) { int err = 0; int result; int i;
/* Test the io_base address parameter to be sure it is one of the ones available to our card. */ switch (io_base) { case 0x310: case 0x330: case 0x360: case 0x380: case 0x3A0: case 0x3E0: break; default: { err = -EINVAL; printk ("Error: %d.\n", err); printk ("Unable to load TSIO module at specified io_base address.\n"); printk ("Illegal address passed to module: 0x%x.\n", io_base); printk ("Use one of 0x310, 0x330, 0x360, or 0x3E0.\n"); return (err); } } /* Request I/O port. */ printk ("Initializing TSIO board driver at io_base: 0x%x.\n", io_base); tsio_request_port (io_base, RANGE);
result = register_chrdev (tsio_major, DEVICE_NAME, &tsio_fops); if (result < 0) { printk ("TSIO: can't get major %d.\n", tsio_major); return result; } if (tsio_major == 0) { tsio_major = result; } printk ("TSIO: major = %d.\n", tsio_major);
/* Initialize all ports to 0xff (turn off all I/O bits). */ for (i = 15; i >= 0; i--) { if ((i + 1 ) % 4) { outb (0xff, io_base + i); } else { outb (0x80, io_base + i); } }
return (0); }
void cleanup_module (void) { int err = 0; int i; /* Turn off all I/O bits by writing 0xff to ports. */ for (i = 15; i >= 0; i--) { if (( i + 1) % 4 ) { outb (0xff, io_base + i); } else { outb (0x80, io_base + i); } }
printk ("Removing TSIO board driver.\n"); tsio_release_port (io_base, RANGE); if ((err = unregister_chrdev (tsio_major, DEVICE_NAME))) { printk ("TSIO: Can't unregister major number %d.\n", tsio_major); } }
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |