Re: [PATCH 1/2] platform/x86: Add new Dell UART backlight driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 5/13/24 2:12 PM, Ilpo Järvinen wrote:
> On Mon, 13 May 2024, Hans de Goede wrote:
> 
>> Hi Ilpo,
>>
>> Thank you for the review.
>>
>> On 5/13/24 10:34 AM, Ilpo Järvinen wrote:
>>> On Sun, 12 May 2024, Hans de Goede wrote:
> 
>>>> +	u8 resp[3];
>>>> +
>>>> +	set_brightness[2] = brightness;
>>>> +	set_brightness[3] = dell_uart_checksum(set_brightness, 3);
>>>
>>> Also, couldn't these be accessed through a struct to eliminate most of the 
>>> magic indexes?
>>
>> With the checksum at the end, this would require a VLA in the middle
>> of the struct (get_version return buffer contains more then 1 dat byte)
>> We could treat the checksum as an extra data byte, but then we are
>> mixing struct usage for some fields + using an array of bytes
>> approach for the data + checksum. For consistency I prefer to just
>> stick with one approach which means using the array of bytes approach
>> for everything.
> 
> Ok.
> 
>>>> +	const u8 get_brightness[] = { 0x6A, 0x0C, 0x89 };
>>>> +	u8 resp[4];
>>>> +	int ret;
>>>> +
>>>> +	ret = dell_uart_bl_command(dell_bl, get_brightness, ARRAY_SIZE(get_brightness),
>>>> +				   resp, ARRAY_SIZE(resp));
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	if (resp[0] != 4) {
>>>
>>> sizeof(resp)
>>
>> Ack.
>>
>>> but isn't this already checked when reading it??
>>
>> No dell_uart_bl_receive() checks that the response will fit in the supplied
>> buffer and that it has a valid checksum but the controller may send a
>> response smaller then the passed in buffer and it will actually do this for
>> the get_version command.
> 
> Ah, I see now that it checked against constant rather than the actual 
> value.
> 
>>>> +		dev_warn(dell_bl->dev, "Bytes received out of band, dropping them.\n");
>>>> +		return len;
>>>> +	}
>>>> +
>>>> +	for (i = 0; i < len; i++) {
>>>> +		dell_bl->resp[dell_bl->resp_idx] = data[i];
>>>> +
>>>> +		switch (dell_bl->resp_idx) {
>>>> +		case 0: /* Length byte */
>>>> +			dell_bl->resp_len = dell_bl->resp[0];
>>>> +			if (dell_bl->resp_len < DELL_BL_MIN_RESP_SIZE) {
>>>> +				dev_err(dell_bl->dev, "Response length too small %d < %d\n",
>>>> +					dell_bl->resp_len, DELL_BL_MIN_RESP_SIZE);
>>>> +				dell_bl->status = -EIO;
>>>> +				goto wakeup;
>>>> +			} else if (dell_bl->resp_len > dell_bl->resp_max_len) {
>>>> +				dev_err(dell_bl->dev, "Response length too big %d > %d\n",
>>>> +					dell_bl->resp_len, dell_bl->resp_max_len);
>>>> +				dell_bl->status = -EIO;
>>>> +				goto wakeup;
>>>> +			}
>>>> +			break;
>>>> +		case 1: /* CMD byte */
>>>> +			if (dell_bl->resp[1] != dell_bl->pending_cmd) {
>>>> +				dev_err(dell_bl->dev, "Response cmd 0x%02x != pending 0x%02x\n",
>>>> +					dell_bl->resp[1], dell_bl->pending_cmd);
>>>> +				dell_bl->status = -EIO;
>>>> +				goto wakeup;
>>>> +			}
>>>> +			break;
>>>> +		}
>>>> +
>>>> +		dell_bl->resp_idx++;
>>>> +		if (dell_bl->resp_idx < dell_bl->resp_len)
>>>> +			continue;
>>>> +
>>>> +		csum = dell_uart_checksum(dell_bl->resp, dell_bl->resp_len - 1);
>>>> +		if (dell_bl->resp[dell_bl->resp_len - 1] != csum) {
>>>> +			dev_err(dell_bl->dev, "Checksum mismatch got 0x%02x expected 0x%02x\n",
>>>> +				dell_bl->resp[dell_bl->resp_len - 1], csum);
>>>> +			dell_bl->status = -EIO;
>>>> +			goto wakeup;
>>>> +		}
>>>
>>> Why is the checksum calculation and check inside the loop??
>>
>> The loop iterates over received bytes, which may contain extra data 
>> after the response, the: 
>>
>> 		dell_bl->resp_idx++;
>> 		if (dell_bl->resp_idx < dell_bl->resp_len)
>> 			continue;
>>
>> continues looping until we have received all the expected bytes. So here, past this
>> check, we are are at the point where we have a complete response and then we verify it.
>>
>> And on successful verification wake-up any waiters.
> 
> So effectively you want to terminate the loop on two conditions here:
> 
> a) dell_bl->resp_idx == dell_bl->resp_len (complete frame)
> a) if i == len (not yet received a full frame)
> 
> Why not code those rather than the current goto & continue madness?
> 
> Then, after the loop, you can test:
> 
> 	if (dell_bl->resp_idx == dell_bl->resp_len) {
> 		// calc checksum, etc.
> 	}
> 
> ?

Ok, I've added the following change for v3:

diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c
index bf5b12efcb19..66d8c6ddcb83 100644
--- a/drivers/platform/x86/dell/dell-uart-backlight.c
+++ b/drivers/platform/x86/dell/dell-uart-backlight.c
@@ -87,6 +87,7 @@ static int dell_uart_bl_command(struct dell_uart_backlight *dell_bl,
 	dell_bl->status = -EBUSY;
 	dell_bl->resp = resp;
 	dell_bl->resp_idx = 0;
+	dell_bl->resp_len = -1; /* Invalid / unset */
 	dell_bl->resp_max_len = resp_max_len;
 	dell_bl->pending_cmd = cmd[1];
 
@@ -219,7 +219,7 @@ static size_t dell_uart_bl_receive(struct serdev_device *serdev, const u8 *data,
 		return len;
 	}
 
-	for (i = 0; i < len; i++) {
+	for (i = 0; i < len && dell_bl->resp_idx != dell_bl->resp_len; i++, dell_bl->resp_idx++) {
 		dell_bl->resp[dell_bl->resp_idx] = data[i];
 
 		switch (dell_bl->resp_idx) {
@@ -228,46 +228,41 @@ static size_t dell_uart_bl_receive(struct serdev_device *serdev, const u8 *data,
 			if (dell_bl->resp_len < MIN_RESP_LEN) {
 				dev_err(dell_bl->dev, "Response length too small %d < %d\n",
 					dell_bl->resp_len, MIN_RESP_LEN);
-				dell_bl->status = -EIO;
-				goto wakeup;
+				goto error;
 			}
 
 			if (dell_bl->resp_len > dell_bl->resp_max_len) {
 				dev_err(dell_bl->dev, "Response length too big %d > %d\n",
 					dell_bl->resp_len, dell_bl->resp_max_len);
-				dell_bl->status = -EIO;
-				goto wakeup;
+				goto error;
 			}
 			break;
 		case RESP_CMD: /* CMD byte */
 			if (dell_bl->resp[RESP_CMD] != dell_bl->pending_cmd) {
 				dev_err(dell_bl->dev, "Response cmd 0x%02x != pending 0x%02x\n",
 					dell_bl->resp[RESP_CMD], dell_bl->pending_cmd);
-				dell_bl->status = -EIO;
-				goto wakeup;
+				goto error;
 			}
 			break;
 		}
+	}
 
-		dell_bl->resp_idx++;
-		if (dell_bl->resp_idx < dell_bl->resp_len)
-			continue;
-
+	if (dell_bl->resp_idx == dell_bl->resp_len) {
 		csum = dell_uart_checksum(dell_bl->resp, dell_bl->resp_len - 1);
 		if (dell_bl->resp[dell_bl->resp_len - 1] != csum) {
 			dev_err(dell_bl->dev, "Checksum mismatch got 0x%02x expected 0x%02x\n",
 				dell_bl->resp[dell_bl->resp_len - 1], csum);
-			dell_bl->status = -EIO;
-			goto wakeup;
+			goto error;
 		}
-
 		dell_bl->status = 0; /* Success */
-		goto wakeup;
+		wake_up(&dell_bl->wait_queue);
+		return i;
 	}
 
 	return len;
 
-wakeup:
+error:
+	dell_bl->status = -EIO;
 	wake_up(&dell_bl->wait_queue);
 	return i + 1;
 }

Regards,

Hans



> 
>>>> +		dell_bl->status = 1; /* Success */
>>>> +		goto wakeup;
>>>
>>> Huh? Now I'm totally lost how the control flow is supposed to go in this 
>>> function. Can you rethink this loop so it actual makes sense and doesn't 
>>> misuse gotos like this?
>>
>> This is the receive() callback from the UART the loop consumes bytes received
>> by the UART. The gotos stop consuming bytes in 2 cases:
>>
>> 1. An error (unexpected data) is encountered.
>> 2. A complete frame has been successfully received.
>>
>> The checking of the checksum + goto wakeup at the end of the loop is for 2.
>>
>> The:
>>
>> 	return len;
>>
>> after the loop indicates to the UART / tty-layer that all passed data
>> has been consumed and this path gets hit when the driver needs to wait
>> for more data because the response is not complete yet.
>>
>>>> +
>>>> +wakeup:
>>>> +	wake_up(&dell_bl->wait_queue);
>>>> +	return i + 1;
>>>> +}
> 
> 





[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux