Project

General

Profile

Statistics
| Branch: | Revision:

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