Merge remote-tracking branch 'remotes/elmarco/tags/chardev-pull-request' into staging

Chardev fixes

# gpg: Signature made Wed 13 Feb 2019 16:18:36 GMT
# gpg:                using RSA key DAE8E10975969CE5
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276  F62D DAE8 E109 7596 9CE5

* remotes/elmarco/tags/chardev-pull-request: (25 commits)
  char-pty: remove write_lock usage
  char-pty: remove the check for connection on write
  chardev: add a note about frontend sources and context switch
  terminal3270: do not use backend timer sources
  char: update the mux handlers in class callback
  chardev/wctablet: Fix a typo
  char: allow specifying a GMainContext at opening time
  chardev: ensure termios is fully initialized
  tests: expand coverage of socket chardev test
  chardev: fix race with client connections in tcp_chr_wait_connected
  chardev: disallow TLS/telnet/websocket with tcp_chr_wait_connected
  chardev: honour the reconnect setting in tcp_chr_wait_connected
  chardev: use a state machine for socket connection state
  chardev: split up qmp_chardev_open_socket connection code
  chardev: split tcp_chr_wait_connected into two methods
  chardev: remove unused 'sioc' variable & cleanup paths
  chardev: ensure qemu_chr_parse_compat reports missing driver error
  chardev: remove many local variables in qemu_chr_parse_socket
  chardev: forbid 'wait' option with client sockets
  chardev: forbid 'reconnect' option with server sockets
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2019-02-14 14:34:32 +00:00
32 changed files with 1150 additions and 481 deletions
+23 -12
View File
@@ -246,14 +246,15 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del)
}
}
void qemu_chr_fe_set_handlers(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
BackendChangeHandler *be_change,
void *opaque,
GMainContext *context,
bool set_open)
void qemu_chr_fe_set_handlers_full(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
BackendChangeHandler *be_change,
void *opaque,
GMainContext *context,
bool set_open,
bool sync_state)
{
Chardev *s;
int fe_open;
@@ -285,14 +286,24 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
qemu_chr_fe_take_focus(b);
/* We're connecting to an already opened device, so let's make sure we
also get the open event */
if (s->be_open) {
if (sync_state && s->be_open) {
qemu_chr_be_event(s, CHR_EVENT_OPENED);
}
}
}
if (CHARDEV_IS_MUX(s)) {
mux_chr_set_handlers(s, context);
}
void qemu_chr_fe_set_handlers(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
BackendChangeHandler *be_change,
void *opaque,
GMainContext *context,
bool set_open)
{
qemu_chr_fe_set_handlers_full(b, fd_can_read, fd_read, fd_event, be_change,
opaque, context, set_open,
true);
}
void qemu_chr_fe_take_focus(CharBackend *b)
+10 -9
View File
@@ -278,18 +278,18 @@ static void char_mux_finalize(Object *obj)
qemu_chr_fe_deinit(&d->chr, false);
}
void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
static void mux_chr_update_read_handlers(Chardev *chr)
{
MuxChardev *d = MUX_CHARDEV(chr);
/* Fix up the real driver with mux routines */
qemu_chr_fe_set_handlers(&d->chr,
mux_chr_can_read,
mux_chr_read,
mux_chr_event,
NULL,
chr,
context, true);
qemu_chr_fe_set_handlers_full(&d->chr,
mux_chr_can_read,
mux_chr_read,
mux_chr_event,
NULL,
chr,
chr->gcontext, true, false);
}
void mux_set_focus(Chardev *chr, int focus)
@@ -367,7 +367,7 @@ static int open_muxes(Chardev *chr)
* mark mux as OPENED so any new FEs will immediately receive
* OPENED event
*/
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
chr->be_open = 1;
return 0;
}
@@ -383,6 +383,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
cc->chr_add_watch = mux_chr_add_watch;
cc->chr_be_event = mux_chr_be_event;
cc->chr_machine_done = open_muxes;
cc->chr_update_read_handler = mux_chr_update_read_handlers;
}
static const TypeInfo char_mux_type_info = {
+4 -52
View File
@@ -36,15 +36,12 @@ typedef struct {
QIOChannel *ioc;
int read_bytes;
/* Protected by the Chardev chr_write_lock. */
int connected;
GSource *timer_src;
GSource *open_source;
} PtyChardev;
#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
static void pty_chr_update_read_handler_locked(Chardev *chr);
static void pty_chr_state(Chardev *chr, int connected);
static void pty_chr_timer_cancel(PtyChardev *s)
@@ -56,32 +53,19 @@ static void pty_chr_timer_cancel(PtyChardev *s)
}
}
static void pty_chr_open_src_cancel(PtyChardev *s)
{
if (s->open_source) {
g_source_destroy(s->open_source);
g_source_unref(s->open_source);
s->open_source = NULL;
}
}
static gboolean pty_chr_timer(gpointer opaque)
{
struct Chardev *chr = CHARDEV(opaque);
PtyChardev *s = PTY_CHARDEV(opaque);
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_timer_cancel(s);
pty_chr_open_src_cancel(s);
if (!s->connected) {
/* Next poll ... */
pty_chr_update_read_handler_locked(chr);
qemu_chr_be_update_read_handlers(chr, chr->gcontext);
}
qemu_mutex_unlock(&chr->chr_write_lock);
return FALSE;
}
/* Called with chr_write_lock held. */
static void pty_chr_rearm_timer(Chardev *chr, int ms)
{
PtyChardev *s = PTY_CHARDEV(chr);
@@ -94,8 +78,7 @@ static void pty_chr_rearm_timer(Chardev *chr, int ms)
g_free(name);
}
/* Called with chr_write_lock held. */
static void pty_chr_update_read_handler_locked(Chardev *chr)
static void pty_chr_update_read_handler(Chardev *chr)
{
PtyChardev *s = PTY_CHARDEV(chr);
GPollFD pfd;
@@ -117,24 +100,12 @@ static void pty_chr_update_read_handler_locked(Chardev *chr)
}
}
static void pty_chr_update_read_handler(Chardev *chr)
{
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_update_read_handler_locked(chr);
qemu_mutex_unlock(&chr->chr_write_lock);
}
/* Called with chr_write_lock held. */
static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
PtyChardev *s = PTY_CHARDEV(chr);
if (!s->connected) {
/* guest sends data, check for (re-)connect */
pty_chr_update_read_handler_locked(chr);
if (!s->connected) {
return len;
}
return len;
}
return io_channel_send(s->ioc, buf, len);
}
@@ -183,23 +154,11 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
return TRUE;
}
static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
{
Chardev *chr = CHARDEV(opaque);
PtyChardev *s = PTY_CHARDEV(opaque);
s->open_source = NULL;
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
return FALSE;
}
/* Called with chr_write_lock held. */
static void pty_chr_state(Chardev *chr, int connected)
{
PtyChardev *s = PTY_CHARDEV(chr);
if (!connected) {
pty_chr_open_src_cancel(s);
remove_fd_in_watch(chr);
s->connected = 0;
/* (re-)connect poll interval for idle guests: once per second.
@@ -209,13 +168,8 @@ static void pty_chr_state(Chardev *chr, int connected)
} else {
pty_chr_timer_cancel(s);
if (!s->connected) {
g_assert(s->open_source == NULL);
s->open_source = g_idle_source_new();
s->connected = 1;
g_source_set_callback(s->open_source,
qemu_chr_be_generic_open_func,
chr, NULL);
g_source_attach(s->open_source, chr->gcontext);
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
if (!chr->gsource) {
chr->gsource = io_add_watch_poll(chr, s->ioc,
@@ -231,11 +185,9 @@ static void char_pty_finalize(Object *obj)
Chardev *chr = CHARDEV(obj);
PtyChardev *s = PTY_CHARDEV(obj);
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_state(chr, 0);
object_unref(OBJECT(s->ioc));
pty_chr_timer_cancel(s);
qemu_mutex_unlock(&chr->chr_write_lock);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
+1 -1
View File
@@ -57,7 +57,7 @@ static void qmp_chardev_open_serial(Chardev *chr,
static void tty_serial_init(int fd, int speed,
int parity, int data_bits, int stop_bits)
{
struct termios tty;
struct termios tty = {0};
speed_t spd;
#if 0
+355 -135
View File
File diff suppressed because it is too large Load Diff
+21 -13
View File
@@ -490,6 +490,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
return opts;
}
error_report("'%s' is not a valid char driver", filename);
fail:
qemu_opts_del(opts);
return NULL;
@@ -634,7 +636,8 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp)
return backend;
}
Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
Error **errp)
{
const ChardevClass *cc;
Chardev *chr = NULL;
@@ -674,7 +677,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
backend, context, errp);
if (chr == NULL) {
goto out;
@@ -687,7 +690,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
backend->type = CHARDEV_BACKEND_KIND_MUX;
backend->u.mux.data = g_new0(ChardevMux, 1);
backend->u.mux.data->chardev = g_strdup(bid);
mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp);
if (mux == NULL) {
object_unparent(OBJECT(chr));
chr = NULL;
@@ -703,7 +706,7 @@ out:
}
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
bool permit_mux_mon)
bool permit_mux_mon, GMainContext *context)
{
const char *p;
Chardev *chr;
@@ -718,7 +721,7 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
if (!opts)
return NULL;
chr = qemu_chr_new_from_opts(opts, &err);
chr = qemu_chr_new_from_opts(opts, context, &err);
if (!chr) {
error_report_err(err);
goto out;
@@ -736,10 +739,11 @@ out:
static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
const char *filename,
bool permit_mux_mon)
bool permit_mux_mon,
GMainContext *context)
{
Chardev *chr;
chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon);
chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context);
if (chr) {
if (replay_mode != REPLAY_MODE_NONE) {
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
@@ -753,14 +757,16 @@ static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
return chr;
}
Chardev *qemu_chr_new(const char *label, const char *filename)
Chardev *qemu_chr_new(const char *label, const char *filename,
GMainContext *context)
{
return qemu_chr_new_permit_mux_mon(label, filename, false);
return qemu_chr_new_permit_mux_mon(label, filename, false, context);
}
Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename)
Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename,
GMainContext *context)
{
return qemu_chr_new_permit_mux_mon(label, filename, true);
return qemu_chr_new_permit_mux_mon(label, filename, true, context);
}
static int qmp_query_chardev_foreach(Object *obj, void *data)
@@ -935,6 +941,7 @@ void qemu_chr_set_feature(Chardev *chr,
Chardev *qemu_chardev_new(const char *id, const char *typename,
ChardevBackend *backend,
GMainContext *gcontext,
Error **errp)
{
Object *obj;
@@ -947,6 +954,7 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
obj = object_new(typename);
chr = CHARDEV(obj);
chr->label = g_strdup(id);
chr->gcontext = gcontext;
qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) {
@@ -991,7 +999,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
}
chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
backend, NULL, errp);
if (!chr) {
return NULL;
}
@@ -1049,7 +1057,7 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
}
chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
backend, chr->gcontext, errp);
if (!chr_new) {
return NULL;
}
+1 -1
View File
@@ -177,7 +177,7 @@ static void wctablet_input_sync(DeviceState *dev)
}
static QemuInputHandler wctablet_handler = {
.name = "QEMU Wacome Pen Tablet",
.name = "QEMU Wacom Pen Tablet",
.mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
.event = wctablet_input_event,
.sync = wctablet_input_sync,
+2 -2
View File
@@ -2531,7 +2531,7 @@ int gdbserver_start(const char *device)
* FIXME: it's a bit weird to allow using a mux chardev here
* and implicitly setup a monitor. We may want to break this.
*/
chr = qemu_chr_new_noreplay("gdb", device, true);
chr = qemu_chr_new_noreplay("gdb", device, true, NULL);
if (!chr)
return -1;
}
@@ -2545,7 +2545,7 @@ int gdbserver_start(const char *device)
/* Initialize a monitor terminal for gdb */
mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
NULL, &error_abort);
NULL, NULL, &error_abort);
monitor_init(mon_chr, 0);
} else {
qemu_chr_fe_deinit(&s->chr, true);
+1 -1
View File
@@ -2395,7 +2395,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict)
if (opts == NULL) {
error_setg(&err, "Parsing chardev args failed");
} else {
qemu_chr_new_from_opts(opts, &err);
qemu_chr_new_from_opts(opts, NULL, &err);
qemu_opts_del(opts);
}
hmp_handle_error(mon, &err);
+1 -1
View File
@@ -799,7 +799,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
s->irq = irq;
omap_sti_reset(s);
qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null"),
qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null", NULL),
&error_abort);
memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti",
+1 -1
View File
@@ -501,7 +501,7 @@ static const TypeInfo char_hci_type_info = {
Chardev *uart_hci_init(void)
{
return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI,
NULL, &error_abort);
NULL, NULL, &error_abort);
}
static void register_types(void)
+2 -2
View File
@@ -63,7 +63,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base,
s->irq = irq;
s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
omap_clk_getrate(fclk)/16,
chr ?: qemu_chr_new(label, "null"),
chr ?: qemu_chr_new(label, "null", NULL),
DEVICE_NATIVE_ENDIAN);
return s;
}
@@ -183,6 +183,6 @@ void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
/* TODO: Should reuse or destroy current s->serial */
s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
omap_clk_getrate(s->fclk) / 16,
chr ?: qemu_chr_new("null", "null"),
chr ?: qemu_chr_new("null", "null", NULL),
DEVICE_NATIVE_ENDIAN);
}
+6 -9
View File
@@ -31,7 +31,7 @@ typedef struct Terminal3270 {
uint8_t outv[OUTPUT_BUFFER_SIZE];
int in_len;
bool handshake_done;
GSource *timer_src;
guint timer_tag;
} Terminal3270;
#define TYPE_TERMINAL_3270 "x-terminal3270"
@@ -47,10 +47,9 @@ static int terminal_can_read(void *opaque)
static void terminal_timer_cancel(Terminal3270 *t)
{
if (t->timer_src) {
g_source_destroy(t->timer_src);
g_source_unref(t->timer_src);
t->timer_src = NULL;
if (t->timer_tag) {
g_source_remove(t->timer_tag);
t->timer_tag = 0;
}
}
@@ -100,8 +99,7 @@ static void terminal_read(void *opaque, const uint8_t *buf, int size)
assert(size <= (INPUT_BUFFER_SIZE - t->in_len));
terminal_timer_cancel(t);
t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000,
send_timing_mark_cb, t);
t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
memcpy(&t->inv[t->in_len], buf, size);
t->in_len += size;
if (t->in_len < 2) {
@@ -160,8 +158,7 @@ static void chr_event(void *opaque, int event)
* char-socket.c. Once qemu receives the terminal-type of the
* client, mark handshake done and trigger everything rolling again.
*/
t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000,
send_timing_mark_cb, t);
t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
break;
case CHR_EVENT_CLOSED:
sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END;
+2 -1
View File
@@ -211,7 +211,8 @@ static int con_init(struct XenLegacyDevice *xendev)
* FIXME: sure we want to support implicit
* muxed monitors here?
*/
qemu_chr_new_mux_mon(label, output), &error_abort);
qemu_chr_new_mux_mon(label, output, NULL),
&error_abort);
}
xenstore_store_pv_console_info(con->xendev.dev,
+2 -2
View File
@@ -44,7 +44,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp)
chr = parallel_hds[i];
if (chr == NULL) {
name = g_strdup_printf("discarding-parallel%d", i);
chr = qemu_chr_new(name, "null");
chr = qemu_chr_new(name, "null", NULL);
} else {
name = g_strdup_printf("parallel%d", i);
}
@@ -84,7 +84,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp)
chr = serial_hd(i);
if (chr == NULL) {
name = g_strdup_printf("discarding-serial%d", i);
chr = qemu_chr_new(name, "null");
chr = qemu_chr_new(name, "null", NULL);
} else {
name = g_strdup_printf("serial%d", i);
}
+1 -1
View File
@@ -512,7 +512,7 @@ static void boston_mach_init(MachineState *machine)
memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0);
chr = qemu_chr_new("lcd", "vc:320x240");
chr = qemu_chr_new("lcd", "vc:320x240", NULL);
qemu_chr_fe_init(&s->lcd_display, chr, NULL);
qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL,
boston_lcd_event, NULL, s, NULL, true);
+1 -1
View File
@@ -568,7 +568,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
memory_region_add_subregion(address_space, base, &s->iomem_lo);
memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
chr = qemu_chr_new("fpga", "vc:320x200");
chr = qemu_chr_new("fpga", "vc:320x200", NULL);
qemu_chr_fe_init(&s->display, chr, NULL);
qemu_chr_fe_set_handlers(&s->display, NULL, NULL,
malta_fgpa_display_event, NULL, s, NULL, true);
+1 -1
View File
@@ -514,7 +514,7 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
USBDevice *dev;
Chardev *cdrv;
cdrv = qemu_chr_new("braille", "braille");
cdrv = qemu_chr_new("braille", "braille", NULL);
if (!cdrv)
return NULL;
+20 -1
View File
@@ -67,7 +67,7 @@ bool qemu_chr_fe_backend_connected(CharBackend *be);
bool qemu_chr_fe_backend_open(CharBackend *be);
/**
* qemu_chr_fe_set_handlers:
* qemu_chr_fe_set_handlers_full:
* @b: a CharBackend
* @fd_can_read: callback to get the amount of data the frontend may
* receive
@@ -79,12 +79,28 @@ bool qemu_chr_fe_backend_open(CharBackend *be);
* @context: a main loop context or NULL for the default
* @set_open: whether to call qemu_chr_fe_set_open() implicitely when
* any of the handler is non-NULL
* @sync_state: whether to issue event callback with updated state
*
* Set the front end char handlers. The front end takes the focus if
* any of the handler is non-NULL.
*
* Without associated Chardev, nothing is changed.
*/
void qemu_chr_fe_set_handlers_full(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
BackendChangeHandler *be_change,
void *opaque,
GMainContext *context,
bool set_open,
bool sync_state);
/**
* qemu_chr_fe_set_handlers:
*
* Version of qemu_chr_fe_set_handlers_full() with sync_state = true.
*/
void qemu_chr_fe_set_handlers(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
@@ -168,6 +184,9 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
* is active; return the #GSource's tag. If it is disconnected,
* or without associated Chardev, return 0.
*
* Note that you are responsible to update the front-end sources if
* you are switching the main context with qemu_chr_fe_set_handlers().
*
* Returns: the source tag
*/
guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
-1
View File
@@ -55,7 +55,6 @@ typedef struct MuxChardev {
#define CHARDEV_IS_MUX(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
void mux_set_focus(Chardev *chr, int focus);
void mux_chr_send_all_event(Chardev *chr, int event);

Some files were not shown because too many files have changed in this diff Show More