diff options
| -rw-r--r-- | drivers/auxdisplay/charlcd.c | 95 |
1 files changed, 78 insertions, 17 deletions
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 674ffbae1c65..e92f3e25f51d 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/atomic.h> | 13 | #include <linux/atomic.h> |
| 14 | #include <linux/ctype.h> | ||
| 14 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
| 15 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
| 16 | #include <linux/miscdevice.h> | 17 | #include <linux/miscdevice.h> |
| @@ -294,6 +295,79 @@ static int charlcd_init_display(struct charlcd *lcd) | |||
| 294 | } | 295 | } |
| 295 | 296 | ||
| 296 | /* | 297 | /* |
| 298 | * Parses an unsigned integer from a string, until a non-digit character | ||
| 299 | * is found. The empty string is not accepted. No overflow checks are done. | ||
| 300 | * | ||
| 301 | * Returns whether the parsing was successful. Only in that case | ||
| 302 | * the output parameters are written to. | ||
| 303 | * | ||
| 304 | * TODO: If the kernel adds an inplace version of kstrtoul(), this function | ||
| 305 | * could be easily replaced by that. | ||
| 306 | */ | ||
| 307 | static bool parse_n(const char *s, unsigned long *res, const char **next_s) | ||
| 308 | { | ||
| 309 | if (!isdigit(*s)) | ||
| 310 | return false; | ||
| 311 | |||
| 312 | *res = 0; | ||
| 313 | while (isdigit(*s)) { | ||
| 314 | *res = *res * 10 + (*s - '0'); | ||
| 315 | ++s; | ||
| 316 | } | ||
| 317 | |||
| 318 | *next_s = s; | ||
| 319 | return true; | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * Parses a movement command of the form "(.*);", where the group can be | ||
| 324 | * any number of subcommands of the form "(x|y)[0-9]+". | ||
| 325 | * | ||
| 326 | * Returns whether the command is valid. The position arguments are | ||
| 327 | * only written if the parsing was successful. | ||
| 328 | * | ||
| 329 | * For instance: | ||
| 330 | * - ";" returns (<original x>, <original y>). | ||
| 331 | * - "x1;" returns (1, <original y>). | ||
| 332 | * - "y2x1;" returns (1, 2). | ||
| 333 | * - "x12y34x56;" returns (56, 34). | ||
| 334 | * - "" fails. | ||
| 335 | * - "x" fails. | ||
| 336 | * - "x;" fails. | ||
| 337 | * - "x1" fails. | ||
| 338 | * - "xy12;" fails. | ||
| 339 | * - "x12yy12;" fails. | ||
| 340 | * - "xx" fails. | ||
| 341 | */ | ||
| 342 | static bool parse_xy(const char *s, unsigned long *x, unsigned long *y) | ||
| 343 | { | ||
| 344 | unsigned long new_x = *x; | ||
| 345 | unsigned long new_y = *y; | ||
| 346 | |||
| 347 | for (;;) { | ||
| 348 | if (!*s) | ||
| 349 | return false; | ||
| 350 | |||
| 351 | if (*s == ';') | ||
| 352 | break; | ||
| 353 | |||
| 354 | if (*s == 'x') { | ||
| 355 | if (!parse_n(s + 1, &new_x, &s)) | ||
| 356 | return false; | ||
| 357 | } else if (*s == 'y') { | ||
| 358 | if (!parse_n(s + 1, &new_y, &s)) | ||
| 359 | return false; | ||
| 360 | } else { | ||
| 361 | return false; | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | *x = new_x; | ||
| 366 | *y = new_y; | ||
| 367 | return true; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* | ||
| 297 | * These are the file operation function for user access to /dev/lcd | 371 | * These are the file operation function for user access to /dev/lcd |
| 298 | * This function can also be called from inside the kernel, by | 372 | * This function can also be called from inside the kernel, by |
| 299 | * setting file and ppos to NULL. | 373 | * setting file and ppos to NULL. |
| @@ -471,24 +545,11 @@ static inline int handle_lcd_special_code(struct charlcd *lcd) | |||
| 471 | } | 545 | } |
| 472 | case 'x': /* gotoxy : LxXXX[yYYY]; */ | 546 | case 'x': /* gotoxy : LxXXX[yYYY]; */ |
| 473 | case 'y': /* gotoxy : LyYYY[xXXX]; */ | 547 | case 'y': /* gotoxy : LyYYY[xXXX]; */ |
| 474 | if (!strchr(esc, ';')) | 548 | /* If the command is valid, move to the new address */ |
| 475 | break; | 549 | if (parse_xy(esc, &priv->addr.x, &priv->addr.y)) |
| 476 | 550 | charlcd_gotoxy(lcd); | |
| 477 | while (*esc) { | ||
| 478 | if (*esc == 'x') { | ||
| 479 | esc++; | ||
| 480 | if (kstrtoul(esc, 10, &priv->addr.x) < 0) | ||
| 481 | break; | ||
| 482 | } else if (*esc == 'y') { | ||
| 483 | esc++; | ||
| 484 | if (kstrtoul(esc, 10, &priv->addr.y) < 0) | ||
| 485 | break; | ||
| 486 | } else { | ||
| 487 | break; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | 551 | ||
| 491 | charlcd_gotoxy(lcd); | 552 | /* Regardless of its validity, mark as processed */ |
| 492 | processed = 1; | 553 | processed = 1; |
| 493 | break; | 554 | break; |
| 494 | } | 555 | } |
