#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/parport.h>

#include <asm/uaccess.h>

#include <linux/pci.h>


static dev_t dev_number;        /* Allotted 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 */

/* Sysfs attribute of the leds */
struct led_attr {
    struct attribute attr;
    ssize_t (*show)(char *);
    ssize_t (*store)(const char *, size_t count);
};

#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, "ON\n");                     \
    } else {                                                \
    return sprintf(buffer, "OFF\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");
}

/* 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",
    .attach = led_attach,
};

/* Driver Initialization */
int __init
led_init(void)
{
    struct class_device *c_d;
    if (alloc_chrdev_region (&dev_number, 0, 1, DEVICE_NAME)
        < 0) {
        printk(KERN_DEBUG "Canít register new device\n");
        return -1;
    }

    /* Create the pardevice class - /sys/class/pardevice */
    led_class = class_create(THIS_MODULE, "pardevice");
    if (IS_ERR(led_class)) printk("Bad class create\n");

    /* Create the led class device - /sys/class/pardevice/led/ */
    c_d = class_device_create(led_class, NULL, dev_number,
                              NULL, DEVICE_NAME);
    /* Register this driver with parport */
    if (parport_register_driver(&led_driver)) {
        printk(KERN_ERR "Bad Parport Register\n");
        return -EIO;
    }

    /* Instantiate a kobject to control each LED
       on the board */
    /* Parent is /sys/class/pardevice/led/ */
    kobj.parent = &c_d->kobj;

    /* The sysfs file corresponding to kobj is
       /sys/class/pardevice/led/control/ */
    strlcpy(kobj.name, "control", KOBJ_NAME_LEN);

    /* Description of the kobject. Specifies the list of attribute
       files in /sys/class/pardevice/led/control/ */
    kobj.ktype = &ktype_led;

    /* Register the kobject */
    kobject_register(&kobj);
    printk("LED Driver Initialized.\n");

    return 0;
}

/* Driver Exit */
void
led_cleanup(void)
{
    /* Unregister kobject corresponding to
       /sys/class/pardevice/led/control */
    kobject_unregister(&kobj);

    /* Destroy class device corresponding to
       /sys/class/pardevice/led/ */
    class_device_destroy(led_class, dev_number);

    /* Destroy /sys/class/pardevice */
    class_destroy(led_class);
    return;
}

module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");


syntax highlighted by Code2HTML, v. 0.9.1