Project

General

Profile

Statistics
| Branch: | Revision:

root / scout_gumstix / encoder / encoder.c @ 2b0ba4bb

History | View | Annotate | Download (4.97 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 <linux/interrupt.h>
9
#include <linux/gpio.h>
10
#include <linux/irq.h>
11
#include <asm/atomic.h>
12

    
13
MODULE_AUTHOR("Tom Mullins");
14
MODULE_LICENSE("GPL");
15

    
16
#define N_ENCODERS 4
17
const int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}};
18
struct class *enc_class = NULL;
19

    
20
struct encoder_device
21
{
22
  struct device *device;
23
  int pins[2];
24
  int irq[2];
25
  int status;
26
  atomic_t value;
27
  dev_t dev;
28
}
29
encs[N_ENCODERS];
30

    
31
static ssize_t enc_show(struct device *dev, struct device_attribute *attr,
32
    char *buf);
33
static ssize_t enc_store(struct device *dev, struct device_attribute *attr,
34
    const char *buf, size_t count);
35
static void enc_free(struct encoder_device *enc);
36
static int enc_init(dev_t dev, struct encoder_device *enc, const int *pins);
37

    
38
DEVICE_ATTR(ticks, S_IWUSR | S_IRUGO, enc_show, enc_store);
39

    
40
static ssize_t enc_show(struct device *dev, struct device_attribute *attr,
41
    char *buf)
42
{
43
  size_t nwritten;
44
  struct encoder_device *enc = dev_get_drvdata(dev);
45
  nwritten = scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&enc->value));
46
  return nwritten;
47
}
48

    
49
static ssize_t enc_store(struct device *dev, struct device_attribute *attr,
50
    const char *buf, size_t count)
51
{
52
  char *end;
53
  struct encoder_device *enc = dev_get_drvdata(dev);
54
  atomic_set(&enc->value, simple_strtol(buf, &end, 10));
55
  return count;
56
}
57

    
58
irqreturn_t intA(int irq, void *encp) {
59
  struct encoder_device *enc = encp;
60
  int this = gpio_get_value(enc->pins[0]);
61
  int other = gpio_get_value(enc->pins[1]);
62
  if (this == other) {
63
    atomic_inc(&enc->value);
64
  } else {
65
    atomic_dec(&enc->value);
66
  }
67
  return IRQ_HANDLED;
68
}
69

    
70
irqreturn_t intB(int irq, void *encp) {
71
  struct encoder_device *enc = encp;
72
  int this = gpio_get_value(enc->pins[1]);
73
  int other = gpio_get_value(enc->pins[0]);
74
  if (this == other) {
75
    atomic_dec(&enc->value);
76
  } else {
77
    atomic_inc(&enc->value);
78
  }
79
  return IRQ_HANDLED;
80
}
81

    
82
static int enc_init(dev_t dev, struct encoder_device *enc, const int *pins)
83
{
84
  int i, err;
85

    
86
  // initialize members
87
  enc->dev = dev;
88
  enc->status = 0;
89

    
90
  // register gpio and interrupts
91
  for (i = 0; i < 2; i++) {
92

    
93
    enc->pins[i] = pins[i];
94

    
95
    enc->irq[i] = gpio_to_irq(pins[i]);
96
    if (enc->irq[i] < 0) {
97
      printk("Error %d requesting irq number for gpio %d\n", enc->irq[i],
98
          enc->pins[i]);
99
      return 1;
100
    }
101

    
102
    err = gpio_direction_input(enc->pins[i]);
103
    if (err < 0) {
104
      printk("Error %d setting gpio %d to input\n", err, enc->pins[i]);
105
      return 1;
106
    }
107

    
108
    err = request_irq(enc->irq[i], i? intB:intA, 0, "encoder", enc);
109
    if (err < 0) {
110
      printk("Error %d requesting irq %d\n", err, enc->irq[i]);
111
      return 1;
112
    }
113

    
114
    err = irq_set_irq_type(enc->irq[i], IRQ_TYPE_EDGE_BOTH);
115
    if (err < 0) {
116
      printk("Error %d setting irq %d type\n", err, enc->irq[i]);
117
      return 1;
118
    }
119

    
120
    // TODO the error checking here does not properly clean up after itself
121
    // TODO perhaps we should use gpio_request? probably not necessary...
122
  }
123
  enc->status = 2;
124

    
125
  return 0;
126
}
127

    
128
int enc_create_dev(struct encoder_device *enc) {
129
  int err;
130

    
131
  // make file in /dev
132
  enc->device = device_create(enc_class, NULL, enc->dev, enc, "enc%d",
133
      MINOR(enc->dev));
134
  if (IS_ERR(enc->device))
135
  {
136
    err = PTR_ERR(enc->device);
137
    enc->device = NULL;
138
    enc_free(enc);
139
    return err;
140
  }
141
  enc->status = 3;
142

    
143
  device_create_file(enc->device, &dev_attr_ticks);
144
  // TODO error check
145
  enc->status = 4;
146

    
147
  return 0;
148
}
149

    
150
static void enc_free(struct encoder_device *enc)
151
{
152
  switch (enc->status)
153
  {
154
    case 4:
155
      device_remove_file(enc->device, &dev_attr_ticks);
156
    case 3:
157
      device_destroy(enc_class, enc->dev);
158
      enc->device = NULL;
159
    case 2:
160
      free_irq(enc->irq[0], enc);
161
      free_irq(enc->irq[1], enc);
162
  }
163
  enc->status = 0;
164
}
165

    
166
static void enc_exit_module(void)
167
{
168
  int i;
169
  for (i = 0; i < N_ENCODERS; i++)
170
  {
171
    enc_free(&encs[i]);
172
  }
173
  if (enc_class) {
174
    class_destroy(enc_class);
175
    enc_class = NULL;
176
  }
177
}
178
module_exit(enc_exit_module);
179

    
180
static int __init enc_init_module(void)
181
{
182
  int i, err;
183

    
184
  // initialize enc structures
185
  for (i = 0; i < N_ENCODERS; i++) {
186
    err = enc_init(MKDEV(0, i), &encs[i], enc_pins[i]);
187
    if (err)
188
    {
189
      printk(KERN_WARNING "Error %d initializing encoder %d\n", err, i);
190
      enc_exit_module();
191
      return err;
192
    }
193
  }
194

    
195
  // register our device class for files in /dev
196
  enc_class = class_create(THIS_MODULE, "encoder");
197
  if (IS_ERR(enc_class))
198
  {
199
    err = PTR_ERR(enc_class);
200
    enc_class = NULL;
201
    printk(KERN_WARNING "Error %d creating device class\n", err);
202
  }
203
  else
204
  {
205
    // create each device
206
    for (i = 0; i < N_ENCODERS; i++) {
207
      err = enc_create_dev(&encs[i]);
208
      if (err)
209
      {
210
        printk(KERN_WARNING "Error %d creating enc%d\n", err, i);
211
        enc_exit_module();
212
        return err;
213
      }
214
    }
215
  }
216

    
217
  return 0;
218
}
219
module_init(enc_init_module);