wpiExtensions.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. /*
  2. * extensions.c:
  3. * Originally part of the GPIO program to test, peek, poke and otherwise
  4. * noodle with the GPIO hardware on the Raspberry Pi.
  5. * Now used as a general purpose library to allow systems to dynamically
  6. * add in new devices into wiringPi at program run-time.
  7. * Copyright (c) 2012-2015 Gordon Henderson
  8. ***********************************************************************
  9. * This file is part of wiringPi:
  10. * https://projects.drogon.net/raspberry-pi/wiringpi/
  11. *
  12. * wiringPi is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Lesser General Public License as published by
  14. * the Free Software Foundation, either version 3 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * wiringPi is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public License
  23. * along with wiringPi. If not, see <http://www.gnu.org/licenses/>.
  24. ***********************************************************************
  25. */
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <stdint.h>
  29. #include <stdarg.h>
  30. #include <ctype.h>
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include <errno.h>
  34. #include <sys/types.h>
  35. #include <fcntl.h>
  36. #include <wiringPi.h>
  37. #include "mcp23008.h"
  38. #include "mcp23016.h"
  39. #include "mcp23017.h"
  40. #include "mcp23s08.h"
  41. #include "mcp23s17.h"
  42. #include "sr595.h"
  43. #include "pcf8574.h"
  44. #include "pcf8591.h"
  45. #include "mcp3002.h"
  46. #include "mcp3004.h"
  47. #include "mcp4802.h"
  48. #include "mcp3422.h"
  49. #include "max31855.h"
  50. #include "max5322.h"
  51. #include "ads1115.h"
  52. #include "sn3218.h"
  53. #include "drcSerial.h"
  54. #include "pseudoPins.h"
  55. #include "bmp180.h"
  56. #include "htu21d.h"
  57. #include "ds18b20.h"
  58. #include "wpiExtensions.h"
  59. extern int wiringPiDebug ;
  60. static int verbose ;
  61. static char errorMessage [1024] ;
  62. // Local structure to hold details
  63. struct extensionFunctionStruct
  64. {
  65. const char *name ;
  66. int (*function)(char *progName, int pinBase, char *params) ;
  67. } ;
  68. /*
  69. * verbError:
  70. * Convenient error handling
  71. *********************************************************************************
  72. */
  73. static void verbError (const char *message, ...)
  74. {
  75. va_list argp ;
  76. va_start (argp, message) ;
  77. vsnprintf (errorMessage, 1023, message, argp) ;
  78. va_end (argp) ;
  79. if (verbose)
  80. fprintf (stderr, "%s\n", errorMessage) ;
  81. }
  82. /*
  83. * extractInt:
  84. * Check & return an integer at the given location (prefixed by a :)
  85. *********************************************************************************
  86. */
  87. static char *extractInt (char *progName, char *p, int *num)
  88. {
  89. if (*p != ':')
  90. {
  91. verbError ("%s: colon expected", progName) ;
  92. return NULL ;
  93. }
  94. ++p ;
  95. if (!isdigit (*p))
  96. {
  97. verbError ("%s: digit expected", progName) ;
  98. return NULL ;
  99. }
  100. *num = strtol (p, NULL, 0) ;
  101. // Increment p, but we need to check for hex 0x
  102. if ((*p == '0') && (*(p + 1) == 'x'))
  103. p +=2 ;
  104. while (isxdigit (*p))
  105. ++p ;
  106. return p ;
  107. }
  108. /*
  109. * extractStr:
  110. * Check & return a string at the given location (prefixed by a :)
  111. *********************************************************************************
  112. */
  113. static char *extractStr (char *progName, char *p, char **str)
  114. {
  115. char *q, *r ;
  116. if (*p != ':')
  117. {
  118. verbError ("%s: colon expected", progName) ;
  119. return NULL ;
  120. }
  121. ++p ;
  122. if (!isprint (*p))
  123. {
  124. verbError ("%s: character expected", progName) ;
  125. return NULL ;
  126. }
  127. q = p ;
  128. while ((*q != 0) && (*q != ':'))
  129. ++q ;
  130. *str = r = calloc (q - p + 2, 1) ; // Zeros it
  131. while (p != q)
  132. *r++ = *p++ ;
  133. return p ;
  134. }
  135. /*
  136. * doExtensionMcp23008:
  137. * MCP23008 - 8-bit I2C GPIO expansion chip
  138. * mcp23002:base:i2cAddr
  139. *********************************************************************************
  140. */
  141. static int doExtensionMcp23008 (char *progName, int pinBase, char *params)
  142. {
  143. int i2c ;
  144. if ((params = extractInt (progName, params, &i2c)) == NULL)
  145. return FALSE ;
  146. if ((i2c < 0x01) || (i2c > 0x77))
  147. {
  148. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  149. return FALSE ;
  150. }
  151. mcp23008Setup (pinBase, i2c) ;
  152. return TRUE ;
  153. }
  154. /*
  155. * doExtensionMcp23016:
  156. * MCP230016- 16-bit I2C GPIO expansion chip
  157. * mcp23016:base:i2cAddr
  158. *********************************************************************************
  159. */
  160. static int doExtensionMcp23016 (char *progName, int pinBase, char *params)
  161. {
  162. int i2c ;
  163. if ((params = extractInt (progName, params, &i2c)) == NULL)
  164. return FALSE ;
  165. if ((i2c < 0x03) || (i2c > 0x77))
  166. {
  167. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  168. return FALSE ;
  169. }
  170. mcp23016Setup (pinBase, i2c) ;
  171. return TRUE ;
  172. }
  173. /*
  174. * doExtensionMcp23017:
  175. * MCP230017- 16-bit I2C GPIO expansion chip
  176. * mcp23017:base:i2cAddr
  177. *********************************************************************************
  178. */
  179. static int doExtensionMcp23017 (char *progName, int pinBase, char *params)
  180. {
  181. int i2c ;
  182. if ((params = extractInt (progName, params, &i2c)) == NULL)
  183. return FALSE ;
  184. if ((i2c < 0x03) || (i2c > 0x77))
  185. {
  186. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  187. return FALSE ;
  188. }
  189. mcp23017Setup (pinBase, i2c) ;
  190. return TRUE ;
  191. }
  192. /*
  193. * doExtensionMcp23s08:
  194. * MCP23s08 - 8-bit SPI GPIO expansion chip
  195. * mcp23s08:base:spi:port
  196. *********************************************************************************
  197. */
  198. static int doExtensionMcp23s08 (char *progName, int pinBase, char *params)
  199. {
  200. int spi, port ;
  201. if ((params = extractInt (progName, params, &spi)) == NULL)
  202. return FALSE ;
  203. if ((spi < 0) || (spi > 1))
  204. {
  205. verbError ("%s: SPI address (%d) out of range", progName, spi) ;
  206. return FALSE ;
  207. }
  208. if ((params = extractInt (progName, params, &port)) == NULL)
  209. return FALSE ;
  210. if ((port < 0) || (port > 7))
  211. {
  212. verbError ("%s: port address (%d) out of range", progName, port) ;
  213. return FALSE ;
  214. }
  215. mcp23s08Setup (pinBase, spi, port) ;
  216. return TRUE ;
  217. }
  218. /*
  219. * doExtensionMcp23s17:
  220. * MCP23s17 - 16-bit SPI GPIO expansion chip
  221. * mcp23s17:base:spi:port
  222. *********************************************************************************
  223. */
  224. static int doExtensionMcp23s17 (char *progName, int pinBase, char *params)
  225. {
  226. int spi, port ;
  227. if ((params = extractInt (progName, params, &spi)) == NULL)
  228. return FALSE ;
  229. if ((spi < 0) || (spi > 1))
  230. {
  231. verbError ("%s: SPI address (%d) out of range", progName, spi) ;
  232. return FALSE ;
  233. }
  234. if ((params = extractInt (progName, params, &port)) == NULL)
  235. return FALSE ;
  236. if ((port < 0) || (port > 7))
  237. {
  238. verbError ("%s: port address (%d) out of range", progName, port) ;
  239. return FALSE ;
  240. }
  241. mcp23s17Setup (pinBase, spi, port) ;
  242. return TRUE ;
  243. }
  244. /*
  245. * doExtensionSr595:
  246. * Shift Register 74x595
  247. * sr595:base:pins:data:clock:latch
  248. *********************************************************************************
  249. */
  250. static int doExtensionSr595 (char *progName, int pinBase, char *params)
  251. {
  252. int pins, data, clock, latch ;
  253. // Extract pins
  254. if ((params = extractInt (progName, params, &pins)) == NULL)
  255. return FALSE ;
  256. if ((pins < 8) || (pins > 32))
  257. {
  258. verbError ("%s: pin count (%d) out of range - 8-32 expected.", progName, pins) ;
  259. return FALSE ;
  260. }
  261. if ((params = extractInt (progName, params, &data)) == NULL)
  262. return FALSE ;
  263. if ((params = extractInt (progName, params, &clock)) == NULL)
  264. return FALSE ;
  265. if ((params = extractInt (progName, params, &latch)) == NULL)
  266. return FALSE ;
  267. sr595Setup (pinBase, pins, data, clock, latch) ;
  268. return TRUE ;
  269. }
  270. /*
  271. * doExtensionPcf8574:
  272. * Digital IO (Crude!)
  273. * pcf8574:base:i2cAddr
  274. *********************************************************************************
  275. */
  276. static int doExtensionPcf8574 (char *progName, int pinBase, char *params)
  277. {
  278. int i2c ;
  279. if ((params = extractInt (progName, params, &i2c)) == NULL)
  280. return FALSE ;
  281. if ((i2c < 0x03) || (i2c > 0x77))
  282. {
  283. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  284. return FALSE ;
  285. }
  286. pcf8574Setup (pinBase, i2c) ;
  287. return TRUE ;
  288. }
  289. /*
  290. * doExtensionAds1115:
  291. * Analog Input
  292. * ads1115:base:i2cAddr
  293. *********************************************************************************
  294. */
  295. static int doExtensionAds1115 (char *progName, int pinBase, char *params)
  296. {
  297. int i2c ;
  298. if ((params = extractInt (progName, params, &i2c)) == NULL)
  299. return FALSE ;
  300. if ((i2c < 0x03) || (i2c > 0x77))
  301. {
  302. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  303. return FALSE ;
  304. }
  305. ads1115Setup (pinBase, i2c) ;
  306. return TRUE ;
  307. }
  308. /*
  309. * doExtensionPcf8591:
  310. * Analog IO
  311. * pcf8591:base:i2cAddr
  312. *********************************************************************************
  313. */
  314. static int doExtensionPcf8591 (char *progName, int pinBase, char *params)
  315. {
  316. int i2c ;
  317. if ((params = extractInt (progName, params, &i2c)) == NULL)
  318. return FALSE ;
  319. if ((i2c < 0x03) || (i2c > 0x77))
  320. {
  321. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  322. return FALSE ;
  323. }
  324. pcf8591Setup (pinBase, i2c) ;
  325. return TRUE ;
  326. }
  327. /*
  328. * doExtensionPseudoPins:
  329. * 64 Memory resident pseudo pins
  330. * pseudoPins:base
  331. *********************************************************************************
  332. */
  333. static int doExtensionPseudoPins (UNU char *progName, int pinBase, UNU char *params)
  334. {
  335. pseudoPinsSetup (pinBase) ;
  336. return TRUE ;
  337. }
  338. /*
  339. * doExtensionBmp180:
  340. * Analog Temp + Pressure
  341. * bmp180:base
  342. *********************************************************************************
  343. */
  344. static int doExtensionBmp180 (UNU char *progName, int pinBase, UNU char *params)
  345. {
  346. bmp180Setup (pinBase) ;
  347. return TRUE ;
  348. }
  349. /*
  350. * doExtensionHtu21d:
  351. * Analog humidity + Pressure
  352. * htu21d:base
  353. *********************************************************************************
  354. */
  355. static int doExtensionHtu21d (UNU char *progName, int pinBase, UNU char *params)
  356. {
  357. htu21dSetup (pinBase) ;
  358. return TRUE ;
  359. }
  360. /*
  361. * doExtensionDs18b20:
  362. * 1-Wire Temperature
  363. * htu21d:base:serialNum
  364. *********************************************************************************
  365. */
  366. static int doExtensionDs18b20 (char *progName, int pinBase, char *params)
  367. {
  368. char *serialNum ;
  369. if ((params = extractStr (progName, params, &serialNum)) == NULL)
  370. return FALSE ;
  371. return ds18b20Setup (pinBase, serialNum) ;
  372. }
  373. /*
  374. * doExtensionMax31855:
  375. * Analog IO
  376. * max31855:base:spiChan
  377. *********************************************************************************
  378. */
  379. static int doExtensionMax31855 (char *progName, int pinBase, char *params)
  380. {
  381. int spi ;
  382. if ((params = extractInt (progName, params, &spi)) == NULL)
  383. return FALSE ;
  384. if ((spi < 0) || (spi > 1))
  385. {
  386. verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
  387. return FALSE ;
  388. }
  389. max31855Setup (pinBase, spi) ;
  390. return TRUE ;
  391. }
  392. /*
  393. * doExtensionMcp3002:
  394. * Analog IO
  395. * mcp3002:base:spiChan
  396. *********************************************************************************
  397. */
  398. static int doExtensionMcp3002 (char *progName, int pinBase, char *params)
  399. {
  400. int spi ;
  401. if ((params = extractInt (progName, params, &spi)) == NULL)
  402. return FALSE ;
  403. if ((spi < 0) || (spi > 1))
  404. {
  405. verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
  406. return FALSE ;
  407. }
  408. mcp3002Setup (pinBase, spi) ;
  409. return TRUE ;
  410. }
  411. /*
  412. * doExtensionMcp3004:
  413. * Analog IO
  414. * mcp3004:base:spiChan
  415. *********************************************************************************
  416. */
  417. static int doExtensionMcp3004 (char *progName, int pinBase, char *params)
  418. {
  419. int spi ;
  420. if ((params = extractInt (progName, params, &spi)) == NULL)
  421. return FALSE ;
  422. if ((spi < 0) || (spi > 1))
  423. {
  424. verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
  425. return FALSE ;
  426. }
  427. mcp3004Setup (pinBase, spi) ;
  428. return TRUE ;
  429. }
  430. /*
  431. * doExtensionMax5322:
  432. * Analog O
  433. * max5322:base:spiChan
  434. *********************************************************************************
  435. */
  436. static int doExtensionMax5322 (char *progName, int pinBase, char *params)
  437. {
  438. int spi ;
  439. if ((params = extractInt (progName, params, &spi)) == NULL)
  440. return FALSE ;
  441. if ((spi < 0) || (spi > 1))
  442. {
  443. verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
  444. return FALSE ;
  445. }
  446. max5322Setup (pinBase, spi) ;
  447. return TRUE ;
  448. }
  449. /*
  450. * doExtensionMcp4802:
  451. * Analog IO
  452. * mcp4802:base:spiChan
  453. *********************************************************************************
  454. */
  455. static int doExtensionMcp4802 (char *progName, int pinBase, char *params)
  456. {
  457. int spi ;
  458. if ((params = extractInt (progName, params, &spi)) == NULL)
  459. return FALSE ;
  460. if ((spi < 0) || (spi > 1))
  461. {
  462. verbError ("%s: SPI channel (%d) out of range", progName, spi) ;
  463. return FALSE ;
  464. }
  465. mcp4802Setup (pinBase, spi) ;
  466. return TRUE ;
  467. }
  468. /*
  469. * doExtensionSn3218:
  470. * Analog Output (LED Driver)
  471. * sn3218:base
  472. *********************************************************************************
  473. */
  474. static int doExtensionSn3218 (UNU char *progName, int pinBase, UNU char *params)
  475. {
  476. sn3218Setup (pinBase) ;
  477. return TRUE ;
  478. }
  479. /*
  480. * doExtensionMcp3422:
  481. * Analog IO
  482. * mcp3422:base:i2cAddr
  483. *********************************************************************************
  484. */
  485. static int doExtensionMcp3422 (char *progName, int pinBase, char *params)
  486. {
  487. int i2c, sampleRate, gain ;
  488. if ((params = extractInt (progName, params, &i2c)) == NULL)
  489. return FALSE ;
  490. if ((i2c < 0x03) || (i2c > 0x77))
  491. {
  492. verbError ("%s: i2c address (0x%X) out of range", progName, i2c) ;
  493. return FALSE ;
  494. }
  495. if ((params = extractInt (progName, params, &sampleRate)) == NULL)
  496. return FALSE ;
  497. if ((sampleRate < 0) || (sampleRate > 3))
  498. {
  499. verbError ("%s: sample rate (%d) out of range", progName, sampleRate) ;
  500. return FALSE ;
  501. }
  502. if ((params = extractInt (progName, params, &gain)) == NULL)
  503. return FALSE ;
  504. if ((gain < 0) || (gain > 3))
  505. {
  506. verbError ("%s: gain (%d) out of range", progName, gain) ;
  507. return FALSE ;
  508. }
  509. mcp3422Setup (pinBase, i2c, sampleRate, gain) ;
  510. return TRUE ;
  511. }
  512. /*
  513. * doExtensionDrcS:
  514. * Interface to a DRC Serial system
  515. * drcs:base:pins:serialPort:baud
  516. *********************************************************************************
  517. */
  518. static int doExtensionDrcS (char *progName, int pinBase, char *params)
  519. {
  520. char *port ;
  521. int pins, baud ;
  522. if ((params = extractInt (progName, params, &pins)) == NULL)
  523. return FALSE ;
  524. if ((pins < 1) || (pins > 100))
  525. {
  526. verbError ("%s: pins (%d) out of range (2-100)", progName, pins) ;
  527. return FALSE ;
  528. }
  529. if ((params = extractStr (progName, params, &port)) == NULL)
  530. return FALSE ;
  531. if (strlen (port) == 0)
  532. {
  533. verbError ("%s: serial port device name required", progName) ;
  534. return FALSE ;
  535. }
  536. if ((params = extractInt (progName, params, &baud)) == NULL)
  537. return FALSE ;
  538. if ((baud < 1) || (baud > 4000000))
  539. {
  540. verbError ("%s: baud rate (%d) out of range", progName, baud) ;
  541. return FALSE ;
  542. }
  543. drcSetupSerial (pinBase, pins, port, baud) ;
  544. return TRUE ;
  545. }
  546. /*
  547. * Function list
  548. *********************************************************************************
  549. */
  550. static struct extensionFunctionStruct extensionFunctions [] =
  551. {
  552. { "mcp23008", &doExtensionMcp23008 },
  553. { "mcp23016", &doExtensionMcp23016 },
  554. { "mcp23017", &doExtensionMcp23017 },
  555. { "mcp23s08", &doExtensionMcp23s08 },
  556. { "mcp23s17", &doExtensionMcp23s17 },
  557. { "sr595", &doExtensionSr595 },
  558. { "pcf8574", &doExtensionPcf8574 },
  559. { "pcf8591", &doExtensionPcf8591 },
  560. { "bmp180", &doExtensionBmp180 },
  561. { "pseudoPins", &doExtensionPseudoPins },
  562. { "htu21d", &doExtensionHtu21d },
  563. { "ds18b20", &doExtensionDs18b20 },
  564. { "mcp3002", &doExtensionMcp3002 },
  565. { "mcp3004", &doExtensionMcp3004 },
  566. { "mcp4802", &doExtensionMcp4802 },
  567. { "mcp3422", &doExtensionMcp3422 },
  568. { "max31855", &doExtensionMax31855 },
  569. { "ads1115", &doExtensionAds1115 },
  570. { "max5322", &doExtensionMax5322 },
  571. { "sn3218", &doExtensionSn3218 },
  572. { "drcs", &doExtensionDrcS },
  573. { NULL, NULL },
  574. } ;
  575. /*
  576. * loadWPiExtension:
  577. * Load in a wiringPi extension
  578. * The extensionData always starts with the name, a colon then the pinBase
  579. * number. Other parameters after that are decoded by the module in question.
  580. *********************************************************************************
  581. */
  582. int loadWPiExtension (char *progName, char *extensionData, int printErrors)
  583. {
  584. char *p ;
  585. char *extension = extensionData ;
  586. struct extensionFunctionStruct *extensionFn ;
  587. unsigned pinBase = 0 ;
  588. verbose = printErrors ;
  589. // Get the extension name by finding the first colon
  590. p = extension ;
  591. while (*p != ':')
  592. {
  593. if (!*p) // ran out of characters
  594. {
  595. verbError ("%s: extension name not terminated by a colon", progName) ;
  596. return FALSE ;
  597. }
  598. ++p ;
  599. }
  600. *p++ = 0 ;
  601. // Simple ATOI code
  602. if (!isdigit (*p))
  603. {
  604. verbError ("%s: decimal pinBase number expected after extension name", progName) ;
  605. return FALSE ;
  606. }
  607. while (isdigit (*p))
  608. {
  609. if (pinBase > 2147483647) // 2^31-1 ... Lets be realistic here...
  610. {
  611. verbError ("%s: pinBase too large", progName) ;
  612. return FALSE ;
  613. }
  614. pinBase = pinBase * 10 + (*p - '0') ;
  615. ++p ;
  616. }
  617. if (pinBase < 64)
  618. {
  619. verbError ("%s: pinBase (%d) too small. Minimum is 64.", progName, pinBase) ;
  620. return FALSE ;
  621. }
  622. // Search for extensions:
  623. for (extensionFn = extensionFunctions ; extensionFn->name != NULL ; ++extensionFn)
  624. {
  625. if (strcmp (extensionFn->name, extension) == 0)
  626. return extensionFn->function (progName, pinBase, p) ;
  627. }
  628. verbError ("%s: extension %s not found", progName, extension) ;
  629. return FALSE ;
  630. }