diff --git a/.gitignore b/.gitignore index a7dfe2b..a9c0c84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ smartlamp-brightnessd/ tasks-daemon-2026/ +.probe_2025* +probe_2025* +.*.cmd +Module.symvers +modules.order \ No newline at end of file diff --git a/daemon-2026-base/brightnessd_3_2.c b/daemon-2026-base/brightnessd_3_2.c index 180c7d2..9e03c1f 100644 --- a/daemon-2026-base/brightnessd_3_2.c +++ b/daemon-2026-base/brightnessd_3_2.c @@ -33,6 +33,19 @@ static int read_int_file(const char *path, int *value) // Retorne 0 em caso de sucesso ou um codigo negativo em caso de erro. (void)path; (void)value; + + FILE *ftpr = fopen(path,"r"); + + if(ftpr == NULL) { + return -ENOSYS; + } + + int found = fscanf(ftpr,"%i",value); + fclose(ftpr); + if(found > 0) { + return 0; + } + return -ENOSYS; } @@ -40,6 +53,15 @@ static int ldr_to_percent(int ldr) { // TASK 3.2: limite o LDR para 0-100 e aplique um brilho minimo. (void)ldr; + + if(ldr > MIN_PERCENT && ldr <= 100){ + return ldr; + } + + if(ldr > 100) { + return 100; + } + return MIN_PERCENT; } diff --git a/daemon-2026-base/brightnessd_3_3.c b/daemon-2026-base/brightnessd_3_3.c index 7cc064f..0bf603c 100644 --- a/daemon-2026-base/brightnessd_3_3.c +++ b/daemon-2026-base/brightnessd_3_3.c @@ -14,7 +14,8 @@ static volatile sig_atomic_t running = 1; -struct config { +struct config +{ const char *ldr_path; const char *backlight_path; int interval_ms; @@ -40,6 +41,21 @@ static int read_int_file(const char *path, int *value) // TASK 3.3: reaproveite a implementacao da task 3.2. (void)path; (void)value; + + FILE *ftpr = fopen(path, "r"); + + if (ftpr == NULL) + { + return -ENOSYS; + } + + int found = fscanf(ftpr, "%i", value); + fclose(ftpr); + if (found > 0) + { + return 0; + } + return -ENOSYS; } @@ -49,6 +65,22 @@ static int __attribute__((unused)) write_int_file(const char *path, int value) // Use essa funcao para atualizar o brilho real da tela. (void)path; (void)value; + + FILE *ftpr = fopen(path, "w"); + + if (ftpr == NULL) + { + return -ENOSYS; + } + + int found = fprintf(ftpr, "%i", value); + + fclose(ftpr); + if (found > 0) + { + return 0; + } + return -ENOSYS; } @@ -61,6 +93,14 @@ static int ldr_to_brightness(int ldr, int max_brightness) percent = MIN_PERCENT; (void)ldr; (void)max_brightness; + int comparison = ldr * (max_brightness / 100); + + if (comparison < percent) + { + return percent; + } + percent = comparison; + return percent; } @@ -71,7 +111,8 @@ static void sleep_ms(int milliseconds) request.tv_sec = milliseconds / 1000; request.tv_nsec = (long)(milliseconds % 1000) * 1000000L; - while (running && nanosleep(&request, &request) == -1 && errno == EINTR) { + while (running && nanosleep(&request, &request) == -1 && errno == EINTR) + { } } @@ -79,8 +120,10 @@ static int parse_args(int argc, char **argv, struct config *config) { int opt; - while ((opt = getopt(argc, argv, "l:b:i:h")) != -1) { - switch (opt) { + while ((opt = getopt(argc, argv, "l:b:i:h")) != -1) + { + switch (opt) + { case 'l': config->ldr_path = optarg; break; @@ -123,22 +166,31 @@ int main(int argc, char **argv) signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); - if (read_int_file(max_path, &max_brightness) < 0 || max_brightness <= 0) { + if (read_int_file(max_path, &max_brightness) < 0 || max_brightness <= 0) + { fprintf(stderr, "failed to read %s\n", max_path); return EXIT_FAILURE; } - while (running) { + while (running) + { int ldr; int brightness; - if (read_int_file(config.ldr_path, &ldr) == 0) { + if (read_int_file(config.ldr_path, &ldr) == 0) + { brightness = ldr_to_brightness(ldr, max_brightness); // TASK 3.3: escreva brightness em brightness_path usando write_int_file(). + if (write_int_file(brightness_path, brightness) != 0) + { + return EXIT_FAILURE; + } printf("ldr=%d brightness=%d max_brightness=%d\n", ldr, brightness, max_brightness); fflush(stdout); - } else { + } + else + { fprintf(stderr, "failed to read %s\n", config.ldr_path); } diff --git a/esp32/.gitignore b/esp32/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/esp32/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/esp32/.vscode/extensions.json b/esp32/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/esp32/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/esp32/include/README b/esp32/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/esp32/include/README @@ -0,0 +1,37 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the convention is to give header files names that end with `.h'. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/esp32/lib/README b/esp32/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/esp32/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/esp32/pinos.txt b/esp32/pinos.txt deleted file mode 100644 index e69de29..0000000 diff --git a/esp32/platformio.ini b/esp32/platformio.ini new file mode 100644 index 0000000..ed64f7f --- /dev/null +++ b/esp32/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +upload_speed = 115200 +monitor_speed = 115200 diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp new file mode 100644 index 0000000..6457085 --- /dev/null +++ b/esp32/src/main.cpp @@ -0,0 +1,143 @@ +#include + +// Constants +const int baudrate = 115200; +const int ledPin = 4; +const int ldrPin = 34; +const float min_percentage = 0.0; +const float max_percentage = 100.0; +const float resolution = 4095.0; +const int channel = 0; +const int freq = 5000; +const int resolution_pwm = 8; + +// Global variables +int threshold = 0; +int currentLedPct = 0; + +// Function headers +void print_ldr(void); +void automatic_led(void); +void get_threshold(void); +void error_command(void); +void set_threshold(String input); +void get_ldr(void); +void get_led(void); +void set_led(String input); +void setLedPercent(int pct); + +// Setup Arduino Framework +void setup() { + Serial.begin(baudrate); + Serial.println("Ping! I'm alive!"); + ledcSetup(channel, freq, resolution_pwm); + ledcAttachPin(ledPin, channel); + pinMode(ldrPin, INPUT); +} + +// Loop Arduino Framework +void loop() { + if (Serial.available()) { + String input = Serial.readStringUntil('\n'); + input.replace("\r", ""); + input.trim(); + if (input.length() == 0) return; // ignore trash + if (input.startsWith("SET_LED ")) set_led(input); + else if (input.startsWith("GET_LED")) get_led(); + else if (input.startsWith("GET_LDR")) get_ldr(); + else if (input.startsWith("SET_THRESHOLD ")) set_threshold(input); + else if (input.startsWith("GET_THRESHOLD")) get_threshold(); + else error_command(); + } + + automatic_led(); + print_ldr(); + delay(2000); +} + +void print_ldr(void) { + int ldr_pwm = analogRead(ldrPin); + float ldr_pct = (ldr_pwm * max_percentage) / resolution; + Serial.println("GET_LDR " + String(int(ldr_pct))); +} + +void automatic_led(void) { + + int ldr = analogRead(ldrPin); + float ldr_pct = (ldr * max_percentage) / resolution; + if (ldr_pct > threshold) { + setLedPercent(currentLedPct); + } else { + setLedPercent(0); + } +} + +void get_threshold(void) { + /* + Retorna o valor atual do threshold. + */ + + Serial.println("RES GET_THRESHOLD " + String(threshold)); +} + +void error_command(void) { + Serial.println("ERR Unknown command."); +} + +void set_threshold(String input) { + /* + Define o limiar de ativação automática do LED (X entre 0-100). + O LED acende automaticamente quando a leitura normalizada do LDR + ultrapassar esse valor. + */ + + String threshold_str = input.substring(14); + threshold = threshold_str.toInt(); + Serial.println("RES SET_THRESHOLD 1"); +} + +void get_ldr(void) { + /* + Retorna o valor da leitura atual do LDR + (normalizado entre 0-100) + */ + + int ldrValue = analogRead(ldrPin); + float percentage = (ldrValue * max_percentage) / resolution; + Serial.println("RES GET_LDR " + String(int(percentage))); +} + +void get_led(void) { + /* + Retorna a intensidade atual do LED. + */ + + Serial.println("RES GET_LED " + String(currentLedPct)); +} + +void set_led(String input) { + /* + Define a intensidade do LED (X entre 0-100). Valores fora + desse intervalo devem ser ignorados e retornar erro. + */ + + String valueStr = input.substring(8); + int luminosity = valueStr.toInt(); + + + if (luminosity >= min_percentage && luminosity <= max_percentage) { + + currentLedPct = luminosity; + setLedPercent(luminosity); + + Serial.println("RES SET_LED 1"); + } + else { + Serial.println("RES SET_LED -1"); + } +} + +void setLedPercent(int pct) { + int duty = (pct * 255) / 100; + ledcWrite(channel, duty); +} \ No newline at end of file diff --git a/esp32/test/README b/esp32/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/esp32/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/smartlamp-kernel-module/probe_2025.c b/smartlamp-kernel-module/probe_2025.c index de1e623..412728b 100644 --- a/smartlamp-kernel-module/probe_2025.c +++ b/smartlamp-kernel-module/probe_2025.c @@ -14,8 +14,8 @@ static uint usb_in, usb_out; // Endereços das portas de e static char *usb_in_buffer, *usb_out_buffer; // Buffers de entrada e saída da USB static int usb_max_size; // Tamanho máximo de uma mensagem USB -#define VENDOR_ID SUBSTITUA_PELO_VENDORID /* Encontre o VendorID do smartlamp */ -#define PRODUCT_ID SUBSTITUA_PELO_PRODUCTID /* Encontre o ProductID do smartlamp */ +#define VENDOR_ID 0x10c4 /* Encontre o VendorID do smartlamp */ +#define PRODUCT_ID 0xea60 /* Encontre o ProductID do smartlamp */ static const struct usb_device_id id_table[] = { { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, {} }; static int usb_probe(struct usb_interface *ifce, const struct usb_device_id *id); // Executado quando o dispositivo é conectado na USB diff --git a/smartlamp-kernel-module/serial_read_2025.c b/smartlamp-kernel-module/serial_read_2025.c index 7d64536..8020f95 100644 --- a/smartlamp-kernel-module/serial_read_2025.c +++ b/smartlamp-kernel-module/serial_read_2025.c @@ -15,8 +15,9 @@ static uint usb_in, usb_out; // Endereços das portas de e static char *usb_in_buffer, *usb_out_buffer; // Buffers de entrada e saída da USB static int usb_max_size; // Tamanho máximo de uma mensagem USB -#define VENDOR_ID SUBSTITUA_PELO_VENDORID /* Encontre o VendorID do smartlamp */ -#define PRODUCT_ID SUBSTITUA_PELO_PRODUCTID /* Encontre o ProductID do smartlamp */ +#define VENDOR_ID 0x10c4 +#define PRODUCT_ID 0xea60 + static const struct usb_device_id id_table[] = { { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, {} }; static int usb_probe(struct usb_interface *ifce, const struct usb_device_id *id); // Executado quando o dispositivo é conectado na USB @@ -28,7 +29,7 @@ static int usb_read_serial(void); static int smartlamp_config_serial(struct usb_device *dev) { int ret; - u32 baudrate = 9600; // Defina o baud rate que seu ESP32 usa! + u32 baudrate = 115200; // Defina o baud rate que seu ESP32 usa! printk(KERN_INFO "SmartLamp: Configurando a porta serial...\n"); @@ -101,10 +102,10 @@ static int usb_probe(struct usb_interface *interface, const struct usb_device_id // TASK 2.3: Leitura de dados periódicos enviados pelo firmware // O firmware envia RES GET_LDR Z automaticamente a cada 2 segundos // Descomente as linhas abaixo após implementar usb_read_serial - // ret = usb_read_serial(); - // if (ret >= 0) { - // printk(KERN_INFO "SmartLamp: Valor do LDR recebido: %d\n", ret); - // } + ret = usb_read_serial(); + if (ret >= 0) { + printk(KERN_INFO "SmartLamp: Valor do LDR recebido: %d\n", ret); + } return 0; } @@ -139,29 +140,53 @@ static int usb_write_serial(char *cmd, int param) { return 0; } -// Lê uma linha completa da porta serial (até encontrar '\n') -// Retorna o valor numérico da resposta ou -1 em caso de erro -// Exemplo de resposta: "RES GET_LDR 450\n" -> retorna 450 -// Exemplo de resposta: "RES SET_LED 1\n" -> retorna 1 static int usb_read_serial(void) { int ret, actual_size; - int recv_size = 0; // Quantidade de caracteres já recebidos em recv_line + int recv_size = 0; int i; + int value; printk(KERN_INFO "SmartLamp: Aguardando resposta do dispositivo...\n"); - // TASK 2.3: Implemente a leitura de dados da porta serial - // - // IMPORTANTE: Os dados podem chegar fragmentados (byte a byte ou em blocos) - // Você deve acumular os dados em recv_line até encontrar o caractere '\n' - // - // Dicas: - // - Use um loop para continuar lendo até encontrar '\n' - // - Use usb_bulk_msg com usb_rcvbulkpipe para cada leitura - // - Copie os dados de usb_in_buffer para recv_line - // - Cuidado com buffer overflow: verifique recv_size < MAX_RECV_LINE - // - Defina um timeout adequado (ex: 2000ms) - // - Após receber a linha completa, extraia o valor numérico com sscanf + ret = usb_bulk_msg( + smartlamp_device, + usb_rcvbulkpipe(smartlamp_device, usb_in), + usb_in_buffer, + usb_max_size, + &actual_size, + 2500 + ); + + if (ret) { + printk(KERN_ERR "SmartLamp: Erro na leitura USB (%d)\n", ret); + return ret; + } + + for (i = 0; i < actual_size; i++) { + + if (recv_size >= MAX_RECV_LINE - 1) { + printk(KERN_ERR "SmartLamp: Buffer overflow\n"); + return -1; + } + recv_line[recv_size++] = usb_in_buffer[i]; + + if (usb_in_buffer[i] == '\n') { + + recv_line[recv_size] = '\0'; + + printk(KERN_INFO "SmartLamp: Linha recebida: %s\n", recv_line); + + if (sscanf(recv_line, "GET_LDR %d", &value) == 1) { + return value; + } + + printk(KERN_ERR "SmartLamp: Erro ao converter valor\n"); + return -1; + } + } + + printk(KERN_ERR "SmartLamp: Linha incompleta\n"); return -1; } + diff --git a/smartlamp-kernel-module/serial_write_2025.c b/smartlamp-kernel-module/serial_write_2025.c index 369359f..224be28 100644 --- a/smartlamp-kernel-module/serial_write_2025.c +++ b/smartlamp-kernel-module/serial_write_2025.c @@ -98,7 +98,14 @@ static int usb_probe(struct usb_interface *interface, const struct usb_device_id // TASK 2.2: Chame a função usb_write_serial para enviar o comando SET_LED com valor 100 // Descomente a linha abaixo e implemente a função usb_write_serial - // ret = usb_write_serial("SET_LED", 100); + ret = usb_write_serial("SET_LED", 100); + if (ret) + { + printk(KERN_ERR "SmartLamp: Falha ao escrever no serial\n"); + kfree(usb_in_buffer); + kfree(usb_out_buffer); + return ret; + } return 0; } @@ -116,11 +123,23 @@ static void usb_disconnect(struct usb_interface *interface) { static int usb_write_serial(char *cmd, int param) { int ret, actual_size; + sprintf(usb_out_buffer, "%s %d", cmd, param); + printk(KERN_INFO "SmartLamp: Enviando comando: %s %d\n", cmd, param); - // TASK 2.2: Implemente o envio do comando para o dispositivo - // Dica: Formate o comando no buffer usb_out_buffer e envie usando usb_bulk_msg - // O formato esperado é: "COMANDO PARAMETRO\n" + ret = usb_bulk_msg( + smartlamp_device, + usb_sndbulkpipe(smartlamp, usb_out), + usb_out_buffer, + strlen(usb_out_buffer), + &actual_size, + 1000 + ); + + if (ret) { + printk(KERN_ERR "SmartLamp: Erro de codigo %d ao enviar comando!\n", ret); + return -1; + } return 0; } diff --git a/smartlamp-kernel-module/sysfs_2025.c b/smartlamp-kernel-module/sysfs_2025.c index 47a0ea9..9a4fac1 100644 --- a/smartlamp-kernel-module/sysfs_2025.c +++ b/smartlamp-kernel-module/sysfs_2025.c @@ -10,28 +10,23 @@ MODULE_LICENSE("GPL"); static char recv_line[MAX_RECV_LINE]; // Buffer para armazenar linha completa recebida static struct usb_device *smartlamp_device; // Referência para o dispositivo USB -static uint usb_in, usb_out; // Endereços das portas de entrada e saida da USB +static uint usb_in, usb_out; // Endereços das portas de entrada e saída da USB static char *usb_in_buffer, *usb_out_buffer; // Buffers de entrada e saída da USB static int usb_max_size; // Tamanho máximo de uma mensagem USB -#define VENDOR_ID SUBSTITUA_PELO_VENDORID /* Encontre o VendorID do smartlamp */ -#define PRODUCT_ID SUBSTITUA_PELO_PRODUCTID /* Encontre o ProductID do smartlamp */ +#define VENDOR_ID 0x10c4 +#define PRODUCT_ID 0xea60 + static const struct usb_device_id id_table[] = { { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, {} }; -static int usb_probe(struct usb_interface *ifce, const struct usb_device_id *id); // Executado quando o dispositivo é conectado na USB -static void usb_disconnect(struct usb_interface *ifce); // Executado quando o dispositivo USB é desconectado da USB -static int usb_write_serial(char *cmd, int param); // Executado para enviar um comando para o dispositivo -static int usb_read_serial(char *cmd); // Executado para ler a resposta esperada da porta serial +static int usb_probe(struct usb_interface *ifce, const struct usb_device_id *id); +static void usb_disconnect(struct usb_interface *ifce); +static int usb_write_serial(char *cmd, int param); +static int usb_read_serial(char *cmd); -// Executado quando o arquivo /sys/kernel/smartlamp/{led, ldr, threshold} é lido -// Exemplo: cat /sys/kernel/smartlamp/led static ssize_t attr_show(struct kobject *sys_obj, struct kobj_attribute *attr, char *buff); - -// Executado quando o arquivo /sys/kernel/smartlamp/{led, ldr, threshold} é escrito -// Exemplo: echo "100" | sudo tee /sys/kernel/smartlamp/led static ssize_t attr_store(struct kobject *sys_obj, struct kobj_attribute *attr, const char *buff, size_t count); -// Variáveis para criar os arquivos em /sys/kernel/smartlamp/{led, ldr, threshold} static struct kobj_attribute led_attribute = __ATTR(led, S_IRUGO | S_IWUSR, attr_show, attr_store); static struct kobj_attribute ldr_attribute = __ATTR(ldr, S_IRUGO | S_IWUSR, attr_show, attr_store); static struct kobj_attribute threshold_attribute = __ATTR(threshold, S_IRUGO | S_IWUSR, attr_show, attr_store); @@ -39,34 +34,23 @@ static struct attribute *attrs[] = { &led_attribute.attr, &ldr_ static struct attribute_group attr_group = { .attrs = attrs }; static struct kobject *sys_obj; -// Função para configurar os parâmetros seriais do CP2102 via Control-Messages static int smartlamp_config_serial(struct usb_device *dev) { int ret; - u32 baudrate = 9600; // Defina o baud rate que seu ESP32 usa! + u32 baudrate = 115200; printk(KERN_INFO "SmartLamp: Configurando a porta serial...\n"); - // 1. Habilita a interface UART do CP2102 - // Comando específico do vendor Silicon Labs (CP210X_IFC_ENABLE) - // bmRequestType: 0x41 (Vendor, Host-to-Device, Interface) - // bRequest: 0x00 (CP210X_IFC_ENABLE) - // wValue: 0x0001 (UART Enable) ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x00, 0x41, 0x0001, 0, NULL, 0, 1000); - if (ret) - { + if (ret) { printk(KERN_ERR "SmartLamp: Erro ao habilitar a UART (código %d)\n", ret); return ret; } - // 2. Define o baud rate - // Comando específico do vendor Silicon Labs (CP210X_SET_BAUDRATE) - // bRequest: 0x1E (CP210X_SET_BAUDRATE) ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x1E, 0x41, 0, 0, &baudrate, sizeof(baudrate), 1000); - if (ret < 0) - { + if (ret < 0) { printk(KERN_ERR "SmartLamp: Erro ao configurar o baud rate (código %d)\n", ret); return ret; } @@ -76,44 +60,38 @@ static int smartlamp_config_serial(struct usb_device *dev) } MODULE_DEVICE_TABLE(usb, id_table); -bool ignore = true; static struct usb_driver smartlamp_driver = { - .name = "smartlamp", // Nome do driver - .probe = usb_probe, // Executado quando o dispositivo é conectado na USB - .disconnect = usb_disconnect, // Executado quando o dispositivo é desconectado na USB - .id_table = id_table, // Tabela com o VendorID e ProductID do dispositivo + .name = "smartlamp", + .probe = usb_probe, + .disconnect = usb_disconnect, + .id_table = id_table, }; module_usb_driver(smartlamp_driver); -// Executado quando o dispositivo é conectado na USB static int usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_endpoint_descriptor *usb_endpoint_in, *usb_endpoint_out; int ret; printk(KERN_INFO "SmartLamp: Dispositivo conectado ...\n"); - // Detecta portas e aloca buffers de entrada e saída de dados na USB smartlamp_device = interface_to_usbdev(interface); - ignore = usb_find_common_endpoints(interface->cur_altsetting, &usb_endpoint_in, &usb_endpoint_out, NULL, NULL); + usb_find_common_endpoints(interface->cur_altsetting, &usb_endpoint_in, &usb_endpoint_out, NULL, NULL); usb_max_size = usb_endpoint_maxp(usb_endpoint_in); usb_in = usb_endpoint_in->bEndpointAddress; usb_out = usb_endpoint_out->bEndpointAddress; usb_in_buffer = kmalloc(usb_max_size, GFP_KERNEL); usb_out_buffer = kmalloc(usb_max_size, GFP_KERNEL); - // Chama a função para configurar a porta serial antes de usar ret = smartlamp_config_serial(smartlamp_device); - if (ret) - { + if (ret) { printk(KERN_ERR "SmartLamp: Falha na configuração da serial\n"); kfree(usb_in_buffer); kfree(usb_out_buffer); return ret; } - // Cria os arquivos /sys/kernel/smartlamp/led, /sys/kernel/smartlamp/ldr e /sys/kernel/smartlamp/threshold sys_obj = kobject_create_and_add("smartlamp", kernel_kobj); if (!sys_obj) { kfree(usb_in_buffer); @@ -133,7 +111,6 @@ static int usb_probe(struct usb_interface *interface, const struct usb_device_id return 0; } -// Executado quando o dispositivo USB é desconectado da USB static void usb_disconnect(struct usb_interface *interface) { printk(KERN_INFO "SmartLamp: Dispositivo desconectado.\n"); @@ -142,121 +119,157 @@ static void usb_disconnect(struct usb_interface *interface) { kobject_put(sys_obj); } - kfree(usb_in_buffer); // Desaloca buffers + kfree(usb_in_buffer); kfree(usb_out_buffer); } -// Envia um comando para o dispositivo USB. -// Esta função deve reaproveitar a implementação feita na Tarefa 2.2. -// Em sysfs_2025.c, param >= 0 indica comando com parâmetro (ex: SET_LED 80) -// e param < 0 indica comando sem parâmetro (ex: GET_LDR). -// Exemplos de uso: -// - usb_write_serial("SET_LED", 80) deve enviar "SET_LED 80\n" -// - usb_write_serial("GET_LDR", -1) deve enviar "GET_LDR\n" static int usb_write_serial(char *cmd, int param) { int ret, actual_size; - printk(KERN_INFO "SmartLamp: Enviando comando: %s %d\n", cmd, param); + if (param >= 0) + sprintf(usb_out_buffer, "%s %d\n", cmd, param); + else + sprintf(usb_out_buffer, "%s\n", cmd); + + printk(KERN_INFO "SmartLamp: Enviando comando: %s", usb_out_buffer); - // TASK 2.4: adapte aqui a solução da Tarefa 2.2. - // Dica: quando param for negativo, envie apenas "COMANDO\n". - // Quando param for 0 ou maior, envie "COMANDO PARAMETRO\n". - // Depois, envie o buffer pela USB usando usb_bulk_msg. + ret = usb_bulk_msg( + smartlamp_device, + usb_sndbulkpipe(smartlamp_device, usb_out), + usb_out_buffer, + strlen(usb_out_buffer), + &actual_size, + 1000 + ); + + if (ret) { + printk(KERN_ERR "SmartLamp: Erro de codigo %d ao enviar comando!\n", ret); + return -1; + } return 0; } -// Lê respostas da porta serial até encontrar uma resposta para o comando esperado. -// Esta função deve reaproveitar a implementação feita na Tarefa 2.3. -// O parâmetro cmd indica qual resposta deve ser aceita. -// Exemplos: -// - usb_read_serial("GET_LDR") deve aceitar "RES GET_LDR 45\n" e retornar 45. -// - usb_read_serial("SET_LED") deve aceitar "RES SET_LED 1\n" e retornar 1. -// Mensagens de outros comandos devem ser ignoradas. -static int usb_read_serial(char *cmd) { +static int usb_read_serial(char *cmd) +{ int ret, actual_size; - int recv_size = 0; // Quantidade de caracteres já recebidos em recv_line + int recv_size = 0; int i; + int value; + + printk(KERN_INFO "SmartLamp: Aguardando resposta do dispositivo...\n"); + + ret = usb_bulk_msg( + smartlamp_device, + usb_rcvbulkpipe(smartlamp_device, usb_in), + usb_in_buffer, + usb_max_size, + &actual_size, + 2500 + ); - printk(KERN_INFO "SmartLamp: Aguardando resposta para %s...\n", cmd); - - // TASK 2.4: adapte aqui a solução da Tarefa 2.3. - // - // IMPORTANTE: Os dados podem chegar fragmentados (byte a byte ou em blocos) - // Você deve acumular os dados em recv_line até encontrar o caractere '\n' - // e processar apenas linhas que comecem com "RES ". - // - // Dicas: - // - Use um loop para continuar lendo até encontrar '\n' - // - Use usb_bulk_msg com usb_rcvbulkpipe para cada leitura - // - Copie os dados de usb_in_buffer para recv_line - // - Cuidado com buffer overflow: verifique recv_size < MAX_RECV_LINE - // - Defina um timeout adequado (ex: 2000ms) - // - Ignore linhas que não correspondem ao comando esperado - // - Após receber a linha correta, extraia o valor numérico e retorne + if (ret) { + printk(KERN_ERR "SmartLamp: Erro na leitura USB (%d)\n", ret); + return ret; + } + + for (i = 0; i < actual_size; i++) { + if (recv_size >= MAX_RECV_LINE - 1) { + printk(KERN_ERR "SmartLamp: Buffer overflow\n"); + return -1; + } + + recv_line[recv_size++] = usb_in_buffer[i]; + + if (usb_in_buffer[i] == '\n') { + recv_line[recv_size] = '\0'; + printk(KERN_INFO "SmartLamp: Linha recebida: %s\n", recv_line); + + if (strcmp(cmd, "GET_LDR") == 0) { + if (sscanf(recv_line, "GET_LDR %d", &value) == 1) + return value; + } else if (strcmp(cmd, "GET_LED") == 0) { + if (sscanf(recv_line, "GET_LED %d", &value) == 1) + return value; + } else if (strcmp(cmd, "GET_THRESHOLD") == 0) { + if (sscanf(recv_line, "GET_THRESHOLD %d", &value) == 1) + return value; + } else if (strcmp(cmd, "SET_LED") == 0) { + if (sscanf(recv_line, "SET_LED %d", &value) == 1) + return value; + } else if (strcmp(cmd, "SET_THRESHOLD") == 0) { + if (sscanf(recv_line, "SET_THRESHOLD %d", &value) == 1) + return value; + } else { + printk(KERN_ERR "SmartLamp: comando desconhecido: %s\n", cmd); + return -1; + } + + printk(KERN_ERR "SmartLamp: Erro ao converter valor\n"); + return -1; + } + } return -1; } - -// Executado quando algum arquivo em /sys/kernel/smartlamp/{led, ldr, threshold} é lido. -// Parâmetros: -// - sys_obj: objeto do kernel que representa o diretório /sys/kernel/smartlamp. -// - attr: atributo que representa o arquivo acessado (led, ldr ou threshold). -// - buff: buffer onde a função deve escrever o texto que será retornado para o usuário. -// Retorno: quantidade de bytes escritos em buff. -static ssize_t attr_show(struct kobject *sys_obj, struct kobj_attribute *attr, char *buff) { +static ssize_t attr_show(struct kobject *sys_obj, struct kobj_attribute *attr, char *buff) +{ int value = -1; - // attr_name guarda o nome do arquivo sysfs acessado. - // Exemplos: "led" quando o usuário roda cat /sys/kernel/smartlamp/led, - // "ldr" quando lê /sys/kernel/smartlamp/ldr e "threshold" para threshold. const char *attr_name = attr->attr.name; printk(KERN_INFO "SmartLamp: Lendo %s ...\n", attr_name); - // TASK 2.4: implemente a leitura via sysfs. - // Use attr_name para identificar se o usuario leu led, ldr ou threshold. - // Para cada arquivo, envie o comando GET correspondente ao firmware - // e use usb_read_serial("GET_...") para obter o valor retornado em buff. + if (strcmp(attr_name, "led") == 0) { + usb_write_serial("GET_LED", -1); + value = usb_read_serial("GET_LED"); + } else if (strcmp(attr_name, "ldr") == 0) { + usb_write_serial("GET_LDR", -1); + value = usb_read_serial("GET_LDR"); + } else if (strcmp(attr_name, "threshold") == 0) { + usb_write_serial("GET_THRESHOLD", -1); + value = usb_read_serial("GET_THRESHOLD"); + } else { + printk(KERN_ERR "SmartLamp: atributo inválido: %s\n", attr_name); + return -EINVAL; + } + + if (value < 0) { + printk(KERN_ERR "SmartLamp: erro ao ler %s\n", attr_name); + return -EIO; + } - sprintf(buff, "%d\n", value); - return strlen(buff); + return sprintf(buff, "%d\n", value); } -// Executado quando algum arquivo em /sys/kernel/smartlamp/{led, ldr, threshold} recebe escrita. -// Exemplo: echo 100 | sudo tee /sys/kernel/smartlamp/led -// Parâmetros: -// - sys_obj: objeto do kernel que representa o diretório /sys/kernel/smartlamp. -// - attr: atributo que representa o arquivo escrito (led, ldr ou threshold). -// - buff: buffer com o texto enviado pelo usuário. -// - count: quantidade de bytes recebidos em buff. -// Retorno: quantidade de bytes processados ou código de erro negativo. -static ssize_t attr_store(struct kobject *sys_obj, struct kobj_attribute *attr, const char *buff, size_t count) { +static ssize_t attr_store(struct kobject *sys_obj, struct kobj_attribute *attr, const char *buff, size_t count) +{ long ret, value; - // attr_name guarda o nome do arquivo sysfs que recebeu a escrita. - // Use esse valor para decidir se o comando será SET_LED, SET_THRESHOLD - // ou se a operação deve ser recusada porque ldr é somente leitura. const char *attr_name = attr->attr.name; ret = kstrtol(buff, 10, &value); if (ret) { printk(KERN_ALERT "SmartLamp: valor de %s invalido.\n", attr_name); - return -EACCES; + return -EINVAL; } printk(KERN_INFO "SmartLamp: Setando %s para %ld ...\n", attr_name, value); - // TASK 2.4: implemente a escrita via sysfs. - // Use attr_name para permitir escrita em led e threshold. - // Para led, envie SET_LED com o valor recebido. - // Para threshold, envie SET_THRESHOLD com o valor recebido. - // Depois de enviar, leia a resposta do firmware com usb_read_serial("SET_..."). - // O arquivo ldr representa o sensor de luz e deve ser somente leitura. - - if (ret < 0) { - printk(KERN_ALERT "SmartLamp: erro ao setar o valor do %s.\n", attr_name); + if (strcmp(attr_name, "ldr") == 0) { + printk(KERN_ERR "SmartLamp: ldr é somente leitura\n"); return -EACCES; } - return strlen(buff); -} + if (strcmp(attr_name, "led") == 0) { + usb_write_serial("SET_LED", value); + usb_read_serial("SET_LED"); + } else if (strcmp(attr_name, "threshold") == 0) { + usb_write_serial("SET_THRESHOLD", value); + usb_read_serial("SET_THRESHOLD"); + } else { + printk(KERN_ERR "SmartLamp: atributo inválido: %s\n", attr_name); + return -EINVAL; + } + + return count; +} \ No newline at end of file diff --git a/smartlamp-kernel-module/test_driver.c b/smartlamp-kernel-module/test_driver.c index b9ffe40..52000ee 100644 --- a/smartlamp-kernel-module/test_driver.c +++ b/smartlamp-kernel-module/test_driver.c @@ -47,6 +47,15 @@ static ssize_t attr_show(struct kobject *sys_obj, struct kobj_attribute *attr, c (void)sys_obj; (void)attr_name; + if(strcmp(attr_name,"ldr")) { + value = mock_ldr; + } + if(strcmp(attr_name,"led")) { + value = mock_led; + } + if(strcmp(attr_name,"threshold")) { + value = mock_threshold; + } // TASK 3.1: esta funcao e chamada quando o usuario le um arquivo com cat. // Exemplo: cat /sys/kernel/smartlamp/ldr // @@ -76,6 +85,15 @@ static ssize_t attr_store(struct kobject *sys_obj, struct kobj_attribute *attr, value = clamp_percent(value); (void)attr_name; + if(strcmp(attr_name,"ldr")) { + mock_ldr = value; + } + if(strcmp(attr_name,"led")) { + mock_led = value; + } + if(strcmp(attr_name,"threshold")) { + mock_threshold = value; + } // TASK 3.1: esta funcao e chamada quando o usuario escreve em um arquivo. // Exemplo: echo 75 | sudo tee /sys/kernel/smartlamp/ldr //