Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ obj-m += soft_uart.o

soft_uart-objs := module.o raspberry_soft_uart.o queue.o

ccflags-y := -Wno-incompatible-pointer-types

RELEASE = $(shell uname -r)
LINUX = /usr/src/linux-headers-$(RELEASE)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This module creates a software-based serial port using a configurable pair of GP

Fetch the source:
```
git clone https://github.com/adrianomarto/soft_uart
git clone --depth 1 https://github.com/sfera-labs/soft_uart
```

Install the package `raspberrypi-kernel-headers`:
Expand Down
15 changes: 12 additions & 3 deletions module.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/version.h>
#include <linux/gpio.h>

#define SOFT_UART_MAJOR 0
#define N_PORTS 1
Expand All @@ -30,7 +31,7 @@ static int soft_uart_write(struct tty_struct*, const unsigned char*, int);
static unsigned int soft_uart_write_room(struct tty_struct*);
static void soft_uart_flush_buffer(struct tty_struct*);
static unsigned int soft_uart_chars_in_buffer(struct tty_struct*);
static void soft_uart_set_termios(struct tty_struct*, struct ktermios*);
static void soft_uart_set_termios(struct tty_struct*, const struct ktermios*);
static void soft_uart_stop(struct tty_struct*);
static void soft_uart_start(struct tty_struct*);
static void soft_uart_hangup(struct tty_struct*);
Expand Down Expand Up @@ -71,9 +72,17 @@ static struct tty_port port;
*/
static int __init soft_uart_init(void)
{
bool success = true;

printk(KERN_INFO "soft_uart: Initializing module...\n");

success &= gpio_request(gpio_tx, "soft_uart_tx") == 0;
success &= gpio_direction_output(gpio_tx, 1) == 0;

success &= gpio_request(gpio_rx, "soft_uart_rx") == 0;
success &= gpio_direction_input(gpio_rx) == 0;

if (!raspberry_soft_uart_init(gpio_tx, gpio_rx))
if (!success || !raspberry_soft_uart_init(gpio_to_desc(gpio_tx), gpio_to_desc(gpio_rx)))
{
printk(KERN_ALERT "soft_uart: Failed initialize GPIO.\n");
return -ENOMEM;
Expand Down Expand Up @@ -256,7 +265,7 @@ static unsigned int soft_uart_chars_in_buffer(struct tty_struct* tty)
* @param tty given TTY
* @param termios parameters
*/
static void soft_uart_set_termios(struct tty_struct* tty, struct ktermios* termios)
static void soft_uart_set_termios(struct tty_struct* tty, const struct ktermios* termios)
{
int cflag = 0;
speed_t baudrate = tty_get_baud_rate(tty);
Expand Down
100 changes: 53 additions & 47 deletions raspberry_soft_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
#include "raspberry_soft_uart.h"
#include "queue.h"

#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/ktime.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/version.h>

static irq_handler_t handle_rx_start(unsigned int irq, void* device, struct pt_regs* registers);
static irqreturn_t handle_rx_start(int irq, void *device);
static enum hrtimer_restart handle_tx(struct hrtimer* timer);
static enum hrtimer_restart handle_rx(struct hrtimer* timer);
static void receive_character(unsigned char character);
Expand All @@ -21,9 +20,11 @@ static DEFINE_MUTEX(current_tty_mutex);
static struct hrtimer timer_tx;
static struct hrtimer timer_rx;
static ktime_t period;
static int gpio_tx = 0;
static int gpio_rx = 0;
static ktime_t half_period;
static struct gpio_desc *gpio_tx;
static struct gpio_desc *gpio_rx;
static int rx_bit_index = -1;
static void (*rx_callback)(unsigned char) = NULL;

/**
* Initializes the Raspberry Soft UART infrastructure.
Expand All @@ -34,7 +35,7 @@ static int rx_bit_index = -1;
* @param gpio_rx GPIO pin used as RX
* @return 1 if the initialization is successful. 0 otherwise.
*/
int raspberry_soft_uart_init(const int _gpio_tx, const int _gpio_rx)
int raspberry_soft_uart_init(struct gpio_desc *_gpio_tx, struct gpio_desc *_gpio_rx)
{
bool success = true;

Expand All @@ -51,21 +52,15 @@ int raspberry_soft_uart_init(const int _gpio_tx, const int _gpio_rx)
// Initializes the GPIO pins.
gpio_tx = _gpio_tx;
gpio_rx = _gpio_rx;

success &= gpio_request(gpio_tx, "soft_uart_tx") == 0;
success &= gpio_direction_output(gpio_tx, 1) == 0;

success &= gpio_request(gpio_rx, "soft_uart_rx") == 0;
success &= gpio_direction_input(gpio_rx) == 0;

// Initializes the interruption.
success &= request_irq(
gpio_to_irq(gpio_rx),
(irq_handler_t) handle_rx_start,
gpiod_to_irq(gpio_rx),
handle_rx_start,
IRQF_TRIGGER_FALLING,
"soft_uart_irq_handler",
NULL) == 0;
disable_irq(gpio_to_irq(gpio_rx));
disable_irq(gpiod_to_irq(gpio_rx));

return success;
}
Expand All @@ -75,10 +70,10 @@ int raspberry_soft_uart_init(const int _gpio_tx, const int _gpio_rx)
*/
int raspberry_soft_uart_finalize(void)
{
free_irq(gpio_to_irq(gpio_rx), NULL);
gpio_set_value(gpio_tx, 0);
gpio_free(gpio_tx);
gpio_free(gpio_rx);
free_irq(gpiod_to_irq(gpio_rx), NULL);
gpiod_set_value(gpio_tx, 0);
gpiod_put(gpio_tx);
gpiod_put(gpio_rx);
return 1;
}

Expand All @@ -91,12 +86,13 @@ int raspberry_soft_uart_open(struct tty_struct* tty)
{
int success = 0;
mutex_lock(&current_tty_mutex);
rx_bit_index = -1;
if (current_tty == NULL)
{
current_tty = tty;
initialize_queue(&queue_tx);
success = 1;
enable_irq(gpio_to_irq(gpio_rx));
enable_irq(gpiod_to_irq(gpio_rx));
}
mutex_unlock(&current_tty_mutex);
return success;
Expand All @@ -107,18 +103,13 @@ int raspberry_soft_uart_open(struct tty_struct* tty)
*/
int raspberry_soft_uart_close(void)
{
int success = 0;
mutex_lock(&current_tty_mutex);
if (current_tty != NULL)
{
disable_irq(gpio_to_irq(gpio_rx));
hrtimer_cancel(&timer_tx);
hrtimer_cancel(&timer_rx);
current_tty = NULL;
success = 1;
}
disable_irq(gpiod_to_irq(gpio_rx));
hrtimer_cancel(&timer_tx);
hrtimer_cancel(&timer_rx);
current_tty = NULL;
mutex_unlock(&current_tty_mutex);
return success;
return 1;
}

/**
Expand All @@ -129,7 +120,8 @@ int raspberry_soft_uart_close(void)
int raspberry_soft_uart_set_baudrate(const int baudrate)
{
period = ktime_set(0, 1000000000/baudrate);
gpio_set_debounce(gpio_rx, 1000/baudrate/2);
half_period = ktime_set(0, 1000000000/baudrate/2);
gpiod_set_debounce(gpio_rx, 1000/baudrate/2);
return 1;
}

Expand Down Expand Up @@ -170,6 +162,16 @@ int raspberry_soft_uart_get_tx_queue_size(void)
return get_queue_size(&queue_tx);
}

/**
* Sets the callback function to be called on received character.
* @param callback the callback function
*/
int raspberry_soft_uart_set_rx_callback(void (*callback)(unsigned char))
{
rx_callback = callback;
return 1;
}

//-----------------------------------------------------------------------------
// Internals
//-----------------------------------------------------------------------------
Expand All @@ -178,13 +180,13 @@ int raspberry_soft_uart_get_tx_queue_size(void)
* If we are waiting for the RX start bit, then starts the RX timer. Otherwise,
* does nothing.
*/
static irq_handler_t handle_rx_start(unsigned int irq, void* device, struct pt_regs* registers)
static irqreturn_t handle_rx_start(int irq, void *device)
{
if (rx_bit_index == -1)
{
hrtimer_start(&timer_rx, ktime_set(0, period / 2), HRTIMER_MODE_REL);
hrtimer_start(&timer_rx, half_period, HRTIMER_MODE_REL);
}
return (irq_handler_t) IRQ_HANDLED;
return IRQ_HANDLED;
}


Expand All @@ -204,7 +206,7 @@ static enum hrtimer_restart handle_tx(struct hrtimer* timer)
{
if (dequeue_character(&queue_tx, &character))
{
gpio_set_value(gpio_tx, 0);
gpiod_set_value(gpio_tx, 0);
bit_index++;
must_restart_timer = true;
}
Expand All @@ -213,15 +215,15 @@ static enum hrtimer_restart handle_tx(struct hrtimer* timer)
// Data bits.
else if (0 <= bit_index && bit_index < 8)
{
gpio_set_value(gpio_tx, 1 & (character >> bit_index));
gpiod_set_value(gpio_tx, 1 & (character >> bit_index));
bit_index++;
must_restart_timer = true;
}

// Stop bit.
else if (bit_index == 8)
{
gpio_set_value(gpio_tx, 1);
gpiod_set_value(gpio_tx, 1);
character = 0;
bit_index = -1;
must_restart_timer = get_queue_size(&queue_tx) > 0;
Expand All @@ -244,7 +246,7 @@ static enum hrtimer_restart handle_rx(struct hrtimer* timer)
{
ktime_t current_time = ktime_get();
static unsigned int character = 0;
int bit_value = gpio_get_value(gpio_rx);
int bit_value = gpiod_get_value(gpio_rx);
enum hrtimer_restart result = HRTIMER_NORESTART;
bool must_restart_timer = false;

Expand Down Expand Up @@ -298,18 +300,22 @@ static enum hrtimer_restart handle_rx(struct hrtimer* timer)
void receive_character(unsigned char character)
{
mutex_lock(&current_tty_mutex);
if (rx_callback != NULL) {
(*rx_callback)(character);
} else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
if (current_tty != NULL && current_tty->port != NULL)
{
tty_insert_flip_char(current_tty->port, character, TTY_NORMAL);
tty_flip_buffer_push(current_tty->port);
}
if (current_tty != NULL && current_tty->port != NULL)
{
tty_insert_flip_char(current_tty->port, character, TTY_NORMAL);
tty_flip_buffer_push(current_tty->port);
}
#else
if (tty != NULL)
{
tty_insert_flip_char(current_tty, character, TTY_NORMAL);
tty_flip_buffer_push(tty);
}
if (tty != NULL)
{
tty_insert_flip_char(current_tty, character, TTY_NORMAL);
tty_flip_buffer_push(tty);
}
#endif
}
mutex_unlock(&current_tty_mutex);
}
4 changes: 3 additions & 1 deletion raspberry_soft_uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
#define RASPBERRY_SOFT_UART_H

#include <linux/tty.h>
#include <linux/gpio/consumer.h>

int raspberry_soft_uart_init(const int gpio_tx, const int gpio_rx);
int raspberry_soft_uart_init(struct gpio_desc *_gpio_tx, struct gpio_desc *_gpio_rx);
int raspberry_soft_uart_finalize(void);
int raspberry_soft_uart_open(struct tty_struct* tty);
int raspberry_soft_uart_close(void);
int raspberry_soft_uart_set_baudrate(const int baudrate);
int raspberry_soft_uart_send_string(const unsigned char* string, int string_size);
int raspberry_soft_uart_get_tx_queue_room(void);
int raspberry_soft_uart_get_tx_queue_size(void);
int raspberry_soft_uart_set_rx_callback(void (*callback)(unsigned char));

#endif