[Linux-parport] My own driver...

hinko.kocevar at cetrtapot.si hinko.kocevar at cetrtapot.si
Tue Jan 31 11:22:15 EST 2006


Hi,

I'm trying out the driver that came with LDD3 as an example. It is 
called short.c. It implements basic LPT port access for read/write every 
bit(pin that is).
I can't seem to get any valid info to/from the port if using this 
short.c driver - if using parport and parport_pc I can set pins from 
userspace just fine..
Here is code for kernel module, let me know if you spot what am I missing:

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/delay.h>    /* udelay */
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/poll.h>
#include <linux/wait.h>

#include <asm/io.h>

#define SHORT_NR_PORTS  8   /* use 8 ports by default */

/*
  * all of the parameters have no "short_" prefix, to save typing when
  * specifying them at load time
  */
static int major = 0;   /* dynamic by default */

/* default is the first printer port on PC's. "short_base" is there too
    because it's what we want to use in the code */
static unsigned long base = 0x378;
unsigned long short_base = 0;

MODULE_AUTHOR ("Alessandro Rubini");
MODULE_LICENSE("Dual BSD/GPL");

int short_open (struct inode *inode, struct file *filp)
{
     return 0;
}


int short_release (struct inode *inode, struct file *filp)
{
     return 0;
}


/* first, the port-oriented device */

ssize_t do_short_read (struct inode *inode, struct file *filp, char 
__user *buf,
         size_t count, loff_t *f_pos)
{
     int retval = count, minor = iminor (inode);
     unsigned long port = short_base + (minor&0x0f);
     unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr;

     if (!kbuf)
         return -ENOMEM;
     ptr = kbuf;

     printk("do_short_read: ");
     while (count--) {
         *(ptr++) = inb(port);
         rmb();
         printk(" 0x%x", *(ptr-1));
     }
     printk("\n");

     if ((retval > 0) && copy_to_user(buf, kbuf, retval))
         retval = -EFAULT;
     kfree(kbuf);
     return retval;
}

ssize_t short_read(struct file *filp, char __user *buf, size_t count, 
loff_t *f_pos)
{
     return do_short_read(filp->f_dentry->d_inode, filp, buf, count, f_pos);
}

ssize_t do_short_write (struct inode *inode, struct file *filp, const 
char __user *buf,
         size_t count, loff_t *f_pos)
{
     int retval = count, minor = iminor(inode);
     unsigned long port = short_base + (minor&0x0f);
     unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr;

     if (!kbuf)
         return -ENOMEM;
     if (copy_from_user(kbuf, buf, count))
         return -EFAULT;
     ptr = kbuf;

     printk("do_short_write: ");
     while (count--) {
         outb(*(ptr++), port);
         wmb();
         printk(" 0x%x", *(ptr-1));
     }
     printk("\n");

     kfree(kbuf);
     return retval;
}


ssize_t short_write(struct file *filp, const char __user *buf, size_t count,
         loff_t *f_pos)
{
     return do_short_write(filp->f_dentry->d_inode, filp, buf, count, 
f_pos);
}

unsigned int short_poll(struct file *filp, poll_table *wait)
{
     return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}

struct file_operations short_fops = {
     .owner   = THIS_MODULE,
     .read    = short_read,
     .write   = short_write,
     .poll    = short_poll,
     .open    = short_open,
     .release = short_release,
};

/* Finally, init and cleanup */

int short_init(void)
{
     int result;

     /*
      * first, sort out the base/short_base ambiguity: we'd better
      * use short_base in the code, for clarity, but allow setting
      * just "base" at load time. Same for "irq".
      */
     short_base = base;

     if (! request_region(short_base, SHORT_NR_PORTS, "short")) {
         printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",
                 short_base);
         return -ENODEV;
     }

     /* Here we register our device - should not fail thereafter */
     result = register_chrdev(major, "short", &short_fops);
     if (result < 0) {
         printk(KERN_INFO "short: can't get major number\n");
         release_region(short_base,SHORT_NR_PORTS);  /* FIXME - use-mem 
case? */
         return result;
     }
     if (major == 0) major = result; /* dynamic */
     printk("SHORT: Registered driver, result=%d\n", result);

     return 0;
}

void short_cleanup(void)
{
     printk("SHORT: UN-register driver\n");
     unregister_chrdev(major, "short");
     release_region(short_base,SHORT_NR_PORTS);
}

module_init(short_init);
module_exit(short_cleanup);


best regards,
hinko
-- 
ČETRTA POT, d.o.o., Kranj
Planina 3
4000 Kranj
Slovenija
Tel. +386 (0) 4 280 66 37
E-mail: hinko.kocevar at cetrtapot.si
Http: www.cetrtapot.si




More information about the Linux-parport mailing list