File: /usr/src/linux/drivers/net/irda/actisys.c

1     /*********************************************************************
2      *                
3      * Filename:      actisys.c
4      * Version:       1.0
5      * Description:   Implementation for the ACTiSYS IR-220L and IR-220L+ 
6      *                dongles
7      * Status:        Beta.
8      * Authors:       Dag Brattli <dagb@cs.uit.no> (initially)
9      *		  Jean Tourrilhes <jt@hpl.hp.com> (new version)
10      * Created at:    Wed Oct 21 20:02:35 1998
11      * Modified at:   Fri Dec 17 09:10:43 1999
12      * Modified by:   Dag Brattli <dagb@cs.uit.no>
13      * 
14      *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
15      *     Copyright (c) 1999 Jean Tourrilhes
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      *     Neither Dag Brattli nor University of Tromsų admit liability nor
23      *     provide warranty for any of this software. This material is 
24      *     provided "AS-IS" and at no charge.
25      *     
26      ********************************************************************/
27     
28     /*
29      * Changelog
30      *
31      * 0.8 -> 0.9999 - Jean
32      *	o New initialisation procedure : much safer and correct
33      *	o New procedure the change speed : much faster and simpler
34      *	o Other cleanups & comments
35      *	Thanks to Lichen Wang @ Actisys for his excellent help...
36      */
37     
38     #include <linux/module.h>
39     #include <linux/delay.h>
40     #include <linux/tty.h>
41     #include <linux/sched.h>
42     #include <linux/init.h>
43     
44     #include <net/irda/irda.h>
45     #include <net/irda/irmod.h>
46     #include <net/irda/irda_device.h>
47     
48     /* 
49      * Define the timing of the pulses we send to the dongle (to reset it, and
50      * to toggle speeds). Basically, the limit here is the propagation speed of
51      * the signals through the serial port, the dongle being much faster.  Any
52      * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can
53      * go through cleanly . If you are on the wild side, you can try to lower
54      * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!)
55      */
56     #define MIN_DELAY 10	/* 10 us to be on the conservative side */
57     
58     static int  actisys_change_speed(struct irda_task *task);
59     static int  actisys_reset(struct irda_task *task);
60     static void actisys_open(dongle_t *self, struct qos_info *qos);
61     static void actisys_close(dongle_t *self);
62     
63     /* These are the baudrates supported, in the order available */
64     /* Note : the 220L doesn't support 38400, but we will fix that below */
65     static __u32 baud_rates[] = { 9600, 19200, 57600, 115200, 38400 };
66     #define MAX_SPEEDS 5
67     
68     static struct dongle_reg dongle = {
69     	Q_NULL,
70     	IRDA_ACTISYS_DONGLE,
71     	actisys_open,
72     	actisys_close,
73     	actisys_reset,
74     	actisys_change_speed,
75     };
76     
77     static struct dongle_reg dongle_plus = {
78     	Q_NULL,
79     	IRDA_ACTISYS_PLUS_DONGLE,
80     	actisys_open,
81     	actisys_close,
82     	actisys_reset,
83     	actisys_change_speed,
84     };
85     
86     /*
87      * Function actisys_change_speed (task)
88      *
89      *	There is two model of Actisys dongle we are dealing with,
90      * the 220L and 220L+. At this point, only irattach knows with
91      * kind the user has requested (it was an argument on irattach
92      * command line).
93      *	So, we register a dongle of each sort and let irattach
94      * pick the right one...
95      */
96     int __init actisys_init(void)
97     {
98     	int ret;
99     
100     	/* First, register an Actisys 220L dongle */
101     	ret = irda_device_register_dongle(&dongle);
102     	if (ret < 0)
103     		return ret;
104     	/* Now, register an Actisys 220L+ dongle */
105     	ret = irda_device_register_dongle(&dongle_plus);
106     	if (ret < 0) {
107     		irda_device_unregister_dongle(&dongle);
108     		return ret;
109     	}	
110     	return 0;
111     }
112     
113     void actisys_cleanup(void)
114     {
115     	/* We have to remove both dongles */
116     	irda_device_unregister_dongle(&dongle);
117     	irda_device_unregister_dongle(&dongle_plus);
118     }
119     
120     static void actisys_open(dongle_t *self, struct qos_info *qos)
121     {
122     	/* Power on the dongle */
123     	self->set_dtr_rts(self->dev, TRUE, TRUE);
124     
125     	/* Set the speeds we can accept */
126     	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
127     
128     	/* Remove support for 38400 if this is not a 220L+ dongle */
129     	if (self->issue->type == IRDA_ACTISYS_DONGLE)
130     		qos->baud_rate.bits &= ~IR_38400;
131     	
132     	qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
133     
134     	MOD_INC_USE_COUNT;
135     }
136     
137     static void actisys_close(dongle_t *self)
138     {
139     	/* Power off the dongle */
140     	self->set_dtr_rts(self->dev, FALSE, FALSE);
141     
142     	MOD_DEC_USE_COUNT;
143     }
144     
145     /*
146      * Function actisys_change_speed (task)
147      *
148      *    Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles.
149      *    To cycle through the available baud rates, pulse RTS low for a few us.
150      *
151      *	First, we reset the dongle to always start from a known state.
152      *	Then, we cycle through the speeds by pulsing RTS low and then up.
153      *	The dongle allow us to pulse quite fast, se we can set speed in one go,
154      * which is must faster ( < 100 us) and less complex than what is found
155      * in some other dongle drivers...
156      *	Note that even if the new speed is the same as the current speed,
157      * we reassert the speed. This make sure that things are all right,
158      * and it's fast anyway...
159      *	By the way, this function will work for both type of dongles,
160      * because the additional speed is at the end of the sequence...
161      */
162     static int actisys_change_speed(struct irda_task *task)
163     {
164     	dongle_t *self = (dongle_t *) task->instance;
165     	__u32 speed = (__u32) task->param;	/* Target speed */
166     	int ret = 0;
167     	int i = 0;
168     
169             IRDA_DEBUG(4, __FUNCTION__ "(), speed=%d (was %d)\n", speed, 
170     		   self->speed);
171     
172     	/* Go to a known state by reseting the dongle */
173     
174     	/* Reset the dongle : set DTR low for 10 us */
175     	self->set_dtr_rts(self->dev, FALSE, TRUE);
176     	udelay(MIN_DELAY);
177     
178     	/* Go back to normal mode (we are now at 9600 b/s) */
179     	self->set_dtr_rts(self->dev, TRUE, TRUE);
180      
181     	/* 
182     	 * Now, we can set the speed requested. Send RTS pulses until we
183              * reach the target speed 
184     	 */
185     	for (i=0; i<MAX_SPEEDS; i++) {
186     		if (speed == baud_rates[i]) {
187     			self->speed = baud_rates[i];
188     			break;
189     		}
190     		/* Make sure previous pulse is finished */
191     		udelay(MIN_DELAY);
192     
193     		/* Set RTS low for 10 us */
194     		self->set_dtr_rts(self->dev, TRUE, FALSE);
195     		udelay(MIN_DELAY);
196     
197     		/* Set RTS high for 10 us */
198     		self->set_dtr_rts(self->dev, TRUE, TRUE);
199     	}
200     
201     	/* Check if life is sweet... */
202     	if (i >= MAX_SPEEDS)
203     		ret = -1;  /* This should not happen */
204     
205     	/* Basta lavoro, on se casse d'ici... */
206     	irda_task_next_state(task, IRDA_TASK_DONE);
207     
208     	return ret;
209     }
210     
211     /*
212      * Function actisys_reset (task)
213      *
214      *      Reset the Actisys type dongle. Warning, this function must only be
215      *      called with a process context!
216      *
217      * We need to do two things in this function :
218      *	o first make sure that the dongle is in a state where it can operate
219      *	o second put the dongle in a know state
220      *
221      *	The dongle is powered of the RTS and DTR lines. In the dongle, there
222      * is a big capacitor to accomodate the current spikes. This capacitor
223      * takes a least 50 ms to be charged. In theory, the Bios set those lines
224      * up, so by the time we arrive here we should be set. It doesn't hurt
225      * to be on the conservative side, so we will wait...
226      *	Then, we set the speed to 9600 b/s to get in a known state (see in
227      * change_speed for details). It is needed because the IrDA stack
228      * has tried to set the speed immediately after our first return,
229      * so before we can be sure the dongle is up and running.
230      */
231     static int actisys_reset(struct irda_task *task)
232     {
233     	dongle_t *self = (dongle_t *) task->instance;
234     	int ret = 0;
235     
236     	ASSERT(task != NULL, return -1;);
237     
238     	self->reset_task = task;
239     
240     	switch (task->state) {
241     	case IRDA_TASK_INIT:
242     		/* Set both DTR & RTS to power up the dongle */
243     		/* In theory redundant with power up in actisys_open() */
244     		self->set_dtr_rts(self->dev, TRUE, TRUE);
245     		
246     		/* Sleep 50 ms to make sure capacitor is charged */
247     		ret = MSECS_TO_JIFFIES(50);
248     		irda_task_next_state(task, IRDA_TASK_WAIT);
249     		break;
250     	case IRDA_TASK_WAIT:			
251     		/* Reset the dongle : set DTR low for 10 us */
252     		self->set_dtr_rts(self->dev, FALSE, TRUE);
253     		udelay(MIN_DELAY);
254     
255     		/* Go back to normal mode */
256     		self->set_dtr_rts(self->dev, TRUE, TRUE);
257     	
258     		irda_task_next_state(task, IRDA_TASK_DONE);
259     		self->reset_task = NULL;
260     		self->speed = 9600;	/* That's the default */
261     		break;
262     	default:
263     		ERROR(__FUNCTION__ "(), unknown state %d\n", task->state);
264     		irda_task_next_state(task, IRDA_TASK_DONE);
265     		self->reset_task = NULL;
266     		ret = -1;
267     		break;
268     	}
269     	return ret;
270     }
271     
272     #ifdef MODULE
273     MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>");
274     MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver");	
275     		
276     /*
277      * Function init_module (void)
278      *
279      *    Initialize Actisys module
280      *
281      */
282     int init_module(void)
283     {
284     	return actisys_init();
285     }
286     
287     /*
288      * Function cleanup_module (void)
289      *
290      *    Cleanup Actisys module
291      *
292      */
293     void cleanup_module(void)
294     {
295     	actisys_cleanup();
296     }
297     #endif /* MODULE */
298