]>
Commit | Line | Data |
---|---|---|
c995fe94 ADG |
1 | /** |
2 | @verbatim | |
3 | ||
4 | Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. | |
5 | ||
356cdbcb BP |
6 | ADDI-DATA GmbH |
7 | Dieselstrasse 3 | |
8 | D-77833 Ottersweier | |
9 | Tel: +19(0)7223/9493-0 | |
10 | Fax: +49(0)7223/9493-92 | |
25417922 | 11 | http://www.addi-data.com |
356cdbcb | 12 | info@addi-data.com |
c995fe94 ADG |
13 | |
14 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | ||
39cfb97b | 20 | You should also find the complete GPL in the COPYING file accompanying this source code. |
c995fe94 ADG |
21 | |
22 | @endverbatim | |
23 | */ | |
24 | /* | |
25 | ||
26 | +-----------------------------------------------------------------------+ | |
27 | | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier | | |
28 | +-----------------------------------------------------------------------+ | |
29 | | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com | | |
30 | | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com | | |
31 | +-----------------------------------------------------------------------+ | |
32 | | Project : ADDI DATA | Compiler : GCC | | |
33 | | Modulname : addi_common.c | Version : 2.96 | | |
34 | +-------------------------------+---------------------------------------+ | |
35 | | Author : | Date : | | |
36 | +-----------------------------------------------------------------------+ | |
37 | | Description : ADDI COMMON Main Module | | |
38 | +-----------------------------------------------------------------------+ | |
39 | | CONFIG OPTIONS | | |
40 | | option[0] - PCI bus number - if bus number and slot number are 0, | | |
41 | | then driver search for first unused card | | |
42 | | option[1] - PCI slot number | | |
43 | | | | |
44 | | option[2] = 0 - DMA ENABLE | | |
45 | | = 1 - DMA DISABLE | | |
46 | +----------+-----------+------------------------------------------------+ | |
47 | */ | |
48 | ||
c995fe94 ADG |
49 | #ifndef COMEDI_SUBD_TTLIO |
50 | #define COMEDI_SUBD_TTLIO 11 /* Digital Input Output But TTL */ | |
51 | #endif | |
52 | ||
17d51852 HS |
53 | static int i_ADDIDATA_InsnReadEeprom(struct comedi_device *dev, |
54 | struct comedi_subdevice *s, | |
55 | struct comedi_insn *insn, | |
56 | unsigned int *data) | |
57 | { | |
58 | const struct addi_board *this_board = comedi_board(dev); | |
59 | struct addi_private *devpriv = dev->private; | |
60 | unsigned short w_Address = CR_CHAN(insn->chanspec); | |
61 | unsigned short w_Data; | |
62 | ||
66be78f6 | 63 | w_Data = addi_eeprom_readw(devpriv->i_IobaseAmcc, |
85c1dcba | 64 | this_board->pc_EepromChip, 2 * w_Address); |
17d51852 HS |
65 | data[0] = w_Data; |
66 | ||
67 | return insn->n; | |
68 | } | |
69 | ||
70 | static irqreturn_t v_ADDI_Interrupt(int irq, void *d) | |
71 | { | |
72 | struct comedi_device *dev = d; | |
73 | const struct addi_board *this_board = comedi_board(dev); | |
74 | ||
75 | this_board->interrupt(irq, d); | |
76 | return IRQ_RETVAL(1); | |
77 | } | |
78 | ||
79 | static int i_ADDI_Reset(struct comedi_device *dev) | |
80 | { | |
81 | const struct addi_board *this_board = comedi_board(dev); | |
82 | ||
83 | this_board->reset(dev); | |
84 | return 0; | |
85 | } | |
86 | ||
da91b269 | 87 | static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it) |
c995fe94 | 88 | { |
02913e06 | 89 | const struct addi_board *this_board = comedi_board(dev); |
843690b7 | 90 | struct addi_private *devpriv; |
34c43922 | 91 | struct comedi_subdevice *s; |
c995fe94 | 92 | int ret, pages, i, n_subdevices; |
756e9d7c | 93 | unsigned int dw_Dummy; |
c995fe94 ADG |
94 | resource_size_t iobase_a, iobase_main, iobase_addon, iobase_reserved; |
95 | struct pcilst_struct *card = NULL; | |
c995fe94 | 96 | int i_Dma = 0; |
c995fe94 | 97 | |
c34fa261 HS |
98 | devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); |
99 | if (!devpriv) | |
100 | return -ENOMEM; | |
101 | dev->private = devpriv; | |
c995fe94 ADG |
102 | |
103 | if (!pci_list_builded) { | |
92db8be4 | 104 | v_pci_card_list_init(this_board->i_VendorId); |
c995fe94 ADG |
105 | pci_list_builded = 1; |
106 | } | |
c995fe94 ADG |
107 | |
108 | if ((this_board->i_Dma) && (it->options[2] == 0)) { | |
109 | i_Dma = 1; | |
110 | } | |
111 | ||
c3744138 BP |
112 | card = ptr_select_and_alloc_pci_card(this_board->i_VendorId, |
113 | this_board->i_DeviceId, | |
114 | it->options[0], | |
115 | it->options[1], i_Dma); | |
116 | ||
117 | if (card == NULL) | |
c995fe94 | 118 | return -EIO; |
c3744138 | 119 | |
c995fe94 ADG |
120 | devpriv->allocated = 1; |
121 | ||
e864e2c8 HS |
122 | iobase_a = pci_resource_start(card->pcidev, 0); |
123 | iobase_main = pci_resource_start(card->pcidev, 1); | |
124 | iobase_addon = pci_resource_start(card->pcidev, 2); | |
125 | iobase_reserved = pci_resource_start(card->pcidev, 3); | |
c995fe94 ADG |
126 | |
127 | if ((this_board->pc_EepromChip == NULL) | |
128 | || (strcmp(this_board->pc_EepromChip, ADDIDATA_9054) != 0)) { | |
129 | /************************************/ | |
130 | /* Test if more that 1 address used */ | |
131 | /************************************/ | |
132 | ||
133 | if (this_board->i_IorangeBase1 != 0) { | |
2696fb57 | 134 | dev->iobase = (unsigned long)iobase_main; /* DAQ base address... */ |
c995fe94 | 135 | } else { |
2696fb57 | 136 | dev->iobase = (unsigned long)iobase_a; /* DAQ base address... */ |
c995fe94 ADG |
137 | } |
138 | ||
139 | dev->board_name = this_board->pc_DriverName; | |
140 | devpriv->amcc = card; | |
74b894e5 | 141 | devpriv->iobase = (int) dev->iobase; |
2696fb57 BP |
142 | devpriv->i_IobaseAmcc = (int) iobase_a; /* AMCC base address... */ |
143 | devpriv->i_IobaseAddon = (int) iobase_addon; /* ADD ON base address.... */ | |
74b894e5 | 144 | devpriv->i_IobaseReserved = (int) iobase_reserved; |
c995fe94 ADG |
145 | } else { |
146 | dev->board_name = this_board->pc_DriverName; | |
e864e2c8 | 147 | dev->iobase = pci_resource_start(card->pcidev, 2); |
c995fe94 | 148 | devpriv->amcc = card; |
e864e2c8 HS |
149 | devpriv->iobase = pci_resource_start(card->pcidev, 2); |
150 | devpriv->i_IobaseReserved = pci_resource_start(card->pcidev, 3); | |
151 | devpriv->dw_AiBase = ioremap(pci_resource_start(card->pcidev, 3), | |
2f78c642 | 152 | this_board->i_IorangeBase3); |
c995fe94 ADG |
153 | } |
154 | ||
57517878 IA |
155 | /* Initialize parameters that can be overridden in EEPROM */ |
156 | devpriv->s_EeParameters.i_NbrAiChannel = this_board->i_NbrAiChannel; | |
157 | devpriv->s_EeParameters.i_NbrAoChannel = this_board->i_NbrAoChannel; | |
158 | devpriv->s_EeParameters.i_AiMaxdata = this_board->i_AiMaxdata; | |
159 | devpriv->s_EeParameters.i_AoMaxdata = this_board->i_AoMaxdata; | |
160 | devpriv->s_EeParameters.i_NbrDiChannel = this_board->i_NbrDiChannel; | |
161 | devpriv->s_EeParameters.i_NbrDoChannel = this_board->i_NbrDoChannel; | |
162 | devpriv->s_EeParameters.i_DoMaxdata = this_board->i_DoMaxdata; | |
163 | devpriv->s_EeParameters.i_Dma = this_board->i_Dma; | |
164 | devpriv->s_EeParameters.i_Timer = this_board->i_Timer; | |
165 | devpriv->s_EeParameters.ui_MinAcquisitiontimeNs = | |
166 | this_board->ui_MinAcquisitiontimeNs; | |
167 | devpriv->s_EeParameters.ui_MinDelaytimeNs = | |
168 | this_board->ui_MinDelaytimeNs; | |
169 | ||
2696fb57 | 170 | /* ## */ |
c995fe94 | 171 | |
e6fee79e HS |
172 | if (card->irq > 0) { |
173 | ret = request_irq(card->irq, v_ADDI_Interrupt, IRQF_SHARED, | |
174 | this_board->pc_DriverName, dev); | |
175 | if (ret == 0) | |
176 | dev->irq = card->irq; | |
c995fe94 ADG |
177 | } |
178 | ||
2696fb57 | 179 | /* Read eepeom and fill addi_board Structure */ |
c995fe94 ADG |
180 | |
181 | if (this_board->i_PCIEeprom) { | |
c995fe94 | 182 | if (!(strcmp(this_board->pc_EepromChip, "S5920"))) { |
2696fb57 | 183 | /* Set 3 wait stait */ |
c995fe94 ADG |
184 | if (!(strcmp(this_board->pc_DriverName, "apci035"))) { |
185 | outl(0x80808082, devpriv->i_IobaseAmcc + 0x60); | |
186 | } else { | |
187 | outl(0x83838383, devpriv->i_IobaseAmcc + 0x60); | |
188 | } | |
af02b584 | 189 | /* Enable the interrupt for the controller */ |
c995fe94 ADG |
190 | dw_Dummy = inl(devpriv->i_IobaseAmcc + 0x38); |
191 | outl(dw_Dummy | 0x2000, devpriv->i_IobaseAmcc + 0x38); | |
c995fe94 | 192 | } |
e864e2c8 | 193 | addi_eeprom_read_info(dev, pci_resource_start(card->pcidev, 0)); |
c995fe94 ADG |
194 | } |
195 | ||
196 | if (it->options[2] > 0) { | |
197 | devpriv->us_UseDma = ADDI_DISABLE; | |
198 | } else { | |
199 | devpriv->us_UseDma = ADDI_ENABLE; | |
200 | } | |
201 | ||
57517878 | 202 | if (devpriv->s_EeParameters.i_Dma) { |
c995fe94 | 203 | if (devpriv->us_UseDma == ADDI_ENABLE) { |
2696fb57 | 204 | /* alloc DMA buffers */ |
c995fe94 ADG |
205 | devpriv->b_DmaDoubleBuffer = 0; |
206 | for (i = 0; i < 2; i++) { | |
207 | for (pages = 4; pages >= 0; pages--) { | |
c3744138 BP |
208 | devpriv->ul_DmaBufferVirtual[i] = |
209 | (void *) __get_free_pages(GFP_KERNEL, pages); | |
210 | ||
211 | if (devpriv->ul_DmaBufferVirtual[i]) | |
c995fe94 | 212 | break; |
c995fe94 ADG |
213 | } |
214 | if (devpriv->ul_DmaBufferVirtual[i]) { | |
215 | devpriv->ui_DmaBufferPages[i] = pages; | |
216 | devpriv->ui_DmaBufferSize[i] = | |
217 | PAGE_SIZE * pages; | |
218 | devpriv->ui_DmaBufferSamples[i] = | |
219 | devpriv-> | |
220 | ui_DmaBufferSize[i] >> 1; | |
221 | devpriv->ul_DmaBufferHw[i] = | |
222 | virt_to_bus((void *)devpriv-> | |
223 | ul_DmaBufferVirtual[i]); | |
224 | } | |
225 | } | |
48d1db93 | 226 | if (!devpriv->ul_DmaBufferVirtual[0]) |
c995fe94 | 227 | devpriv->us_UseDma = ADDI_DISABLE; |
c995fe94 | 228 | |
48d1db93 | 229 | if (devpriv->ul_DmaBufferVirtual[1]) |
c995fe94 | 230 | devpriv->b_DmaDoubleBuffer = 1; |
c995fe94 ADG |
231 | } |
232 | } | |
233 | ||
234 | if (!strcmp(this_board->pc_DriverName, "apci1710")) { | |
235 | #ifdef CONFIG_APCI_1710 | |
236 | i_ADDI_AttachPCI1710(dev); | |
237 | ||
2696fb57 | 238 | /* save base address */ |
e864e2c8 | 239 | devpriv->s_BoardInfos.ui_Address = pci_resource_start(card->pcidev, 2); |
c995fe94 ADG |
240 | #endif |
241 | } else { | |
c995fe94 | 242 | n_subdevices = 7; |
2f0b9d08 | 243 | ret = comedi_alloc_subdevices(dev, n_subdevices); |
8b6c5694 | 244 | if (ret) |
c995fe94 ADG |
245 | return ret; |
246 | ||
2696fb57 | 247 | /* Allocate and Initialise AI Subdevice Structures */ |
3237c964 | 248 | s = &dev->subdevices[0]; |
57517878 | 249 | if ((devpriv->s_EeParameters.i_NbrAiChannel) |
c995fe94 ADG |
250 | || (this_board->i_NbrAiChannelDiff)) { |
251 | dev->read_subdev = s; | |
252 | s->type = COMEDI_SUBD_AI; | |
253 | s->subdev_flags = | |
fcea1154 | 254 | SDF_READABLE | SDF_COMMON | SDF_GROUND |
c995fe94 | 255 | | SDF_DIFF; |
57517878 IA |
256 | if (devpriv->s_EeParameters.i_NbrAiChannel) { |
257 | s->n_chan = | |
258 | devpriv->s_EeParameters.i_NbrAiChannel; | |
c995fe94 ADG |
259 | devpriv->b_SingelDiff = 0; |
260 | } else { | |
261 | s->n_chan = this_board->i_NbrAiChannelDiff; | |
262 | devpriv->b_SingelDiff = 1; | |
263 | } | |
57517878 | 264 | s->maxdata = devpriv->s_EeParameters.i_AiMaxdata; |
c995fe94 ADG |
265 | s->len_chanlist = this_board->i_AiChannelList; |
266 | s->range_table = this_board->pr_AiRangelist; | |
267 | ||
268 | /* Set the initialisation flag */ | |
269 | devpriv->b_AiInitialisation = 1; | |
270 | ||
a19fb006 HS |
271 | s->insn_config = this_board->ai_config; |
272 | s->insn_read = this_board->ai_read; | |
273 | s->insn_write = this_board->ai_write; | |
274 | s->insn_bits = this_board->ai_bits; | |
275 | s->do_cmdtest = this_board->ai_cmdtest; | |
276 | s->do_cmd = this_board->ai_cmd; | |
277 | s->cancel = this_board->ai_cancel; | |
c995fe94 ADG |
278 | |
279 | } else { | |
280 | s->type = COMEDI_SUBD_UNUSED; | |
281 | } | |
282 | ||
2696fb57 | 283 | /* Allocate and Initialise AO Subdevice Structures */ |
3237c964 | 284 | s = &dev->subdevices[1]; |
57517878 | 285 | if (devpriv->s_EeParameters.i_NbrAoChannel) { |
c995fe94 | 286 | s->type = COMEDI_SUBD_AO; |
fcea1154 | 287 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; |
57517878 IA |
288 | s->n_chan = devpriv->s_EeParameters.i_NbrAoChannel; |
289 | s->maxdata = devpriv->s_EeParameters.i_AoMaxdata; | |
290 | s->len_chanlist = | |
291 | devpriv->s_EeParameters.i_NbrAoChannel; | |
c995fe94 | 292 | s->range_table = this_board->pr_AoRangelist; |
a19fb006 HS |
293 | s->insn_config = this_board->ao_config; |
294 | s->insn_write = this_board->ao_write; | |
c995fe94 ADG |
295 | } else { |
296 | s->type = COMEDI_SUBD_UNUSED; | |
297 | } | |
2696fb57 | 298 | /* Allocate and Initialise DI Subdevice Structures */ |
3237c964 | 299 | s = &dev->subdevices[2]; |
57517878 | 300 | if (devpriv->s_EeParameters.i_NbrDiChannel) { |
c995fe94 | 301 | s->type = COMEDI_SUBD_DI; |
fcea1154 | 302 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON; |
57517878 | 303 | s->n_chan = devpriv->s_EeParameters.i_NbrDiChannel; |
c995fe94 | 304 | s->maxdata = 1; |
57517878 IA |
305 | s->len_chanlist = |
306 | devpriv->s_EeParameters.i_NbrDiChannel; | |
c995fe94 ADG |
307 | s->range_table = &range_digital; |
308 | s->io_bits = 0; /* all bits input */ | |
a19fb006 HS |
309 | s->insn_config = this_board->di_config; |
310 | s->insn_read = this_board->di_read; | |
311 | s->insn_write = this_board->di_write; | |
312 | s->insn_bits = this_board->di_bits; | |
c995fe94 ADG |
313 | } else { |
314 | s->type = COMEDI_SUBD_UNUSED; | |
315 | } | |
2696fb57 | 316 | /* Allocate and Initialise DO Subdevice Structures */ |
3237c964 | 317 | s = &dev->subdevices[3]; |
57517878 | 318 | if (devpriv->s_EeParameters.i_NbrDoChannel) { |
c995fe94 ADG |
319 | s->type = COMEDI_SUBD_DO; |
320 | s->subdev_flags = | |
fcea1154 | 321 | SDF_READABLE | SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; |
57517878 IA |
322 | s->n_chan = devpriv->s_EeParameters.i_NbrDoChannel; |
323 | s->maxdata = devpriv->s_EeParameters.i_DoMaxdata; | |
324 | s->len_chanlist = | |
325 | devpriv->s_EeParameters.i_NbrDoChannel; | |
c995fe94 ADG |
326 | s->range_table = &range_digital; |
327 | s->io_bits = 0xf; /* all bits output */ | |
328 | ||
a19fb006 HS |
329 | /* insn_config - for digital output memory */ |
330 | s->insn_config = this_board->do_config; | |
331 | s->insn_write = this_board->do_write; | |
332 | s->insn_bits = this_board->do_bits; | |
333 | s->insn_read = this_board->do_read; | |
c995fe94 ADG |
334 | } else { |
335 | s->type = COMEDI_SUBD_UNUSED; | |
336 | } | |
337 | ||
2696fb57 | 338 | /* Allocate and Initialise Timer Subdevice Structures */ |
3237c964 | 339 | s = &dev->subdevices[4]; |
57517878 | 340 | if (devpriv->s_EeParameters.i_Timer) { |
c995fe94 | 341 | s->type = COMEDI_SUBD_TIMER; |
fcea1154 | 342 | s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; |
c995fe94 ADG |
343 | s->n_chan = 1; |
344 | s->maxdata = 0; | |
345 | s->len_chanlist = 1; | |
346 | s->range_table = &range_digital; | |
347 | ||
a19fb006 HS |
348 | s->insn_write = this_board->timer_write; |
349 | s->insn_read = this_board->timer_read; | |
350 | s->insn_config = this_board->timer_config; | |
351 | s->insn_bits = this_board->timer_bits; | |
c995fe94 ADG |
352 | } else { |
353 | s->type = COMEDI_SUBD_UNUSED; | |
354 | } | |
355 | ||
2696fb57 | 356 | /* Allocate and Initialise TTL */ |
3237c964 | 357 | s = &dev->subdevices[5]; |
c995fe94 ADG |
358 | if (this_board->i_NbrTTLChannel) { |
359 | s->type = COMEDI_SUBD_TTLIO; | |
360 | s->subdev_flags = | |
fcea1154 | 361 | SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON; |
c995fe94 ADG |
362 | s->n_chan = this_board->i_NbrTTLChannel; |
363 | s->maxdata = 1; | |
364 | s->io_bits = 0; /* all bits input */ | |
365 | s->len_chanlist = this_board->i_NbrTTLChannel; | |
366 | s->range_table = &range_digital; | |
a19fb006 HS |
367 | s->insn_config = this_board->ttl_config; |
368 | s->insn_bits = this_board->ttl_bits; | |
369 | s->insn_read = this_board->ttl_read; | |
370 | s->insn_write = this_board->ttl_write; | |
c995fe94 ADG |
371 | } else { |
372 | s->type = COMEDI_SUBD_UNUSED; | |
373 | } | |
374 | ||
375 | /* EEPROM */ | |
3237c964 | 376 | s = &dev->subdevices[6]; |
c995fe94 ADG |
377 | if (this_board->i_PCIEeprom) { |
378 | s->type = COMEDI_SUBD_MEMORY; | |
379 | s->subdev_flags = SDF_READABLE | SDF_INTERNAL; | |
380 | s->n_chan = 256; | |
381 | s->maxdata = 0xffff; | |
382 | s->insn_read = i_ADDIDATA_InsnReadEeprom; | |
383 | } else { | |
384 | s->type = COMEDI_SUBD_UNUSED; | |
385 | } | |
386 | } | |
387 | ||
c995fe94 ADG |
388 | i_ADDI_Reset(dev); |
389 | devpriv->b_ValidDriver = 1; | |
390 | return 0; | |
391 | } | |
392 | ||
484ecc95 | 393 | static void i_ADDI_Detach(struct comedi_device *dev) |
c995fe94 | 394 | { |
02913e06 | 395 | const struct addi_board *this_board = comedi_board(dev); |
843690b7 HS |
396 | struct addi_private *devpriv = dev->private; |
397 | ||
398 | if (devpriv) { | |
484ecc95 | 399 | if (devpriv->b_ValidDriver) |
c995fe94 | 400 | i_ADDI_Reset(dev); |
484ecc95 | 401 | if (dev->irq) |
5f74ea14 | 402 | free_irq(dev->irq, dev); |
484ecc95 HS |
403 | if ((this_board->pc_EepromChip == NULL) || |
404 | (strcmp(this_board->pc_EepromChip, ADDIDATA_9054) != 0)) { | |
405 | if (devpriv->allocated) | |
c995fe94 | 406 | i_pci_card_free(devpriv->amcc); |
c995fe94 ADG |
407 | if (devpriv->ul_DmaBufferVirtual[0]) { |
408 | free_pages((unsigned long)devpriv-> | |
409 | ul_DmaBufferVirtual[0], | |
410 | devpriv->ui_DmaBufferPages[0]); | |
411 | } | |
c995fe94 ADG |
412 | if (devpriv->ul_DmaBufferVirtual[1]) { |
413 | free_pages((unsigned long)devpriv-> | |
414 | ul_DmaBufferVirtual[1], | |
415 | devpriv->ui_DmaBufferPages[1]); | |
416 | } | |
417 | } else { | |
2f78c642 | 418 | iounmap(devpriv->dw_AiBase); |
484ecc95 | 419 | if (devpriv->allocated) |
c995fe94 | 420 | i_pci_card_free(devpriv->amcc); |
c995fe94 | 421 | } |
c995fe94 | 422 | if (pci_list_builded) { |
c995fe94 ADG |
423 | v_pci_card_list_cleanup(this_board->i_VendorId); |
424 | pci_list_builded = 0; | |
425 | } | |
426 | } | |
c995fe94 | 427 | } |