Commit | Line | Data |
---|---|---|
f2d937f3 JW |
1 | /* |
2 | * Based on the same principle as kgdboe using the NETPOLL api, this | |
3 | * driver uses a console polling api to implement a gdb serial inteface | |
4 | * which is multiplexed on a console port. | |
5 | * | |
6 | * Maintainer: Jason Wessel <jason.wessel@windriver.com> | |
7 | * | |
8 | * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc. | |
9 | * | |
10 | * This file is licensed under the terms of the GNU General Public | |
11 | * License version 2. This program is licensed "as is" without any | |
12 | * warranty of any kind, whether express or implied. | |
13 | */ | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/ctype.h> | |
16 | #include <linux/kgdb.h> | |
ada64e4c | 17 | #include <linux/kdb.h> |
f2d937f3 | 18 | #include <linux/tty.h> |
efe2f29e | 19 | #include <linux/console.h> |
f2d937f3 JW |
20 | |
21 | #define MAX_CONFIG_LEN 40 | |
22 | ||
23 | static struct kgdb_io kgdboc_io_ops; | |
24 | ||
25 | /* -1 = init not run yet, 0 = unconfigured, 1 = configured. */ | |
26 | static int configured = -1; | |
27 | ||
28 | static char config[MAX_CONFIG_LEN]; | |
29 | static struct kparam_string kps = { | |
30 | .string = config, | |
31 | .maxlen = MAX_CONFIG_LEN, | |
32 | }; | |
33 | ||
34 | static struct tty_driver *kgdb_tty_driver; | |
35 | static int kgdb_tty_line; | |
36 | ||
ada64e4c JW |
37 | #ifdef CONFIG_KDB_KEYBOARD |
38 | static int kgdboc_register_kbd(char **cptr) | |
39 | { | |
40 | if (strncmp(*cptr, "kbd", 3) == 0) { | |
41 | if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { | |
42 | kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; | |
43 | kdb_poll_idx++; | |
44 | if (cptr[0][3] == ',') | |
45 | *cptr += 4; | |
46 | else | |
47 | return 1; | |
48 | } | |
49 | } | |
50 | return 0; | |
51 | } | |
52 | ||
53 | static void kgdboc_unregister_kbd(void) | |
54 | { | |
55 | int i; | |
56 | ||
57 | for (i = 0; i < kdb_poll_idx; i++) { | |
58 | if (kdb_poll_funcs[i] == kdb_get_kbd_char) { | |
59 | kdb_poll_idx--; | |
60 | kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; | |
61 | kdb_poll_funcs[kdb_poll_idx] = NULL; | |
62 | i--; | |
63 | } | |
64 | } | |
65 | } | |
66 | #else /* ! CONFIG_KDB_KEYBOARD */ | |
67 | #define kgdboc_register_kbd(x) 0 | |
68 | #define kgdboc_unregister_kbd() | |
69 | #endif /* ! CONFIG_KDB_KEYBOARD */ | |
70 | ||
f2d937f3 JW |
71 | static int kgdboc_option_setup(char *opt) |
72 | { | |
73 | if (strlen(opt) > MAX_CONFIG_LEN) { | |
74 | printk(KERN_ERR "kgdboc: config string too long\n"); | |
75 | return -ENOSPC; | |
76 | } | |
77 | strcpy(config, opt); | |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
82 | __setup("kgdboc=", kgdboc_option_setup); | |
83 | ||
ada64e4c JW |
84 | static void cleanup_kgdboc(void) |
85 | { | |
86 | kgdboc_unregister_kbd(); | |
87 | if (configured == 1) | |
88 | kgdb_unregister_io_module(&kgdboc_io_ops); | |
89 | } | |
90 | ||
f2d937f3 JW |
91 | static int configure_kgdboc(void) |
92 | { | |
93 | struct tty_driver *p; | |
94 | int tty_line = 0; | |
95 | int err; | |
ada64e4c | 96 | char *cptr = config; |
efe2f29e | 97 | struct console *cons; |
f2d937f3 JW |
98 | |
99 | err = kgdboc_option_setup(config); | |
100 | if (err || !strlen(config) || isspace(config[0])) | |
101 | goto noconfig; | |
102 | ||
103 | err = -ENODEV; | |
efe2f29e | 104 | kgdboc_io_ops.is_console = 0; |
ada64e4c JW |
105 | kgdb_tty_driver = NULL; |
106 | ||
107 | if (kgdboc_register_kbd(&cptr)) | |
108 | goto do_register; | |
f2d937f3 | 109 | |
ada64e4c | 110 | p = tty_find_polling_driver(cptr, &tty_line); |
f2d937f3 JW |
111 | if (!p) |
112 | goto noconfig; | |
113 | ||
efe2f29e JW |
114 | cons = console_drivers; |
115 | while (cons) { | |
116 | int idx; | |
117 | if (cons->device && cons->device(cons, &idx) == p && | |
118 | idx == tty_line) { | |
119 | kgdboc_io_ops.is_console = 1; | |
120 | break; | |
121 | } | |
122 | cons = cons->next; | |
123 | } | |
124 | ||
f2d937f3 JW |
125 | kgdb_tty_driver = p; |
126 | kgdb_tty_line = tty_line; | |
127 | ||
ada64e4c | 128 | do_register: |
f2d937f3 JW |
129 | err = kgdb_register_io_module(&kgdboc_io_ops); |
130 | if (err) | |
131 | goto noconfig; | |
132 | ||
133 | configured = 1; | |
134 | ||
135 | return 0; | |
136 | ||
137 | noconfig: | |
138 | config[0] = 0; | |
139 | configured = 0; | |
ada64e4c | 140 | cleanup_kgdboc(); |
f2d937f3 JW |
141 | |
142 | return err; | |
143 | } | |
144 | ||
145 | static int __init init_kgdboc(void) | |
146 | { | |
147 | /* Already configured? */ | |
148 | if (configured == 1) | |
149 | return 0; | |
150 | ||
151 | return configure_kgdboc(); | |
152 | } | |
153 | ||
f2d937f3 JW |
154 | static int kgdboc_get_char(void) |
155 | { | |
ada64e4c JW |
156 | if (!kgdb_tty_driver) |
157 | return -1; | |
f34d7a5b AC |
158 | return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, |
159 | kgdb_tty_line); | |
f2d937f3 JW |
160 | } |
161 | ||
162 | static void kgdboc_put_char(u8 chr) | |
163 | { | |
ada64e4c JW |
164 | if (!kgdb_tty_driver) |
165 | return; | |
f34d7a5b AC |
166 | kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, |
167 | kgdb_tty_line, chr); | |
f2d937f3 JW |
168 | } |
169 | ||
170 | static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) | |
171 | { | |
c191e5ad JW |
172 | int len = strlen(kmessage); |
173 | ||
174 | if (len >= MAX_CONFIG_LEN) { | |
f2d937f3 JW |
175 | printk(KERN_ERR "kgdboc: config string too long\n"); |
176 | return -ENOSPC; | |
177 | } | |
178 | ||
179 | /* Only copy in the string if the init function has not run yet */ | |
180 | if (configured < 0) { | |
181 | strcpy(config, kmessage); | |
182 | return 0; | |
183 | } | |
184 | ||
185 | if (kgdb_connected) { | |
186 | printk(KERN_ERR | |
187 | "kgdboc: Cannot reconfigure while KGDB is connected.\n"); | |
188 | ||
189 | return -EBUSY; | |
190 | } | |
191 | ||
192 | strcpy(config, kmessage); | |
c191e5ad JW |
193 | /* Chop out \n char as a result of echo */ |
194 | if (config[len - 1] == '\n') | |
195 | config[len - 1] = '\0'; | |
f2d937f3 JW |
196 | |
197 | if (configured == 1) | |
198 | cleanup_kgdboc(); | |
199 | ||
200 | /* Go and configure with the new params. */ | |
201 | return configure_kgdboc(); | |
202 | } | |
203 | ||
204 | static void kgdboc_pre_exp_handler(void) | |
205 | { | |
206 | /* Increment the module count when the debugger is active */ | |
207 | if (!kgdb_connected) | |
208 | try_module_get(THIS_MODULE); | |
209 | } | |
210 | ||
211 | static void kgdboc_post_exp_handler(void) | |
212 | { | |
213 | /* decrement the module count when the debugger detaches */ | |
214 | if (!kgdb_connected) | |
215 | module_put(THIS_MODULE); | |
216 | } | |
217 | ||
218 | static struct kgdb_io kgdboc_io_ops = { | |
219 | .name = "kgdboc", | |
220 | .read_char = kgdboc_get_char, | |
221 | .write_char = kgdboc_put_char, | |
222 | .pre_exception = kgdboc_pre_exp_handler, | |
223 | .post_exception = kgdboc_post_exp_handler, | |
224 | }; | |
225 | ||
9731191f JW |
226 | #ifdef CONFIG_KGDB_SERIAL_CONSOLE |
227 | /* This is only available if kgdboc is a built in for early debugging */ | |
228 | int __init kgdboc_early_init(char *opt) | |
229 | { | |
230 | /* save the first character of the config string because the | |
231 | * init routine can destroy it. | |
232 | */ | |
233 | char save_ch; | |
234 | ||
235 | kgdboc_option_setup(opt); | |
236 | save_ch = config[0]; | |
237 | init_kgdboc(); | |
238 | config[0] = save_ch; | |
239 | return 0; | |
240 | } | |
241 | ||
242 | early_param("ekgdboc", kgdboc_early_init); | |
243 | #endif /* CONFIG_KGDB_SERIAL_CONSOLE */ | |
244 | ||
f2d937f3 JW |
245 | module_init(init_kgdboc); |
246 | module_exit(cleanup_kgdboc); | |
247 | module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); | |
248 | MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]"); | |
249 | MODULE_DESCRIPTION("KGDB Console TTY Driver"); | |
250 | MODULE_LICENSE("GPL"); |