com_cardbus.c revision 1.5.2.2 1 1.5.2.2 bouyer /* $NetBSD: com_cardbus.c,v 1.5.2.2 2000/11/20 11:39:53 bouyer Exp $ */
2 1.5.2.2 bouyer
3 1.5.2.2 bouyer /*
4 1.5.2.2 bouyer * Copyright (c) 2000 Johan Danielsson
5 1.5.2.2 bouyer * All rights reserved.
6 1.5.2.2 bouyer *
7 1.5.2.2 bouyer * Redistribution and use in source and binary forms, with or without
8 1.5.2.2 bouyer * modification, are permitted provided that the following conditions
9 1.5.2.2 bouyer * are met:
10 1.5.2.2 bouyer *
11 1.5.2.2 bouyer * 1. Redistributions of source code must retain the above copyright
12 1.5.2.2 bouyer * notice, this list of conditions and the following disclaimer.
13 1.5.2.2 bouyer *
14 1.5.2.2 bouyer * 2. Redistributions in binary form must reproduce the above copyright
15 1.5.2.2 bouyer * notice, this list of conditions and the following disclaimer in the
16 1.5.2.2 bouyer * documentation and/or other materials provided with the distribution.
17 1.5.2.2 bouyer *
18 1.5.2.2 bouyer * 3. Neither the name of author nor the names of any contributors may
19 1.5.2.2 bouyer * be used to endorse or promote products derived from this
20 1.5.2.2 bouyer * software without specific prior written permission.
21 1.5.2.2 bouyer *
22 1.5.2.2 bouyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 1.5.2.2 bouyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.5.2.2 bouyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 1.5.2.2 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
26 1.5.2.2 bouyer * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 1.5.2.2 bouyer * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 1.5.2.2 bouyer * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 1.5.2.2 bouyer * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 1.5.2.2 bouyer * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 1.5.2.2 bouyer * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 1.5.2.2 bouyer * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 1.5.2.2 bouyer */
34 1.5.2.2 bouyer
35 1.5.2.2 bouyer /* A driver for CardBus based serial devices.
36 1.5.2.2 bouyer
37 1.5.2.2 bouyer If the CardBus device only has one BAR (that is not also the CIS
38 1.5.2.2 bouyer BAR) listed in the CIS, it is assumed to be the one to use. For
39 1.5.2.2 bouyer devices with more than one BAR, the list of known devices has to be
40 1.5.2.2 bouyer updated below. */
41 1.5.2.2 bouyer
42 1.5.2.2 bouyer #include <sys/param.h>
43 1.5.2.2 bouyer #include <sys/systm.h>
44 1.5.2.2 bouyer #include <sys/tty.h>
45 1.5.2.2 bouyer #include <sys/device.h>
46 1.5.2.2 bouyer
47 1.5.2.2 bouyer #include <dev/cardbus/cardbusvar.h>
48 1.5.2.2 bouyer #include <dev/cardbus/cardbusdevs.h>
49 1.5.2.2 bouyer
50 1.5.2.2 bouyer #include <dev/pcmcia/pcmciareg.h>
51 1.5.2.2 bouyer
52 1.5.2.2 bouyer #include <dev/ic/comreg.h>
53 1.5.2.2 bouyer #include <dev/ic/comvar.h>
54 1.5.2.2 bouyer
55 1.5.2.2 bouyer struct com_cardbus_softc {
56 1.5.2.2 bouyer struct com_softc cc_com;
57 1.5.2.2 bouyer void *cc_ih;
58 1.5.2.2 bouyer cardbus_devfunc_t cc_ct;
59 1.5.2.2 bouyer bus_addr_t cc_addr;
60 1.5.2.2 bouyer cardbusreg_t cc_base;
61 1.5.2.2 bouyer bus_size_t cc_size;
62 1.5.2.2 bouyer cardbusreg_t cc_csr;
63 1.5.2.2 bouyer int cc_cben;
64 1.5.2.2 bouyer cardbustag_t cc_tag;
65 1.5.2.2 bouyer cardbusreg_t cc_reg;
66 1.5.2.2 bouyer int cc_type;
67 1.5.2.2 bouyer };
68 1.5.2.2 bouyer
69 1.5.2.2 bouyer #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname)
70 1.5.2.2 bouyer
71 1.5.2.2 bouyer static int com_cardbus_match (struct device*, struct cfdata*, void*);
72 1.5.2.2 bouyer static void com_cardbus_attach (struct device*, struct device*, void*);
73 1.5.2.2 bouyer static int com_cardbus_detach (struct device*, int);
74 1.5.2.2 bouyer
75 1.5.2.2 bouyer static void com_cardbus_setup(struct com_cardbus_softc*);
76 1.5.2.2 bouyer static int com_cardbus_enable (struct com_softc*);
77 1.5.2.2 bouyer static void com_cardbus_disable (struct com_softc*);
78 1.5.2.2 bouyer
79 1.5.2.2 bouyer struct cfattach com_cardbus_ca = {
80 1.5.2.2 bouyer sizeof(struct com_cardbus_softc), com_cardbus_match,
81 1.5.2.2 bouyer com_cardbus_attach, com_cardbus_detach, com_activate
82 1.5.2.2 bouyer };
83 1.5.2.2 bouyer
84 1.5.2.2 bouyer static struct csdev {
85 1.5.2.2 bouyer int vendor;
86 1.5.2.2 bouyer int product;
87 1.5.2.2 bouyer cardbusreg_t reg;
88 1.5.2.2 bouyer int type;
89 1.5.2.2 bouyer } csdevs[] = {
90 1.5.2.2 bouyer { CARDBUS_VENDOR_XIRCOM, CARDBUS_PRODUCT_XIRCOM_MODEM56,
91 1.5.2.2 bouyer CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO },
92 1.5.2.2 bouyer { CARDBUS_VENDOR_INTEL, CARDBUS_PRODUCT_INTEL_MODEM56,
93 1.5.2.2 bouyer CARDBUS_BASE0_REG, CARDBUS_MAPREG_TYPE_IO }
94 1.5.2.2 bouyer };
95 1.5.2.2 bouyer
96 1.5.2.2 bouyer static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]);
97 1.5.2.2 bouyer
98 1.5.2.2 bouyer static struct csdev*
99 1.5.2.2 bouyer find_csdev(struct cardbus_attach_args *ca)
100 1.5.2.2 bouyer {
101 1.5.2.2 bouyer struct csdev *cp;
102 1.5.2.2 bouyer
103 1.5.2.2 bouyer for(cp = csdevs; cp < csdevs + ncsdevs; cp++)
104 1.5.2.2 bouyer if(cp->vendor == CARDBUS_VENDOR(ca->ca_id) &&
105 1.5.2.2 bouyer cp->product == CARDBUS_PRODUCT(ca->ca_id))
106 1.5.2.2 bouyer return cp;
107 1.5.2.2 bouyer return NULL;
108 1.5.2.2 bouyer }
109 1.5.2.2 bouyer
110 1.5.2.2 bouyer static int
111 1.5.2.2 bouyer com_cardbus_match(struct device *parent, struct cfdata *match, void *aux)
112 1.5.2.2 bouyer {
113 1.5.2.2 bouyer struct cardbus_attach_args *ca = aux;
114 1.5.2.2 bouyer
115 1.5.2.2 bouyer /* known devices are ok */
116 1.5.2.2 bouyer if(find_csdev(ca) != NULL)
117 1.5.2.2 bouyer return 10;
118 1.5.2.2 bouyer
119 1.5.2.2 bouyer /* as are serial devices with a known UART */
120 1.5.2.2 bouyer if(ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL &&
121 1.5.2.2 bouyer ca->ca_cis.funce.serial.uart_present != 0 &&
122 1.5.2.2 bouyer (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */
123 1.5.2.2 bouyer ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */
124 1.5.2.2 bouyer ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */
125 1.5.2.2 bouyer return 1;
126 1.5.2.2 bouyer
127 1.5.2.2 bouyer return 0;
128 1.5.2.2 bouyer }
129 1.5.2.2 bouyer
130 1.5.2.2 bouyer static int
131 1.5.2.2 bouyer gofigure(struct cardbus_attach_args *ca, struct com_cardbus_softc *csc)
132 1.5.2.2 bouyer {
133 1.5.2.2 bouyer int i, index = -1;
134 1.5.2.2 bouyer cardbusreg_t cis_ptr;
135 1.5.2.2 bouyer struct csdev *cp;
136 1.5.2.2 bouyer
137 1.5.2.2 bouyer /* If this device is listed above, use the known values, */
138 1.5.2.2 bouyer cp = find_csdev(ca);
139 1.5.2.2 bouyer if(cp != NULL) {
140 1.5.2.2 bouyer csc->cc_reg = cp->reg;
141 1.5.2.2 bouyer csc->cc_type = cp->type;
142 1.5.2.2 bouyer return 0;
143 1.5.2.2 bouyer }
144 1.5.2.2 bouyer
145 1.5.2.2 bouyer cis_ptr = Cardbus_conf_read(csc->cc_ct, csc->cc_tag, CARDBUS_CIS_REG);
146 1.5.2.2 bouyer
147 1.5.2.2 bouyer /* otherwise try to deduce which BAR and type to use from CIS. If
148 1.5.2.2 bouyer there is only one BAR, it must be the one we should use, if
149 1.5.2.2 bouyer there are more, we're out of luck. */
150 1.5.2.2 bouyer for(i = 0; i < 7; i++) {
151 1.5.2.2 bouyer /* ignore zero sized BARs */
152 1.5.2.2 bouyer if(ca->ca_cis.bar[i].size == 0)
153 1.5.2.2 bouyer continue;
154 1.5.2.2 bouyer /* ignore the CIS BAR */
155 1.5.2.2 bouyer if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
156 1.5.2.2 bouyer CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
157 1.5.2.2 bouyer continue;
158 1.5.2.2 bouyer if(index != -1)
159 1.5.2.2 bouyer goto multi_bar;
160 1.5.2.2 bouyer index = i;
161 1.5.2.2 bouyer }
162 1.5.2.2 bouyer if(index == -1) {
163 1.5.2.2 bouyer printf(": couldn't find any base address tuple\n");
164 1.5.2.2 bouyer return 1;
165 1.5.2.2 bouyer }
166 1.5.2.2 bouyer csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags);
167 1.5.2.2 bouyer if ((ca->ca_cis.bar[index].flags & 0x10) == 0)
168 1.5.2.2 bouyer csc->cc_type = CARDBUS_MAPREG_TYPE_MEM;
169 1.5.2.2 bouyer else
170 1.5.2.2 bouyer csc->cc_type = CARDBUS_MAPREG_TYPE_IO;
171 1.5.2.2 bouyer return 0;
172 1.5.2.2 bouyer
173 1.5.2.2 bouyer multi_bar:
174 1.5.2.2 bouyer printf(": there are more than one possible base\n");
175 1.5.2.2 bouyer
176 1.5.2.2 bouyer printf("%s: address for this device, "
177 1.5.2.2 bouyer "please report the following information\n",
178 1.5.2.2 bouyer DEVNAME(csc));
179 1.5.2.2 bouyer printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc),
180 1.5.2.2 bouyer CARDBUS_VENDOR(ca->ca_id), CARDBUS_PRODUCT(ca->ca_id));
181 1.5.2.2 bouyer for(i = 0; i < 7; i++) {
182 1.5.2.2 bouyer /* ignore zero sized BARs */
183 1.5.2.2 bouyer if(ca->ca_cis.bar[i].size == 0)
184 1.5.2.2 bouyer continue;
185 1.5.2.2 bouyer /* ignore the CIS BAR */
186 1.5.2.2 bouyer if(CARDBUS_CIS_ASI_BAR(cis_ptr) ==
187 1.5.2.2 bouyer CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags))
188 1.5.2.2 bouyer continue;
189 1.5.2.2 bouyer printf("%s: base address %x type %s size %x\n",
190 1.5.2.2 bouyer DEVNAME(csc),
191 1.5.2.2 bouyer CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags),
192 1.5.2.2 bouyer (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem",
193 1.5.2.2 bouyer ca->ca_cis.bar[i].size);
194 1.5.2.2 bouyer }
195 1.5.2.2 bouyer return 1;
196 1.5.2.2 bouyer }
197 1.5.2.2 bouyer
198 1.5.2.2 bouyer static void
199 1.5.2.2 bouyer com_cardbus_attach (struct device *parent, struct device *self, void *aux)
200 1.5.2.2 bouyer {
201 1.5.2.2 bouyer struct com_softc *sc = (struct com_softc*)self;
202 1.5.2.2 bouyer struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self;
203 1.5.2.2 bouyer struct cardbus_attach_args *ca = aux;
204 1.5.2.2 bouyer
205 1.5.2.2 bouyer csc->cc_ct = ca->ca_ct;
206 1.5.2.2 bouyer csc->cc_tag = Cardbus_make_tag(csc->cc_ct);
207 1.5.2.2 bouyer
208 1.5.2.2 bouyer if(gofigure(ca, csc) != 0)
209 1.5.2.2 bouyer return;
210 1.5.2.2 bouyer
211 1.5.2.2 bouyer if(Cardbus_mapreg_map(ca->ca_ct,
212 1.5.2.2 bouyer csc->cc_reg,
213 1.5.2.2 bouyer csc->cc_type,
214 1.5.2.2 bouyer 0,
215 1.5.2.2 bouyer &sc->sc_iot,
216 1.5.2.2 bouyer &sc->sc_ioh,
217 1.5.2.2 bouyer &csc->cc_addr,
218 1.5.2.2 bouyer &csc->cc_size) != 0) {
219 1.5.2.2 bouyer printf("failed to map memory");
220 1.5.2.2 bouyer return;
221 1.5.2.2 bouyer }
222 1.5.2.2 bouyer
223 1.5.2.2 bouyer csc->cc_base = csc->cc_addr;
224 1.5.2.2 bouyer csc->cc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
225 1.5.2.2 bouyer if(csc->cc_type == CARDBUS_MAPREG_TYPE_IO) {
226 1.5.2.2 bouyer csc->cc_base |= CARDBUS_MAPREG_TYPE_IO;
227 1.5.2.2 bouyer csc->cc_csr |= CARDBUS_COMMAND_IO_ENABLE;
228 1.5.2.2 bouyer csc->cc_cben = CARDBUS_IO_ENABLE;
229 1.5.2.2 bouyer } else {
230 1.5.2.2 bouyer csc->cc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
231 1.5.2.2 bouyer csc->cc_cben = CARDBUS_MEM_ENABLE;
232 1.5.2.2 bouyer }
233 1.5.2.2 bouyer sc->sc_iobase = csc->cc_addr;
234 1.5.2.2 bouyer
235 1.5.2.2 bouyer sc->sc_frequency = COM_FREQ;
236 1.5.2.2 bouyer
237 1.5.2.2 bouyer sc->enable = com_cardbus_enable;
238 1.5.2.2 bouyer sc->disable = com_cardbus_disable;
239 1.5.2.2 bouyer sc->enabled = 0;
240 1.5.2.2 bouyer
241 1.5.2.2 bouyer if (ca->ca_cis.cis1_info[0] && ca->ca_cis.cis1_info[1]) {
242 1.5.2.2 bouyer printf(": %s %s\n", ca->ca_cis.cis1_info[0],
243 1.5.2.2 bouyer ca->ca_cis.cis1_info[1]);
244 1.5.2.2 bouyer printf("%s", DEVNAME(csc));
245 1.5.2.2 bouyer }
246 1.5.2.2 bouyer
247 1.5.2.2 bouyer com_cardbus_setup(csc);
248 1.5.2.2 bouyer
249 1.5.2.2 bouyer com_attach_subr(sc);
250 1.5.2.2 bouyer
251 1.5.2.2 bouyer Cardbus_function_disable(csc->cc_ct);
252 1.5.2.2 bouyer }
253 1.5.2.2 bouyer
254 1.5.2.2 bouyer static void
255 1.5.2.2 bouyer com_cardbus_setup(struct com_cardbus_softc *csc)
256 1.5.2.2 bouyer {
257 1.5.2.2 bouyer cardbus_devfunc_t ct = csc->cc_ct;
258 1.5.2.2 bouyer cardbus_chipset_tag_t cc = ct->ct_cc;
259 1.5.2.2 bouyer cardbus_function_tag_t cf = ct->ct_cf;
260 1.5.2.2 bouyer cardbusreg_t reg;
261 1.5.2.2 bouyer
262 1.5.2.2 bouyer Cardbus_conf_write(ct, csc->cc_tag, csc->cc_reg, csc->cc_base);
263 1.5.2.2 bouyer
264 1.5.2.2 bouyer /* enable accesses on cardbus bridge */
265 1.5.2.2 bouyer (*cf->cardbus_ctrl)(cc, csc->cc_cben);
266 1.5.2.2 bouyer (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
267 1.5.2.2 bouyer
268 1.5.2.2 bouyer /* and the card itself */
269 1.5.2.2 bouyer reg = Cardbus_conf_read(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG);
270 1.5.2.2 bouyer reg &= ~(CARDBUS_COMMAND_IO_ENABLE | CARDBUS_COMMAND_MEM_ENABLE);
271 1.5.2.2 bouyer reg |= csc->cc_csr;
272 1.5.2.2 bouyer Cardbus_conf_write(ct, csc->cc_tag, CARDBUS_COMMAND_STATUS_REG, reg);
273 1.5.2.2 bouyer
274 1.5.2.2 bouyer /*
275 1.5.2.2 bouyer * Make sure the latency timer is set to some reasonable
276 1.5.2.2 bouyer * value.
277 1.5.2.2 bouyer */
278 1.5.2.2 bouyer reg = cardbus_conf_read(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG);
279 1.5.2.2 bouyer if (CARDBUS_LATTIMER(reg) < 0x20) {
280 1.5.2.2 bouyer reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
281 1.5.2.2 bouyer reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
282 1.5.2.2 bouyer cardbus_conf_write(cc, cf, csc->cc_tag, CARDBUS_BHLC_REG, reg);
283 1.5.2.2 bouyer }
284 1.5.2.2 bouyer }
285 1.5.2.2 bouyer
286 1.5.2.2 bouyer static int
287 1.5.2.2 bouyer com_cardbus_enable(struct com_softc *sc)
288 1.5.2.2 bouyer {
289 1.5.2.2 bouyer struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
290 1.5.2.2 bouyer struct cardbus_softc *psc =
291 1.5.2.2 bouyer (struct cardbus_softc *)sc->sc_dev.dv_parent;
292 1.5.2.2 bouyer cardbus_chipset_tag_t cc = psc->sc_cc;
293 1.5.2.2 bouyer cardbus_function_tag_t cf = psc->sc_cf;
294 1.5.2.2 bouyer
295 1.5.2.2 bouyer Cardbus_function_enable(csc->cc_ct);
296 1.5.2.2 bouyer
297 1.5.2.2 bouyer com_cardbus_setup(csc);
298 1.5.2.2 bouyer
299 1.5.2.2 bouyer /* establish the interrupt. */
300 1.5.2.2 bouyer csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline,
301 1.5.2.2 bouyer IPL_SERIAL, comintr, sc);
302 1.5.2.2 bouyer if (csc->cc_ih == NULL) {
303 1.5.2.2 bouyer printf("%s: couldn't establish interrupt\n",
304 1.5.2.2 bouyer DEVNAME(csc));
305 1.5.2.2 bouyer return 1;
306 1.5.2.2 bouyer }
307 1.5.2.2 bouyer
308 1.5.2.2 bouyer printf("%s: interrupting at irq %d\n",
309 1.5.2.2 bouyer DEVNAME(csc), psc->sc_intrline);
310 1.5.2.2 bouyer
311 1.5.2.2 bouyer return 0;
312 1.5.2.2 bouyer }
313 1.5.2.2 bouyer
314 1.5.2.2 bouyer static void
315 1.5.2.2 bouyer com_cardbus_disable(struct com_softc *sc)
316 1.5.2.2 bouyer {
317 1.5.2.2 bouyer struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc;
318 1.5.2.2 bouyer struct cardbus_softc *psc =
319 1.5.2.2 bouyer (struct cardbus_softc *)sc->sc_dev.dv_parent;
320 1.5.2.2 bouyer cardbus_chipset_tag_t cc = psc->sc_cc;
321 1.5.2.2 bouyer cardbus_function_tag_t cf = psc->sc_cf;
322 1.5.2.2 bouyer
323 1.5.2.2 bouyer cardbus_intr_disestablish(cc, cf, csc->cc_ih);
324 1.5.2.2 bouyer Cardbus_function_disable(csc->cc_ct);
325 1.5.2.2 bouyer }
326 1.5.2.2 bouyer
327 1.5.2.2 bouyer static int
328 1.5.2.2 bouyer com_cardbus_detach(struct device *self, int flags)
329 1.5.2.2 bouyer {
330 1.5.2.2 bouyer struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self;
331 1.5.2.2 bouyer struct com_softc *sc = (struct com_softc *) self;
332 1.5.2.2 bouyer struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent;
333 1.5.2.2 bouyer int error;
334 1.5.2.2 bouyer
335 1.5.2.2 bouyer if ((error = com_detach(self, flags)) != 0)
336 1.5.2.2 bouyer return error;
337 1.5.2.2 bouyer
338 1.5.2.2 bouyer cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih);
339 1.5.2.2 bouyer
340 1.5.2.2 bouyer Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh,
341 1.5.2.2 bouyer csc->cc_size);
342 1.5.2.2 bouyer
343 1.5.2.2 bouyer return 0;
344 1.5.2.2 bouyer }
345