|
|
@@ -16,7 +16,7 @@
|
|
|
#define CLASS_NAME "cashd_class"
|
|
|
#define MINOR_BASE 0
|
|
|
#define MINOR_COUNT 2
|
|
|
-#define BUFFER_SIZE PAGE_SIZE
|
|
|
+#define BUFFER_SIZE 4
|
|
|
|
|
|
#define GPIO0_CTL 0xFD6D0B50
|
|
|
#define GPIO0_STATUS 0xFD6D0940
|
|
|
@@ -26,7 +26,6 @@
|
|
|
|
|
|
struct cashd_device {
|
|
|
unsigned char *buffer;
|
|
|
- size_t data_size;
|
|
|
size_t buffer_size;
|
|
|
wait_queue_head_t read_wait;
|
|
|
wait_queue_head_t write_wait;
|
|
|
@@ -38,49 +37,76 @@ struct cashd_device {
|
|
|
bool can_write;
|
|
|
unsigned int ctl_status;
|
|
|
unsigned int in_status;
|
|
|
+ unsigned int status_changed;
|
|
|
+ int last_status;
|
|
|
struct delayed_work delay_work1;
|
|
|
+ struct delayed_work delay_work2;
|
|
|
+ void __iomem *gpio_ctl_reg_base;
|
|
|
+ void __iomem *gpio_status_reg_base;
|
|
|
+ int count;
|
|
|
};
|
|
|
|
|
|
static int cashd_major = 0;
|
|
|
static struct class *cashd_class = NULL;
|
|
|
static struct cashd_device *cashd_devices[MINOR_COUNT];
|
|
|
+static unsigned int minors[MINOR_COUNT] = {10, 11};
|
|
|
|
|
|
-static void delay_work_func(struct work_struct *work)
|
|
|
+static void gpio_ctl_delay_work_func(struct work_struct *work)
|
|
|
{
|
|
|
- void __iomem *reg_base;
|
|
|
unsigned int value = 0;
|
|
|
struct delayed_work *dwork = to_delayed_work(work);
|
|
|
struct cashd_device *dev = container_of(dwork, struct cashd_device, delay_work1);
|
|
|
- printk("%s %s %d\n", __FILE__, __func__, __LINE__);
|
|
|
- if(dev->dev_minor == 0)
|
|
|
+
|
|
|
+ value = readl(dev->gpio_ctl_reg_base);
|
|
|
+ value &= 0xfffffffe;
|
|
|
+ writel(value, dev->gpio_ctl_reg_base);
|
|
|
+}
|
|
|
+
|
|
|
+static void gpio_status_delay_work_func(struct work_struct *work)
|
|
|
+{
|
|
|
+ unsigned int value = 0;
|
|
|
+ struct delayed_work *dwork = to_delayed_work(work);
|
|
|
+ struct cashd_device *dev = container_of(dwork, struct cashd_device, delay_work2);
|
|
|
+
|
|
|
+ value = readl(dev->gpio_status_reg_base);
|
|
|
+ value = value & 0x1;
|
|
|
+ if(value != dev->last_status)
|
|
|
{
|
|
|
- reg_base = ioremap(GPIO0_CTL, 0x1000);
|
|
|
+ dev->last_status = value;
|
|
|
+ dev->status_changed = 1;
|
|
|
+ wake_up_interruptible(&dev->read_wait);
|
|
|
}
|
|
|
- else if(dev->dev_minor == 1)
|
|
|
+ dev->count++;
|
|
|
+ if(dev->count >= 50)
|
|
|
{
|
|
|
- reg_base = ioremap(GPIO1_CTL, 0x1000);
|
|
|
+ dev->count = 0;
|
|
|
+ dev->status_changed = 1;
|
|
|
+ printk("%s %s %d\n", __FILE__, __func__, __LINE__);
|
|
|
+ wake_up_interruptible(&dev->read_wait);
|
|
|
}
|
|
|
- value = readl(reg_base);
|
|
|
- value &= 0xfffffffe;
|
|
|
- writel(value, reg_base);
|
|
|
+ printk("%s %s %d\n", __FILE__, __func__, __LINE__);
|
|
|
+ schedule_delayed_work(&dev->delay_work2, msecs_to_jiffies(100));
|
|
|
}
|
|
|
|
|
|
static int cashd_open(struct inode *inode, struct file *filp)
|
|
|
{
|
|
|
- struct cashd_device *dev;
|
|
|
+ struct cashd_device *dev = NULL;
|
|
|
unsigned int minor = iminor(inode);
|
|
|
-
|
|
|
- if (minor >= MINOR_COUNT) {
|
|
|
- pr_err("cashd: Invalid minor number %d\n", minor);
|
|
|
- return -ENODEV;
|
|
|
+ int i = 0;
|
|
|
+ for(i = 0; i < MINOR_COUNT; i++)
|
|
|
+ {
|
|
|
+ if(minor == minors[i])
|
|
|
+ {
|
|
|
+ dev = cashd_devices[i];
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- dev = cashd_devices[minor];
|
|
|
if (!dev) {
|
|
|
pr_err("cashd: Device not initialized for minor %d\n", minor);
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
filp->private_data = dev;
|
|
|
pr_info("cashd: Device /dev/cashd%d opened\n", minor);
|
|
|
|
|
|
@@ -99,45 +125,36 @@ static ssize_t cashd_read(struct file *filp, char __user *buf,
|
|
|
{
|
|
|
struct cashd_device *dev = filp->private_data;
|
|
|
ssize_t bytes_read = 0;
|
|
|
- size_t available;
|
|
|
- int ret;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- void __iomem *reg_base;
|
|
|
-
|
|
|
- if(*f_pos > 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
-
|
|
|
if (!dev)
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (!buf || count == 0)
|
|
|
return -EINVAL;
|
|
|
-
|
|
|
- if(dev->dev_minor == 0)
|
|
|
- {
|
|
|
- reg_base = ioremap(GPIO0_STATUS, 0x1000);
|
|
|
- dev->in_status = readl(reg_base);
|
|
|
- printk(KERN_INFO "cashd: GPIO0 Status: 0x%x\n", dev->in_status);
|
|
|
- }
|
|
|
- else if(dev->dev_minor == 1)
|
|
|
- {
|
|
|
- reg_base = ioremap(GPIO1_STATUS, 0x1000);
|
|
|
- dev->in_status = readl(reg_base);
|
|
|
- printk(KERN_INFO "cashd: GPIO1 Status: 0x%x\n", dev->in_status);
|
|
|
- }
|
|
|
+
|
|
|
+ dev->in_status = readl(dev->gpio_status_reg_base);
|
|
|
|
|
|
if(dev->in_status & 0x1)
|
|
|
{
|
|
|
- copy_to_user(buf, "o", 1);
|
|
|
+ ret = copy_to_user(buf, "o", 1);
|
|
|
+ if(ret != 0)
|
|
|
+ {
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- copy_to_user(buf, "c", 1);
|
|
|
+ ret = copy_to_user(buf, "c", 1);
|
|
|
+ if(ret != 0)
|
|
|
+ {
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ dev->status_changed = 0;
|
|
|
+
|
|
|
bytes_read = 1;
|
|
|
- *f_pos += bytes_read;
|
|
|
|
|
|
return bytes_read;
|
|
|
}
|
|
|
@@ -146,11 +163,8 @@ static ssize_t cashd_write(struct file *filp, const char __user *buf,
|
|
|
size_t count, loff_t *f_pos)
|
|
|
{
|
|
|
struct cashd_device *dev = filp->private_data;
|
|
|
- ssize_t bytes_written = 0;
|
|
|
- size_t space;
|
|
|
int ret;
|
|
|
unsigned int value = 0;
|
|
|
- void __iomem *reg_base;
|
|
|
|
|
|
if (!dev)
|
|
|
return -EINVAL;
|
|
|
@@ -160,20 +174,17 @@ static ssize_t cashd_write(struct file *filp, const char __user *buf,
|
|
|
count = BUFFER_SIZE;
|
|
|
}
|
|
|
ret = copy_from_user(dev->buffer, buf, count);
|
|
|
+ if(ret != 0)
|
|
|
+ {
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
if(dev->buffer[0] == 'o' || dev->buffer[0] == 'O')
|
|
|
{
|
|
|
printk("%s %s %d minor=%d\n", __FILE__, __func__, __LINE__, dev->dev_minor);
|
|
|
- if(dev->dev_minor == 0)
|
|
|
- {
|
|
|
- reg_base = ioremap(GPIO0_CTL, 0x1000);
|
|
|
- }
|
|
|
- else if(dev->dev_minor == 1)
|
|
|
- {
|
|
|
- reg_base = ioremap(GPIO1_CTL, 0x1000);
|
|
|
- }
|
|
|
- value = readl(reg_base);
|
|
|
+
|
|
|
+ value = readl(dev->gpio_ctl_reg_base);
|
|
|
value |= 0x1;
|
|
|
- writel(value, reg_base);
|
|
|
+ writel(value, dev->gpio_ctl_reg_base);
|
|
|
schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(1000));
|
|
|
}
|
|
|
else
|
|
|
@@ -195,13 +206,9 @@ static unsigned int cashd_poll(struct file *filp, poll_table *wait)
|
|
|
// poll_wait(filp, &dev->write_wait, wait);
|
|
|
|
|
|
mutex_lock(&dev->lock);
|
|
|
-
|
|
|
- if (dev->data_size > 0)
|
|
|
+ if(dev->status_changed)
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
|
- // if (dev->data_size < dev->buffer_size)
|
|
|
- // mask |= POLLOUT | POLLWRNORM;
|
|
|
-
|
|
|
mutex_unlock(&dev->lock);
|
|
|
|
|
|
return mask;
|
|
|
@@ -213,7 +220,7 @@ static const struct file_operations cashd_fops = {
|
|
|
.release = cashd_release,
|
|
|
.read = cashd_read,
|
|
|
.write = cashd_write,
|
|
|
- // .poll = cashd_poll,
|
|
|
+ .poll = cashd_poll,
|
|
|
};
|
|
|
|
|
|
static int __init cashd_init_device(struct cashd_device *dev, int minor)
|
|
|
@@ -227,7 +234,6 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
|
|
|
}
|
|
|
|
|
|
memset(dev->buffer, 0, BUFFER_SIZE);
|
|
|
- dev->data_size = 0;
|
|
|
dev->buffer_size = BUFFER_SIZE;
|
|
|
dev->dev_major = cashd_major;
|
|
|
dev->dev_minor = minor;
|
|
|
@@ -238,7 +244,8 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
|
|
|
init_waitqueue_head(&dev->write_wait);
|
|
|
|
|
|
mutex_init(&dev->lock);
|
|
|
- INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
|
|
|
+ INIT_DELAYED_WORK(&dev->delay_work1, gpio_ctl_delay_work_func);
|
|
|
+ INIT_DELAYED_WORK(&dev->delay_work2, gpio_status_delay_work_func);
|
|
|
|
|
|
cdev_init(&dev->cdev, &cashd_fops);
|
|
|
dev->cdev.owner = THIS_MODULE;
|
|
|
@@ -250,33 +257,31 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static char *my_devnode(struct device *dev, umode_t *mode) {
|
|
|
+ if (mode) {
|
|
|
+ *mode = 0666;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
int cashd_init(void)
|
|
|
{
|
|
|
int ret;
|
|
|
int i;
|
|
|
- dev_t dev_num;
|
|
|
struct device *device;
|
|
|
-
|
|
|
- pr_info("cashd: Initializing driver\n");
|
|
|
-
|
|
|
- ret = alloc_chrdev_region(&dev_num, MINOR_BASE, MINOR_COUNT, DEVICE_NAME);
|
|
|
- if (ret < 0) {
|
|
|
- pr_err("cashd: Failed to allocate device numbers\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- cashd_major = MAJOR(dev_num);
|
|
|
- pr_info("cashd: Allocated major number %d\n", cashd_major);
|
|
|
-
|
|
|
+ cashd_major = 56;
|
|
|
+
|
|
|
cashd_class = class_create(THIS_MODULE, CLASS_NAME);
|
|
|
if (IS_ERR(cashd_class)) {
|
|
|
ret = PTR_ERR(cashd_class);
|
|
|
pr_err("cashd: Failed to create class\n");
|
|
|
goto fail_class;
|
|
|
}
|
|
|
+ cashd_class->devnode = my_devnode;
|
|
|
|
|
|
for (i = 0; i < MINOR_COUNT; i++) {
|
|
|
cashd_devices[i] = kzalloc(sizeof(struct cashd_device), GFP_KERNEL);
|
|
|
@@ -286,7 +291,7 @@ int cashd_init(void)
|
|
|
goto fail_devices;
|
|
|
}
|
|
|
|
|
|
- ret = cashd_init_device(cashd_devices[i], i);
|
|
|
+ ret = cashd_init_device(cashd_devices[i], minors[i]);
|
|
|
if (ret) {
|
|
|
pr_err("cashd: Failed to init device for minor %d\n", i);
|
|
|
kfree(cashd_devices[i]);
|
|
|
@@ -295,7 +300,7 @@ int cashd_init(void)
|
|
|
}
|
|
|
|
|
|
device = device_create(cashd_class, NULL,
|
|
|
- MKDEV(cashd_major, i), NULL,
|
|
|
+ MKDEV(cashd_major, minors[i]), NULL,
|
|
|
"cashd%d", i);
|
|
|
if (IS_ERR(device)) {
|
|
|
ret = PTR_ERR(device);
|
|
|
@@ -305,17 +310,30 @@ int cashd_init(void)
|
|
|
cashd_devices[i] = NULL;
|
|
|
goto fail_devices;
|
|
|
}
|
|
|
+
|
|
|
+ if(i == 0)
|
|
|
+ {
|
|
|
+ cashd_devices[i]->gpio_ctl_reg_base = ioremap(GPIO0_CTL, 4);
|
|
|
+ cashd_devices[i]->gpio_status_reg_base = ioremap(GPIO0_STATUS, 4);
|
|
|
+ }
|
|
|
+ else if(i == 1)
|
|
|
+ {
|
|
|
+ cashd_devices[i]->gpio_ctl_reg_base = ioremap(GPIO1_CTL, 4);
|
|
|
+ cashd_devices[i]->gpio_status_reg_base = ioremap(GPIO1_STATUS, 4);
|
|
|
+ }
|
|
|
+ schedule_delayed_work(&cashd_devices[i]->delay_work2, msecs_to_jiffies(100));
|
|
|
|
|
|
pr_info("cashd: Created /dev/cashd%d\n", i);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
pr_info("cashd: Driver initialized successfully\n");
|
|
|
return 0;
|
|
|
|
|
|
fail_devices:
|
|
|
for (i = 0; i < MINOR_COUNT; i++) {
|
|
|
if (cashd_devices[i]) {
|
|
|
- device_destroy(cashd_class, MKDEV(cashd_major, i));
|
|
|
+ device_destroy(cashd_class, MKDEV(cashd_major, minors[i]));
|
|
|
cdev_del(&cashd_devices[i]->cdev);
|
|
|
if (cashd_devices[i]->buffer)
|
|
|
kfree(cashd_devices[i]->buffer);
|
|
|
@@ -337,13 +355,16 @@ void cashd_exit(void)
|
|
|
|
|
|
for (i = 0; i < MINOR_COUNT; i++) {
|
|
|
if (cashd_devices[i]) {
|
|
|
- device_destroy(cashd_class, MKDEV(cashd_major, i));
|
|
|
+ cancel_delayed_work_sync(&cashd_devices[i]->delay_work1);
|
|
|
+ cancel_delayed_work_sync(&cashd_devices[i]->delay_work2);
|
|
|
+ device_destroy(cashd_class, MKDEV(cashd_major, minors[i]));
|
|
|
|
|
|
cdev_del(&cashd_devices[i]->cdev);
|
|
|
|
|
|
if (cashd_devices[i]->buffer)
|
|
|
kfree(cashd_devices[i]->buffer);
|
|
|
-
|
|
|
+ iounmap(cashd_devices[i]->gpio_ctl_reg_base);
|
|
|
+ iounmap(cashd_devices[i]->gpio_status_reg_base);
|
|
|
kfree(cashd_devices[i]);
|
|
|
|
|
|
pr_info("cashd: Removed /dev/cashd%d\n", i);
|