Project

General

Profile

Revision 83163372

ID8316337284df2c876d5280419cac544041a14c56
Parent 560d2317
Child 8969d535

Added by Thomas Mullins over 11 years ago

Added kernel module for quad encoders

The kernel module is not finished yet. Also, added various config files
that go on every Gumstix. It would be nice to have an automated script
for installing them.

View differences:

scout_gumstix/README
1
etc/ contains various configuration files that should go into /etc/ on the
2
Gumstix. etc/hostname and etc/hosts have "scout1" in them, which should be
3
replaced with a unique name for each scout.
4

  
5
encoder/ contains the source code for a kernel module which must be built on
6
the Gumstix. It provides /dev/encN with 0 <= N < 4. Count value are signed
7
integers. Examples:
8
$ cat /dev/enc0
9
104522
10
$ echo 0 > /dev/enc0
11
$ cat /dev/enc0
12
0
scout_gumstix/encoder/Makefile
1
obj-m += encoder.o
2

  
3
all: encoder.ko
4

  
5
encoder.ko: encoder.c
6
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
7

  
8
clean:
9
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
scout_gumstix/encoder/encoder.c
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);
scout_gumstix/etc/fstab
1
proc /proc proc nodev,noexec,nosuid 0 0
2
/dev/mmcblk0p2 / ext3 errors=remount-ro,user_xattr,noatime,nodiratime,commit=120 0 1
scout_gumstix/etc/hostname
1
scout1
scout_gumstix/etc/hosts
1
127.0.0.1	localhost
2
127.0.1.1	scout1
3
::1		localhost ip6-localhost ip6-loopback
4
fe00::0		ip6-localnet
5
ff00::0		ip6-mcastprefix
6
ff02::1		ip6-allnodes
7
ff02::2		ip6-allrouters
8

  
scout_gumstix/etc/init/ttyO2.conf
1
# This service maintains a getty on ttyO2 from the point the system is
2
# started until it is shut down again.
3

  
4
start on stopped rc RUNLEVEL=[12345]
5

  
6
stop on runlevel [!12345]
7

  
8
respawn
9
exec /sbin/getty -8 115200 ttyO2
scout_gumstix/etc/network/interfaces
1
# interfaces(5) file used by ifup(8) and ifdown(8)
2
auto lo
3
iface lo inet loopback
4
auto usb0
5
iface usb0 inet dhcp
scout_gumstix/etc/rc.local
1
#!/bin/sh -e
2
#
3
# rc.local
4
#
5
# This script is executed at the end of each multiuser runlevel.
6
# Make sure that the script will "exit 0" on success or any other
7
# value on error.
8
#
9
# In order to enable or disable this script just change the execution
10
# bits.
11
#
12
# By default this script does nothing.
13

  
14
export_as_output() {
15
  last_dir="$PWD"
16
  cd /sys/class/gpio
17
  echo "$1" > export
18
  cd "gpio$1"
19
  echo out > direction
20
  chgrp gpio value
21
  chown g+w value # TODO necessary?
22
  cd "$last_dir"
23
}
24

  
25
# export motor driver gpio pins
26
for i in 70 71 72 73 74 75 76 77; do
27
  export_as_output $i
28
done
29

  
30
exit 0
scout_gumstix/etc/udev/rules.d/40-encoder.rules
1
SUBSYSTEM=="encoder", GROUP="gpio"

Also available in: Unified diff