[nylug-talk] help on print drivers
Deepak Bawari
deepakbawari at lycos.co.uk
Tue May 20 00:13:00 EDT 2003
hello ,
i am linux newbie trying to develop a test driver for my printers ( dot
matrix and inkjet ) connected to the parallel port using the book by
A.Rubini and other sources.i have written this first prog and running it on
redhat 8.0 ( kernel 2.4.18 ) it complies and loads succcessfully return
major no 254 on my system.then i made a device using
mknod /dev/dee c 254 0
now when i do
cat some_file > /dev/dee
<<< is this valid testing method ?? i am not even sure of that >>>
the tty hangs , when i cheked on other tty using
dmesg tail
it shows
print : spurious interrupt
and doesn't print
the header and source code is included below , Please help me to find and
solve the problem also guide me over how do i know which particular driver
my device is using if i have more than 1 drivers installed for that?
regards
deepak
============================printerh.h============================
/*printerh.h
* Useful info describing the parallel port device.
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* ADAPTED form Linux Device Drivers by Alessandro Rubini and Jonathan
Corbet
*/
/*
* Register offsets
*/
#define P_DATA 0x00
#define P_STATUS 0x01
#define P_CONTROL 0x02
#define P_NPORTS 3
/*
* Status register bits.
*/
#define P_SR_BUSY 0x80
#define P_SR_ACK 0x40
#define P_SR_PAPER 0x20
#define P_SR_ONLINE 0x10
#define P_SR_ERR 0x08
/*
* Control register.
*/
#define P_CR_IRQ 0x10
#define P_CR_SELECT 0x08
#define P_CR_INIT 0x04
#define P_CR_AUTOLF 0x02
#define P_CR_STROBE 0x01
/*
* Minimum space before waking up a writer.
*/
#define P_MIN_SPACE PAGE_SIZE/2
========================printer.c=the source
code==============================
/* printer.c
compile with -I/usr/src/linux-2.4.18-24.8.0/include
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.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/ioport.h>
#include <linux/interrupt.h>
#include <linux/tqueue.h>
#include <linux/timer.h>
#include <asm/io.h> /*inb, outb, outb_p */
#include <asm/semaphore.h>
#include <asm/atomic.h>
#include "/home/deepak/Desktop/Project/printer/defn.h" // AS IN MY
SYSTEM
#include "/home/deepak/Desktop/Project/printer/printerh.h" // AS IN MY
SYSTEM
#define PRINT_NR_PORTS 3
static int major = 0; /* dynamic allocation */
MODULE_PARM(major, "i");
/* default is the first printer port on PC's. "print_base" is there too
because it's what we want to use in the code */
static unsigned long base = 0x378;
unsigned long print_base = 0;
MODULE_PARM(base, "l");
/* The interrupt line is undefined by default. "print_irq" is as above */
static int irq = -1;
static int print_irq = -1;
MODULE_PARM(irq, "i");
/* Microsecond delay around strobe. */
static int delay = 0;
static int print_delay;
MODULE_PARM(delay, "i");
MODULE_AUTHOR ("");
void cleanup_module (); //print_cleanup
static void print_timeout(unsigned long unused);
/*
* Input is managed through a simple circular buffer which, among other
things,
* is allowed to OVERRUN if the reader isn't fast enough. That makes life
simple
* on the "read" interrupt side, where we don't want to block.
*/
static unsigned long print_in_buffer = 0;
static unsigned long volatile print_in_head;
static volatile unsigned long print_in_tail;
DECLARE_WAIT_QUEUE_HEAD(print_in_queue);
static struct timeval print_tv;
/* Atomicly increment an index into print_in_buffer */
static inline void print_incr_bp(volatile unsigned long *index, int delta)
{
unsigned long new = *index + delta;
barrier ();
*index = (new >= (print_in_buffer + PAGE_SIZE)) ? print_in_buffer : new;
}
/*
*The semaphore is used to serialize write-side access to the buffer;
* there is only one consumer, so read-side access is unregulated. The
* wait queue will be awakened when space becomes available in the buffer.
*/
unsigned char *print_out_buffer = NULL;
volatile unsigned char *print_out_head, *print_out_tail;
struct semaphore print_out_sem;
DECLARE_WAIT_QUEUE_HEAD(print_out_queue);
/*
* Available space in the output buffer;
* should be called with the semaphore held.
* Returns contiguous space, so need not worry about wraps.
*/
static inline int print_out_space()
{
if (print_out_head >= print_out_tail)
{
int space = PAGE_SIZE - (print_out_head - print_out_buffer);
return (print_out_tail == print_out_buffer) ? space - 1 : space;
}
else
return ((print_out_tail - print_out_head) - 1);
}
static inline void print_incr_out_bp(volatile unsigned char **bp, int incr)
{
unsigned char *new = (unsigned char *) *bp + incr;
if (new >= (print_out_buffer + PAGE_SIZE))
new -= PAGE_SIZE;
*bp = new;
}
/*
* The output "process" is controlled by a spin lock;
* decisions on print_output_active or manipulation of print_out_tail
require
* that this lock be held.
*/
static spinlock_t print_out_lock;
volatile static int print_output_active;
DECLARE_WAIT_QUEUE_HEAD (print_empty_queue); /* waked when queue empties */
static int nwrote = 0;
/*
* When output is active, the timer is too, in case we miss interrupts.
Hold
* print_out_lock if you mess with the timer.
*/
static struct timer_list print_timer;
#define TIMEOUT 5*HZ /* Wait a long time */
int print_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return 0;
}
int print_release(struct inode *inode, struct file *filp)
{
wait_event_interruptible(print_empty_queue, print_output_active==0);
MOD_DEC_USE_COUNT;
return 0;
}
#ifdef __USE_OLD_SELECT__
int print_poll(struct inode *inode, struct file *filp,int mode,select_table
*table)
{
return mode==SEL_EX ? 0 : 1;
}
#define poll select
#else /* Use poll */
unsigned int print_poll(struct file *filp, poll_table *wait)
{
return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; ///?????????????
}
#endif /* __USE_OLD_SELECT__ */
ssize_t print_read(struct file *filp, char *buf, size_t count,loff_t *f_pos)
{
int count0;
while (print_in_head == print_in_tail)
{
interruptible_sleep_on(&print_in_queue);
if (signal_pending (current)) /* a signal arrived */
return -ERESTARTSYS; /* tell the fs layer to handle it */
/* else, loop */
}
/* count0 is the number of READABLE data bytes */
count0 = print_in_head - print_in_tail;
if (count0 < 0) /* wrapped */
count0 = print_in_buffer + PAGE_SIZE - print_in_tail;
if (count0 < count) count = count0;
if (copy_to_user(buf, (char *)print_in_tail, count))
return -EFAULT;
print_incr_bp (&print_in_tail, count);
return count;
}
/* Write the next character from the buffer. There should *be* a next
* character... The spinlock should be held when this routine is called.
*/
static void print_do_write()
{
unsigned char cr = inb(print_base + P_CONTROL);
/* Make sure the device is ready for use */
if ((inb(print_base + P_STATUS) & P_SR_BUSY) == 0)
{
printk (KERN_INFO "printrint: waiting for printer ; printer busy\n");
printk (KERN_INFO "Status is 0x%x\n", inb(print_base + P_STATUS));
while ((inb(print_base + P_STATUS) & P_SR_BUSY) == 0)
// busy is set
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10*HZ);
}
}
/* Mark output active and start up our timer if need be */
if (!print_output_active)
{
print_output_active = 1;
print_timer.expires = jiffies + TIMEOUT;
add_timer (&print_timer);
}
else
mod_timer(&print_timer, jiffies + TIMEOUT);
/*
* mod_timer is a more efficient way to update the expire field of an active
timer
* (if the timer is inactive it will be activated)
* mod_timer(a,b) is equivalent to del_timer(a); a->expires = b;
add_timer(a).
* If the timer is known to be not pending (ie, in the handler),
* mod_timer is less efficient than a->expires = b; add_timer(a).
*/
/* Strobe a byte out to the device */
outb_p(*print_out_tail, print_base+P_DATA);
print_incr_out_bp(&print_out_tail, 1);
if (print_delay) udelay(print_delay);
outb_p(cr | P_CR_STROBE, print_base+P_CONTROL);
if (print_delay) udelay(print_delay);
outb_p(cr & ~P_CR_STROBE, print_base+P_CONTROL);
nwrote++;
}
/*
* Write to the device.
*/
ssize_t print_write (struct file *filp, const char *buf, size_t count,loff_t
*f_pos)
{
int space, written = 0;
unsigned long flags;
/*
* Take and hold the semaphore for the entire duration of the operation.
The
* consumer side ignores it, and it will keep other data from interleaving
*/
if (down_interruptible (&print_out_sem))
return -ERESTARTSYS;
/*
* Out with the data.
*/
while (written < count)
{
/* Hang out until some buffer space is available. */
space = print_out_space();
if (space <= 0)
{
if (wait_event_interruptible(print_out_queue,(space = print_out_space())
> 0))
// wait_event_interruptible ::--> sched.h
{
*f_pos += written;
up(&print_out_sem); /// Up semaphore
return -ERESTARTSYS;
}
}
/* Move data into the buffer. */
if ((space + written) > count)
space = count - written;
if (copy_from_user((char *) print_out_head, buf, space))
{
up(&print_out_sem);
return -EFAULT;
}
print_incr_out_bp(&print_out_head, space);
buf += space;
written += space;
/* If no output is active, make it active.
* #define spin_lock_irqsave(lock, flags)
* do { local_irq_save(flags); spin_lock(lock);} while (0)
*/
spin_lock_irqsave(&print_out_lock, flags);
if (! print_output_active)
print_do_write ();
spin_unlock_irqrestore(&print_out_lock, flags);
}
*f_pos += written;
up(&print_out_sem);
return written;
}
/*
* The bottom-half handler.
*/
static struct tq_struct print_task; // tqueue.h :: linked-list
void print_do_task (void *unused)
{
int written;
unsigned long flags;
/* Keep the output going */
spin_lock_irqsave(&print_out_lock, flags);
if (print_out_head == print_out_tail)
{ /* empty */
print_output_active = 0;
wake_up_interruptible(&print_empty_queue);
del_timer_sync(&print_timer);
}
else
print_do_write();
/* If somebody's waiting, wake them up. */
if (((PAGE_SIZE + print_out_tail - print_out_head) % PAGE_SIZE) >
P_MIN_SPACE)
{
wake_up_interruptible(&print_out_queue);
}
spin_unlock_irqrestore(&print_out_lock, flags);
/* Handle the "read" side operation */
written = sprintf((char *)print_in_head, "%08u.%06u\n",
(int)(print_tv.tv_sec % 100000000),
(int)(print_tv.tv_usec));
print_incr_bp(&print_in_head, written);
wake_up_interruptible(&print_in_queue); /* awake any reading process */
}
/*
* The top-half interrupt handler.
*/
void print_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
if (! print_output_active)
printk(KERN_INFO "print : spurious interrupt\n");
/* Remember the time, and farm off the rest to the task queue function
*/
do_gettimeofday(&print_tv);
schedule_task(&print_task);
queue_task(&print_task, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
/*
* Interrupt timeouts. Just because we got a timeout doesn't mean that
* things have gone wrong, however; printers can spend an awful long time
* just thinking about things.
*/
static void print_timeout(unsigned long unused)
{
unsigned long flags;
unsigned char status;
spin_lock_irqsave(&print_out_lock, flags);
/**/ status = inb(print_base + P_STATUS);
/* If the printer is still busy we just reset the timer */
if ((status & P_SR_BUSY) == 0 || (status & P_SR_ACK))
{
print_timer.expires = jiffies + TIMEOUT;
add_timer(&print_timer);
spin_unlock_irqrestore(&print_out_lock, flags);
return;
}
/* Otherwise we must have dropped an interrupt. */
spin_unlock_irqrestore(&print_out_lock, flags);
print_interrupt (print_irq, NULL, NULL);
}
struct file_operations print_fops =
{
read: print_read,
write: print_write,
open: print_open,
release: print_release,
};
int init_module(void)
{
int result;
print_base = base;
print_irq = irq; // XXX
print_delay = delay; // initialised to zero
/* Set up owner pointers.*/
SET_MODULE_OWNER(&print_fops);
/* Get our needed resources. */
result = check_region(print_base, P_NPORTS);
if (result)
{
printk(KERN_INFO "print : can't get I/O port address 0x%lx\n",print_base);
return result;
}
request_region(print_base, PRINT_NR_PORTS, "print");
/* Register the device */
result = register_chrdev(major, "print", &print_fops);
if (result < 0)
{
printk(KERN_INFO "print: can't get major number\n");
release_region(print_base,PRINT_NR_PORTS);
return result;
}
if (major == 0) major = result; /* dynamic */
/* Initialize the input buffer. */
print_in_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */
print_in_head = print_in_tail = print_in_buffer;
/* And the output buffer. */
print_out_buffer = (unsigned char *) __get_free_pages(GFP_KERNEL, 0);
print_out_head = print_out_tail = print_out_buffer;
sema_init (&print_out_sem, 1);
/* And the output info */
print_output_active = 0;
spin_lock_init (&print_out_lock);
init_timer(&print_timer);
print_timer.function = print_timeout;
print_timer.data = 0;
/* Fill the print_task structure, used for the bottom half handler. */
print_task.routine = print_do_task;
print_task.data = NULL; /* unused */
/* If no IRQ was explicitly requested, pick a default */
if (print_irq < 0)
switch(print_base)
{
case 0x378: print_irq = 7; break; ///
case 0x278: print_irq = 2; break; ///
case 0x3bc: print_irq = 5; break; ///
/// LP are char dev for || line printer, having major no 6 and minor 0-2
( 3 too )
// monir no corresponds to printer port base addresses above
}
/* Request the IRQ */
result = request_irq(print_irq, print_interrupt, 0, "print", NULL);
if (result)
{
printk(KERN_INFO "print: can't get assigned irq %i\n",print_irq);
print_irq = -1;
cleanup_module ();
return result;
}
/* Initialize the control register, turning on interrupts. */
outb (P_CR_IRQ | P_CR_SELECT | P_CR_INIT, print_base + P_CONTROL);
return 0;
}
void cleanup_module(void)
{
/* Return the IRQ if we have one */
if (print_irq >= 0)
{
outb(0x0, print_base + P_CONTROL); /* disable the interrupt */
free_irq(print_irq, NULL);
}
if (print_output_active)
del_timer_sync (&print_timer);
/* All done with the device */
unregister_chrdev(major, "print");
release_region(print_base,PRINT_NR_PORTS);
if (print_in_buffer) free_page(print_in_buffer);
}
/*module_init(print_init);
module_exit(print_cleanup);*/
=================================defn.h====================================
/* sysdep.h -- centralizing compatibility issues between 2.0, 2.2, 2.4
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet Copyright (C)
2001 O'Reilly & Associates
*
* ADAPTED form Linux Device Drivers by Alessandro Rubini and Jonathan
Corbet
*/
#ifndef _SYSDEP_H_
#define _SYSDEP_H_
#ifndef LINUX_VERSION_CODE
# include <linux/version.h>
#endif
#ifndef KERNEL_VERSION /* pre-2.1.90 didn't have it */
# define KERNEL_VERSION(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
#endif
/* only allow 2.0.x 2.2.y and 2.4.z */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) /* not < 2.0 */
# error "This kernel is too old: not supported by this file"
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) /* not > 2.4, by now */
# error "This kernel is too recent: not supported by this file"
#endif
#if (LINUX_VERSION_CODE & 0xff00) == 1 /* not 2.1 */
# error "Please don't use linux-2.1, use 2.2 or 2.4 instead"
#endif
#if (LINUX_VERSION_CODE & 0xff00) == 3 /* not 2.3 */
# error "Please don't use linux-2.3, use 2.4 instead"
#endif
/* remember about the current version */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
# define LINUX_20
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
# define LINUX_22
#else
# define LINUX_24
#endif
/* we can't support versioning in pre-2.4 because we #define some functions
*/
#if !defined(LINUX_24) && defined(CONFIG_MODVERSIONS)
# error "This sysdep.h can't support CONFIG_MODVERSIONS"
# error "and old kernels at the same time."
# error "Either use 2.4 or avoid using versioning"
#endif
#ifndef LINUX_20 /* include vmalloc.h if this is 2.2/2.4 */
# ifdef VM_READ /* a typical flag defined by mm.h */
# include <linux/vmalloc.h>
# endif
#endif
#include <linux/sched.h>
/* Modularization issues */
#ifdef LINUX_20
# define __USE_OLD_SYMTAB__
# define EXPORT_NO_SYMBOLS register_symtab(NULL);
# define REGISTER_SYMTAB(tab) register_symtab(tab)
#else
# define REGISTER_SYMTAB(tab) /* nothing */
#endif
#ifdef __USE_OLD_SYMTAB__
# define __MODULE_STRING(s) /* nothing */
# define MODULE_PARM(v,t) /* nothing */
# define MODULE_PARM_DESC(v,t) /* nothing */
# define MODULE_AUTHOR(n) /* nothing */
# define MODULE_DESCRIPTION(d) /* nothing */
# define MODULE_SUPPORTED_DEVICE(n) /* nothing */
#endif
/*
* In version 2.2 (up to 2.2.19, at least), the macro for request_module()
* when no kmod is there is wrong. It's a "do {} while 0" but it shouldbe
int
*/
#ifdef LINUX_22
# ifndef CONFIG_KMOD
# undef request_module
# define request_module(name) -ENOSYS
# endif
#endif
#ifndef LINUX_20
# include <linux/init.h> /* module_init/module_exit */
#endif
/*#ifndef module_init
# define module_init(x) int init_module(void) { return x(); }
# define module_exit(x) void cleanup_module(void) { x(); }
#endif*/
#ifndef SET_MODULE_OWNER
# define SET_MODULE_OWNER(structure) /* nothing */
#endif
/*
* "select" changed in 2.1.23. The implementation is twin, but this
* header is new
*
*/
#ifdef LINUX_20
# define __USE_OLD_SELECT__
#else
# include <linux/poll.h>
#endif
#ifdef LINUX_20
# define INODE_FROM_F(filp) ((filp)->f_inode)
#else
# define INODE_FROM_F(filp) ((filp)->f_dentry->d_inode)
#endif
/* Other changes in the fops are solved using wrappers */
/*
* Wait queues changed with 2.3
*/
#ifndef DECLARE_WAIT_QUEUE_HEAD
# define DECLARE_WAIT_QUEUE_HEAD(head) struct wait_queue *head = NULL
typedef struct wait_queue *wait_queue_head_t;
# define init_waitqueue_head(head) (*(head)) = NULL
/* offer wake_up_sync as an alias for wake_up */
# define wake_up_sync(head) wake_up(head)
# define wake_up_interruptible_sync(head) wake_up_interruptible(head)
/* Pretend we have add_wait_queue_exclusive */
# define add_wait_queue_exclusive(q,entry) add_wait_queue ((q), (entry))
#endif /* no DECLARE_WAIT_QUEUE_HEAD */
/*
* Define wait_event for 2.0 kernels. (This ripped off directly from
* the 2.2.18 sched.h)
*/
#ifdef LINUX_20
#define __wait_event(wq, condition) \
do { \
struct wait_queue __wait; \
\
__wait.task = current; \
add_wait_queue(&wq, &__wait); \
for (;;) { \
current->state = TASK_UNINTERRUPTIBLE; \
mb(); \
if (condition) \
break; \
schedule(); \
} \
current->state = TASK_RUNNING; \
remove_wait_queue(&wq, &__wait); \
} while (0)
#define wait_event(wq, condition) \
do { \
if (condition) \
break; \
__wait_event(wq, condition); \
} while (0)
#define __wait_event_interruptible(wq, condition, ret) \
do { \
struct wait_queue __wait; \
\
__wait.task = current; \
add_wait_queue(&wq, &__wait); \
for (;;) { \
current->state = TASK_INTERRUPTIBLE; \
mb(); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
current->state = TASK_RUNNING; \
remove_wait_queue(&wq, &__wait); \
} while (0)
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
#endif
/*
* 2.3 added tasklets
*/
#ifdef LINUX_24
# define HAVE_TASKLETS
#endif
/* FIXME: implement the other versions of wake_up etc */
/*
* access to user space: use the 2.2 functions,
* and implement them as macros for 2.0
*/
#ifdef LINUX_20
# include <asm/segment.h>
# define access_ok(t,a,sz) (verify_area((t),(void *) (a),(sz)) ?
0 : 1)
# define verify_area_20 verify_area
# define copy_to_user(t,f,n) (memcpy_tofs((t), (f), (n)), 0)
# define copy_from_user(t,f,n) (memcpy_fromfs((t), (f), (n)), 0)
# define __copy_to_user(t,f,n) copy_to_user((t), (f), (n))
# define __copy_from_user(t,f,n) copy_from_user((t), (f), (n))
# define PUT_USER(val,add) (put_user((val),(add)), 0)
# define __PUT_USER(val,add) PUT_USER((val),(add))
# define GET_USER(dest,add) ((dest)=get_user((add)), 0)
# define __GET_USER(dest,add) GET_USER((dest),(add))
#else
# include <asm/uaccess.h>
# include <asm/io.h>
# define verify_area_20(t,a,sz) (0) /* == success */
# define PUT_USER put_user
# define __PUT_USER __put_user
# define GET_USER get_user
# define __GET_USER __get_user
#endif
/*
* Allocation issues
*/
#ifdef GFP_USER /* only if mm.h has been included */
# ifdef LINUX_20
# define __GFP_DMA GFP_DMA /* 2.0 didn't have the leading __ */
# endif
# ifndef LINUX_24
# define __GFP_HIGHMEM 0 /* was not there */
# define GFP_HIGHUSER 0 /* idem */
# endif
# ifdef LINUX_20
# define __get_free_pages(a,b) __get_free_pages((a),(b),0)
# endif
# ifndef LINUX_24
# define get_zeroed_page get_free_page
# endif
#endif
/* ioremap */
#if defined(LINUX_20) && defined(_LINUX_MM_H)
# define ioremap_nocache ioremap
# ifndef __i386__
/* This simple approach works for non-PC platforms. */
# define ioremap vremap
# define iounmap vfree
# else /* the PC has <expletive> ISA; 2.2 and 2.4 remap it, 2.0 needs not
*/
extern inline void *ioremap(unsigned long phys_addr, unsigned long size)
{
if (phys_addr >= 0xA0000 && phys_addr + size <= 0x100000)
return (void *)phys_addr;
return vremap(phys_addr, size);
}
extern inline void iounmap(void *addr)
{
if ((unsigned long)addr >= 0xA0000
&& (unsigned long)addr < 0x100000)
return;
vfree(addr);
}
# endif
#endif
/* Also, define check_mem_region etc */
#ifndef LINUX_24
# define check_mem_region(a,b) 0 /* success */
# define request_mem_region(a,b,c) /* nothing */
# define release_mem_region(a,b) /* nothing */
#endif
/* implement capable() for 2.0 */
#ifdef LINUX_20
# define capable(anything) suser()
#endif
/* The use_count of exec_domain and binfmt changed in 2.1.23 */
#ifdef LINUX_20
# define INCRCOUNT(p) ((p)->module ? __MOD_INC_USE_COUNT((p)->module) : 0)
# define DECRCOUNT(p) ((p)->module ? __MOD_DEC_USE_COUNT((p)->module) : 0)
# define CURRCOUNT(p) ((p)->module && (p)->module->usecount)
#else
# define INCRCOUNT(p) ((p)->use_count++)
# define DECRCOUNT(p) ((p)->use_count--)
# define CURRCOUNT(p) ((p)->use_count)
#endif
/*
* /proc has changed a lot across the versions...
*/
#ifdef LINUX_20
# define USE_PROC_REGISTER
#endif
/*
* 2.2 didn't have create_proc_{read|info}_entry yet.
* And it looks like there are no other "interesting" entry point, as
* the rest is somehow esotique (mknod, symlink, ...)
*/
#ifdef LINUX_22
# ifdef PROC_SUPER_MAGIC /* Only if procfs is being used */
extern inline struct proc_dir_entry *create_proc_read_entry(const char
*name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void * data)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base);
if (res) {
res->read_proc=read_proc;
res->data=data;
}
return res;
}
# ifndef create_proc_info_entry /* added in 2.2.18 */
typedef int (get_info_t)(char *, char **, off_t, int, int);
extern inline struct proc_dir_entry *create_proc_info_entry(const char
*name,
mode_t mode, struct proc_dir_entry *base, get_info_t *get_info)
{
struct proc_dir_entry *res=create_proc_entry(name,mode,base);
if (res) res->get_info=get_info;
return res;
}
# endif /* no create_proc_info_entry */
# endif
#endif
#ifdef LINUX_20
# define test_and_set_bit(nr,addr) test_bit((nr),(addr))
# define test_and_clear_bit(nr,addr) clear_bit((nr),(addr))
# define test_and_change_bit(nr,addr) change_bit((nr),(addr))
#endif
/* 2.0 had no read and write memory barriers, and 2.2 lacks the
set_ functions */
#ifndef LINUX_24
# ifdef LINUX_20
# define wmb() mb() /* this is a big penalty on non-reordering platfs */
# define rmb() mb() /* this is a big penalty on non-reordering platfs */
# endif /* LINUX_20 */
#define set_mb() do { var = value; mb(); } while (0)
#define set_wmb() do { var = value; wmb(); } while (0)
#endif /* ! LINUX_24 */
/* 2.1.30 removed these functions. Let's define them, just in case */
#ifndef LINUX_20
# define queue_task_irq queue_task
# define queue_task_irq_off queue_task
#endif
/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
#ifdef LINUX_20
# include <asm/byteorder.h>
# ifdef __LITTLE_ENDIAN
# define cpu_to_le16(x) (x)
# define cpu_to_le32(x) (x)
# define cpu_to_be16(x) htons((x))
# define cpu_to_be32(x) htonl((x))
# else
# define cpu_to_be16(x) (x)
# define cpu_to_be32(x) (x)
extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
extern inline __u32 cpu_to_le32(__u32 x) { return (x>>24) |
((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24);}
# endif
# define le16_to_cpu(x) cpu_to_le16(x)
# define le32_to_cpu(x) cpu_to_le32(x)
# define be16_to_cpu(x) cpu_to_be16(x)
# define be32_to_cpu(x) cpu_to_be32(x)
# define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
# define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
# define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
# define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
# define le16_to_cpup(x) cpu_to_le16p(x)
# define le32_to_cpup(x) cpu_to_le32p(x)
# define be16_to_cpup(x) cpu_to_be16p(x)
# define be32_to_cpup(x) cpu_to_be32p(x)
# define le16_to_cpus(x) cpu_to_le16s(x)
# define le32_to_cpus(x) cpu_to_le32s(x)
# define be16_to_cpus(x) cpu_to_be16s(x)
# define be32_to_cpus(x) cpu_to_be32s(x)
#endif
#ifdef LINUX_20
# define __USE_OLD_REBUILD_HEADER__
#endif
/*
* 2.0 didn't include sema_init, so we make our own - but only if it
* looks like semaphore.h got included.
*/
#ifdef LINUX_20
# ifdef MUTEX_LOCKED /* Only if semaphore.h included */
extern inline void sema_init (struct semaphore *sem, int val)
{
sem->count = val;
sem->waking = sem->lock = 0;
sem->wait = NULL;
}
# endif
#endif /* LINUX_20 */
/*
* In 2.0, there is no real need for spinlocks, and they weren't really
* implemented anyway.
*
* XXX the _irqsave variant should be defined eventually to do the
* right thing.
*/
#ifdef LINUX_20
typedef int spinlock_t;
# define spin_lock(lock)
# define spin_unlock(lock)
# define spin_lock_init(lock)
# define spin_lock_irqsave(lock,flags) do { \
save_flags(flags); cli(); } while (0);
# define spin_unlock_irqrestore(lock,flags) restore_flags(flags);
#endif
/*
* 2.1 stuffed the "flush" method into the middle of the file_operations
* structure. The FOP_NO_FLUSH symbol is for drivers that do not implement
* flush (most of them), it can be inserted in initializers for all 2.x
* kernel versions.
*/
#ifdef LINUX_20
# define FOP_NO_FLUSH /* nothing */
# define TAG_LLSEEK lseek
# define TAG_POLL select
#else
# define FOP_NO_FLUSH NULL,
# define TAG_LLSEEK llseek
# define TAG_POLL poll
#endif
/*
* fasync changed in 2.2.
*/
#ifdef LINUX_20
/* typedef struct inode *fasync_file; */
# define fasync_file struct inode *
#else
typedef int fasync_file;
#endif
/* kill_fasync had less arguments, and a different indirection in the first
*/
#ifndef LINUX_24
# define kill_fasync(ptrptr,sig,band) kill_fasync(*(ptrptr),(sig))
#endif
/* other things that are virtualized: define the new functions for the old k
*/
#ifdef LINUX_20
# define in_interrupt() (intr_count!=0)
# define mdelay(x) udelay((x)*1000)
# define signal_pending(current) ((current)->signal & ~(current)->blocked)
#endif
#ifdef LINUX_PCI_H /* only if PCI stuff is being used */
# ifdef LINUX_20
# include "pci-compat.h" /* a whole set of replacement functions */
# else
# define pci_release_device(d) /* placeholder, used in 2.0 to free stuff
*/
# endif
#endif
/*
* Some task state stuff
*/
#ifndef set_current_state
# define set_current_state(s) current->state = (s);
#endif
#ifdef LINUX_20
extern inline void schedule_timeout(int timeout)
{
current->timeout = jiffies + timeout;
current->state = TASK_INTERRUPTIBLE;
schedule();
current->timeout = 0;
}
extern inline long sleep_on_timeout(wait_queue_head_t *q, signed long
timeout)
{
signed long early = 0;
current->timeout = jiffies + timeout;
sleep_on (q);
if (current->timeout > 0) {
early = current->timeout - jiffies;
current->timeout = 0;
}
return early;
}
extern inline long interruptible_sleep_on_timeout(wait_queue_head_t *q,
signed long timeout)
{
signed long early = 0;
current->timeout = jiffies + timeout;
interruptible_sleep_on (q);
if (current->timeout > 0) {
early = current->timeout - jiffies;
current->timeout = 0;
}
return early;
}
#endif /* LINUX_20 */
/*
* Schedule_task was a late 2.4 addition.
*/
#ifndef LINUX_24
extern inline int schedule_task(struct tq_struct *task)
{
queue_task(task, &tq_scheduler);
return 1;
}
#endif
/*
* Timing issues
*/
#ifdef LINUX_20
# define get_fast_time do_gettimeofday
#endif
#ifdef _LINUX_DELAY_H /* only if linux/delay.h is included */
# ifndef mdelay /* linux-2.0 */
# ifndef MAX_UDELAY_MS
# define MAX_UDELAY_MS 5
# endif
# define mdelay(n) (\
(__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) :
\
({unsigned long msec=(n); while (msec--) udelay(1000);}))
# endif /* mdelay */
#endif /* _LINUX_DELAY_H */
/*
* No del_timer_sync before 2.4
*/
#ifndef LINUX_24
# define del_timer_sync(timer) del_timer(timer) /* and hope */
#endif
/*
* mod_timer wasn't present in 2.0
*/
#ifdef LINUX_20
static inline int mod_timer(struct timer_list *timer, unsigned long expires)
{
int pending = del_timer(timer);
if (pending) {
timer->expires = expires;
add_timer(timer);
}
return pending;
}
#endif
/*
* Various changes in mmap and friends.
*/
#ifndef NOPAGE_SIGBUS
# define NOPAGE_SIGBUS NULL /* return value of the nopage memory method
*/
# define NOPAGE_OOM NULL /* No real equivalent in older kernels */
#endif
#ifndef VM_RESERVED /* Added 2.4.0-test10 */
# define VM_RESERVED 0
#endif
#ifdef LINUX_24 /* use "vm_pgoff" to get an offset */
#define VMA_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
#else /* use "vm_offset" */
#define VMA_OFFSET(vma) ((vma)->vm_offset)
#endif
#ifdef MAP_NR
#define virt_to_page(page) (mem_map + MAP_NR(page))
#endif
#ifndef get_page
# define get_page(p) atomic_inc(&(p)->count)
#endif
/*
* No DMA lock in 2.0.
*/
#ifdef LINUX_20
static inline unsigned long claim_dma_lock(void)
{
unsigned long flags;
save_flags(flags);
cli();
return flags;
}
static inline void release_dma_lock(unsigned long flags)
{
restore_flags(flags);
}
#endif
/*
* I/O memory was not managed by ealier kernels, define them as success
*/
#if 0 /* FIXME: what is the right way to do request_mem_region? */
#ifndef LINUX_24
# define check_mem_region(start, len) 0
# define request_mem_region(start, len, name) 0
# define release_mem_region(start, len) 0
/*
* Also, request_ and release_ region used to return void. Return 0
instead
*/
# define request_region(s, l, n) ({request_region((s),(l),(n));0;})
# define release_region(s, l) ({release_region((s),(l));0;})
#endif /* not LINUX_24 */
#endif
/*
* Block layer stuff.
*/
#ifndef LINUX_24
/* BLK_DEFAULT_QUEUE for use with these macros only!!!! */
#define BLK_DEFAULT_QUEUE(major) blk_dev[(major)].request_fn
#define blk_init_queue(where,request_fn) where = request_fn;
#define blk_cleanup_queue(where) where = NULL;
/* No QUEUE_EMPTY in older kernels */
#ifndef QUEUE_EMPTY /* Driver can redefine it too */
# define QUEUE_EMPTY (CURRENT != NULL)
#endif
#ifdef RO_IOCTLS
static inline int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
{
int err;
switch (cmd) {
case BLKRAGET: /* return the readahead value */
if (!arg) return -EINVAL;
err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
if (err) return -EFAULT;
PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);
return 0;
case BLKRASET: /* set the readahead value */
if (!capable(CAP_SYS_ADMIN)) return -EACCES;
if (arg > 0xff) return -EINVAL; /* limit it */
read_ahead[MAJOR(dev)] = arg;
return 0;
case BLKFLSBUF: /* flush */
if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* only root */
fsync_dev(dev);
invalidate_buffers(dev);
return 0;
RO_IOCTLS(dev, arg);
}
return -ENOTTY;
}
#endif /* RO_IOCTLS */
#ifdef LINUX_EXTENDED_PARTITION /* defined in genhd.h */
static inline void register_disk(struct gendisk *gdev, kdev_t dev,
unsigned minors, struct file_operations *ops, long size)
{
if (! gdev)
return;
resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);
}
#endif /* LINUX_EXTENDED_PARTITION */
#else /* it is Linux 2.4 */
#define HAVE_BLKPG_H
#endif /* LINUX_24 */
#ifdef LINUX_20 /* physical and virtual addresses had the same value */
# define __pa(a) (a)
# define __va(a) (a)
#endif
/*
* Network driver compatibility
*/
/*
* 2.0 dev_kfree_skb had an extra arg. The following is a little dangerous
* in that it assumes that FREE_WRITE is always wanted. Very few 2.0
drivers
* use FREE_READ, but the number is *not* zero...
*
* Also: implement the non-checking versions of a couple skb functions -
* but they still check in 2.0.
*/
#ifdef LINUX_20
# define dev_kfree_skb(skb) dev_kfree_skb((skb), FREE_WRITE);
# define __skb_push(skb, len) skb_push((skb), (len))
# define __skb_put(skb, len) skb_put((skb), (len))
#endif
/*
* Softnet changes in 2.4
*/
#ifndef LINUX_24
# ifdef _LINUX_NETDEVICE_H /* only if netdevice.h was included */
# define netif_start_queue(dev) clear_bit(0, (void *) &(dev)->tbusy);
# define netif_stop_queue(dev) set_bit(0, (void *) &(dev)->tbusy);
static inline void netif_wake_queue(struct device *dev)
{
clear_bit(0, (void *) &(dev)->tbusy);
mark_bh(NET_BH);
}
/* struct device became struct net_device */
# define net_device device
# endif /* netdevice.h */
#endif /* ! LINUX_24 */
/*
* Memory barrier stuff, define what's missing from older kernel versions
*/
#ifdef switch_to /* this is always a macro, defined in <asm/sysstem.h> */
# ifndef set_mb
# define set_mb(var, value) do {(var) = (value); mb();} while 0
# endif
# ifndef set_rmb
# define set_rmb(var, value) do {(var) = (value); rmb();} while 0
# endif
# ifndef set_wmb
# define set_wmb(var, value) do {(var) = (value); wmb();} while 0
# endif
/* The hw barriers are defined as sw barriers. A correct thing if this
specific kernel/platform is supported but has no specific instruction */
# ifndef mb
# define mb barrier
# endif
# ifndef rmb
# define rmb barrier
# endif
# ifndef wmb
# define wmb barrier
# endif
#endif /* switch to (i.e. <asm/system.h>) */
#endif /* _SYSDEP_H_ */
==============================================================
More information about the nylug-talk
mailing list