root / scout_gumstix / encoder / encoder.c @ 83163372
History | View | Annotate | Download (4.23 KB)
1 |
#include <linux/init.h> |
---|---|
2 |
#include <linux/module.h> |
3 |
#include <linux/fs.h> |
4 |
#include <linux/cdev.h> |
5 |
#include <linux/slab.h> |
6 |
#include <linux/device.h> |
7 |
#include <linux/semaphore.h> |
8 |
#include <asm/uaccess.h> |
9 |
|
10 |
MODULE_AUTHOR("Tom Mullins");
|
11 |
MODULE_LICENSE("GPL");
|
12 |
|
13 |
/*
|
14 |
* TODO
|
15 |
*
|
16 |
* clean up before final move to gumstix
|
17 |
* add interrupt counting
|
18 |
*
|
19 |
*/
|
20 |
|
21 |
typedef long enc_val_t; |
22 |
|
23 |
#define BUF_LEN 20 |
24 |
#define N_ENCODERS 4 |
25 |
int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}}; |
26 |
struct class *enc_class = NULL; |
27 |
dev_t first_dev; |
28 |
|
29 |
struct enc_t
|
30 |
{ |
31 |
char buf[BUF_LEN+1]; |
32 |
struct cdev cdev;
|
33 |
struct semaphore sem;
|
34 |
struct device *device;
|
35 |
int status;
|
36 |
enc_val_t value; |
37 |
dev_t dev; |
38 |
} |
39 |
encs[N_ENCODERS]; |
40 |
|
41 |
static int enc_open(struct inode *inode, struct file *file); |
42 |
static ssize_t enc_read(struct file *file, char __user *udata, size_t count, |
43 |
loff_t *offset); |
44 |
static ssize_t enc_write(struct file *file, const char __user *udata, |
45 |
size_t count, loff_t *offset); |
46 |
static void enc_free(struct enc_t *enc); |
47 |
static int enc_init(dev_t dev, struct enc_t *enc); |
48 |
|
49 |
struct file_operations fops =
|
50 |
{ |
51 |
.owner = THIS_MODULE, |
52 |
.open = enc_open, |
53 |
.read = enc_read, |
54 |
.write = enc_write |
55 |
}; |
56 |
|
57 |
static int enc_open(struct inode *inode, struct file *file) |
58 |
{ |
59 |
int i = iminor(inode);
|
60 |
if (i < 0 || i >= N_ENCODERS) |
61 |
{ |
62 |
return -ENXIO;
|
63 |
} |
64 |
file->private_data = &encs[i]; |
65 |
return 0; |
66 |
} |
67 |
|
68 |
static ssize_t enc_read(struct file *file, char __user *udata, size_t count, |
69 |
loff_t *offset) |
70 |
{ |
71 |
int len;
|
72 |
struct enc_t *enc = file->private_data;
|
73 |
if (down_interruptible(&enc->sem))
|
74 |
{ |
75 |
return -ERESTARTSYS;
|
76 |
} |
77 |
len = snprintf(enc->buf, BUF_LEN+1, "%ld\n", enc->value); |
78 |
if (*offset > len)
|
79 |
{ |
80 |
return 0; |
81 |
} |
82 |
len -= *offset; |
83 |
if (len > count)
|
84 |
{ |
85 |
len = count; |
86 |
} |
87 |
copy_to_user(udata, enc->buf + *offset, len); |
88 |
*offset += len; |
89 |
up(&enc->sem); |
90 |
return len;
|
91 |
} |
92 |
|
93 |
static ssize_t enc_write(struct file *file, const char __user *udata, |
94 |
size_t count, loff_t *offset) |
95 |
{ |
96 |
char *end;
|
97 |
struct enc_t *enc = file->private_data;
|
98 |
if (down_interruptible(&enc->sem))
|
99 |
{ |
100 |
return -ERESTARTSYS;
|
101 |
} |
102 |
if (count > BUF_LEN)
|
103 |
{ |
104 |
count = BUF_LEN; |
105 |
} |
106 |
copy_from_user(enc->buf, udata, count); |
107 |
enc->buf[count] = '\0';
|
108 |
enc->value = simple_strtol(enc->buf, &end, 10);
|
109 |
*offset += count; |
110 |
up(&enc->sem); |
111 |
return count;
|
112 |
} |
113 |
|
114 |
static int enc_init(dev_t dev, struct enc_t *enc) |
115 |
{ |
116 |
int err;
|
117 |
|
118 |
// initialize members
|
119 |
cdev_init(&enc->cdev, &fops); |
120 |
enc->cdev.owner = THIS_MODULE; |
121 |
enc->dev = dev; |
122 |
enc->status = 0;
|
123 |
sema_init(&enc->sem, 1);
|
124 |
|
125 |
// make cdev file operations accessible
|
126 |
err = cdev_add(&enc->cdev, enc->dev, N_ENCODERS); |
127 |
if (err)
|
128 |
{ |
129 |
enc_free(enc); |
130 |
return err;
|
131 |
} |
132 |
enc->status = 1;
|
133 |
|
134 |
// make file in /dev
|
135 |
enc->device = device_create(enc_class, NULL, enc->dev, NULL, "enc%d", |
136 |
MINOR(enc->dev)); |
137 |
if (IS_ERR(enc->device))
|
138 |
{ |
139 |
err = PTR_ERR(enc->device); |
140 |
enc->device = NULL;
|
141 |
enc_free(enc); |
142 |
return err;
|
143 |
} |
144 |
enc->status = 2;
|
145 |
|
146 |
return 0; |
147 |
} |
148 |
|
149 |
static void enc_free(struct enc_t *enc) |
150 |
{ |
151 |
switch (enc->status)
|
152 |
{ |
153 |
case 2: |
154 |
device_destroy(enc_class, enc->dev); |
155 |
enc->device = NULL;
|
156 |
case 1: |
157 |
cdev_del(&enc->cdev); |
158 |
} |
159 |
enc->status = 0;
|
160 |
} |
161 |
|
162 |
static void enc_exit_module(void) |
163 |
{ |
164 |
int i;
|
165 |
for (i = 0; i < N_ENCODERS; i++) |
166 |
{ |
167 |
enc_free(&encs[i]); |
168 |
} |
169 |
if (enc_class) {
|
170 |
class_destroy(enc_class); |
171 |
enc_class = NULL;
|
172 |
} |
173 |
unregister_chrdev_region(first_dev, N_ENCODERS); |
174 |
} |
175 |
module_exit(enc_exit_module); |
176 |
|
177 |
static int __init enc_init_module(void) |
178 |
{ |
179 |
int i, err;
|
180 |
|
181 |
// allocate character devices and get major number
|
182 |
err = alloc_chrdev_region(&first_dev, 0, N_ENCODERS, "encoder"); |
183 |
if (err)
|
184 |
{ |
185 |
printk(KERN_ALERT "Error %d in alloc_chrdev_region\n", err);
|
186 |
return 1; |
187 |
} |
188 |
|
189 |
// register our device class for files in /dev
|
190 |
enc_class = class_create(THIS_MODULE, "encoder");
|
191 |
if (IS_ERR(enc_class))
|
192 |
{ |
193 |
err = PTR_ERR(enc_class); |
194 |
enc_class = NULL;
|
195 |
printk(KERN_WARNING "Error %d creating device class\n", err);
|
196 |
} |
197 |
else
|
198 |
{ |
199 |
// initialize enc structures
|
200 |
for (i = 0; i < N_ENCODERS; i++) { |
201 |
err = enc_init(first_dev + i, &encs[i]); |
202 |
if (err)
|
203 |
{ |
204 |
printk(KERN_WARNING "Error %d adding enc%d\n", err, i);
|
205 |
enc_exit_module(); |
206 |
return 1; |
207 |
} |
208 |
} |
209 |
} |
210 |
|
211 |
return 0; |
212 |
} |
213 |
module_init(enc_init_module); |