Revision 83163372
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.
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