1 /*********************************************************************
2  *
3  * Filename:      tekram.c
4  * Version:       1.2
5  * Description:   Implementation of the Tekram IrMate IR-210B dongle
6  * Status:        Experimental.
7  * Author:        Dag Brattli <dagb@cs.uit.no>
8  * Created at:    Wed Oct 21 20:02:35 1998
9  * Modified at:   Fri Dec 17 09:13:09 1999
10  * Modified by:   Dag Brattli <dagb@cs.uit.no>
11  *
12  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
13  *
14  *     This program is free software; you can redistribute it and/or
15  *     modify it under the terms of the GNU General Public License as
16  *     published by the Free Software Foundation; either version 2 of
17  *     the License, or (at your option) any later version.
18  *
19  *     Neither Dag Brattli nor University of Troms� admit liability nor
20  *     provide warranty for any of this software. This material is
21  *     provided "AS-IS" and at no charge.
22  *
23  ********************************************************************/
24 
25 #include <linux/module.h>
26 #include <linux/delay.h>
27 #include <linux/tty.h>
28 #include <linux/sched.h>
29 #include <linux/init.h>
30 
31 #include <net/irda/irda.h>
32 #include <net/irda/irmod.h>
33 #include <net/irda/irda_device.h>
34 #include <net/irda/irtty.h>
35 
36 static void tekram_open(dongle_t *self, struct qos_info *qos);
37 static void tekram_close(dongle_t *self);
38 static int  tekram_change_speed(struct irda_task *task);
39 static int  tekram_reset(struct irda_task *task);
40 
41 #define TEKRAM_115200 0x00
42 #define TEKRAM_57600  0x01
43 #define TEKRAM_38400  0x02
44 #define TEKRAM_19200  0x03
45 #define TEKRAM_9600   0x04
46 
47 #define TEKRAM_PW     0x10 /* Pulse select bit */
48 
49 static struct dongle_reg dongle = {
50 	Q_NULL,
51 	IRDA_TEKRAM_DONGLE,
52 	tekram_open,
53 	tekram_close,
54 	tekram_reset,
55 	tekram_change_speed,
56 };
57 
tekram_init(void)58 int __init tekram_init(void)
59 {
60 	return irda_device_register_dongle(&dongle);
61 }
62 
tekram_cleanup(void)63 void tekram_cleanup(void)
64 {
65 	irda_device_unregister_dongle(&dongle);
66 }
67 
tekram_open(dongle_t * self,struct qos_info * qos)68 static void tekram_open(dongle_t *self, struct qos_info *qos)
69 {
70 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
71 
72 	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
73 	qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
74 	irda_qos_bits_to_value(qos);
75 
76 	MOD_INC_USE_COUNT;
77 }
78 
tekram_close(dongle_t * self)79 static void tekram_close(dongle_t *self)
80 {
81 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
82 
83 	/* Power off dongle */
84 	self->set_dtr_rts(self->dev, FALSE, FALSE);
85 
86 	if (self->reset_task)
87 		irda_task_delete(self->reset_task);
88 	if (self->speed_task)
89 		irda_task_delete(self->speed_task);
90 
91 	MOD_DEC_USE_COUNT;
92 }
93 
94 /*
95  * Function tekram_change_speed (dev, state, speed)
96  *
97  *    Set the speed for the Tekram IRMate 210 type dongle. Warning, this
98  *    function must be called with a process context!
99  *
100  *    Algorithm
101  *    1. clear DTR
102  *    2. set RTS, and wait at least 7 us
103  *    3. send Control Byte to the IR-210 through TXD to set new baud rate
104  *       wait until the stop bit of Control Byte is sent (for 9600 baud rate,
105  *       it takes about 100 msec)
106  *    5. clear RTS (return to NORMAL Operation)
107  *    6. wait at least 50 us, new setting (baud rate, etc) takes effect here
108  *       after
109  */
tekram_change_speed(struct irda_task * task)110 static int tekram_change_speed(struct irda_task *task)
111 {
112 	dongle_t *self = (dongle_t *) task->instance;
113 	__u32 speed = (__u32) task->param;
114 	__u8 byte;
115 	int ret = 0;
116 
117 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
118 
119 	ASSERT(task != NULL, return -1;);
120 
121 	if (self->speed_task && self->speed_task != task) {
122 		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__);
123 		return MSECS_TO_JIFFIES(10);
124 	} else
125 		self->speed_task = task;
126 
127 	switch (speed) {
128 	default:
129 	case 9600:
130 		byte = TEKRAM_PW|TEKRAM_9600;
131 		break;
132 	case 19200:
133 		byte = TEKRAM_PW|TEKRAM_19200;
134 		break;
135 	case 38400:
136 		byte = TEKRAM_PW|TEKRAM_38400;
137 		break;
138 	case 57600:
139 		byte = TEKRAM_PW|TEKRAM_57600;
140 		break;
141 	case 115200:
142 		byte = TEKRAM_115200;
143 		break;
144 	}
145 
146 	switch (task->state) {
147 	case IRDA_TASK_INIT:
148 	case IRDA_TASK_CHILD_INIT:
149 		/*
150 		 * Need to reset the dongle and go to 9600 bps before
151                  * programming
152 		 */
153 		if (irda_task_execute(self, tekram_reset, NULL, task,
154 				      (void *) speed))
155 		{
156 			/* Dongle need more time to reset */
157 			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
158 
159 			/* Give reset 1 sec to finish */
160 			ret = MSECS_TO_JIFFIES(1000);
161 		} else
162 			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
163 		break;
164 	case IRDA_TASK_CHILD_WAIT:
165 		WARNING("%s(), resetting dongle timed out!\n", __FUNCTION__);
166 		ret = -1;
167 		break;
168 	case IRDA_TASK_CHILD_DONE:
169 		/* Set DTR, Clear RTS */
170 		self->set_dtr_rts(self->dev, TRUE, FALSE);
171 
172 		/* Wait at least 7us */
173 		udelay(14);
174 
175 		/* Write control byte */
176 		self->write(self->dev, &byte, 1);
177 
178 		irda_task_next_state(task, IRDA_TASK_WAIT);
179 
180 		/* Wait at least 100 ms */
181 		ret = MSECS_TO_JIFFIES(150);
182 		break;
183 	case IRDA_TASK_WAIT:
184 		/* Set DTR, Set RTS */
185 		self->set_dtr_rts(self->dev, TRUE, TRUE);
186 
187 		irda_task_next_state(task, IRDA_TASK_DONE);
188 		self->speed_task = NULL;
189 		break;
190 	default:
191 		ERROR("%s(), unknown state %d\n", __FUNCTION__, task->state);
192 		irda_task_next_state(task, IRDA_TASK_DONE);
193 		self->speed_task = NULL;
194 		ret = -1;
195 		break;
196 	}
197 	return ret;
198 }
199 
200 /*
201  * Function tekram_reset (driver)
202  *
203  *      This function resets the tekram dongle. Warning, this function
204  *      must be called with a process context!!
205  *
206  *      Algorithm:
207  *    	  0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 )
208  *        1. clear RTS
209  *        2. set DTR, and wait at least 1 ms
210  *        3. clear DTR to SPACE state, wait at least 50 us for further
211  *         operation
212  */
tekram_reset(struct irda_task * task)213 int tekram_reset(struct irda_task *task)
214 {
215 	dongle_t *self = (dongle_t *) task->instance;
216 	int ret = 0;
217 
218 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
219 
220 	ASSERT(task != NULL, return -1;);
221 
222 	if (self->reset_task && self->reset_task != task) {
223 		IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__);
224 		return MSECS_TO_JIFFIES(10);
225 	} else
226 		self->reset_task = task;
227 
228 	/* Power off dongle */
229 	//self->set_dtr_rts(self->dev, FALSE, FALSE);
230 	self->set_dtr_rts(self->dev, TRUE, TRUE);
231 
232 	switch (task->state) {
233 	case IRDA_TASK_INIT:
234 		irda_task_next_state(task, IRDA_TASK_WAIT1);
235 
236 		/* Sleep 50 ms */
237 		ret = MSECS_TO_JIFFIES(50);
238 		break;
239 	case IRDA_TASK_WAIT1:
240 		/* Clear DTR, Set RTS */
241 		self->set_dtr_rts(self->dev, FALSE, TRUE);
242 
243 		irda_task_next_state(task, IRDA_TASK_WAIT2);
244 
245 		/* Should sleep 1 ms */
246 		ret = MSECS_TO_JIFFIES(1);
247 		break;
248 	case IRDA_TASK_WAIT2:
249 		/* Set DTR, Set RTS */
250 		self->set_dtr_rts(self->dev, TRUE, TRUE);
251 
252 		/* Wait at least 50 us */
253 		udelay(75);
254 
255 		irda_task_next_state(task, IRDA_TASK_DONE);
256 		self->reset_task = NULL;
257 		break;
258 	default:
259 		ERROR("%s(), unknown state %d\n", __FUNCTION__, task->state);
260 		irda_task_next_state(task, IRDA_TASK_DONE);
261 		self->reset_task = NULL;
262 		ret = -1;
263 	}
264 	return ret;
265 }
266 
267 #ifdef MODULE
268 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
269 MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver");
270 MODULE_LICENSE("GPL");
271 
272 /*
273  * Function init_module (void)
274  *
275  *    Initialize Tekram module
276  *
277  */
init_module(void)278 int init_module(void)
279 {
280 	return tekram_init();
281 }
282 
283 /*
284  * Function cleanup_module (void)
285  *
286  *    Cleanup Tekram module
287  *
288  */
cleanup_module(void)289 void cleanup_module(void)
290 {
291         tekram_cleanup();
292 }
293 #endif /* MODULE */
294