Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /********************************************************************* |
2 | * | |
3 | * Filename: ma600.c | |
4 | * Version: 0.1 | |
5 | * Description: Implementation of the MA600 dongle | |
6 | * Status: Experimental. | |
7 | * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 | |
8 | * Created at: Sat Jun 10 20:02:35 2000 | |
9 | * Modified at: | |
10 | * Modified by: | |
11 | * | |
12 | * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing | |
13 | * information on the MA600 dongle | |
14 | * | |
15 | * Copyright (c) 2000 Leung, All Rights Reserved. | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or | |
18 | * modify it under the terms of the GNU General Public License as | |
19 | * published by the Free Software Foundation; either version 2 of | |
20 | * the License, or (at your option) any later version. | |
21 | * | |
22 | * This program is distributed in the hope that it will be useful, | |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26 | * | |
27 | * You should have received a copy of the GNU General Public License | |
28 | * along with this program; if not, write to the Free Software | |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
30 | * MA 02111-1307 USA | |
31 | * | |
32 | ********************************************************************/ | |
33 | ||
34 | /* define this macro for release version */ | |
35 | //#define NDEBUG | |
36 | ||
37 | #include <linux/module.h> | |
38 | #include <linux/delay.h> | |
39 | #include <linux/tty.h> | |
40 | #include <linux/init.h> | |
41 | ||
42 | #include <net/irda/irda.h> | |
43 | #include <net/irda/irda_device.h> | |
44 | ||
45 | #ifndef NDEBUG | |
46 | #undef IRDA_DEBUG | |
47 | #define IRDA_DEBUG(n, args...) (printk(KERN_DEBUG args)) | |
48 | ||
49 | #undef ASSERT | |
50 | #define ASSERT(expr, func) \ | |
51 | if(!(expr)) { \ | |
52 | printk( "Assertion failed! %s,%s,%s,line=%d\n",\ | |
53 | #expr,__FILE__,__FUNCTION__,__LINE__); \ | |
54 | func} | |
55 | #endif | |
56 | ||
57 | /* convert hex value to ascii hex */ | |
58 | static const char hexTbl[] = "0123456789ABCDEF"; | |
59 | ||
60 | ||
61 | static void ma600_open(dongle_t *self, struct qos_info *qos); | |
62 | static void ma600_close(dongle_t *self); | |
63 | static int ma600_change_speed(struct irda_task *task); | |
64 | static int ma600_reset(struct irda_task *task); | |
65 | ||
66 | /* control byte for MA600 */ | |
67 | #define MA600_9600 0x00 | |
68 | #define MA600_19200 0x01 | |
69 | #define MA600_38400 0x02 | |
70 | #define MA600_57600 0x03 | |
71 | #define MA600_115200 0x04 | |
72 | #define MA600_DEV_ID1 0x05 | |
73 | #define MA600_DEV_ID2 0x06 | |
74 | #define MA600_2400 0x08 | |
75 | ||
76 | static struct dongle_reg dongle = { | |
77 | .type = IRDA_MA600_DONGLE, | |
78 | .open = ma600_open, | |
79 | .close = ma600_close, | |
80 | .reset = ma600_reset, | |
81 | .change_speed = ma600_change_speed, | |
82 | .owner = THIS_MODULE, | |
83 | }; | |
84 | ||
85 | static int __init ma600_init(void) | |
86 | { | |
87 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
88 | return irda_device_register_dongle(&dongle); | |
89 | } | |
90 | ||
91 | static void __exit ma600_cleanup(void) | |
92 | { | |
93 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
94 | irda_device_unregister_dongle(&dongle); | |
95 | } | |
96 | ||
97 | /* | |
98 | Power on: | |
99 | (0) Clear RTS and DTR for 1 second | |
100 | (1) Set RTS and DTR for 1 second | |
101 | (2) 9600 bps now | |
102 | Note: assume RTS, DTR are clear before | |
103 | */ | |
104 | static void ma600_open(dongle_t *self, struct qos_info *qos) | |
105 | { | |
106 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
107 | ||
108 | qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 | |
109 | |IR_57600|IR_115200; | |
110 | qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ | |
111 | irda_qos_bits_to_value(qos); | |
112 | ||
113 | //self->set_dtr_rts(self->dev, FALSE, FALSE); | |
114 | // should wait 1 second | |
115 | ||
116 | self->set_dtr_rts(self->dev, TRUE, TRUE); | |
117 | // should wait 1 second | |
118 | } | |
119 | ||
120 | static void ma600_close(dongle_t *self) | |
121 | { | |
122 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
123 | ||
124 | /* Power off dongle */ | |
125 | self->set_dtr_rts(self->dev, FALSE, FALSE); | |
126 | } | |
127 | ||
128 | static __u8 get_control_byte(__u32 speed) | |
129 | { | |
130 | __u8 byte; | |
131 | ||
132 | switch (speed) { | |
133 | default: | |
134 | case 115200: | |
135 | byte = MA600_115200; | |
136 | break; | |
137 | case 57600: | |
138 | byte = MA600_57600; | |
139 | break; | |
140 | case 38400: | |
141 | byte = MA600_38400; | |
142 | break; | |
143 | case 19200: | |
144 | byte = MA600_19200; | |
145 | break; | |
146 | case 9600: | |
147 | byte = MA600_9600; | |
148 | break; | |
149 | case 2400: | |
150 | byte = MA600_2400; | |
151 | break; | |
152 | } | |
153 | ||
154 | return byte; | |
155 | } | |
156 | ||
157 | /* | |
158 | * Function ma600_change_speed (dev, state, speed) | |
159 | * | |
160 | * Set the speed for the MA600 type dongle. Warning, this | |
161 | * function must be called with a process context! | |
162 | * | |
163 | * Algorithm | |
164 | * 1. Reset | |
165 | * 2. clear RTS, set DTR and wait for 1ms | |
166 | * 3. send Control Byte to the MA600 through TXD to set new baud rate | |
167 | * wait until the stop bit of Control Byte is sent (for 9600 baud rate, | |
168 | * it takes about 10 msec) | |
169 | * 4. set RTS, set DTR (return to NORMAL Operation) | |
170 | * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here | |
171 | * after | |
172 | */ | |
173 | static int ma600_change_speed(struct irda_task *task) | |
174 | { | |
175 | dongle_t *self = (dongle_t *) task->instance; | |
176 | __u32 speed = (__u32) task->param; | |
177 | static __u8 byte; | |
178 | __u8 byte_echo; | |
179 | int ret = 0; | |
180 | ||
181 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
182 | ||
183 | ASSERT(task != NULL, return -1;); | |
184 | ||
185 | if (self->speed_task && self->speed_task != task) { | |
186 | IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__); | |
187 | return msecs_to_jiffies(10); | |
188 | } else { | |
189 | self->speed_task = task; | |
190 | } | |
191 | ||
192 | switch (task->state) { | |
193 | case IRDA_TASK_INIT: | |
194 | case IRDA_TASK_CHILD_INIT: | |
195 | /* | |
196 | * Need to reset the dongle and go to 9600 bps before | |
197 | * programming | |
198 | */ | |
199 | if (irda_task_execute(self, ma600_reset, NULL, task, | |
200 | (void *) speed)) { | |
201 | /* Dongle need more time to reset */ | |
202 | irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); | |
203 | ||
204 | /* give 1 second to finish */ | |
205 | ret = msecs_to_jiffies(1000); | |
206 | } else { | |
207 | irda_task_next_state(task, IRDA_TASK_CHILD_DONE); | |
208 | } | |
209 | break; | |
210 | ||
211 | case IRDA_TASK_CHILD_WAIT: | |
212 | IRDA_WARNING("%s(), resetting dongle timed out!\n", | |
213 | __FUNCTION__); | |
214 | ret = -1; | |
215 | break; | |
216 | ||
217 | case IRDA_TASK_CHILD_DONE: | |
218 | /* Set DTR, Clear RTS */ | |
219 | self->set_dtr_rts(self->dev, TRUE, FALSE); | |
220 | ||
221 | ret = msecs_to_jiffies(1); /* Sleep 1 ms */ | |
222 | irda_task_next_state(task, IRDA_TASK_WAIT); | |
223 | break; | |
224 | ||
225 | case IRDA_TASK_WAIT: | |
226 | speed = (__u32) task->param; | |
227 | byte = get_control_byte(speed); | |
228 | ||
229 | /* Write control byte */ | |
230 | self->write(self->dev, &byte, sizeof(byte)); | |
231 | ||
232 | irda_task_next_state(task, IRDA_TASK_WAIT1); | |
233 | ||
234 | /* Wait at least 10 ms */ | |
235 | ret = msecs_to_jiffies(15); | |
236 | break; | |
237 | ||
238 | case IRDA_TASK_WAIT1: | |
239 | /* Read control byte echo */ | |
240 | self->read(self->dev, &byte_echo, sizeof(byte_echo)); | |
241 | ||
242 | if(byte != byte_echo) { | |
243 | /* if control byte != echo, I don't know what to do */ | |
244 | printk(KERN_WARNING "%s() control byte written != read!\n", __FUNCTION__); | |
245 | printk(KERN_WARNING "control byte = 0x%c%c\n", | |
246 | hexTbl[(byte>>4)&0x0f], hexTbl[byte&0x0f]); | |
247 | printk(KERN_WARNING "byte echo = 0x%c%c\n", | |
248 | hexTbl[(byte_echo>>4) & 0x0f], | |
249 | hexTbl[byte_echo & 0x0f]); | |
250 | #ifndef NDEBUG | |
251 | } else { | |
252 | IRDA_DEBUG(2, "%s() control byte write read OK\n", __FUNCTION__); | |
253 | #endif | |
254 | } | |
255 | ||
256 | /* Set DTR, Set RTS */ | |
257 | self->set_dtr_rts(self->dev, TRUE, TRUE); | |
258 | ||
259 | irda_task_next_state(task, IRDA_TASK_WAIT2); | |
260 | ||
261 | /* Wait at least 10 ms */ | |
262 | ret = msecs_to_jiffies(10); | |
263 | break; | |
264 | ||
265 | case IRDA_TASK_WAIT2: | |
266 | irda_task_next_state(task, IRDA_TASK_DONE); | |
267 | self->speed_task = NULL; | |
268 | break; | |
269 | ||
270 | default: | |
271 | IRDA_ERROR("%s(), unknown state %d\n", | |
272 | __FUNCTION__, task->state); | |
273 | irda_task_next_state(task, IRDA_TASK_DONE); | |
274 | self->speed_task = NULL; | |
275 | ret = -1; | |
276 | break; | |
277 | } | |
278 | return ret; | |
279 | } | |
280 | ||
281 | /* | |
282 | * Function ma600_reset (driver) | |
283 | * | |
284 | * This function resets the ma600 dongle. Warning, this function | |
285 | * must be called with a process context!! | |
286 | * | |
287 | * Algorithm: | |
288 | * 0. DTR=0, RTS=1 and wait 10 ms | |
289 | * 1. DTR=1, RTS=1 and wait 10 ms | |
290 | * 2. 9600 bps now | |
291 | */ | |
292 | int ma600_reset(struct irda_task *task) | |
293 | { | |
294 | dongle_t *self = (dongle_t *) task->instance; | |
295 | int ret = 0; | |
296 | ||
297 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | |
298 | ||
299 | ASSERT(task != NULL, return -1;); | |
300 | ||
301 | if (self->reset_task && self->reset_task != task) { | |
302 | IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__); | |
303 | return msecs_to_jiffies(10); | |
304 | } else | |
305 | self->reset_task = task; | |
306 | ||
307 | switch (task->state) { | |
308 | case IRDA_TASK_INIT: | |
309 | /* Clear DTR and Set RTS */ | |
310 | self->set_dtr_rts(self->dev, FALSE, TRUE); | |
311 | irda_task_next_state(task, IRDA_TASK_WAIT1); | |
312 | ret = msecs_to_jiffies(10); /* Sleep 10 ms */ | |
313 | break; | |
314 | case IRDA_TASK_WAIT1: | |
315 | /* Set DTR and RTS */ | |
316 | self->set_dtr_rts(self->dev, TRUE, TRUE); | |
317 | irda_task_next_state(task, IRDA_TASK_WAIT2); | |
318 | ret = msecs_to_jiffies(10); /* Sleep 10 ms */ | |
319 | break; | |
320 | case IRDA_TASK_WAIT2: | |
321 | irda_task_next_state(task, IRDA_TASK_DONE); | |
322 | self->reset_task = NULL; | |
323 | break; | |
324 | default: | |
325 | IRDA_ERROR("%s(), unknown state %d\n", | |
326 | __FUNCTION__, task->state); | |
327 | irda_task_next_state(task, IRDA_TASK_DONE); | |
328 | self->reset_task = NULL; | |
329 | ret = -1; | |
330 | } | |
331 | return ret; | |
332 | } | |
333 | ||
334 | MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); | |
335 | MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); | |
336 | MODULE_LICENSE("GPL"); | |
337 | MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ | |
338 | ||
339 | /* | |
340 | * Function init_module (void) | |
341 | * | |
342 | * Initialize MA600 module | |
343 | * | |
344 | */ | |
345 | module_init(ma600_init); | |
346 | ||
347 | /* | |
348 | * Function cleanup_module (void) | |
349 | * | |
350 | * Cleanup MA600 module | |
351 | * | |
352 | */ | |
353 | module_exit(ma600_cleanup); | |
354 |