xref: /openbmc/linux/drivers/media/rc/rc-ir-raw.c (revision 4924a311)
14924a311SDavid Härdeman /* rc-ir-raw.c - handle IR pulse/space events
24924a311SDavid Härdeman  *
34924a311SDavid Härdeman  * Copyright (C) 2010 by Mauro Carvalho Chehab
44924a311SDavid Härdeman  *
54924a311SDavid Härdeman  * This program is free software; you can redistribute it and/or modify
64924a311SDavid Härdeman  *  it under the terms of the GNU General Public License as published by
74924a311SDavid Härdeman  *  the Free Software Foundation version 2 of the License.
84924a311SDavid Härdeman  *
94924a311SDavid Härdeman  *  This program is distributed in the hope that it will be useful,
104924a311SDavid Härdeman  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
114924a311SDavid Härdeman  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
124924a311SDavid Härdeman  *  GNU General Public License for more details.
134924a311SDavid Härdeman  */
144924a311SDavid Härdeman 
154924a311SDavid Härdeman #include <linux/export.h>
164924a311SDavid Härdeman #include <linux/kthread.h>
174924a311SDavid Härdeman #include <linux/mutex.h>
184924a311SDavid Härdeman #include <linux/kmod.h>
194924a311SDavid Härdeman #include <linux/sched.h>
204924a311SDavid Härdeman #include <linux/freezer.h>
214924a311SDavid Härdeman #include "rc-core-priv.h"
224924a311SDavid Härdeman 
234924a311SDavid Härdeman /* Define the max number of pulse/space transitions to buffer */
244924a311SDavid Härdeman #define MAX_IR_EVENT_SIZE      512
254924a311SDavid Härdeman 
264924a311SDavid Härdeman /* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
274924a311SDavid Härdeman static LIST_HEAD(ir_raw_client_list);
284924a311SDavid Härdeman 
294924a311SDavid Härdeman /* Used to handle IR raw handler extensions */
304924a311SDavid Härdeman static DEFINE_MUTEX(ir_raw_handler_lock);
314924a311SDavid Härdeman static LIST_HEAD(ir_raw_handler_list);
324924a311SDavid Härdeman static u64 available_protocols;
334924a311SDavid Härdeman 
344924a311SDavid Härdeman static int ir_raw_event_thread(void *data)
354924a311SDavid Härdeman {
364924a311SDavid Härdeman 	struct ir_raw_event ev;
374924a311SDavid Härdeman 	struct ir_raw_handler *handler;
384924a311SDavid Härdeman 	struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
394924a311SDavid Härdeman 	int retval;
404924a311SDavid Härdeman 
414924a311SDavid Härdeman 	while (!kthread_should_stop()) {
424924a311SDavid Härdeman 
434924a311SDavid Härdeman 		spin_lock_irq(&raw->lock);
444924a311SDavid Härdeman 		retval = kfifo_len(&raw->kfifo);
454924a311SDavid Härdeman 
464924a311SDavid Härdeman 		if (retval < sizeof(ev)) {
474924a311SDavid Härdeman 			set_current_state(TASK_INTERRUPTIBLE);
484924a311SDavid Härdeman 
494924a311SDavid Härdeman 			if (kthread_should_stop())
504924a311SDavid Härdeman 				set_current_state(TASK_RUNNING);
514924a311SDavid Härdeman 
524924a311SDavid Härdeman 			spin_unlock_irq(&raw->lock);
534924a311SDavid Härdeman 			schedule();
544924a311SDavid Härdeman 			continue;
554924a311SDavid Härdeman 		}
564924a311SDavid Härdeman 
574924a311SDavid Härdeman 		retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
584924a311SDavid Härdeman 		spin_unlock_irq(&raw->lock);
594924a311SDavid Härdeman 
604924a311SDavid Härdeman 		mutex_lock(&ir_raw_handler_lock);
614924a311SDavid Härdeman 		list_for_each_entry(handler, &ir_raw_handler_list, list)
624924a311SDavid Härdeman 			handler->decode(raw->dev, ev);
634924a311SDavid Härdeman 		raw->prev_ev = ev;
644924a311SDavid Härdeman 		mutex_unlock(&ir_raw_handler_lock);
654924a311SDavid Härdeman 	}
664924a311SDavid Härdeman 
674924a311SDavid Härdeman 	return 0;
684924a311SDavid Härdeman }
694924a311SDavid Härdeman 
704924a311SDavid Härdeman /**
714924a311SDavid Härdeman  * ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders
724924a311SDavid Härdeman  * @dev:	the struct rc_dev device descriptor
734924a311SDavid Härdeman  * @ev:		the struct ir_raw_event descriptor of the pulse/space
744924a311SDavid Härdeman  *
754924a311SDavid Härdeman  * This routine (which may be called from an interrupt context) stores a
764924a311SDavid Härdeman  * pulse/space duration for the raw ir decoding state machines. Pulses are
774924a311SDavid Härdeman  * signalled as positive values and spaces as negative values. A zero value
784924a311SDavid Härdeman  * will reset the decoding state machines.
794924a311SDavid Härdeman  */
804924a311SDavid Härdeman int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev)
814924a311SDavid Härdeman {
824924a311SDavid Härdeman 	if (!dev->raw)
834924a311SDavid Härdeman 		return -EINVAL;
844924a311SDavid Härdeman 
854924a311SDavid Härdeman 	IR_dprintk(2, "sample: (%05dus %s)\n",
864924a311SDavid Härdeman 		   TO_US(ev->duration), TO_STR(ev->pulse));
874924a311SDavid Härdeman 
884924a311SDavid Härdeman 	if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
894924a311SDavid Härdeman 		return -ENOMEM;
904924a311SDavid Härdeman 
914924a311SDavid Härdeman 	return 0;
924924a311SDavid Härdeman }
934924a311SDavid Härdeman EXPORT_SYMBOL_GPL(ir_raw_event_store);
944924a311SDavid Härdeman 
954924a311SDavid Härdeman /**
964924a311SDavid Härdeman  * ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space
974924a311SDavid Härdeman  * @dev:	the struct rc_dev device descriptor
984924a311SDavid Härdeman  * @type:	the type of the event that has occurred
994924a311SDavid Härdeman  *
1004924a311SDavid Härdeman  * This routine (which may be called from an interrupt context) is used to
1014924a311SDavid Härdeman  * store the beginning of an ir pulse or space (or the start/end of ir
1024924a311SDavid Härdeman  * reception) for the raw ir decoding state machines. This is used by
1034924a311SDavid Härdeman  * hardware which does not provide durations directly but only interrupts
1044924a311SDavid Härdeman  * (or similar events) on state change.
1054924a311SDavid Härdeman  */
1064924a311SDavid Härdeman int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type)
1074924a311SDavid Härdeman {
1084924a311SDavid Härdeman 	ktime_t			now;
1094924a311SDavid Härdeman 	s64			delta; /* ns */
1104924a311SDavid Härdeman 	DEFINE_IR_RAW_EVENT(ev);
1114924a311SDavid Härdeman 	int			rc = 0;
1124924a311SDavid Härdeman 	int			delay;
1134924a311SDavid Härdeman 
1144924a311SDavid Härdeman 	if (!dev->raw)
1154924a311SDavid Härdeman 		return -EINVAL;
1164924a311SDavid Härdeman 
1174924a311SDavid Härdeman 	now = ktime_get();
1184924a311SDavid Härdeman 	delta = ktime_to_ns(ktime_sub(now, dev->raw->last_event));
1194924a311SDavid Härdeman 	delay = MS_TO_NS(dev->input_dev->rep[REP_DELAY]);
1204924a311SDavid Härdeman 
1214924a311SDavid Härdeman 	/* Check for a long duration since last event or if we're
1224924a311SDavid Härdeman 	 * being called for the first time, note that delta can't
1234924a311SDavid Härdeman 	 * possibly be negative.
1244924a311SDavid Härdeman 	 */
1254924a311SDavid Härdeman 	if (delta > delay || !dev->raw->last_type)
1264924a311SDavid Härdeman 		type |= IR_START_EVENT;
1274924a311SDavid Härdeman 	else
1284924a311SDavid Härdeman 		ev.duration = delta;
1294924a311SDavid Härdeman 
1304924a311SDavid Härdeman 	if (type & IR_START_EVENT)
1314924a311SDavid Härdeman 		ir_raw_event_reset(dev);
1324924a311SDavid Härdeman 	else if (dev->raw->last_type & IR_SPACE) {
1334924a311SDavid Härdeman 		ev.pulse = false;
1344924a311SDavid Härdeman 		rc = ir_raw_event_store(dev, &ev);
1354924a311SDavid Härdeman 	} else if (dev->raw->last_type & IR_PULSE) {
1364924a311SDavid Härdeman 		ev.pulse = true;
1374924a311SDavid Härdeman 		rc = ir_raw_event_store(dev, &ev);
1384924a311SDavid Härdeman 	} else
1394924a311SDavid Härdeman 		return 0;
1404924a311SDavid Härdeman 
1414924a311SDavid Härdeman 	dev->raw->last_event = now;
1424924a311SDavid Härdeman 	dev->raw->last_type = type;
1434924a311SDavid Härdeman 	return rc;
1444924a311SDavid Härdeman }
1454924a311SDavid Härdeman EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
1464924a311SDavid Härdeman 
1474924a311SDavid Härdeman /**
1484924a311SDavid Härdeman  * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
1494924a311SDavid Härdeman  * @dev:	the struct rc_dev device descriptor
1504924a311SDavid Härdeman  * @type:	the type of the event that has occurred
1514924a311SDavid Härdeman  *
1524924a311SDavid Härdeman  * This routine (which may be called from an interrupt context) works
1534924a311SDavid Härdeman  * in similar manner to ir_raw_event_store_edge.
1544924a311SDavid Härdeman  * This routine is intended for devices with limited internal buffer
1554924a311SDavid Härdeman  * It automerges samples of same type, and handles timeouts. Returns non-zero
1564924a311SDavid Härdeman  * if the event was added, and zero if the event was ignored due to idle
1574924a311SDavid Härdeman  * processing.
1584924a311SDavid Härdeman  */
1594924a311SDavid Härdeman int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
1604924a311SDavid Härdeman {
1614924a311SDavid Härdeman 	if (!dev->raw)
1624924a311SDavid Härdeman 		return -EINVAL;
1634924a311SDavid Härdeman 
1644924a311SDavid Härdeman 	/* Ignore spaces in idle mode */
1654924a311SDavid Härdeman 	if (dev->idle && !ev->pulse)
1664924a311SDavid Härdeman 		return 0;
1674924a311SDavid Härdeman 	else if (dev->idle)
1684924a311SDavid Härdeman 		ir_raw_event_set_idle(dev, false);
1694924a311SDavid Härdeman 
1704924a311SDavid Härdeman 	if (!dev->raw->this_ev.duration)
1714924a311SDavid Härdeman 		dev->raw->this_ev = *ev;
1724924a311SDavid Härdeman 	else if (ev->pulse == dev->raw->this_ev.pulse)
1734924a311SDavid Härdeman 		dev->raw->this_ev.duration += ev->duration;
1744924a311SDavid Härdeman 	else {
1754924a311SDavid Härdeman 		ir_raw_event_store(dev, &dev->raw->this_ev);
1764924a311SDavid Härdeman 		dev->raw->this_ev = *ev;
1774924a311SDavid Härdeman 	}
1784924a311SDavid Härdeman 
1794924a311SDavid Härdeman 	/* Enter idle mode if nessesary */
1804924a311SDavid Härdeman 	if (!ev->pulse && dev->timeout &&
1814924a311SDavid Härdeman 	    dev->raw->this_ev.duration >= dev->timeout)
1824924a311SDavid Härdeman 		ir_raw_event_set_idle(dev, true);
1834924a311SDavid Härdeman 
1844924a311SDavid Härdeman 	return 1;
1854924a311SDavid Härdeman }
1864924a311SDavid Härdeman EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
1874924a311SDavid Härdeman 
1884924a311SDavid Härdeman /**
1894924a311SDavid Härdeman  * ir_raw_event_set_idle() - provide hint to rc-core when the device is idle or not
1904924a311SDavid Härdeman  * @dev:	the struct rc_dev device descriptor
1914924a311SDavid Härdeman  * @idle:	whether the device is idle or not
1924924a311SDavid Härdeman  */
1934924a311SDavid Härdeman void ir_raw_event_set_idle(struct rc_dev *dev, bool idle)
1944924a311SDavid Härdeman {
1954924a311SDavid Härdeman 	if (!dev->raw)
1964924a311SDavid Härdeman 		return;
1974924a311SDavid Härdeman 
1984924a311SDavid Härdeman 	IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave");
1994924a311SDavid Härdeman 
2004924a311SDavid Härdeman 	if (idle) {
2014924a311SDavid Härdeman 		dev->raw->this_ev.timeout = true;
2024924a311SDavid Härdeman 		ir_raw_event_store(dev, &dev->raw->this_ev);
2034924a311SDavid Härdeman 		init_ir_raw_event(&dev->raw->this_ev);
2044924a311SDavid Härdeman 	}
2054924a311SDavid Härdeman 
2064924a311SDavid Härdeman 	if (dev->s_idle)
2074924a311SDavid Härdeman 		dev->s_idle(dev, idle);
2084924a311SDavid Härdeman 
2094924a311SDavid Härdeman 	dev->idle = idle;
2104924a311SDavid Härdeman }
2114924a311SDavid Härdeman EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
2124924a311SDavid Härdeman 
2134924a311SDavid Härdeman /**
2144924a311SDavid Härdeman  * ir_raw_event_handle() - schedules the decoding of stored ir data
2154924a311SDavid Härdeman  * @dev:	the struct rc_dev device descriptor
2164924a311SDavid Härdeman  *
2174924a311SDavid Härdeman  * This routine will tell rc-core to start decoding stored ir data.
2184924a311SDavid Härdeman  */
2194924a311SDavid Härdeman void ir_raw_event_handle(struct rc_dev *dev)
2204924a311SDavid Härdeman {
2214924a311SDavid Härdeman 	unsigned long flags;
2224924a311SDavid Härdeman 
2234924a311SDavid Härdeman 	if (!dev->raw)
2244924a311SDavid Härdeman 		return;
2254924a311SDavid Härdeman 
2264924a311SDavid Härdeman 	spin_lock_irqsave(&dev->raw->lock, flags);
2274924a311SDavid Härdeman 	wake_up_process(dev->raw->thread);
2284924a311SDavid Härdeman 	spin_unlock_irqrestore(&dev->raw->lock, flags);
2294924a311SDavid Härdeman }
2304924a311SDavid Härdeman EXPORT_SYMBOL_GPL(ir_raw_event_handle);
2314924a311SDavid Härdeman 
2324924a311SDavid Härdeman /* used internally by the sysfs interface */
2334924a311SDavid Härdeman u64
2344924a311SDavid Härdeman ir_raw_get_allowed_protocols(void)
2354924a311SDavid Härdeman {
2364924a311SDavid Härdeman 	u64 protocols;
2374924a311SDavid Härdeman 	mutex_lock(&ir_raw_handler_lock);
2384924a311SDavid Härdeman 	protocols = available_protocols;
2394924a311SDavid Härdeman 	mutex_unlock(&ir_raw_handler_lock);
2404924a311SDavid Härdeman 	return protocols;
2414924a311SDavid Härdeman }
2424924a311SDavid Härdeman 
2434924a311SDavid Härdeman static int change_protocol(struct rc_dev *dev, u64 *rc_type)
2444924a311SDavid Härdeman {
2454924a311SDavid Härdeman 	/* the caller will update dev->enabled_protocols */
2464924a311SDavid Härdeman 	return 0;
2474924a311SDavid Härdeman }
2484924a311SDavid Härdeman 
2494924a311SDavid Härdeman /*
2504924a311SDavid Härdeman  * Used to (un)register raw event clients
2514924a311SDavid Härdeman  */
2524924a311SDavid Härdeman int ir_raw_event_register(struct rc_dev *dev)
2534924a311SDavid Härdeman {
2544924a311SDavid Härdeman 	int rc;
2554924a311SDavid Härdeman 	struct ir_raw_handler *handler;
2564924a311SDavid Härdeman 
2574924a311SDavid Härdeman 	if (!dev)
2584924a311SDavid Härdeman 		return -EINVAL;
2594924a311SDavid Härdeman 
2604924a311SDavid Härdeman 	dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL);
2614924a311SDavid Härdeman 	if (!dev->raw)
2624924a311SDavid Härdeman 		return -ENOMEM;
2634924a311SDavid Härdeman 
2644924a311SDavid Härdeman 	dev->raw->dev = dev;
2654924a311SDavid Härdeman 	dev->enabled_protocols = ~0;
2664924a311SDavid Härdeman 	dev->change_protocol = change_protocol;
2674924a311SDavid Härdeman 	rc = kfifo_alloc(&dev->raw->kfifo,
2684924a311SDavid Härdeman 			 sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
2694924a311SDavid Härdeman 			 GFP_KERNEL);
2704924a311SDavid Härdeman 	if (rc < 0)
2714924a311SDavid Härdeman 		goto out;
2724924a311SDavid Härdeman 
2734924a311SDavid Härdeman 	spin_lock_init(&dev->raw->lock);
2744924a311SDavid Härdeman 	dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw,
2754924a311SDavid Härdeman 				       "rc%ld", dev->devno);
2764924a311SDavid Härdeman 
2774924a311SDavid Härdeman 	if (IS_ERR(dev->raw->thread)) {
2784924a311SDavid Härdeman 		rc = PTR_ERR(dev->raw->thread);
2794924a311SDavid Härdeman 		goto out;
2804924a311SDavid Härdeman 	}
2814924a311SDavid Härdeman 
2824924a311SDavid Härdeman 	mutex_lock(&ir_raw_handler_lock);
2834924a311SDavid Härdeman 	list_add_tail(&dev->raw->list, &ir_raw_client_list);
2844924a311SDavid Härdeman 	list_for_each_entry(handler, &ir_raw_handler_list, list)
2854924a311SDavid Härdeman 		if (handler->raw_register)
2864924a311SDavid Härdeman 			handler->raw_register(dev);
2874924a311SDavid Härdeman 	mutex_unlock(&ir_raw_handler_lock);
2884924a311SDavid Härdeman 
2894924a311SDavid Härdeman 	return 0;
2904924a311SDavid Härdeman 
2914924a311SDavid Härdeman out:
2924924a311SDavid Härdeman 	kfree(dev->raw);
2934924a311SDavid Härdeman 	dev->raw = NULL;
2944924a311SDavid Härdeman 	return rc;
2954924a311SDavid Härdeman }
2964924a311SDavid Härdeman 
2974924a311SDavid Härdeman void ir_raw_event_unregister(struct rc_dev *dev)
2984924a311SDavid Härdeman {
2994924a311SDavid Härdeman 	struct ir_raw_handler *handler;
3004924a311SDavid Härdeman 
3014924a311SDavid Härdeman 	if (!dev || !dev->raw)
3024924a311SDavid Härdeman 		return;
3034924a311SDavid Härdeman 
3044924a311SDavid Härdeman 	kthread_stop(dev->raw->thread);
3054924a311SDavid Härdeman 
3064924a311SDavid Härdeman 	mutex_lock(&ir_raw_handler_lock);
3074924a311SDavid Härdeman 	list_del(&dev->raw->list);
3084924a311SDavid Härdeman 	list_for_each_entry(handler, &ir_raw_handler_list, list)
3094924a311SDavid Härdeman 		if (handler->raw_unregister)
3104924a311SDavid Härdeman 			handler->raw_unregister(dev);
3114924a311SDavid Härdeman 	mutex_unlock(&ir_raw_handler_lock);
3124924a311SDavid Härdeman 
3134924a311SDavid Härdeman 	kfifo_free(&dev->raw->kfifo);
3144924a311SDavid Härdeman 	kfree(dev->raw);
3154924a311SDavid Härdeman 	dev->raw = NULL;
3164924a311SDavid Härdeman }
3174924a311SDavid Härdeman 
3184924a311SDavid Härdeman /*
3194924a311SDavid Härdeman  * Extension interface - used to register the IR decoders
3204924a311SDavid Härdeman  */
3214924a311SDavid Härdeman 
3224924a311SDavid Härdeman int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
3234924a311SDavid Härdeman {
3244924a311SDavid Härdeman 	struct ir_raw_event_ctrl *raw;
3254924a311SDavid Härdeman 
3264924a311SDavid Härdeman 	mutex_lock(&ir_raw_handler_lock);
3274924a311SDavid Härdeman 	list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
3284924a311SDavid Härdeman 	if (ir_raw_handler->raw_register)
3294924a311SDavid Härdeman 		list_for_each_entry(raw, &ir_raw_client_list, list)
3304924a311SDavid Härdeman 			ir_raw_handler->raw_register(raw->dev);
3314924a311SDavid Härdeman 	available_protocols |= ir_raw_handler->protocols;
3324924a311SDavid Härdeman 	mutex_unlock(&ir_raw_handler_lock);
3334924a311SDavid Härdeman 
3344924a311SDavid Härdeman 	return 0;
3354924a311SDavid Härdeman }
3364924a311SDavid Härdeman EXPORT_SYMBOL(ir_raw_handler_register);
3374924a311SDavid Härdeman 
3384924a311SDavid Härdeman void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
3394924a311SDavid Härdeman {
3404924a311SDavid Härdeman 	struct ir_raw_event_ctrl *raw;
3414924a311SDavid Härdeman 
3424924a311SDavid Härdeman 	mutex_lock(&ir_raw_handler_lock);
3434924a311SDavid Härdeman 	list_del(&ir_raw_handler->list);
3444924a311SDavid Härdeman 	if (ir_raw_handler->raw_unregister)
3454924a311SDavid Härdeman 		list_for_each_entry(raw, &ir_raw_client_list, list)
3464924a311SDavid Härdeman 			ir_raw_handler->raw_unregister(raw->dev);
3474924a311SDavid Härdeman 	available_protocols &= ~ir_raw_handler->protocols;
3484924a311SDavid Härdeman 	mutex_unlock(&ir_raw_handler_lock);
3494924a311SDavid Härdeman }
3504924a311SDavid Härdeman EXPORT_SYMBOL(ir_raw_handler_unregister);
3514924a311SDavid Härdeman 
3524924a311SDavid Härdeman void ir_raw_init(void)
3534924a311SDavid Härdeman {
3544924a311SDavid Härdeman 	/* Load the decoder modules */
3554924a311SDavid Härdeman 
3564924a311SDavid Härdeman 	load_nec_decode();
3574924a311SDavid Härdeman 	load_rc5_decode();
3584924a311SDavid Härdeman 	load_rc6_decode();
3594924a311SDavid Härdeman 	load_jvc_decode();
3604924a311SDavid Härdeman 	load_sony_decode();
3614924a311SDavid Härdeman 	load_sanyo_decode();
3624924a311SDavid Härdeman 	load_sharp_decode();
3634924a311SDavid Härdeman 	load_mce_kbd_decode();
3644924a311SDavid Härdeman 	load_lirc_codec();
3654924a311SDavid Härdeman 
3664924a311SDavid Härdeman 	/* If needed, we may later add some init code. In this case,
3674924a311SDavid Härdeman 	   it is needed to change the CONFIG_MODULE test at rc-core.h
3684924a311SDavid Härdeman 	 */
3694924a311SDavid Härdeman }
370