1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Kernel command line console options for hardware based addressing 4 * 5 * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ 6 * Author: Tony Lindgren <tony@atomide.com> 7 */ 8 9#include <linux/console.h> 10#include <linux/init.h> 11#include <linux/string.h> 12#include <linux/types.h> 13 14#include <asm/errno.h> 15 16#include "console_cmdline.h" 17 18/* 19 * Allow longer DEVNAME:0.0 style console naming such as abcd0000.serial:0.0 20 * in addition to the legacy ttyS0 style naming. 21 */ 22#define CONSOLE_NAME_MAX 32 23 24#define CONSOLE_OPT_MAX 16 25#define CONSOLE_BRL_OPT_MAX 16 26 27struct console_option { 28 char name[CONSOLE_NAME_MAX]; 29 char opt[CONSOLE_OPT_MAX]; 30 char brl_opt[CONSOLE_BRL_OPT_MAX]; 31 u8 has_brl_opt:1; 32}; 33 34/* Updated only at console_setup() time, no locking needed */ 35static struct console_option conopt[MAX_CMDLINECONSOLES]; 36 37/** 38 * console_opt_save - Saves kernel command line console option for driver use 39 * @str: Kernel command line console name and option 40 * @brl_opt: Braille console options 41 * 42 * Saves a kernel command line console option for driver subsystems to use for 43 * adding a preferred console during init. Called from console_setup() only. 44 * 45 * Return: 0 on success, negative error code on failure. 46 */ 47int __init console_opt_save(const char *str, const char *brl_opt) 48{ 49 struct console_option *con; 50 size_t namelen, optlen; 51 const char *opt; 52 int i; 53 54 namelen = strcspn(str, ","); 55 if (namelen == 0 || namelen >= CONSOLE_NAME_MAX) 56 return -EINVAL; 57 58 opt = str + namelen; 59 if (*opt == ',') 60 opt++; 61 62 optlen = strlen(opt); 63 if (optlen >= CONSOLE_OPT_MAX) 64 return -EINVAL; 65 66 for (i = 0; i < MAX_CMDLINECONSOLES; i++) { 67 con = &conopt[i]; 68 69 if (con->name[0]) { 70 if (!strncmp(str, con->name, namelen)) 71 return 0; 72 continue; 73 } 74 75 /* 76 * The name isn't terminated, only opt is. Empty opt is fine, 77 * but brl_opt can be either empty or NULL. For more info, see 78 * _braille_console_setup(). 79 */ 80 strscpy(con->name, str, namelen + 1); 81 strscpy(con->opt, opt, CONSOLE_OPT_MAX); 82 if (brl_opt) { 83 strscpy(con->brl_opt, brl_opt, CONSOLE_BRL_OPT_MAX); 84 con->has_brl_opt = 1; 85 } 86 87 return 0; 88 } 89 90 return -ENOMEM; 91} 92 93static struct console_option *console_opt_find(const char *name) 94{ 95 struct console_option *con; 96 int i; 97 98 for (i = 0; i < MAX_CMDLINECONSOLES; i++) { 99 con = &conopt[i]; 100 if (!strcmp(name, con->name)) 101 return con; 102 } 103 104 return NULL; 105} 106 107/** 108 * add_preferred_console_match - Adds a preferred console if a match is found 109 * @match: Expected console on kernel command line, such as console=DEVNAME:0.0 110 * @name: Name of the console character device to add such as ttyS 111 * @idx: Index for the console 112 * 113 * Allows driver subsystems to add a console after translating the command 114 * line name to the character device name used for the console. Options are 115 * added automatically based on the kernel command line. Duplicate preferred 116 * consoles are ignored by __add_preferred_console(). 117 * 118 * Return: 0 on success, negative error code on failure. 119 */ 120int add_preferred_console_match(const char *match, const char *name, 121 const short idx) 122{ 123 struct console_option *con; 124 char *brl_opt = NULL; 125 126 if (!match || !strlen(match) || !name || !strlen(name) || 127 idx < 0) 128 return -EINVAL; 129 130 con = console_opt_find(match); 131 if (!con) 132 return -ENOENT; 133 134 /* 135 * See __add_preferred_console(). It checks for NULL brl_options to set 136 * the preferred_console flag. Empty brl_opt instead of NULL leads into 137 * the preferred_console flag not set, and CON_CONSDEV not being set, 138 * and the boot console won't get disabled at the end of console_setup(). 139 */ 140 if (con->has_brl_opt) 141 brl_opt = con->brl_opt; 142 143 console_opt_add_preferred_console(name, idx, con->opt, brl_opt); 144 145 return 0; 146} 147