Skip to content

Instantly share code, notes, and snippets.

@AndreiDuma
Created January 5, 2016 09:57
Show Gist options
  • Save AndreiDuma/21ec0a01d6580708170b to your computer and use it in GitHub Desktop.
Save AndreiDuma/21ec0a01d6580708170b to your computer and use it in GitHub Desktop.
SI EpicClock
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
MODULE_DESCRIPTION("Epiclock Module");
MODULE_AUTHOR("SI");
MODULE_LICENSE("GPL");
static int open(struct inode *, struct file *);
static int release(struct inode *, struct file *);
static int read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset);
static int write(struct file *file, const char __user *user_buffer, size_t size, loff_t *offset);
static struct file_operations fops = {
.open = open,
.release = release,
.read = read,
.write = write,
};
static int major; // our assigned major number
static atomic_t used; // 0 - led is available, 1 - led is already opened
static int state; // 0 - led is off, 1 - led is on
#define MS_TO_NS(x) (x * 1E6L)
#define BLINK_DELAY 1
int digits[10][7] =
{
{1,1,1,1,1,1,0}, //0
{0,1,1,0,0,0,0}, //1
{1,1,0,1,1,0,1}, //2
{1,1,1,1,0,0,1}, //3
{0,1,1,0,0,1,1}, //4
{1,0,1,1,0,1,1}, //5
{1,0,1,1,1,1,1}, //6
{1,1,1,0,0,0,0}, //7
{1,1,1,1,1,1,1}, //8
{1,1,1,1,0,1,1}, //9
};
int seg[7] ={7,11,8,9,25,10,24};//25, 24, 23, 22, 27, 18, 17};
int en[8] = {22,23,27,18,17,2,14,4};//11, 10, 9, 8, 7, 4};
//saves the digit to display on the corresponding position
//0-9 the digits to display, except the last 2 elements, were 1 means enable the dots, 0 disable
//the last two elements control the dots
static int saved_digit[8]={0};
static struct hrtimer hr_timer;
static ktime_t ktime_period;
/* Enables the correspondings segments of the digit
* /param n The number to display
*/
void digit(int n)
{
/* sanity checks*/
//TODO 1 - read above;
int i;
for (i = 0; i < 7; i++) {
gpio_set_value(seg[i], digits[n][i]);
}
}
/* Enables the driver to turn on one of the digits
* \param d Line of the driver to be enabled, -1 for none
*/
void enable(int d)
{
//TODO 1 - read above;
int i;
for (i = 0; i < 6; i++) {
gpio_set_value(en[i], 0);
}
if (d == -1) {
return;
}
gpio_set_value(en[d], 1);
}
/* Timer callback to update display
*/
enum hrtimer_restart epiclock_update(struct hrtimer* timer)
{
static unsigned pos = 0;
ktime_t kt_now;
int ret;
//TODO 1 - increment pos and display the digit in the coresponding position
enable(pos);
digit(saved_digit[pos]);
pos = (pos + 1) % 6;
kt_now = hrtimer_cb_get_time(&hr_timer);
ret = hrtimer_forward(&hr_timer, kt_now, ktime_period);
return HRTIMER_RESTART;
}
static int __init epiclock_init(void)
{
int i;
ktime_t ktime;
printk(KERN_INFO "epiclock: loading\n");
/* initialize all signals to output, but off */
for(i = 0; i < 7; i++)
{
gpio_direction_output(seg[i], 0);
}
for(i = 0; i < 8; i++)
{
gpio_direction_output(en[i], 0);
}
/* set up the timer for the first time */
ktime_period = ktime_set(0, MS_TO_NS(BLINK_DELAY));
hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hr_timer.function = epiclock_update;
hrtimer_start(&hr_timer, ktime, HRTIMER_MODE_REL);
// register as chardev and get our major number
if((major = register_chrdev(0, "epic_clock", &fops)) < 0)
return major;
return 0;
}
static void __exit epiclock_cleanup(void)
{
printk(KERN_INFO "epiclock: unloading...\n");
/* disable timer */
hrtimer_cancel(&hr_timer);
unregister_chrdev(major, "epic_clock");
/* disable all digits and points */
enable(-1);
}
static int open(struct inode *inode, struct file *file)
{
// led becomes used if it was previously unused, otherwise EPERM
if(atomic_cmpxchg(&used, 0, 1) != 0)
return -EPERM;
return 0;
}
static int release(struct inode *inode, struct file *file)
{
int old_used;
// led becomes unused
if((old_used = atomic_cmpxchg(&used, 1, 0)) != 1)
printk(KERN_ERR "closing device that was not opened; state is %d\n", old_used);
return 0;
}
static int read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset)
{
//DO NOT TOUCH THIS.
return 0;
}
static int write(struct file *file, const char __user *user_buffer, size_t size, loff_t *offset)
{
// UGLY HACK - ignore concurrency issues
// even if we allow only one process to open the device
// we can still get concurrent accesses from a multithreaded process
//TODO 2 - read data from userspace
char buf[6];
if (size < 6) {
return 0;
}
copy_from_user(buf, user_buffer, 6);
int i;
for (i = 0; i < 6; i++) {
saved_digit[i] = buf[i] - '0';
}
return 6;
}
module_init(epiclock_init);
module_exit(epiclock_cleanup);
obj-m = epicclock.o
ARCH := arm
CROSS_COMPILE := arm-linux-gnueabihf-
KDIR := /opt/rpi/kernel
PWD := $(shell pwd)
obj-m := epicclock.o
kbuild:
make -C $(KDIR) M=`pwd` ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
make -C $(KDIR) M=`pwd` clean
-rm -f *~ Module.symvers Module.markers modules.order
while [ true ]; do
date +%H%M%S #> /dev/ec
sleep 2
date +%d%m%y #> /dev/ec
sleep 2
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment