charlcd.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Character LCD driver for Linux
  4. *
  5. * Copyright (C) 2000-2008, Willy Tarreau <[email protected]>
  6. * Copyright (C) 2016-2017 Glider bvba
  7. */
  8. #include <linux/atomic.h>
  9. #include <linux/ctype.h>
  10. #include <linux/fs.h>
  11. #include <linux/miscdevice.h>
  12. #include <linux/module.h>
  13. #include <linux/notifier.h>
  14. #include <linux/reboot.h>
  15. #include <linux/slab.h>
  16. #include <linux/uaccess.h>
  17. #include <linux/workqueue.h>
  18. #include <generated/utsrelease.h>
  19. #include "charlcd.h"
  20. /* Keep the backlight on this many seconds for each flash */
  21. #define LCD_BL_TEMPO_PERIOD 4
  22. #define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
  23. #define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */
  24. struct charlcd_priv {
  25. struct charlcd lcd;
  26. struct delayed_work bl_work;
  27. struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
  28. bool bl_tempo;
  29. bool must_clear;
  30. /* contains the LCD config state */
  31. unsigned long flags;
  32. /* Current escape sequence and it's length or -1 if outside */
  33. struct {
  34. char buf[LCD_ESCAPE_LEN + 1];
  35. int len;
  36. } esc_seq;
  37. unsigned long long drvdata[];
  38. };
  39. #define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd)
  40. /* Device single-open policy control */
  41. static atomic_t charlcd_available = ATOMIC_INIT(1);
  42. /* turn the backlight on or off */
  43. void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
  44. {
  45. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  46. if (!lcd->ops->backlight)
  47. return;
  48. mutex_lock(&priv->bl_tempo_lock);
  49. if (!priv->bl_tempo)
  50. lcd->ops->backlight(lcd, on);
  51. mutex_unlock(&priv->bl_tempo_lock);
  52. }
  53. EXPORT_SYMBOL_GPL(charlcd_backlight);
  54. static void charlcd_bl_off(struct work_struct *work)
  55. {
  56. struct delayed_work *dwork = to_delayed_work(work);
  57. struct charlcd_priv *priv =
  58. container_of(dwork, struct charlcd_priv, bl_work);
  59. mutex_lock(&priv->bl_tempo_lock);
  60. if (priv->bl_tempo) {
  61. priv->bl_tempo = false;
  62. if (!(priv->flags & LCD_FLAG_L))
  63. priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
  64. }
  65. mutex_unlock(&priv->bl_tempo_lock);
  66. }
  67. /* turn the backlight on for a little while */
  68. void charlcd_poke(struct charlcd *lcd)
  69. {
  70. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  71. if (!lcd->ops->backlight)
  72. return;
  73. cancel_delayed_work_sync(&priv->bl_work);
  74. mutex_lock(&priv->bl_tempo_lock);
  75. if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
  76. lcd->ops->backlight(lcd, CHARLCD_ON);
  77. priv->bl_tempo = true;
  78. schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
  79. mutex_unlock(&priv->bl_tempo_lock);
  80. }
  81. EXPORT_SYMBOL_GPL(charlcd_poke);
  82. static void charlcd_home(struct charlcd *lcd)
  83. {
  84. lcd->addr.x = 0;
  85. lcd->addr.y = 0;
  86. lcd->ops->home(lcd);
  87. }
  88. static void charlcd_print(struct charlcd *lcd, char c)
  89. {
  90. if (lcd->addr.x >= lcd->width)
  91. return;
  92. if (lcd->char_conv)
  93. c = lcd->char_conv[(unsigned char)c];
  94. if (!lcd->ops->print(lcd, c))
  95. lcd->addr.x++;
  96. /* prevents the cursor from wrapping onto the next line */
  97. if (lcd->addr.x == lcd->width)
  98. lcd->ops->gotoxy(lcd, lcd->addr.x - 1, lcd->addr.y);
  99. }
  100. static void charlcd_clear_display(struct charlcd *lcd)
  101. {
  102. lcd->ops->clear_display(lcd);
  103. lcd->addr.x = 0;
  104. lcd->addr.y = 0;
  105. }
  106. /*
  107. * Parses a movement command of the form "(.*);", where the group can be
  108. * any number of subcommands of the form "(x|y)[0-9]+".
  109. *
  110. * Returns whether the command is valid. The position arguments are
  111. * only written if the parsing was successful.
  112. *
  113. * For instance:
  114. * - ";" returns (<original x>, <original y>).
  115. * - "x1;" returns (1, <original y>).
  116. * - "y2x1;" returns (1, 2).
  117. * - "x12y34x56;" returns (56, 34).
  118. * - "" fails.
  119. * - "x" fails.
  120. * - "x;" fails.
  121. * - "x1" fails.
  122. * - "xy12;" fails.
  123. * - "x12yy12;" fails.
  124. * - "xx" fails.
  125. */
  126. static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
  127. {
  128. unsigned long new_x = *x;
  129. unsigned long new_y = *y;
  130. char *p;
  131. for (;;) {
  132. if (!*s)
  133. return false;
  134. if (*s == ';')
  135. break;
  136. if (*s == 'x') {
  137. new_x = simple_strtoul(s + 1, &p, 10);
  138. if (p == s + 1)
  139. return false;
  140. s = p;
  141. } else if (*s == 'y') {
  142. new_y = simple_strtoul(s + 1, &p, 10);
  143. if (p == s + 1)
  144. return false;
  145. s = p;
  146. } else {
  147. return false;
  148. }
  149. }
  150. *x = new_x;
  151. *y = new_y;
  152. return true;
  153. }
  154. /*
  155. * These are the file operation function for user access to /dev/lcd
  156. * This function can also be called from inside the kernel, by
  157. * setting file and ppos to NULL.
  158. *
  159. */
  160. static inline int handle_lcd_special_code(struct charlcd *lcd)
  161. {
  162. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  163. /* LCD special codes */
  164. int processed = 0;
  165. char *esc = priv->esc_seq.buf + 2;
  166. int oldflags = priv->flags;
  167. /* check for display mode flags */
  168. switch (*esc) {
  169. case 'D': /* Display ON */
  170. priv->flags |= LCD_FLAG_D;
  171. if (priv->flags != oldflags)
  172. lcd->ops->display(lcd, CHARLCD_ON);
  173. processed = 1;
  174. break;
  175. case 'd': /* Display OFF */
  176. priv->flags &= ~LCD_FLAG_D;
  177. if (priv->flags != oldflags)
  178. lcd->ops->display(lcd, CHARLCD_OFF);
  179. processed = 1;
  180. break;
  181. case 'C': /* Cursor ON */
  182. priv->flags |= LCD_FLAG_C;
  183. if (priv->flags != oldflags)
  184. lcd->ops->cursor(lcd, CHARLCD_ON);
  185. processed = 1;
  186. break;
  187. case 'c': /* Cursor OFF */
  188. priv->flags &= ~LCD_FLAG_C;
  189. if (priv->flags != oldflags)
  190. lcd->ops->cursor(lcd, CHARLCD_OFF);
  191. processed = 1;
  192. break;
  193. case 'B': /* Blink ON */
  194. priv->flags |= LCD_FLAG_B;
  195. if (priv->flags != oldflags)
  196. lcd->ops->blink(lcd, CHARLCD_ON);
  197. processed = 1;
  198. break;
  199. case 'b': /* Blink OFF */
  200. priv->flags &= ~LCD_FLAG_B;
  201. if (priv->flags != oldflags)
  202. lcd->ops->blink(lcd, CHARLCD_OFF);
  203. processed = 1;
  204. break;
  205. case '+': /* Back light ON */
  206. priv->flags |= LCD_FLAG_L;
  207. if (priv->flags != oldflags)
  208. charlcd_backlight(lcd, CHARLCD_ON);
  209. processed = 1;
  210. break;
  211. case '-': /* Back light OFF */
  212. priv->flags &= ~LCD_FLAG_L;
  213. if (priv->flags != oldflags)
  214. charlcd_backlight(lcd, CHARLCD_OFF);
  215. processed = 1;
  216. break;
  217. case '*': /* Flash back light */
  218. charlcd_poke(lcd);
  219. processed = 1;
  220. break;
  221. case 'f': /* Small Font */
  222. priv->flags &= ~LCD_FLAG_F;
  223. if (priv->flags != oldflags)
  224. lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_SMALL);
  225. processed = 1;
  226. break;
  227. case 'F': /* Large Font */
  228. priv->flags |= LCD_FLAG_F;
  229. if (priv->flags != oldflags)
  230. lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_LARGE);
  231. processed = 1;
  232. break;
  233. case 'n': /* One Line */
  234. priv->flags &= ~LCD_FLAG_N;
  235. if (priv->flags != oldflags)
  236. lcd->ops->lines(lcd, CHARLCD_LINES_1);
  237. processed = 1;
  238. break;
  239. case 'N': /* Two Lines */
  240. priv->flags |= LCD_FLAG_N;
  241. if (priv->flags != oldflags)
  242. lcd->ops->lines(lcd, CHARLCD_LINES_2);
  243. processed = 1;
  244. break;
  245. case 'l': /* Shift Cursor Left */
  246. if (lcd->addr.x > 0) {
  247. if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
  248. lcd->addr.x--;
  249. }
  250. processed = 1;
  251. break;
  252. case 'r': /* shift cursor right */
  253. if (lcd->addr.x < lcd->width) {
  254. if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT))
  255. lcd->addr.x++;
  256. }
  257. processed = 1;
  258. break;
  259. case 'L': /* shift display left */
  260. lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT);
  261. processed = 1;
  262. break;
  263. case 'R': /* shift display right */
  264. lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT);
  265. processed = 1;
  266. break;
  267. case 'k': { /* kill end of line */
  268. int x, xs, ys;
  269. xs = lcd->addr.x;
  270. ys = lcd->addr.y;
  271. for (x = lcd->addr.x; x < lcd->width; x++)
  272. lcd->ops->print(lcd, ' ');
  273. /* restore cursor position */
  274. lcd->addr.x = xs;
  275. lcd->addr.y = ys;
  276. lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
  277. processed = 1;
  278. break;
  279. }
  280. case 'I': /* reinitialize display */
  281. lcd->ops->init_display(lcd);
  282. priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
  283. LCD_FLAG_C | LCD_FLAG_B;
  284. processed = 1;
  285. break;
  286. case 'G':
  287. if (lcd->ops->redefine_char)
  288. processed = lcd->ops->redefine_char(lcd, esc);
  289. else
  290. processed = 1;
  291. break;
  292. case 'x': /* gotoxy : LxXXX[yYYY]; */
  293. case 'y': /* gotoxy : LyYYY[xXXX]; */
  294. if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
  295. break;
  296. /* If the command is valid, move to the new address */
  297. if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
  298. lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
  299. /* Regardless of its validity, mark as processed */
  300. processed = 1;
  301. break;
  302. }
  303. return processed;
  304. }
  305. static void charlcd_write_char(struct charlcd *lcd, char c)
  306. {
  307. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  308. /* first, we'll test if we're in escape mode */
  309. if ((c != '\n') && priv->esc_seq.len >= 0) {
  310. /* yes, let's add this char to the buffer */
  311. priv->esc_seq.buf[priv->esc_seq.len++] = c;
  312. priv->esc_seq.buf[priv->esc_seq.len] = '\0';
  313. } else {
  314. /* aborts any previous escape sequence */
  315. priv->esc_seq.len = -1;
  316. switch (c) {
  317. case LCD_ESCAPE_CHAR:
  318. /* start of an escape sequence */
  319. priv->esc_seq.len = 0;
  320. priv->esc_seq.buf[priv->esc_seq.len] = '\0';
  321. break;
  322. case '\b':
  323. /* go back one char and clear it */
  324. if (lcd->addr.x > 0) {
  325. /* back one char */
  326. if (!lcd->ops->shift_cursor(lcd,
  327. CHARLCD_SHIFT_LEFT))
  328. lcd->addr.x--;
  329. }
  330. /* replace with a space */
  331. charlcd_print(lcd, ' ');
  332. /* back one char again */
  333. if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
  334. lcd->addr.x--;
  335. break;
  336. case '\f':
  337. /* quickly clear the display */
  338. charlcd_clear_display(lcd);
  339. break;
  340. case '\n':
  341. /*
  342. * flush the remainder of the current line and
  343. * go to the beginning of the next line
  344. */
  345. for (; lcd->addr.x < lcd->width; lcd->addr.x++)
  346. lcd->ops->print(lcd, ' ');
  347. lcd->addr.x = 0;
  348. lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
  349. lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
  350. break;
  351. case '\r':
  352. /* go to the beginning of the same line */
  353. lcd->addr.x = 0;
  354. lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
  355. break;
  356. case '\t':
  357. /* print a space instead of the tab */
  358. charlcd_print(lcd, ' ');
  359. break;
  360. default:
  361. /* simply print this char */
  362. charlcd_print(lcd, c);
  363. break;
  364. }
  365. }
  366. /*
  367. * now we'll see if we're in an escape mode and if the current
  368. * escape sequence can be understood.
  369. */
  370. if (priv->esc_seq.len >= 2) {
  371. int processed = 0;
  372. if (!strcmp(priv->esc_seq.buf, "[2J")) {
  373. /* clear the display */
  374. charlcd_clear_display(lcd);
  375. processed = 1;
  376. } else if (!strcmp(priv->esc_seq.buf, "[H")) {
  377. /* cursor to home */
  378. charlcd_home(lcd);
  379. processed = 1;
  380. }
  381. /* codes starting with ^[[L */
  382. else if ((priv->esc_seq.len >= 3) &&
  383. (priv->esc_seq.buf[0] == '[') &&
  384. (priv->esc_seq.buf[1] == 'L')) {
  385. processed = handle_lcd_special_code(lcd);
  386. }
  387. /* LCD special escape codes */
  388. /*
  389. * flush the escape sequence if it's been processed
  390. * or if it is getting too long.
  391. */
  392. if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
  393. priv->esc_seq.len = -1;
  394. } /* escape codes */
  395. }
  396. static struct charlcd *the_charlcd;
  397. static ssize_t charlcd_write(struct file *file, const char __user *buf,
  398. size_t count, loff_t *ppos)
  399. {
  400. const char __user *tmp = buf;
  401. char c;
  402. for (; count-- > 0; (*ppos)++, tmp++) {
  403. if (((count + 1) & 0x1f) == 0) {
  404. /*
  405. * charlcd_write() is invoked as a VFS->write() callback
  406. * and as such it is always invoked from preemptible
  407. * context and may sleep.
  408. */
  409. cond_resched();
  410. }
  411. if (get_user(c, tmp))
  412. return -EFAULT;
  413. charlcd_write_char(the_charlcd, c);
  414. }
  415. return tmp - buf;
  416. }
  417. static int charlcd_open(struct inode *inode, struct file *file)
  418. {
  419. struct charlcd_priv *priv = charlcd_to_priv(the_charlcd);
  420. int ret;
  421. ret = -EBUSY;
  422. if (!atomic_dec_and_test(&charlcd_available))
  423. goto fail; /* open only once at a time */
  424. ret = -EPERM;
  425. if (file->f_mode & FMODE_READ) /* device is write-only */
  426. goto fail;
  427. if (priv->must_clear) {
  428. priv->lcd.ops->clear_display(&priv->lcd);
  429. priv->must_clear = false;
  430. priv->lcd.addr.x = 0;
  431. priv->lcd.addr.y = 0;
  432. }
  433. return nonseekable_open(inode, file);
  434. fail:
  435. atomic_inc(&charlcd_available);
  436. return ret;
  437. }
  438. static int charlcd_release(struct inode *inode, struct file *file)
  439. {
  440. atomic_inc(&charlcd_available);
  441. return 0;
  442. }
  443. static const struct file_operations charlcd_fops = {
  444. .write = charlcd_write,
  445. .open = charlcd_open,
  446. .release = charlcd_release,
  447. .llseek = no_llseek,
  448. };
  449. static struct miscdevice charlcd_dev = {
  450. .minor = LCD_MINOR,
  451. .name = "lcd",
  452. .fops = &charlcd_fops,
  453. };
  454. static void charlcd_puts(struct charlcd *lcd, const char *s)
  455. {
  456. const char *tmp = s;
  457. int count = strlen(s);
  458. for (; count-- > 0; tmp++) {
  459. if (((count + 1) & 0x1f) == 0)
  460. cond_resched();
  461. charlcd_write_char(lcd, *tmp);
  462. }
  463. }
  464. #ifdef CONFIG_PANEL_BOOT_MESSAGE
  465. #define LCD_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
  466. #else
  467. #define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n"
  468. #endif
  469. #ifdef CONFIG_CHARLCD_BL_ON
  470. #define LCD_INIT_BL "\x1b[L+"
  471. #elif defined(CONFIG_CHARLCD_BL_FLASH)
  472. #define LCD_INIT_BL "\x1b[L*"
  473. #else
  474. #define LCD_INIT_BL "\x1b[L-"
  475. #endif
  476. /* initialize the LCD driver */
  477. static int charlcd_init(struct charlcd *lcd)
  478. {
  479. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  480. int ret;
  481. priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
  482. LCD_FLAG_C | LCD_FLAG_B;
  483. if (lcd->ops->backlight) {
  484. mutex_init(&priv->bl_tempo_lock);
  485. INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
  486. }
  487. /*
  488. * before this line, we must NOT send anything to the display.
  489. * Since charlcd_init_display() needs to write data, we have to
  490. * enable mark the LCD initialized just before.
  491. */
  492. if (WARN_ON(!lcd->ops->init_display))
  493. return -EINVAL;
  494. ret = lcd->ops->init_display(lcd);
  495. if (ret)
  496. return ret;
  497. /* display a short message */
  498. charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT);
  499. /* clear the display on the next device opening */
  500. priv->must_clear = true;
  501. charlcd_home(lcd);
  502. return 0;
  503. }
  504. struct charlcd *charlcd_alloc(void)
  505. {
  506. struct charlcd_priv *priv;
  507. struct charlcd *lcd;
  508. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  509. if (!priv)
  510. return NULL;
  511. priv->esc_seq.len = -1;
  512. lcd = &priv->lcd;
  513. return lcd;
  514. }
  515. EXPORT_SYMBOL_GPL(charlcd_alloc);
  516. void charlcd_free(struct charlcd *lcd)
  517. {
  518. kfree(charlcd_to_priv(lcd));
  519. }
  520. EXPORT_SYMBOL_GPL(charlcd_free);
  521. static int panel_notify_sys(struct notifier_block *this, unsigned long code,
  522. void *unused)
  523. {
  524. struct charlcd *lcd = the_charlcd;
  525. switch (code) {
  526. case SYS_DOWN:
  527. charlcd_puts(lcd,
  528. "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
  529. break;
  530. case SYS_HALT:
  531. charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
  532. break;
  533. case SYS_POWER_OFF:
  534. charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
  535. break;
  536. default:
  537. break;
  538. }
  539. return NOTIFY_DONE;
  540. }
  541. static struct notifier_block panel_notifier = {
  542. .notifier_call = panel_notify_sys,
  543. };
  544. int charlcd_register(struct charlcd *lcd)
  545. {
  546. int ret;
  547. ret = charlcd_init(lcd);
  548. if (ret)
  549. return ret;
  550. ret = misc_register(&charlcd_dev);
  551. if (ret)
  552. return ret;
  553. the_charlcd = lcd;
  554. register_reboot_notifier(&panel_notifier);
  555. return 0;
  556. }
  557. EXPORT_SYMBOL_GPL(charlcd_register);
  558. int charlcd_unregister(struct charlcd *lcd)
  559. {
  560. struct charlcd_priv *priv = charlcd_to_priv(lcd);
  561. unregister_reboot_notifier(&panel_notifier);
  562. charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
  563. misc_deregister(&charlcd_dev);
  564. the_charlcd = NULL;
  565. if (lcd->ops->backlight) {
  566. cancel_delayed_work_sync(&priv->bl_work);
  567. priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
  568. }
  569. return 0;
  570. }
  571. EXPORT_SYMBOL_GPL(charlcd_unregister);
  572. MODULE_LICENSE("GPL");