Re: writing a parallel port driver to support external hardware

Barry Lagerweij (barry@mail.3wis.nl)
Wed, 24 Apr 1996 21:45:01 +0200 (MET DST)


Hi Richard,

On Tue, 23 Apr 1996, Richard A. Reitmeyer wrote:

> Some people here are in the process of building a small robot, and have
> decided to use a parallel port interface because the hardware's
> easier that way.

I've recently build a stepping motor device driver for Linux, which can
be used as a loadable module. The delay part is an example of how *NOT*
to program in a multitasking environment, but I needed a quick dirty
hack, and usleep() didn't work the way I expected, so there it is.

I have two stepping motors directly attached to the parallel port, and I
can control them by writing to /dev/motor, e.g. "0+010" will move motor 0
ten degrees clockwise, where "1=200" will move motor 1 to a fixed
position (200).

Barry

/*------------------------------------------------------------
step motor
------------------------------------------------------------*/

#define __KERNEL__
#define MODULE

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>

#define VER_MAJ 1
#define VER_MIN 0
#define NAME "motor"

int io_base = 0x278;
int major = 60;
int position[2];

const unsigned char steps[]=
{
0x1,0x5,0x4,0x6,0x2,0xA,0x8,0x9
};

/*
* the following code is needed for the loadable driver stuff
*/
static int do_open ( struct inode *inode, struct file *file )
{
if (MINOR(inode->i_rdev) != 0)
return(-ENODEV);
if (MOD_IN_USE)
return(-EBUSY);
MOD_INC_USE_COUNT;

return(0);
}

static int do_write ( struct inode *inode, struct file *file,
const char *buf, int len )
{
int rv;
unsigned char motor_nr;
int oldpos;
int delta;
int n,i;
int step;

if (len != 5) return(-EINVAL);
if ((rv = verify_area(VERIFY_READ, buf, len)))
return(rv);
motor_nr = get_fs_byte(buf) - '0';
if (motor_nr > 9) return(-EINVAL);
oldpos = position[motor_nr];
i = ((get_fs_byte(buf+2)-'0')*100)+
((get_fs_byte(buf+3)-'0')*10)+
(get_fs_byte(buf+4)-'0');
switch (get_fs_byte(buf+1))
{
case '=' :
delta = i - oldpos;
break;
case '-' :
delta = -i;
break;
case '+' :
delta = i;
break;
default :
return(-EINVAL);
}
delta %= 400;
if (delta > 200)
{
delta -= 400;
}
else
{
if (delta < -200) delta += 400;
}
if (oldpos + delta < -400)
{
delta += 400;
}
else
{
if (oldpos + delta > 400)
{
delta -= 400;
}
}
step = ( delta < 0 ? -1 : +1 );
if (delta < 0) delta = -delta;
for (i=0; i<delta; i++)
{
position[motor_nr] = oldpos;
outb(steps[position[0]&7]+(steps[position[1]&7]<<4),io_base);

for (n=0; n<500000; n++)
{
if (need_resched)
schedule();
}
oldpos += step;
}
position[motor_nr] = oldpos;

return(len);
}

static void do_release ( struct inode *inode, struct file *file )
{
MOD_DEC_USE_COUNT;
}

static struct file_operations fops =
{
NULL, /* lseek */
NULL, /* read */
do_write, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
do_open, /* open */
do_release, /* release */
NULL,
NULL,
NULL,
NULL
};

int init_module ( void )
{
printk("motor: driver at 0x%x version %d.%d\n",
io_base,VER_MAJ,VER_MIN);
if (check_region(io_base, 3) < 0)
{
printk("motor: Error: Cannot request I/O area\n");
return -1;
}
request_region(io_base, 3, NAME);
if (register_chrdev(major, NAME, &fops))
{
printk("motor: Error: Cannot register major number %d\n",major);
return(-EIO);
}
position[0] = position[1] = 0;
return(0);
}

void cleanup_module ( void )
{
printk("removing stepmotor driver\n");
if (MOD_IN_USE)
{
printk("motor: Warning: Device is busy, delaying driver removal\n");
}
if (unregister_chrdev(major,NAME))
{
printk("motor: Error: unregister_chrdev() failed\n");
}
release_region(io_base,3);
}