Project

General

Profile

Statistics
| Branch: | Revision:

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);