Skip to content

Commit e08ab74

Browse files
committed
drm/modes: Rewrite the command line parser
Rewrite the command line parser in order to get away from the state machine parsing the video mode lines. Hopefully, this will allow to extend it more easily to support named modes and / or properties set directly on the command line. Reviewed-by: Noralf Trønnes <[email protected]> Signed-off-by: Maxime Ripard <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/e32cd4009153b184103554009135c7bf7c9975d7.1560783090.git-series.maxime.ripard@bootlin.com
1 parent a99076e commit e08ab74

File tree

1 file changed

+210
-115
lines changed

1 file changed

+210
-115
lines changed

drivers/gpu/drm/drm_modes.c

Lines changed: 210 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* authorization from the copyright holder(s) and author(s).
3131
*/
3232

33+
#include <linux/ctype.h>
3334
#include <linux/list.h>
3435
#include <linux/list_sort.h>
3536
#include <linux/export.h>
@@ -1408,6 +1409,151 @@ void drm_connector_list_update(struct drm_connector *connector)
14081409
}
14091410
EXPORT_SYMBOL(drm_connector_list_update);
14101411

1412+
static int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr,
1413+
struct drm_cmdline_mode *mode)
1414+
{
1415+
unsigned int bpp;
1416+
1417+
if (str[0] != '-')
1418+
return -EINVAL;
1419+
1420+
str++;
1421+
bpp = simple_strtol(str, end_ptr, 10);
1422+
if (*end_ptr == str)
1423+
return -EINVAL;
1424+
1425+
mode->bpp = bpp;
1426+
mode->bpp_specified = true;
1427+
1428+
return 0;
1429+
}
1430+
1431+
static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr,
1432+
struct drm_cmdline_mode *mode)
1433+
{
1434+
unsigned int refresh;
1435+
1436+
if (str[0] != '@')
1437+
return -EINVAL;
1438+
1439+
str++;
1440+
refresh = simple_strtol(str, end_ptr, 10);
1441+
if (*end_ptr == str)
1442+
return -EINVAL;
1443+
1444+
mode->refresh = refresh;
1445+
mode->refresh_specified = true;
1446+
1447+
return 0;
1448+
}
1449+
1450+
static int drm_mode_parse_cmdline_extra(const char *str, int length,
1451+
struct drm_connector *connector,
1452+
struct drm_cmdline_mode *mode)
1453+
{
1454+
int i;
1455+
1456+
for (i = 0; i < length; i++) {
1457+
switch (str[i]) {
1458+
case 'i':
1459+
mode->interlace = true;
1460+
break;
1461+
case 'm':
1462+
mode->margins = true;
1463+
break;
1464+
case 'D':
1465+
if (mode->force != DRM_FORCE_UNSPECIFIED)
1466+
return -EINVAL;
1467+
1468+
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
1469+
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
1470+
mode->force = DRM_FORCE_ON;
1471+
else
1472+
mode->force = DRM_FORCE_ON_DIGITAL;
1473+
break;
1474+
case 'd':
1475+
if (mode->force != DRM_FORCE_UNSPECIFIED)
1476+
return -EINVAL;
1477+
1478+
mode->force = DRM_FORCE_OFF;
1479+
break;
1480+
case 'e':
1481+
if (mode->force != DRM_FORCE_UNSPECIFIED)
1482+
return -EINVAL;
1483+
1484+
mode->force = DRM_FORCE_ON;
1485+
break;
1486+
default:
1487+
return -EINVAL;
1488+
}
1489+
}
1490+
1491+
return 0;
1492+
}
1493+
1494+
static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
1495+
bool extras,
1496+
struct drm_connector *connector,
1497+
struct drm_cmdline_mode *mode)
1498+
{
1499+
const char *str_start = str;
1500+
bool rb = false, cvt = false;
1501+
int xres = 0, yres = 0;
1502+
int remaining, i;
1503+
char *end_ptr;
1504+
1505+
xres = simple_strtol(str, &end_ptr, 10);
1506+
if (end_ptr == str)
1507+
return -EINVAL;
1508+
1509+
if (end_ptr[0] != 'x')
1510+
return -EINVAL;
1511+
end_ptr++;
1512+
1513+
str = end_ptr;
1514+
yres = simple_strtol(str, &end_ptr, 10);
1515+
if (end_ptr == str)
1516+
return -EINVAL;
1517+
1518+
remaining = length - (end_ptr - str_start);
1519+
if (remaining < 0)
1520+
return -EINVAL;
1521+
1522+
for (i = 0; i < remaining; i++) {
1523+
switch (end_ptr[i]) {
1524+
case 'M':
1525+
cvt = true;
1526+
break;
1527+
case 'R':
1528+
rb = true;
1529+
break;
1530+
default:
1531+
/*
1532+
* Try to pass that to our extras parsing
1533+
* function to handle the case where the
1534+
* extras are directly after the resolution
1535+
*/
1536+
if (extras) {
1537+
int ret = drm_mode_parse_cmdline_extra(end_ptr + i,
1538+
1,
1539+
connector,
1540+
mode);
1541+
if (ret)
1542+
return ret;
1543+
} else {
1544+
return -EINVAL;
1545+
}
1546+
}
1547+
}
1548+
1549+
mode->xres = xres;
1550+
mode->yres = yres;
1551+
mode->cvt = cvt;
1552+
mode->rb = rb;
1553+
1554+
return 0;
1555+
}
1556+
14111557
/**
14121558
* drm_mode_parse_command_line_for_connector - parse command line modeline for connector
14131559
* @mode_option: optional per connector mode option
@@ -1434,13 +1580,12 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
14341580
struct drm_cmdline_mode *mode)
14351581
{
14361582
const char *name;
1437-
unsigned int namelen;
1438-
bool res_specified = false, bpp_specified = false, refresh_specified = false;
1439-
unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
1440-
bool yres_specified = false, cvt = false, rb = false;
1441-
bool interlace = false, margins = false, was_digit = false;
1442-
int i;
1443-
enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
1583+
bool parse_extras = false;
1584+
unsigned int bpp_off = 0, refresh_off = 0;
1585+
unsigned int mode_end = 0;
1586+
char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
1587+
char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
1588+
int ret;
14441589

14451590
#ifdef CONFIG_FB
14461591
if (!mode_option)
@@ -1453,127 +1598,77 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
14531598
}
14541599

14551600
name = mode_option;
1456-
namelen = strlen(name);
1457-
for (i = namelen-1; i >= 0; i--) {
1458-
switch (name[i]) {
1459-
case '@':
1460-
if (!refresh_specified && !bpp_specified &&
1461-
!yres_specified && !cvt && !rb && was_digit) {
1462-
refresh = simple_strtol(&name[i+1], NULL, 10);
1463-
refresh_specified = true;
1464-
was_digit = false;
1465-
} else
1466-
goto done;
1467-
break;
1468-
case '-':
1469-
if (!bpp_specified && !yres_specified && !cvt &&
1470-
!rb && was_digit) {
1471-
bpp = simple_strtol(&name[i+1], NULL, 10);
1472-
bpp_specified = true;
1473-
was_digit = false;
1474-
} else
1475-
goto done;
1476-
break;
1477-
case 'x':
1478-
if (!yres_specified && was_digit) {
1479-
yres = simple_strtol(&name[i+1], NULL, 10);
1480-
yres_specified = true;
1481-
was_digit = false;
1482-
} else
1483-
goto done;
1484-
break;
1485-
case '0' ... '9':
1486-
was_digit = true;
1487-
break;
1488-
case 'M':
1489-
if (yres_specified || cvt || was_digit)
1490-
goto done;
1491-
cvt = true;
1492-
break;
1493-
case 'R':
1494-
if (yres_specified || cvt || rb || was_digit)
1495-
goto done;
1496-
rb = true;
1497-
break;
1498-
case 'm':
1499-
if (cvt || yres_specified || was_digit)
1500-
goto done;
1501-
margins = true;
1502-
break;
1503-
case 'i':
1504-
if (cvt || yres_specified || was_digit)
1505-
goto done;
1506-
interlace = true;
1507-
break;
1508-
case 'e':
1509-
if (yres_specified || bpp_specified || refresh_specified ||
1510-
was_digit || (force != DRM_FORCE_UNSPECIFIED))
1511-
goto done;
15121601

1513-
force = DRM_FORCE_ON;
1514-
break;
1515-
case 'D':
1516-
if (yres_specified || bpp_specified || refresh_specified ||
1517-
was_digit || (force != DRM_FORCE_UNSPECIFIED))
1518-
goto done;
1602+
if (!isdigit(name[0]))
1603+
return false;
15191604

1520-
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
1521-
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
1522-
force = DRM_FORCE_ON;
1523-
else
1524-
force = DRM_FORCE_ON_DIGITAL;
1525-
break;
1526-
case 'd':
1527-
if (yres_specified || bpp_specified || refresh_specified ||
1528-
was_digit || (force != DRM_FORCE_UNSPECIFIED))
1529-
goto done;
1605+
/* Try to locate the bpp and refresh specifiers, if any */
1606+
bpp_ptr = strchr(name, '-');
1607+
if (bpp_ptr) {
1608+
bpp_off = bpp_ptr - name;
1609+
mode->bpp_specified = true;
1610+
}
15301611

1531-
force = DRM_FORCE_OFF;
1532-
break;
1533-
default:
1534-
goto done;
1535-
}
1612+
refresh_ptr = strchr(name, '@');
1613+
if (refresh_ptr) {
1614+
refresh_off = refresh_ptr - name;
1615+
mode->refresh_specified = true;
15361616
}
15371617

1538-
if (i < 0 && yres_specified) {
1539-
char *ch;
1540-
xres = simple_strtol(name, &ch, 10);
1541-
if ((ch != NULL) && (*ch == 'x'))
1542-
res_specified = true;
1543-
else
1544-
i = ch - name;
1545-
} else if (!yres_specified && was_digit) {
1546-
/* catch mode that begins with digits but has no 'x' */
1547-
i = 0;
1618+
/* Locate the end of the name / resolution, and parse it */
1619+
if (bpp_ptr && refresh_ptr) {
1620+
mode_end = min(bpp_off, refresh_off);
1621+
} else if (bpp_ptr) {
1622+
mode_end = bpp_off;
1623+
} else if (refresh_ptr) {
1624+
mode_end = refresh_off;
1625+
} else {
1626+
mode_end = strlen(name);
1627+
parse_extras = true;
15481628
}
1549-
done:
1550-
if (i >= 0) {
1551-
pr_warn("[drm] parse error at position %i in video mode '%s'\n",
1552-
i, name);
1553-
mode->specified = false;
1629+
1630+
ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
1631+
parse_extras,
1632+
connector,
1633+
mode);
1634+
if (ret)
15541635
return false;
1555-
}
1636+
mode->specified = true;
15561637

1557-
if (res_specified) {
1558-
mode->specified = true;
1559-
mode->xres = xres;
1560-
mode->yres = yres;
1638+
if (bpp_ptr) {
1639+
ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
1640+
if (ret)
1641+
return false;
15611642
}
15621643

1563-
if (refresh_specified) {
1564-
mode->refresh_specified = true;
1565-
mode->refresh = refresh;
1644+
if (refresh_ptr) {
1645+
ret = drm_mode_parse_cmdline_refresh(refresh_ptr,
1646+
&refresh_end_ptr, mode);
1647+
if (ret)
1648+
return false;
15661649
}
15671650

1568-
if (bpp_specified) {
1569-
mode->bpp_specified = true;
1570-
mode->bpp = bpp;
1651+
/*
1652+
* Locate the end of the bpp / refresh, and parse the extras
1653+
* if relevant
1654+
*/
1655+
if (bpp_ptr && refresh_ptr)
1656+
extra_ptr = max(bpp_end_ptr, refresh_end_ptr);
1657+
else if (bpp_ptr)
1658+
extra_ptr = bpp_end_ptr;
1659+
else if (refresh_ptr)
1660+
extra_ptr = refresh_end_ptr;
1661+
1662+
if (extra_ptr) {
1663+
int remaining = strlen(name) - (extra_ptr - name);
1664+
1665+
/*
1666+
* We still have characters to process, while
1667+
* we shouldn't have any
1668+
*/
1669+
if (remaining > 0)
1670+
return false;
15711671
}
1572-
mode->rb = rb;
1573-
mode->cvt = cvt;
1574-
mode->interlace = interlace;
1575-
mode->margins = margins;
1576-
mode->force = force;
15771672

15781673
return true;
15791674
}

0 commit comments

Comments
 (0)