diff --git a/include/uacpi/internal/types.h b/include/uacpi/internal/types.h index 0e203446..09feffe2 100644 --- a/include/uacpi/internal/types.h +++ b/include/uacpi/internal/types.h @@ -187,19 +187,6 @@ typedef enum uacpi_access_type { UACPI_ACCESS_TYPE_BUFFER = 5, } uacpi_access_type; -typedef enum uacpi_access_attributes { - UACPI_ACCESS_ATTRIBUTE_QUICK = 0x02, - UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE = 0x04, - UACPI_ACCESS_ATTRIBUTE_BYTE = 0x06, - UACPI_ACCESS_ATTRIBUTE_WORD = 0x08, - UACPI_ACCESS_ATTRIBUTE_BLOCK = 0x0A, - UACPI_ACCESS_ATTRIBUTE_BYTES = 0x0B, - UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL = 0x0C, - UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL = 0x0D, - UACPI_ACCESS_ATTRIBUTE_RAW_BYTES = 0x0E, - UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES = 0x0F, -} uacpi_access_attributes; - typedef enum uacpi_lock_rule { UACPI_LOCK_RULE_NO_LOCK = 0, UACPI_LOCK_RULE_LOCK = 1, diff --git a/include/uacpi/types.h b/include/uacpi/types.h index 5819e65a..b05bd7c3 100644 --- a/include/uacpi/types.h +++ b/include/uacpi/types.h @@ -382,6 +382,10 @@ typedef enum uacpi_region_op { // data => uacpi_region_prm_rw_data UACPI_REGION_OP_PRM_COMMAND, + + // data => uacpi_region_serial_rw_data + UACPI_REGION_OP_SERIAL_READ, + UACPI_REGION_OP_SERIAL_WRITE, } uacpi_region_op; typedef struct uacpi_generic_region_info { @@ -458,6 +462,38 @@ typedef struct uacpi_region_prm_rw_data uacpi_data_view in_out_message; } uacpi_region_prm_rw_data; +typedef enum uacpi_access_attribute +{ + UACPI_ACCESS_ATTRIBUTE_QUICK = 0x02, + UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE = 0x04, + UACPI_ACCESS_ATTRIBUTE_BYTE = 0x06, + UACPI_ACCESS_ATTRIBUTE_WORD = 0x08, + UACPI_ACCESS_ATTRIBUTE_BLOCK = 0x0A, + UACPI_ACCESS_ATTRIBUTE_BYTES = 0x0B, + UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL = 0x0C, + UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL = 0x0D, + UACPI_ACCESS_ATTRIBUTE_RAW_BYTES = 0x0E, + UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES = 0x0F, +} uacpi_access_attribute; + +typedef struct uacpi_region_serial_rw_data +{ + void *handler_context; + void *region_context; + uacpi_u64 command; + uacpi_data_view connection; + uacpi_data_view in_out_buffer; + uacpi_access_attribute access_attribute; + + /* + * Applicable if access_attribute is one of: + * - UACPI_ACCESS_ATTRIBUTE_BYTES + * - UACPI_ACCESS_ATTRIBUTE_RAW_BYTES + * - UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES + */ + uacpi_u8 access_length; +} uacpi_region_serial_rw_data; + typedef struct uacpi_region_detach_data { void *handler_context; void *region_context; diff --git a/source/io.c b/source/io.c index 0dba9b3f..0967639a 100644 --- a/source/io.c +++ b/source/io.c @@ -247,22 +247,65 @@ static uacpi_status access_field_unit( #define SERIAL_HEADER_SIZE 2 #define IPMI_DATA_SIZE 64 -static uacpi_size wtr_buffer_size( - uacpi_field_unit *field, uacpi_address_space space +static uacpi_status wtr_buffer_size( + uacpi_field_unit *field, uacpi_address_space space, + uacpi_size *out_size ) { - UACPI_UNUSED(field); - switch (space) { case UACPI_ADDRESS_SPACE_IPMI: - return SERIAL_HEADER_SIZE + IPMI_DATA_SIZE; + *out_size = SERIAL_HEADER_SIZE + IPMI_DATA_SIZE; + break; case UACPI_ADDRESS_SPACE_PRM: - return 26; + *out_size = 26; + break; case UACPI_ADDRESS_SPACE_FFIXEDHW: - return 256; + *out_size = 256; + break; + case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: + case UACPI_ADDRESS_SPACE_SMBUS: { + uacpi_size size_for_protocol = SERIAL_HEADER_SIZE; + + switch (field->attributes) { + case UACPI_ACCESS_ATTRIBUTE_QUICK: + break; // + 0 + case UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE: + case UACPI_ACCESS_ATTRIBUTE_BYTE: + size_for_protocol += 1; + break; + + case UACPI_ACCESS_ATTRIBUTE_WORD: + case UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL: + size_for_protocol += 2; + break; + + case UACPI_ACCESS_ATTRIBUTE_BYTES: + size_for_protocol += field->access_length; + break; + + case UACPI_ACCESS_ATTRIBUTE_BLOCK: + case UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL: + case UACPI_ACCESS_ATTRIBUTE_RAW_BYTES: + case UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES: + size_for_protocol += 255; + break; + + default: + uacpi_error( + "unsupported field@%p access attribute %d\n", + field, field->attributes + ); + return UACPI_STATUS_UNIMPLEMENTED; + } + + *out_size = size_for_protocol; + break; + } default: - return 0; + return UACPI_STATUS_INVALID_ARGUMENT; } + + return UACPI_STATUS_OK; } static uacpi_status handle_special_field( @@ -321,13 +364,17 @@ static uacpi_status handle_special_field( } UACPI_FALLTHROUGH; case UACPI_ADDRESS_SPACE_FFIXEDHW: + case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: + case UACPI_ADDRESS_SPACE_SMBUS: goto do_wtr; default: return ret; } do_wtr: - wtr_buffer.length = wtr_buffer_size(field, region->space); + ret = wtr_buffer_size(field, region->space, &wtr_buffer.length); + if (uacpi_unlikely_error(ret)) + goto out_handled; wtr_buffer.data = uacpi_kernel_alloc(wtr_buffer.length); if (uacpi_unlikely(wtr_buffer.data == UACPI_NULL)) { diff --git a/source/opregion.c b/source/opregion.c index 10049d9e..0a4c8302 100644 --- a/source/opregion.c +++ b/source/opregion.c @@ -853,6 +853,7 @@ uacpi_status uacpi_dispatch_opregion_io( uacpi_region_ipmi_rw_data ipmi; uacpi_region_ffixedhw_rw_data ffixedhw; uacpi_region_prm_rw_data prm; + uacpi_region_serial_rw_data serial; } handler_data; ret = upgrade_to_opregion_lock(); @@ -968,6 +969,31 @@ uacpi_status uacpi_dispatch_opregion_io( handler_data.prm.in_out_message = data.buffer; op = UACPI_REGION_OP_PRM_COMMAND; break; + case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: + case UACPI_ADDRESS_SPACE_SMBUS: + ret = uacpi_object_get_string_or_buffer( + field->connection, &handler_data.serial.connection + ); + if (uacpi_unlikely_error(ret)) + goto io_done; + + handler_data.serial.command = abs_offset; + handler_data.serial.in_out_buffer = data.buffer; + handler_data.serial.access_attribute = field->attributes; + + switch (field->attributes) { + case UACPI_ACCESS_ATTRIBUTE_BYTES: + case UACPI_ACCESS_ATTRIBUTE_RAW_BYTES: + case UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES: + handler_data.serial.access_length = field->access_length; + break; + default: + handler_data.serial.access_length = 0; + } + + op = op == UACPI_REGION_OP_READ ? + UACPI_REGION_OP_SERIAL_READ : UACPI_REGION_OP_SERIAL_WRITE; + break; default: handler_data.rw.byte_width = field->access_width_bytes; handler_data.rw.offset = abs_offset;