scoutos / scout_gumstix / encoder / encoder.c @ f024710b
History | View | Annotate | Download (5.96 KB)
1 | 83163372 | Tom Mullins | #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 | f024710b | Tom Mullins | #include <linux/interrupt.h> |
9 | #include <linux/gpio.h> |
||
10 | 83163372 | Tom Mullins | #include <asm/uaccess.h> |
11 | |||
12 | MODULE_AUTHOR("Tom Mullins");
|
||
13 | MODULE_LICENSE("GPL");
|
||
14 | |||
15 | #define BUF_LEN 20 |
||
16 | #define N_ENCODERS 4 |
||
17 | f024710b | Tom Mullins | const int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}}; |
18 | 83163372 | Tom Mullins | struct class *enc_class = NULL; |
19 | dev_t first_dev; |
||
20 | |||
21 | struct enc_t
|
||
22 | { |
||
23 | char buf[BUF_LEN+1]; |
||
24 | struct cdev cdev;
|
||
25 | struct semaphore sem;
|
||
26 | struct device *device;
|
||
27 | f024710b | Tom Mullins | int irq[2]; |
28 | 83163372 | Tom Mullins | int status;
|
29 | f024710b | Tom Mullins | atomic_t value; |
30 | 83163372 | Tom Mullins | dev_t dev; |
31 | } |
||
32 | encs[N_ENCODERS]; |
||
33 | |||
34 | static int enc_open(struct inode *inode, struct file *file); |
||
35 | static ssize_t enc_read(struct file *file, char __user *udata, size_t count, |
||
36 | loff_t *offset); |
||
37 | static ssize_t enc_write(struct file *file, const char __user *udata, |
||
38 | size_t count, loff_t *offset); |
||
39 | static void enc_free(struct enc_t *enc); |
||
40 | static int enc_init(dev_t dev, struct enc_t *enc); |
||
41 | |||
42 | struct file_operations fops =
|
||
43 | { |
||
44 | .owner = THIS_MODULE, |
||
45 | .open = enc_open, |
||
46 | .read = enc_read, |
||
47 | .write = enc_write |
||
48 | }; |
||
49 | |||
50 | static int enc_open(struct inode *inode, struct file *file) |
||
51 | { |
||
52 | int i = iminor(inode);
|
||
53 | if (i < 0 || i >= N_ENCODERS) |
||
54 | { |
||
55 | return -ENXIO;
|
||
56 | } |
||
57 | file->private_data = &encs[i]; |
||
58 | return 0; |
||
59 | } |
||
60 | |||
61 | static ssize_t enc_read(struct file *file, char __user *udata, size_t count, |
||
62 | loff_t *offset) |
||
63 | { |
||
64 | int len;
|
||
65 | struct enc_t *enc = file->private_data;
|
||
66 | if (down_interruptible(&enc->sem))
|
||
67 | { |
||
68 | return -ERESTARTSYS;
|
||
69 | } |
||
70 | f024710b | Tom Mullins | len = snprintf(enc->buf, BUF_LEN+1, "%d\n", atomic_read(&enc->value)); |
71 | 83163372 | Tom Mullins | if (*offset > len)
|
72 | { |
||
73 | return 0; |
||
74 | } |
||
75 | len -= *offset; |
||
76 | if (len > count)
|
||
77 | { |
||
78 | len = count; |
||
79 | } |
||
80 | copy_to_user(udata, enc->buf + *offset, len); |
||
81 | *offset += len; |
||
82 | up(&enc->sem); |
||
83 | return len;
|
||
84 | } |
||
85 | |||
86 | static ssize_t enc_write(struct file *file, const char __user *udata, |
||
87 | size_t count, loff_t *offset) |
||
88 | { |
||
89 | char *end;
|
||
90 | f024710b | Tom Mullins | int new_value;
|
91 | 83163372 | Tom Mullins | struct enc_t *enc = file->private_data;
|
92 | if (down_interruptible(&enc->sem))
|
||
93 | { |
||
94 | return -ERESTARTSYS;
|
||
95 | } |
||
96 | if (count > BUF_LEN)
|
||
97 | { |
||
98 | count = BUF_LEN; |
||
99 | } |
||
100 | copy_from_user(enc->buf, udata, count); |
||
101 | enc->buf[count] = '\0';
|
||
102 | f024710b | Tom Mullins | new_value = simple_strtoi(enc->buf, &end, 10);
|
103 | atomic_set(&enc->value, new_value); |
||
104 | 83163372 | Tom Mullins | *offset += count; |
105 | up(&enc->sem); |
||
106 | return count;
|
||
107 | } |
||
108 | |||
109 | f024710b | Tom Mullins | irqreturn_t intA(int irq, void *enc, struct pt_regs *regs) { |
110 | int this = gpio_get_value(enc->pins[0]); |
||
111 | int other = gpio_get_value(enc->pins[1]); |
||
112 | if (this == other) {
|
||
113 | atomic_inc(&enc->value); |
||
114 | } else {
|
||
115 | atomic_dec(&enc->value); |
||
116 | } |
||
117 | return IRQ_HANDLED;
|
||
118 | } |
||
119 | |||
120 | irqreturn_t intB(int irq, void *enc, struct pt_regs *regs) { |
||
121 | int this = gpio_get_value(enc->pins[1]); |
||
122 | int other = gpio_get_value(enc->pins[0]); |
||
123 | if (this == other) {
|
||
124 | atomic_dec(&enc->value); |
||
125 | } else {
|
||
126 | atomic_inc(&enc->value); |
||
127 | } |
||
128 | return IRQ_HANDLED;
|
||
129 | } |
||
130 | |||
131 | static int enc_init(dev_t dev, struct enc_t *enc, int *pins) |
||
132 | 83163372 | Tom Mullins | { |
133 | f024710b | Tom Mullins | int err, i;
|
134 | 83163372 | Tom Mullins | |
135 | // initialize members
|
||
136 | cdev_init(&enc->cdev, &fops); |
||
137 | enc->cdev.owner = THIS_MODULE; |
||
138 | enc->dev = dev; |
||
139 | enc->status = 0;
|
||
140 | sema_init(&enc->sem, 1);
|
||
141 | |||
142 | // make cdev file operations accessible
|
||
143 | err = cdev_add(&enc->cdev, enc->dev, N_ENCODERS); |
||
144 | if (err)
|
||
145 | { |
||
146 | enc_free(enc); |
||
147 | return err;
|
||
148 | } |
||
149 | enc->status = 1;
|
||
150 | |||
151 | f024710b | Tom Mullins | // register gpio and interrupts
|
152 | for (i = 0; i < 2; i++) { |
||
153 | |||
154 | enc->pins[i] = pins[i]; |
||
155 | |||
156 | enc->irq[i] = gpio_to_irq(pins[i]); |
||
157 | if (enc->irq[i] < 0) { |
||
158 | printk("Error %d requesting irq number for gpio %d\n", enc->irq[i],
|
||
159 | enc->pins[i]); |
||
160 | return 1; |
||
161 | } |
||
162 | |||
163 | err = gpio_direction_input(enc->pins[i]); |
||
164 | if (err < 0) { |
||
165 | printk("Error %d setting gpio %d to input\n", err, enc->pins[i]);
|
||
166 | return 1; |
||
167 | } |
||
168 | |||
169 | err = request_irq(enc->irq[i], i? intB : intA, SA_INTERRUPT, "encoder", enc);
|
||
170 | if (err < 0) { |
||
171 | printk("Error %d requesting irq %d\n", err, enc->irq[i]);
|
||
172 | return 1; |
||
173 | } |
||
174 | |||
175 | // TODO the error checking here does not properly clean up after itself
|
||
176 | // TODO perhaps we should use gpio_request? probably not necessary...
|
||
177 | } |
||
178 | enc->status = 2;
|
||
179 | |||
180 | return 0; |
||
181 | } |
||
182 | |||
183 | int enc_create_dev(struct enc_t *enc) { |
||
184 | |||
185 | 83163372 | Tom Mullins | // make file in /dev
|
186 | enc->device = device_create(enc_class, NULL, enc->dev, NULL, "enc%d", |
||
187 | MINOR(enc->dev)); |
||
188 | if (IS_ERR(enc->device))
|
||
189 | { |
||
190 | err = PTR_ERR(enc->device); |
||
191 | enc->device = NULL;
|
||
192 | enc_free(enc); |
||
193 | return err;
|
||
194 | } |
||
195 | f024710b | Tom Mullins | enc->status = 3;
|
196 | 83163372 | Tom Mullins | |
197 | return 0; |
||
198 | } |
||
199 | |||
200 | static void enc_free(struct enc_t *enc) |
||
201 | { |
||
202 | switch (enc->status)
|
||
203 | { |
||
204 | f024710b | Tom Mullins | case 3: |
205 | 83163372 | Tom Mullins | device_destroy(enc_class, enc->dev); |
206 | enc->device = NULL;
|
||
207 | f024710b | Tom Mullins | case 2: |
208 | free_irq(enc->irq[0], enc);
|
||
209 | free_irq(enc->irq[1], enc);
|
||
210 | 83163372 | Tom Mullins | case 1: |
211 | cdev_del(&enc->cdev); |
||
212 | } |
||
213 | enc->status = 0;
|
||
214 | } |
||
215 | |||
216 | static void enc_exit_module(void) |
||
217 | { |
||
218 | int i;
|
||
219 | for (i = 0; i < N_ENCODERS; i++) |
||
220 | { |
||
221 | enc_free(&encs[i]); |
||
222 | } |
||
223 | if (enc_class) {
|
||
224 | class_destroy(enc_class); |
||
225 | enc_class = NULL;
|
||
226 | } |
||
227 | unregister_chrdev_region(first_dev, N_ENCODERS); |
||
228 | } |
||
229 | module_exit(enc_exit_module); |
||
230 | |||
231 | static int __init enc_init_module(void) |
||
232 | { |
||
233 | int i, err;
|
||
234 | |||
235 | // allocate character devices and get major number
|
||
236 | err = alloc_chrdev_region(&first_dev, 0, N_ENCODERS, "encoder"); |
||
237 | if (err)
|
||
238 | { |
||
239 | printk(KERN_ALERT "Error %d in alloc_chrdev_region\n", err);
|
||
240 | return 1; |
||
241 | } |
||
242 | |||
243 | f024710b | Tom Mullins | // initialize enc structures
|
244 | for (i = 0; i < N_ENCODERS; i++) { |
||
245 | err = enc_init(first_dev + i, &encs[i], enc_pins[i]); |
||
246 | if (err)
|
||
247 | { |
||
248 | printk(KERN_WARNING "Error %d initializing encoder %d\n", err, i);
|
||
249 | enc_exit_module(); |
||
250 | return 1; |
||
251 | } |
||
252 | } |
||
253 | |||
254 | 83163372 | Tom Mullins | // register our device class for files in /dev
|
255 | enc_class = class_create(THIS_MODULE, "encoder");
|
||
256 | if (IS_ERR(enc_class))
|
||
257 | { |
||
258 | err = PTR_ERR(enc_class); |
||
259 | enc_class = NULL;
|
||
260 | printk(KERN_WARNING "Error %d creating device class\n", err);
|
||
261 | } |
||
262 | else
|
||
263 | { |
||
264 | f024710b | Tom Mullins | // create each device
|
265 | 83163372 | Tom Mullins | for (i = 0; i < N_ENCODERS; i++) { |
266 | f024710b | Tom Mullins | err = enc_create_dev(&encs[i]); |
267 | 83163372 | Tom Mullins | if (err)
|
268 | { |
||
269 | f024710b | Tom Mullins | printk(KERN_WARNING "Error %d creating enc%d\n", err, i);
|
270 | 83163372 | Tom Mullins | enc_exit_module(); |
271 | return 1; |
||
272 | } |
||
273 | } |
||
274 | } |
||
275 | |||
276 | return 0; |
||
277 | } |
||
278 | module_init(enc_init_module); |