The following is from Documentation/parport-lowlevel.txt.
At last, a example is given.
The example is modified by me.
It 's from ELDD book, but can not work properly before my modification.
The `parport' code provides parallel-port support under Linux. This
includes the ability to share one port between multiple devicedrivers.
parport_register_device
parport_register_driver
parport_unregister_driver
parport_claim
parport_release
---------------------------------------------------
parport_register_driver - register a device driver with parport
-----------------------
SYNOPSIS
#include <linux/parport.h>
struct parport_driver {
const char *name;//textual name of your driver
void (*attach) (struct parport *); // used for new port handling
void (*detach) (struct parport *); // handle ports going away due to a low-level driver unloading
struct parport_driver *next;
};
int parport_register_driver (struct parport_driver *driver);
RETURN VALUE
Zero on success, otherwise an error code.
ERRORS
None. (Can it fail? Why return int?)
struct parport
{
struct parport *next; /* next parport in list */
const char *name; /* port's name */
unsigned int modes; /* bitfield of hardware modes */
struct parport_device_info probe_info;
/* IEEE1284 info */
int number; /* parport index */
struct parport_operations *ops;
...
};
static void lp_attach (struct parport *port)
{
...
private = kmalloc (...);
dev[count++] = parport_register_device (...);
...
}
static void lp_detach (struct parport *port)
{
...
}
static struct parport_driver lp_driver = {
"lp",
lp_attach,
lp_detach,
NULL /* always put NULL here */
};
int lp_init (void)
{
...
if (parport_register_driver (&lp_driver)) {
/* Failed; nothing we can do. */
return -EIO;
}
...
}
parport_unregister_driver - tell parport to forget about this driver
DESCRIPTION
This tells parport not to notify the device driver of new ports or of
ports going away. Registered devices belonging to that driver are NOT
unregistered: parport_unregister_device must be used for each one.
EXAMPLE
void cleanup_module (void)
{
...
/* Stop notifications. */
parport_unregister_driver (&lp_driver);
/* Unregister devices. */
for (i = 0; i < NUM_DEVS; i++)
parport_unregister_device (dev[i]);
...
}
parport_register_device - register to use a port
-----------------------
SYNOPSIS
#include <linux/parport.h>
typedef int (*preempt_func) (void *handle);
typedef void (*wakeup_func) (void *handle);
typedef int (*irq_func) (int irq, void *handle, struct pt_regs *);
struct pardevice *parport_register_device(struct parport *port,
const char *name,
preempt_func preempt,
wakeup_func wakeup,
irq_func irq,
int flags,
void *handle);
DESCRIPTION
Use this function to register your device driver on a parallel port
('port'). Once you have done that, you will be able to use
parport_claim and parport_release in order to use the port.
The ('name') argument is the name of the device that appears in /proc
filesystem. The string must be valid for the whole lifetime of the
device (until parport_unregister_device is called).
This function will register three callbacks into your driver:
'preempt', 'wakeup' and 'irq'. Each of these may be NULL in order to
indicate that you do not want a callback.
When the 'preempt' function is called, it is because another driver
wishes to use the parallel port. The 'preempt' function should return
non-zero if the parallel port cannot be released yet -- if zero is
returned, the port is lost to another driver and the port must be
re-claimed before use.
The 'wakeup' function is called once another driver has released the
port and no other driver has yet claimed it. You can claim the
parallel port from within the 'wakeup' function (in which case the
claim is guaranteed to succeed), or choose not to if you don't need it
now.
If an interrupt occurs on the parallel port your driver has claimed,
the 'irq' function will be called. (Write something about shared
interrupts here.)
The 'handle' is a pointer to driver-specific data, and is passed to
the callback functions.
'flags' may be a bitwise combination of the following flags:
Flag Meaning
PARPORT_DEV_EXCL The device cannot share the parallel port at all.
Use this only when absolutely necessary.
The typedefs are not actually defined -- they are only shown in order
to make the function prototype more readable.
The visible parts of the returned 'struct pardevice' are:
struct pardevice {
struct parport *port; /* Associated port */
void *private; /* Device driver's 'handle' */
...
};
RETURN VALUE
A 'struct pardevice *': a handle to the registered parallel port
device that can be used for parport_claim, parport_release, etc.
ERRORS
A return value of NULL indicates that there was a problem registering
a device on that port.
EXAMPLE
static int preempt (void *handle)
{
if (busy_right_now)
return 1;
must_reclaim_port = 1;
return 0;
}
static void wakeup (void *handle)
{
struct toaster *private = handle;
struct pardevice *dev = private->dev;
if (!dev) return; /* avoid races */
if (want_port)
parport_claim (dev);
}
static int toaster_detect (struct toaster *private, struct parport *port)
{
private->dev = parport_register_device (port, "toaster", preempt,
wakeup, NULL, 0,
private);
if (!private->dev)
/* Couldn't register with parport. */
return -EIO;
must_reclaim_port = 0;
busy_right_now = 1;
parport_claim_or_block (private->dev);
...
/* Don't need the port while the toaster warms up. */
busy_right_now = 0;
...
busy_right_now = 1;
if (must_reclaim_port) {
parport_claim_or_block (private->dev);
must_reclaim_port = 0;
}
...
}
parport_unregister_device - finish using a port
-------------------------
SYNPOPSIS
#include <linux/parport.h>
void parport_unregister_device (struct pardevice *dev);
DESCRIPTION
This function is the opposite of parport_register_device. After using
parport_unregister_device, 'dev' is no longer a valid device handle.
You should not unregister a device that is currently claimed, although
if you do it will be released automatically.
EXAMPLE
...
kfree (dev->private); /* before we lose the pointer */
parport_unregister_device (dev);
...
parport_claim, parport_claim_or_block - claim the parallel port for a device
-------------------------------------
SYNOPSIS
#include <linux/parport.h>
int parport_claim (struct pardevice *dev);
int parport_claim_or_block (struct pardevice *dev);
DESCRIPTION
These functions attempt to gain control of the parallel port on which
'dev' is registered. 'parport_claim' does not block, but
'parport_claim_or_block' may do. (Put something here about blocking
interruptibly or non-interruptibly.)
You should not try to claim a port that you have already claimed.
RETURN VALUE
A return value of zero indicates that the port was successfully
claimed, and the caller now has possession of the parallel port.
If 'parport_claim_or_block' blocks before returning successfully, the
return value is positive.
ERRORS
-EAGAIN The port is unavailable at the moment, but another attempt
to claim it may succeed.
parport_release - release the parallel port
---------------
SYNOPSIS
#include <linux/parport.h>
void parport_release (struct pardevice *dev);
DESCRIPTION
Once a parallel port device has been claimed, it can be released using
'parport_release'. It cannot fail, but you should not release a
device that you do not have possession of.
EXAMPLE
static size_t write (struct pardevice *dev, const void *buf,
size_t len)
{
...
written = dev->port->ops->write_ecp_data (dev->port, buf,
len);
parport_release (dev);
...
}
parport_read_data(port) // data reading
parport_write_data(port, value)// data writing
EXAMPLE:
This driver creates /sys/class/pardevice/led.
Under the control dir. there are led0-led7 which can used to control parport's data output.
echo 1 > led0 will let parport's data pin's low bit become low level.
cat led0 can read the current state of parport's data pin.
#include <linux/cdev.h> // cdev
#include <linux/parport.h>// parport
#include <linux/sysfs.h> // struct attribute
#include <linux/device.h> //device
static dev_t dev_number; //allocated device number
static struct class *led_class; //class device Model
struct cdev led_cdev; //character dev struct
struct pardevice *pdev; // Parallel port device
//struct kobject kobj; // sysfs directory object
struct kobject* my_kobj;
//NOTE: kobject should be allocated dynamically.
/*systfs attribute of the leds */
struct led_attr {
struct attribute attr;
ssize_t (*show)(char *);
ssize_t (*store)(const char*, size_t count);
};
void led_cleanup(void);
#define glow_show_led(number) \
static ssize_t \
glow_led_##number(const char *buffer, size_t count)\
{ \
unsigned char buf; \
int value; \
sscanf(buffer, "%d", &value); \
parport_claim_or_block(pdev); \
buf = parport_read_data(pdev->port); \
if (value) { \
parport_write_data(pdev->port, buf | (1<<number));\
} else { \
parport_write_data(pdev->port, buf & ~(1<<number));\
} \
parport_release(pdev); \
return count; \
} \
\
static ssize_t \
show_led_##number(char *buffer) \
{ \
unsigned char buf; \
\
parport_claim_or_block(pdev); \
\
buf = parport_read_data(pdev->port); \
parport_release(pdev); \
\
if (buf & (1 << number)) { \
return sprintf(buffer, "ONON\n"); \
} else { \
return sprintf(buffer, "OFFOFF\n");\
} \
} \
static struct led_attr led##number = \
__ATTR(led##number, 0644, show_led_##number, glow_led_##number);
glow_show_led(0);
glow_show_led(1);
glow_show_led(2);
glow_show_led(3);
glow_show_led(4);
glow_show_led(5);
glow_show_led(6);
glow_show_led(7);
#define DEVICE_NAME "led"
static int led_preempt(void* handle)
{
return 1;
}
/*
Parport attach method
*/
static void led_attach(struct parport *port)
{
pdev = parport_register_device(port, DEVICE_NAME,
led_preempt, NULL, NULL, 0,
NULL);
if (pdev == NULL)
printk("Bad register\n");
}
/*
Parport detach method
*/
static void led_detach(struct parport *port)
{
parport_unregister_device(pdev);
}
/*
Parent sysfs show() method. Calls the show() method
corresponding to the individual sysfs file
*/
static ssize_t l_show (struct kobject *kobj, struct
attribute *a, char *buf)
{
int ret;
struct led_attr *lattr = container_of(a, struct led_attr, attr);
ret = lattr->show ? lattr->show(buf) : -EIO;
return ret;
}
/*
Sysfs store() method. Calls the store() method
corresponding to the individual sysfs file
*/
static ssize_t
l_store(struct kobject *kobj, struct attribute *a,
const char *buf, size_t count)
{
int ret;
struct led_attr * lattr = container_of(a, struct led_attr, attr);
ret = lattr->store ? lattr->store(buf, count) : -EIO;
return ret;
}
/*
Sysfs operations structure
*/
static struct sysfs_ops sysfs_ops = {
.show = l_show,
.store = l_store,
};
/*
Attributes of the /sys/class/pardevice/led/control/ kobject.
Each file in this directory corresponds to one LED. Control
each LED by writing or reading the associated sysfs file.
*/
static struct attribute *led_attrs[] = {
&led0.attr,
&led1.attr,
&led2.attr,
&led3.attr,
&led4.attr,
&led5.attr,
&led6.attr,
&led7.attr,
NULL
};
/*
This describes the kobject. The kobject has 8 files, one
corresponding to each LED. This representation is called
the ktype of the kobject
*/
static struct kobj_type ktype_led = {
.sysfs_ops = &sysfs_ops,
.default_attrs = led_attrs,
};
/*
Parport methods. We don't have a detach method
*/
static struct parport_driver led_driver = {
.name = "led_driver",
.attach = led_attach,
.detach = led_detach,
};
/*
Driver Initialization
*/
int __init led_init(void)
{
struct /*class_*/ device *c_d;
//int retval;
/*
Create the pardevice class - /sys/class/pardevice
*/
led_class = class_create(THIS_MODULE, "pardevice");
if (IS_ERR(led_class))
printk("Bad class create\n");
/*
Allocate device number
*/
alloc_chrdev_region(&dev_number, 0/*minor*/,
1/*dev nums*/, "mypar");
/*
Create the led class device - /sys/class/pardevice/led
*/
c_d = device_create(led_class, NULL, dev_number,
NULL, /*DEVICE_NAME*/"led");
/*
Register this driver with parport
*/
if (parport_register_driver(&led_driver)) {
printk(KERN_ERR "Bad Parport Register\n");
return -EIO;
}
// void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
my_kobj = kzalloc(sizeof(*my_kobj), GFP_KERNEL);
if (!my_kobj) {
led_cleanup();
return -ENOMEM;
}
kobject_init(my_kobj, &ktype_led);//initialize that kobject
// int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
kobject_add(my_kobj, &c_d->kobj, "control");//register the kobject with sysfs
printk("LED Driver Initialized. \n");
return 0;
}
/*
Driver Exit
*/
void led_cleanup(void)
{
//void parport_unregister_driver (struct parport_driver *drv)
parport_unregister_driver(&led_driver);
/*
Unregister kobject corresponding to
/sys/class/pardevice/led/control
*/
//kobject_unregister(&kobj);
// kobject_del(&kobj);
kobject_put(my_kobj);
//void kobject_put(struct kobject *kobj)
unregister_chrdev_region(dev_number, 1);
/*
Destroy class device corresponding to
/sys/class/pardevice/led/
*/
device_destroy(led_class, MKDEV(MAJOR(dev_number), 0));
/*
Destroy /sys/class/pardevice
*/
class_destroy(led_class);
return;
}
module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");