scoutos / scout_gumstix / encoder / encoder.c @ 83163372
History | View | Annotate | Download (4.23 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 | #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); |