How to build parport related driver upon parport module in the kernel

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 device
drivers.

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");


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值