Files
BBeOS/telephony/sms/q20-sms.c
Eliott 7b53cde2ae
Some checks failed
CI / markdown-lint (push) Failing after 14s
Complete BBeOS project implementation with BlackBerry-inspired website
- Updated .gitignore with comprehensive exclusions for build artifacts, IDE files, and OS-specific files
- Created BlackBerry-inspired website with Heroicons and Gitea integration
- Added complete project structure with all 7 phases implemented
- Included kernel drivers, UI components, telephony stack, and packaging tools
- Added emulation scripts for testing and development
- Comprehensive documentation for all development phases
- Security analysis and hardware testing guides
- SDK and application framework for third-party development
2025-08-01 10:20:28 +02:00

555 lines
14 KiB
C

/*
* Q20 SMS Management
* BlackBerry Classic Q20 SMS System
*
* This module provides SMS functionality including
* message sending, receiving, storage, and notifications.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/notifier.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#define Q20_SMS_DRIVER_NAME "q20-sms"
#define Q20_SMS_MAX_MESSAGES 1000
#define Q20_SMS_MAX_LENGTH 160
#define Q20_SMS_PHONE_LENGTH 32
/* SMS types */
enum q20_sms_type {
Q20_SMS_TYPE_INCOMING,
Q20_SMS_TYPE_OUTGOING,
Q20_SMS_TYPE_DRAFT,
Q20_SMS_TYPE_SENT,
Q20_SMS_TYPE_FAILED
};
/* SMS status */
enum q20_sms_status {
Q20_SMS_STATUS_UNREAD,
Q20_SMS_STATUS_READ,
Q20_SMS_STATUS_SENDING,
Q20_SMS_STATUS_SENT,
Q20_SMS_STATUS_FAILED,
Q20_SMS_STATUS_DELIVERED
};
/* SMS message structure */
struct q20_sms_message {
u32 id;
enum q20_sms_type type;
enum q20_sms_status status;
char phone_number[Q20_SMS_PHONE_LENGTH];
char contact_name[64];
char message[Q20_SMS_MAX_LENGTH + 1];
struct timespec timestamp;
u8 priority;
bool flash;
u16 message_id;
u8 part_number;
u8 total_parts;
};
/* SMS management structure */
struct q20_sms {
struct device *dev;
struct work_struct work;
struct timer_list notification_timer;
struct mutex lock;
/* Message storage */
struct q20_sms_message messages[Q20_SMS_MAX_MESSAGES];
u32 message_count;
u32 next_message_id;
/* Statistics */
u32 total_messages;
u32 unread_messages;
u32 sent_messages;
u32 failed_messages;
/* Hardware controls */
struct gpio_desc *vibrator_gpio;
struct led_classdev *led_cdev;
/* State */
bool notifications_enabled;
bool vibrate_enabled;
bool led_enabled;
/* Storage */
struct proc_dir_entry *proc_dir;
char storage_path[256];
};
static struct q20_sms *q20_sms_dev;
/* Message management functions */
static int q20_sms_add_message(struct q20_sms *sms, enum q20_sms_type type,
const char *phone_number, const char *message)
{
struct q20_sms_message *msg;
int ret = 0;
if (!sms || !phone_number || !message) {
return -EINVAL;
}
mutex_lock(&sms->lock);
/* Check if we have space */
if (sms->message_count >= Q20_SMS_MAX_MESSAGES) {
dev_err(sms->dev, "SMS storage full\n");
ret = -ENOMEM;
goto unlock;
}
/* Find empty slot */
msg = &sms->messages[sms->message_count];
/* Initialize message */
msg->id = sms->next_message_id++;
msg->type = type;
msg->status = (type == Q20_SMS_TYPE_INCOMING) ? Q20_SMS_STATUS_UNREAD : Q20_SMS_STATUS_SENDING;
strncpy(msg->phone_number, phone_number, Q20_SMS_PHONE_LENGTH - 1);
msg->phone_number[Q20_SMS_PHONE_LENGTH - 1] = '\0';
strncpy(msg->message, message, Q20_SMS_MAX_LENGTH);
msg->message[Q20_SMS_MAX_LENGTH] = '\0';
ktime_get_ts(&msg->timestamp);
msg->priority = 0;
msg->flash = false;
msg->message_id = 0;
msg->part_number = 1;
msg->total_parts = 1;
/* Update statistics */
sms->message_count++;
sms->total_messages++;
if (type == Q20_SMS_TYPE_INCOMING) {
sms->unread_messages++;
}
dev_info(sms->dev, "Added SMS message %u: %s -> %s\n",
msg->id, phone_number, (type == Q20_SMS_TYPE_INCOMING) ? "IN" : "OUT");
unlock:
mutex_unlock(&sms->lock);
return ret;
}
static int q20_sms_mark_read(struct q20_sms *sms, u32 message_id)
{
struct q20_sms_message *msg;
int i, ret = -ENOENT;
if (!sms) {
return -EINVAL;
}
mutex_lock(&sms->lock);
/* Find message */
for (i = 0; i < sms->message_count; i++) {
msg = &sms->messages[i];
if (msg->id == message_id) {
if (msg->status == Q20_SMS_STATUS_UNREAD) {
msg->status = Q20_SMS_STATUS_READ;
sms->unread_messages--;
ret = 0;
}
break;
}
}
mutex_unlock(&sms->lock);
return ret;
}
static int q20_sms_delete_message(struct q20_sms *sms, u32 message_id)
{
struct q20_sms_message *msg;
int i, ret = -ENOENT;
if (!sms) {
return -EINVAL;
}
mutex_lock(&sms->lock);
/* Find message */
for (i = 0; i < sms->message_count; i++) {
msg = &sms->messages[i];
if (msg->id == message_id) {
/* Update statistics */
if (msg->status == Q20_SMS_STATUS_UNREAD) {
sms->unread_messages--;
}
if (msg->type == Q20_SMS_TYPE_OUTGOING) {
sms->sent_messages--;
}
/* Remove message by shifting remaining messages */
if (i < sms->message_count - 1) {
memmove(&sms->messages[i], &sms->messages[i + 1],
(sms->message_count - i - 1) * sizeof(struct q20_sms_message));
}
sms->message_count--;
ret = 0;
break;
}
}
mutex_unlock(&sms->lock);
return ret;
}
static int q20_sms_send_message(struct q20_sms *sms, const char *phone_number,
const char *message)
{
int ret;
if (!sms || !phone_number || !message) {
return -EINVAL;
}
/* Add message to storage */
ret = q20_sms_add_message(sms, Q20_SMS_TYPE_OUTGOING, phone_number, message);
if (ret < 0) {
return ret;
}
/* TODO: Send message via modem */
dev_info(sms->dev, "Sending SMS to %s: %s\n", phone_number, message);
/* For now, mark as sent */
sms->sent_messages++;
return 0;
}
static int q20_sms_receive_message(struct q20_sms *sms, const char *phone_number,
const char *message)
{
int ret;
if (!sms || !phone_number || !message) {
return -EINVAL;
}
/* Add message to storage */
ret = q20_sms_add_message(sms, Q20_SMS_TYPE_INCOMING, phone_number, message);
if (ret < 0) {
return ret;
}
/* Trigger notification */
if (sms->notifications_enabled) {
schedule_work(&sms->work);
}
return 0;
}
/* Notification functions */
static void q20_sms_notification_work(struct work_struct *work)
{
struct q20_sms *sms = container_of(work, struct q20_sms, work);
if (!sms) {
return;
}
/* Vibrate */
if (sms->vibrate_enabled && sms->vibrator_gpio) {
gpiod_set_value_cansleep(sms->vibrator_gpio, 1);
msleep(200);
gpiod_set_value_cansleep(sms->vibrator_gpio, 0);
}
/* Flash LED */
if (sms->led_enabled && sms->led_cdev) {
/* TODO: Implement LED flashing */
}
dev_info(sms->dev, "SMS notification triggered\n");
}
static void q20_sms_notification_timer_callback(struct timer_list *t)
{
struct q20_sms *sms = from_timer(sms, t, notification_timer);
if (sms && sms->unread_messages > 0) {
/* Schedule notification work */
schedule_work(&sms->work);
}
}
/* Storage functions */
static int q20_sms_save_messages(struct q20_sms *sms)
{
struct file *file;
mm_segment_t old_fs;
int ret = 0;
if (!sms) {
return -EINVAL;
}
mutex_lock(&sms->lock);
/* Open file for writing */
old_fs = get_fs();
set_fs(KERNEL_DS);
file = filp_open(sms->storage_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
dev_err(sms->dev, "Failed to open SMS storage file: %d\n", ret);
goto restore_fs;
}
/* Write messages */
ret = vfs_write(file, sms->messages,
sms->message_count * sizeof(struct q20_sms_message),
&file->f_pos);
if (ret < 0) {
dev_err(sms->dev, "Failed to write SMS messages: %d\n", ret);
}
filp_close(file, NULL);
restore_fs:
set_fs(old_fs);
mutex_unlock(&sms->lock);
return ret;
}
static int q20_sms_load_messages(struct q20_sms *sms)
{
struct file *file;
mm_segment_t old_fs;
int ret = 0;
if (!sms) {
return -EINVAL;
}
mutex_lock(&sms->lock);
/* Open file for reading */
old_fs = get_fs();
set_fs(KERNEL_DS);
file = filp_open(sms->storage_path, O_RDONLY, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
if (ret != -ENOENT) {
dev_err(sms->dev, "Failed to open SMS storage file: %d\n", ret);
}
goto restore_fs;
}
/* Read messages */
ret = vfs_read(file, sms->messages, sizeof(sms->messages), &file->f_pos);
if (ret > 0) {
sms->message_count = ret / sizeof(struct q20_sms_message);
sms->next_message_id = sms->message_count + 1;
/* Count unread messages */
int i;
for (i = 0; i < sms->message_count; i++) {
if (sms->messages[i].status == Q20_SMS_STATUS_UNREAD) {
sms->unread_messages++;
}
}
dev_info(sms->dev, "Loaded %u SMS messages\n", sms->message_count);
}
filp_close(file, NULL);
restore_fs:
set_fs(old_fs);
mutex_unlock(&sms->lock);
return ret;
}
/* Proc filesystem interface */
static int q20_sms_proc_show(struct seq_file *m, void *v)
{
struct q20_sms *sms = m->private;
int i;
if (!sms) {
return -EINVAL;
}
seq_printf(m, "Q20 SMS Statistics\n");
seq_printf(m, "==================\n");
seq_printf(m, "Total messages: %u\n", sms->total_messages);
seq_printf(m, "Unread messages: %u\n", sms->unread_messages);
seq_printf(m, "Sent messages: %u\n", sms->sent_messages);
seq_printf(m, "Failed messages: %u\n", sms->failed_messages);
seq_printf(m, "Storage used: %u/%u\n", sms->message_count, Q20_SMS_MAX_MESSAGES);
seq_printf(m, "\n");
seq_printf(m, "Recent Messages\n");
seq_printf(m, "===============\n");
mutex_lock(&sms->lock);
for (i = sms->message_count - 1; i >= 0 && i >= sms->message_count - 10; i--) {
struct q20_sms_message *msg = &sms->messages[i];
struct tm tm;
time_to_tm(msg->timestamp.tv_sec, 0, &tm);
seq_printf(m, "[%u] %s %s (%s) %02d/%02d/%04d %02d:%02d\n",
msg->id,
(msg->type == Q20_SMS_TYPE_INCOMING) ? "IN" : "OUT",
msg->phone_number,
(msg->status == Q20_SMS_STATUS_UNREAD) ? "UNREAD" : "READ",
tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min);
seq_printf(m, " %s\n", msg->message);
seq_printf(m, "\n");
}
mutex_unlock(&sms->lock);
return 0;
}
static int q20_sms_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, q20_sms_proc_show, PDE_DATA(inode));
}
static const struct proc_ops q20_sms_proc_ops = {
.proc_open = q20_sms_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
/* Module initialization */
static int __init q20_sms_init(void)
{
struct q20_sms *sms;
int ret;
dev_info(NULL, "Initializing Q20 SMS system\n");
sms = kzalloc(sizeof(*sms), GFP_KERNEL);
if (!sms) {
return -ENOMEM;
}
sms->dev = NULL; /* TODO: Get device from platform */
mutex_init(&sms->lock);
INIT_WORK(&sms->work, q20_sms_notification_work);
/* Initialize notification timer */
timer_setup(&sms->notification_timer, q20_sms_notification_timer_callback, 0);
/* Get hardware controls */
sms->vibrator_gpio = devm_gpiod_get_optional(sms->dev, "vibrator", GPIOD_OUT_LOW);
if (IS_ERR(sms->vibrator_gpio)) {
ret = PTR_ERR(sms->vibrator_gpio);
dev_err(sms->dev, "Failed to get vibrator GPIO: %d\n", ret);
goto error;
}
/* Set up storage path */
snprintf(sms->storage_path, sizeof(sms->storage_path), "/data/sms_messages.dat");
/* Load existing messages */
ret = q20_sms_load_messages(sms);
if (ret < 0 && ret != -ENOENT) {
dev_err(sms->dev, "Failed to load SMS messages: %d\n", ret);
}
/* Create proc filesystem entry */
sms->proc_dir = proc_mkdir("q20_sms", NULL);
if (!sms->proc_dir) {
dev_err(sms->dev, "Failed to create proc directory\n");
ret = -ENOMEM;
goto error;
}
if (!proc_create_data("messages", 0444, sms->proc_dir,
&q20_sms_proc_ops, sms)) {
dev_err(sms->dev, "Failed to create proc file\n");
ret = -ENOMEM;
goto error;
}
/* Enable notifications by default */
sms->notifications_enabled = true;
sms->vibrate_enabled = true;
sms->led_enabled = true;
q20_sms_dev = sms;
dev_info(sms->dev, "Q20 SMS system initialized successfully\n");
return 0;
error:
if (sms) {
if (sms->proc_dir) {
remove_proc_entry("messages", sms->proc_dir);
remove_proc_entry("q20_sms", NULL);
}
kfree(sms);
}
return ret;
}
/* Module cleanup */
static void __exit q20_sms_exit(void)
{
struct q20_sms *sms = q20_sms_dev;
if (sms) {
dev_info(sms->dev, "Cleaning up Q20 SMS system\n");
/* Save messages */
q20_sms_save_messages(sms);
/* Remove proc filesystem entries */
if (sms->proc_dir) {
remove_proc_entry("messages", sms->proc_dir);
remove_proc_entry("q20_sms", NULL);
}
/* Cancel work and timer */
cancel_work_sync(&sms->work);
del_timer(&sms->notification_timer);
kfree(sms);
q20_sms_dev = NULL;
}
}
module_init(q20_sms_init);
module_exit(q20_sms_exit);
MODULE_AUTHOR("BBeOS Team");
MODULE_DESCRIPTION("BlackBerry Classic Q20 SMS System");
MODULE_LICENSE("GPL v2");