Project

General

Profile

Statistics
| Branch: | Revision:

scoutos / scout_gumstix / encoder / encoder.c @ f58d377f

History | View | Annotate | Download (6.12 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 <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
const int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}};
18
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
  int irq[2];
28
  int status;
29
  atomic_t value;
30
  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
  len = snprintf(enc->buf, BUF_LEN+1, "%d\n", atomic_read(&enc->value));
71
  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
  int new_value;
91
  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
  new_value = simple_strtoi(enc->buf, &end, 10);
103
  atomic_set(&enc->value, new_value);
104
  *offset += count;
105
  up(&enc->sem);
106
  return count;
107
}
108

    
109
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
{
133
  int err, i;
134

    
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
  // 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
    err = irq_set_irq_type(enc->irq[i], IRQ_TYPE_EDGE_BOTH);
176
    if (err < 0) {
177
      printk("Error %d setting irq %d type\n", err, enc->irq[i]);
178
      return 1;
179
    }
180

    
181
    // TODO the error checking here does not properly clean up after itself
182
    // TODO perhaps we should use gpio_request? probably not necessary...
183
  }
184
  enc->status = 2;
185

    
186
  return 0;
187
}
188

    
189
int enc_create_dev(struct enc_t *enc) {
190

    
191
  // make file in /dev
192
  enc->device = device_create(enc_class, NULL, enc->dev, NULL, "enc%d",
193
      MINOR(enc->dev));
194
  if (IS_ERR(enc->device))
195
  {
196
    err = PTR_ERR(enc->device);
197
    enc->device = NULL;
198
    enc_free(enc);
199
    return err;
200
  }
201
  enc->status = 3;
202

    
203
  return 0;
204
}
205

    
206
static void enc_free(struct enc_t *enc)
207
{
208
  switch (enc->status)
209
  {
210
    case 3:
211
      device_destroy(enc_class, enc->dev);
212
      enc->device = NULL;
213
    case 2:
214
      free_irq(enc->irq[0], enc);
215
      free_irq(enc->irq[1], enc);
216
    case 1:
217
      cdev_del(&enc->cdev);
218
  }
219
  enc->status = 0;
220
}
221

    
222
static void enc_exit_module(void)
223
{
224
  int i;
225
  for (i = 0; i < N_ENCODERS; i++)
226
  {
227
    enc_free(&encs[i]);
228
  }
229
  if (enc_class) {
230
    class_destroy(enc_class);
231
    enc_class = NULL;
232
  }
233
  unregister_chrdev_region(first_dev, N_ENCODERS);
234
}
235
module_exit(enc_exit_module);
236

    
237
static int __init enc_init_module(void)
238
{
239
  int i, err;
240

    
241
  // allocate character devices and get major number
242
  err = alloc_chrdev_region(&first_dev, 0, N_ENCODERS, "encoder");
243
  if (err)
244
  {
245
    printk(KERN_ALERT "Error %d in alloc_chrdev_region\n", err);
246
    return 1;
247
  }
248

    
249
  // initialize enc structures
250
  for (i = 0; i < N_ENCODERS; i++) {
251
    err = enc_init(first_dev + i, &encs[i], enc_pins[i]);
252
    if (err)
253
    {
254
      printk(KERN_WARNING "Error %d initializing encoder %d\n", err, i);
255
      enc_exit_module();
256
      return 1;
257
    }
258
  }
259

    
260
  // register our device class for files in /dev
261
  enc_class = class_create(THIS_MODULE, "encoder");
262
  if (IS_ERR(enc_class))
263
  {
264
    err = PTR_ERR(enc_class);
265
    enc_class = NULL;
266
    printk(KERN_WARNING "Error %d creating device class\n", err);
267
  }
268
  else
269
  {
270
    // create each device
271
    for (i = 0; i < N_ENCODERS; i++) {
272
      err = enc_create_dev(&encs[i]);
273
      if (err)
274
      {
275
        printk(KERN_WARNING "Error %d creating enc%d\n", err, i);
276
        enc_exit_module();
277
        return 1;
278
      }
279
    }
280
  }
281

    
282
  return 0;
283
}
284
module_init(enc_init_module);