Re: [PATCH v4 2/3] greybus: Add BeaglePlay Linux Driver

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

 



On 02/09/2023 20:28, Ayush Singh wrote:
> Add the Greybus host driver for BeaglePlay board by BeagleBoard.org.
> 
> The current greybus setup involves running SVC in a user-space
> application (GBridge) and using netlink to communicate with kernel
> space. GBridge itself uses wpanusb kernel driver, so the greybus messages
> travel from kernel space (gb_netlink) to user-space (GBridge) and then
> back to kernel space (wpanusb) before reaching CC1352.
> 
> This driver directly communicates with CC1352 (running SVC Zephyr
> application). Thus, it simplifies the complete greybus setup eliminating
> user-space GBridge.
> 
> This driver is responsible for the following:
> - Start SVC (CC1352) on driver load.
> - Send/Receive Greybus messages to/from CC1352 using HDLC over UART.
> - Print Logs from CC1352.
> - Stop SVC (CC1352) on driver load.
> 
> Signed-off-by: Ayush Singh <ayushdevel1325@xxxxxxxxx>
> ---
>  MAINTAINERS                     |   1 +
>  drivers/greybus/Kconfig         |  10 +
>  drivers/greybus/Makefile        |   3 +-
>  drivers/greybus/gb-beagleplay.c | 494 ++++++++++++++++++++++++++++++++
>  4 files changed, 507 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/greybus/gb-beagleplay.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9d1b49a6dfad..3cbf2c87fb14 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8974,6 +8974,7 @@ M:	Ayush Singh <ayushdevel1325@xxxxxxxxx>
>  L:	greybus-dev@xxxxxxxxxxxxxxxx (moderated for non-subscribers)
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/serial/beaglecc1352.yaml
> +F:	drivers/greybus/gb-beagleplay.c
>  
>  GREYBUS SUBSYSTEM
>  M:	Johan Hovold <johan@xxxxxxxxxx>
> diff --git a/drivers/greybus/Kconfig b/drivers/greybus/Kconfig
> index 78ba3c3083d5..62f091368272 100644
> --- a/drivers/greybus/Kconfig
> +++ b/drivers/greybus/Kconfig
> @@ -28,5 +28,15 @@ config GREYBUS_ES2
>  	  To compile this code as a module, choose M here: the module
>  	  will be called gb-es2.ko
>  
> +config GREYBUS_BEAGLEPLAY

B shouild be before E. Keep things ordered, usually by name, do not add
stuff at the end.

> +	tristate "Greybus BeaglePlay driver"
> +	depends on TTY
> +	help
> +	  Select this option if you have a BeaglePlay where CC1352
> +		co-processor acts as Greybus SVC.
> +
> +	  To compile this code as a module, chose M here: the module
> +	  will be called gb-beagleplay.ko
> +
>  endif	# GREYBUS
>  
> diff --git a/drivers/greybus/Makefile b/drivers/greybus/Makefile
> index 9bccdd229aa2..63f9cb3b2df0 100644
> --- a/drivers/greybus/Makefile
> +++ b/drivers/greybus/Makefile
> @@ -23,4 +23,5 @@ gb-es2-y := es2.o
>  
>  obj-$(CONFIG_GREYBUS_ES2)	+= gb-es2.o
>  
> -
> +# Beagleplay Greybus driver

Drop useless comment

> +obj-$(CONFIG_GREYBUS_BEAGLEPLAY)	+= gb-beagleplay.o

Keep things ordered, usually by name, do not add stuff at the end.


...

> +/* The maximum number of CPorts supported by Greybus Host Device */
> +#define BEAGLEPLAY_GB_MAX_CPORTS 32
> +
> +static const struct of_device_id gb_beagleplay_of_match[] = {
> +	{
> +		.compatible = "beagle,cc1352",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, gb_beagleplay_of_match);

The entire structure should be next to probe, not at beginning of file.

> +

...

> +static void hdlc_handle_rx_frame(struct gb_beagleplay *bg)
> +{
> +	u8 address = bg->rx_buffer[0];
> +	char *buffer = &bg->rx_buffer[2];
> +	size_t buffer_len = bg->rx_buffer_len - 4;
> +
> +	switch (address) {
> +	case ADDRESS_DBG:
> +		hdlc_handle_dbg_frame(bg, buffer, buffer_len);
> +		break;
> +	case ADDRESS_GREYBUS:
> +		hdlc_handle_greybus_frame(bg, buffer, buffer_len);
> +		break;
> +	default:
> +		dev_warn(&bg->serdev->dev, "Got Unknown Frame %u", address);

ratelimit
Probably as well in several places with possible flooding.

> +	}
> +}
> +
> +static void hdlc_transmit(struct work_struct *work)
> +{
> +	struct gb_beagleplay *bg =
> +		container_of(work, struct gb_beagleplay, tx_work);
> +
> +	spin_lock_bh(&bg->tx_consumer_lock);
> +	hdlc_write(bg);
> +	spin_unlock_bh(&bg->tx_consumer_lock);
> +}
> +
> +static void hdlc_send_async(struct gb_beagleplay *bg, u8 address, u8 control,
> +			    const struct hdlc_payload payloads[], size_t count)
> +{
> +	size_t i;
> +
> +	/* HDLC_FRAME

Use Linux style of comments:
/*
 * foo bar

> +	 * 0 address : 0x01
> +	 * 1 control : 0x03
> +	 * contents
> +	 * x/y crc
> +	 * HDLC_FRAME
> +	 */
> +
> +	spin_lock(&bg->tx_producer_lock);
> +
> +	hdlc_append_tx_frame(bg);
> +	hdlc_append_tx_u8(bg, address);
> +	hdlc_append_tx_u8(bg, control);
> +	for (i = 0; i < count; ++i) {
> +		hdlc_append_tx_buffer(bg, payloads[i].payload,
> +				      payloads[i].length);
> +	}
> +	hdlc_append_tx_crc(bg);
> +	hdlc_append_tx_frame(bg);
> +
> +	spin_unlock(&bg->tx_producer_lock);
> +
> +	schedule_work(&bg->tx_work);
> +}
> +
> +static void hdlc_send_s_frame_ack(struct gb_beagleplay *bg)
> +{
> +	hdlc_send_async(bg, bg->rx_buffer[0], (bg->rx_buffer[1] >> 1) & 0x7,
> +			NULL, 0);
> +}
> +
> +static int hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count)
> +{
> +	u16 crc_check;
> +	size_t i;
> +	u8 c, ctrl;
> +
> +	for (i = 0; i < count; ++i) {
> +		c = data[i];
> +
> +		switch (c) {
> +		case HDLC_FRAME:
> +			if (bg->rx_buffer_len) {
> +				crc_check = crc_ccitt(0xffff, bg->rx_buffer,
> +						      bg->rx_buffer_len);
> +				if (crc_check == 0xf0b8) {
> +					ctrl = bg->rx_buffer[1];
> +					if ((ctrl & 1) == 0) {
> +						/* I-Frame, send S-Frame ACK */
> +						hdlc_send_s_frame_ack(bg);
> +					}
> +					hdlc_handle_rx_frame(bg);
> +				} else {
> +					dev_err(&bg->serdev->dev,
> +						"CRC Failed from %02x: 0x%04x",
> +						bg->rx_buffer[0], crc_check);
> +				}
> +			}
> +			bg->rx_buffer_len = 0;
> +			break;
> +		case HDLC_ESC:
> +			bg->rx_in_esc = 1;
> +			break;
> +		default:
> +			if (bg->rx_in_esc) {
> +				c ^= 0x20;
> +				bg->rx_in_esc = 0;
> +			}
> +
> +			if (bg->rx_buffer_len < MAX_RX_HDLC) {
> +				bg->rx_buffer[bg->rx_buffer_len] = c;
> +				bg->rx_buffer_len++;
> +			} else {
> +				dev_err(&bg->serdev->dev, "RX Buffer Overflow");
> +				bg->rx_buffer_len = 0;
> +			}
> +		}
> +	}
> +
> +	return count;
> +}
> +
> +static void hdlc_init(struct gb_beagleplay *bg)
> +{
> +	INIT_WORK(&bg->tx_work, hdlc_transmit);
> +	spin_lock_init(&bg->tx_producer_lock);
> +	spin_lock_init(&bg->tx_consumer_lock);
> +	bg->tx_circ_buf.head = 0;
> +	bg->tx_circ_buf.tail = 0;
> +	bg->tx_circ_buf.buf =
> +		devm_kmalloc(&bg->serdev->dev, TX_CIRC_BUF_SIZE, GFP_KERNEL);
> +	bg->rx_buffer_len = 0;
> +	bg->rx_in_esc = 0;
> +}
> +
> +static void hdlc_deinit(struct gb_beagleplay *bg)
> +{
> +	flush_work(&bg->tx_work);
> +}
> +
> +static int gb_beagleplay_tty_receive(struct serdev_device *serdev,
> +				     const unsigned char *data, size_t count)
> +{
> +	struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev);
> +
> +	return hdlc_rx(bg, data, count);
> +}
> +
> +static void beagleplay_greybus_tty_wakeup(struct serdev_device *serdev)
> +{
> +	struct gb_beagleplay *bg = serdev_device_get_drvdata(serdev);
> +
> +	schedule_work(&bg->tx_work);
> +}
> +
> +static struct serdev_device_ops gb_beagleplay_ops = {
> +	.receive_buf = gb_beagleplay_tty_receive,
> +	.write_wakeup = beagleplay_greybus_tty_wakeup,
> +};
> +
> +static int gb_message_send(struct gb_host_device *hd, u16 cport_id,
> +			   struct gb_message *msg, gfp_t gfp_mask)
> +{
> +	struct gb_beagleplay *bg = dev_get_drvdata(&hd->dev);
> +	struct hdlc_payload payloads[2];
> +
> +	dev_dbg(&hd->dev,
> +		"Sending Greybus message with Operation %u, Type: %X on Cport %u",
> +		msg->header->operation_id, msg->header->type, cport_id);
> +
> +	if (msg->header->size > RX_HDLC_PAYLOAD) {
> +		dev_err(&hd->dev, "Greybus message too big");
> +		return -E2BIG;
> +	}
> +
> +	memcpy(msg->header->pad, &cport_id, sizeof(cport_id));
> +
> +	payloads[0].payload = msg->header;
> +	payloads[0].length = sizeof(*msg->header);
> +	payloads[1].payload = msg->payload;
> +	payloads[1].length = msg->payload_size;
> +
> +	hdlc_send_async(bg, ADDRESS_GREYBUS, 0x03, payloads, 2);
> +	greybus_message_sent(bg->gb_host_device, msg, 0);
> +
> +	return 0;
> +}
> +
> +static void gb_message_cancel(struct gb_message *message)
> +{
> +}
> +
> +static struct gb_hd_driver gb_hdlc_driver = { .message_send = gb_message_send,
> +					      .message_cancel =
> +						      gb_message_cancel };
> +
> +static void gb_beagleplay_start_svc(struct gb_beagleplay *bg)
> +{
> +	const u8 command = CONTROL_SVC_START;
> +	const struct hdlc_payload payload = { .length = 1,
> +					      .payload = (void *)&command };
> +
> +	hdlc_send_async(bg, ADDRESS_CONTROL, 0x03, &payload, 1);
> +}
> +
> +static void gb_beagleplay_stop_svc(struct gb_beagleplay *bg)
> +{
> +	const u8 command = CONTROL_SVC_STOP;
> +	const struct hdlc_payload payload = { .length = 1,
> +					      .payload = (void *)&command };
> +
> +	hdlc_send_async(bg, ADDRESS_CONTROL, 0x03, &payload, 1);
> +}
> +
> +static int gb_beagleplay_probe(struct serdev_device *serdev)
> +{
> +	u32 speed = 115200;
> +	int ret = 0;
> +	struct gb_beagleplay *bg =
> +		devm_kmalloc(&serdev->dev, sizeof(*bg), GFP_KERNEL);
> +
> +	if (!bg) {
> +		dev_err(&serdev->dev, "Failed to allocate driver");

Nope. Run coccinelle. None of the code in the kernel drivers prints
error messages for allocation failures.

> +		return -ENOMEM;
> +	}
> +
> +	bg->serdev = serdev;
> +	serdev_device_set_drvdata(serdev, bg);
> +	serdev_device_set_client_ops(serdev, &gb_beagleplay_ops);
> +	ret = serdev_device_open(serdev);
> +	if (ret) {
> +		dev_err(&bg->serdev->dev, "Unable to Open Device");

return dev_err_probe

> +		return ret;
> +	}
> +	speed = serdev_device_set_baudrate(serdev, speed);
> +	dev_info(&bg->serdev->dev, "Using baudrate %u", speed);
> +	serdev_device_set_flow_control(serdev, false);
> +
> +	hdlc_init(bg);
> +
> +	/* Greybus setup */
> +	bg->gb_host_device = gb_hd_create(&gb_hdlc_driver, &serdev->dev,
> +					  TX_CIRC_BUF_SIZE,
> +					  BEAGLEPLAY_GB_MAX_CPORTS);
> +	if (IS_ERR(bg->gb_host_device)) {
> +		dev_err(&bg->serdev->dev,
> +			"Unable to create Greybus Host Device");
> +		ret = -1;

ret = PTR_ERR, not some random number.

> +		goto free_hdlc;
> +	}
> +	ret = gb_hd_add(bg->gb_host_device);
> +	if (ret) {
> +		dev_err(&serdev->dev, "Failed to add Greybus Host Device");
> +		goto free_gb_hd;
> +	}
> +	dev_set_drvdata(&bg->gb_host_device->dev, bg);
> +
> +	gb_beagleplay_start_svc(bg);
> +
> +	dev_info(&bg->serdev->dev, "Successful Beagleplay Greybus Probe");


Drop silly tracing messages.

> +
> +	return 0;
> +
> +free_gb_hd:
> +	gb_hd_del(bg->gb_host_device);
> +	gb_hd_put(bg->gb_host_device);
> +free_hdlc:
> +	hdlc_deinit(bg);
> +	serdev_device_close(serdev);
> +	return ret;
> +}
> +
> +static void gb_beagleplay_remove(struct serdev_device *serdev)
> +{
> +	struct gb_beagleplay *beagleplay_greybus =
> +		serdev_device_get_drvdata(serdev);
> +
> +	dev_info(&beagleplay_greybus->serdev->dev,
> +		 "Remove BeaglePlay Greybus Driver");

Drop silly tracing messages.

> +
> +	gb_hd_del(beagleplay_greybus->gb_host_device);
> +	gb_hd_put(beagleplay_greybus->gb_host_device);
> +
> +	gb_beagleplay_stop_svc(beagleplay_greybus);
> +
> +	hdlc_deinit(beagleplay_greybus);
> +
> +	serdev_device_close(serdev);
> +}
> +
> +static struct serdev_device_driver gb_beagleplay_driver = {
> +	.probe = gb_beagleplay_probe,
> +	.remove = gb_beagleplay_remove,
> +	.driver = {
> +	      .name = "gb_beagleplay",
> +	      .of_match_table = of_match_ptr(gb_beagleplay_of_match),

Drop of_match_ptr(). You will have warnings.


Best regards,
Krzysztof




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux