Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/misc/bcm-vk/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 8 kB image not shown  

Quelle  bcm_vk_tty.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2018-2020 Broadcom.
 */


#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>

#include "bcm_vk.h"

/* TTYVK base offset is 0x30000 into BAR1 */
#define BAR1_TTYVK_BASE_OFFSET 0x300000
/* Each TTYVK channel (TO or FROM) is 0x10000 */
#define BAR1_TTYVK_CHAN_OFFSET 0x100000
/* Each TTYVK channel has TO and FROM, hence the * 2 */
#define BAR1_TTYVK_BASE(index) (BAR1_TTYVK_BASE_OFFSET + \
     ((index) * BAR1_TTYVK_CHAN_OFFSET * 2))
/* TO TTYVK channel base comes before FROM for each index */
#define TO_TTYK_BASE(index) BAR1_TTYVK_BASE(index)
#define FROM_TTYK_BASE(index) (BAR1_TTYVK_BASE(index) + \
     BAR1_TTYVK_CHAN_OFFSET)

struct bcm_vk_tty_chan {
 u32 reserved;
 u32 size;
 u32 wr;
 u32 rd;
 u32 *data;
};

#define VK_BAR_CHAN(v, DIR, e) ((v)->DIR##_offset \
     + offsetof(struct bcm_vk_tty_chan, e))
#define VK_BAR_CHAN_SIZE(v, DIR) VK_BAR_CHAN(v, DIR, size)
#define VK_BAR_CHAN_WR(v, DIR)  VK_BAR_CHAN(v, DIR, wr)
#define VK_BAR_CHAN_RD(v, DIR)  VK_BAR_CHAN(v, DIR, rd)
#define VK_BAR_CHAN_DATA(v, DIR, off) (VK_BAR_CHAN(v, DIR, data) + (off))

#define VK_BAR0_REGSEG_TTY_DB_OFFSET 0x86c

/* Poll every 1/10 of second - temp hack till we use MSI interrupt */
#define SERIAL_TIMER_VALUE (HZ / 10)

static void bcm_vk_tty_poll(struct timer_list *t)
{
 struct bcm_vk *vk = timer_container_of(vk, t, serial_timer);

 queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
 mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
}

irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id)
{
 struct bcm_vk *vk = dev_id;

 queue_work(vk->tty_wq_thread, &vk->tty_wq_work);

 return IRQ_HANDLED;
}

static void bcm_vk_tty_wq_handler(struct work_struct *work)
{
 struct bcm_vk *vk = container_of(work, struct bcm_vk, tty_wq_work);
 struct bcm_vk_tty *vktty;
 int card_status;
 int count;
 int i;
 int wr;
 u8 c;

 card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
 if (BCM_VK_INTF_IS_DOWN(card_status))
  return;

 for (i = 0; i < BCM_VK_NUM_TTY; i++) {
  count = 0;
  /* Check the card status that the tty channel is ready */
  if ((card_status & BIT(i)) == 0)
   continue;

  vktty = &vk->tty[i];

  /* Don't increment read index if tty app is closed */
  if (!vktty->is_opened)
   continue;

  /* Fetch the wr offset in buffer from VK */
  wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, from));

  /* safe to ignore until bar read gives proper size */
  if (vktty->from_size == 0)
   continue;

  if (wr >= vktty->from_size) {
   dev_err(&vk->pdev->dev,
    "ERROR: wq handler ttyVK%d wr:0x%x > 0x%x\n",
    i, wr, vktty->from_size);
   /* Need to signal and close device in this case */
   continue;
  }

  /*
 * Simple read of circular buffer and
 * insert into tty flip buffer
 */

  while (vk->tty[i].rd != wr) {
   c = vkread8(vk, BAR_1,
        VK_BAR_CHAN_DATA(vktty, from, vktty->rd));
   vktty->rd++;
   if (vktty->rd >= vktty->from_size)
    vktty->rd = 0;
   tty_insert_flip_char(&vktty->port, c, TTY_NORMAL);
   count++;
  }

  if (count) {
   tty_flip_buffer_push(&vktty->port);

   /* Update read offset from shadow register to card */
   vkwrite32(vk, vktty->rd, BAR_1,
      VK_BAR_CHAN_RD(vktty, from));
  }
 }
}

static int bcm_vk_tty_open(struct tty_struct *tty, struct file *file)
{
 int card_status;
 struct bcm_vk *vk;
 struct bcm_vk_tty *vktty;
 int index;

 /* initialize the pointer in case something fails */
 tty->driver_data = NULL;

 vk = (struct bcm_vk *)dev_get_drvdata(tty->dev);
 index = tty->index;

 if (index >= BCM_VK_NUM_TTY)
  return -EINVAL;

 vktty = &vk->tty[index];

 vktty->pid = task_pid_nr(current);
 vktty->to_offset = TO_TTYK_BASE(index);
 vktty->from_offset = FROM_TTYK_BASE(index);

 /* Do not allow tty device to be opened if tty on card not ready */
 card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
 if (BCM_VK_INTF_IS_DOWN(card_status) || ((card_status & BIT(index)) == 0))
  return -EBUSY;

 /*
 * Get shadow registers of the buffer sizes and the "to" write offset
 * and "from" read offset
 */

 vktty->to_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, to));
 vktty->wr = vkread32(vk, BAR_1,  VK_BAR_CHAN_WR(vktty, to));
 vktty->from_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, from));
 vktty->rd = vkread32(vk, BAR_1,  VK_BAR_CHAN_RD(vktty, from));
 vktty->is_opened = true;

 if (tty->count == 1 && !vktty->irq_enabled) {
  timer_setup(&vk->serial_timer, bcm_vk_tty_poll, 0);
  mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
 }
 return 0;
}

static void bcm_vk_tty_close(struct tty_struct *tty, struct file *file)
{
 struct bcm_vk *vk = dev_get_drvdata(tty->dev);

 if (tty->index >= BCM_VK_NUM_TTY)
  return;

 vk->tty[tty->index].is_opened = false;

 if (tty->count == 1)
  timer_delete_sync(&vk->serial_timer);
}

static void bcm_vk_tty_doorbell(struct bcm_vk *vk, u32 db_val)
{
 vkwrite32(vk, db_val, BAR_0,
    VK_BAR0_REGSEG_DB_BASE + VK_BAR0_REGSEG_TTY_DB_OFFSET);
}

static ssize_t bcm_vk_tty_write(struct tty_struct *tty, const u8 *buffer,
    size_t count)
{
 int index;
 struct bcm_vk *vk;
 struct bcm_vk_tty *vktty;
 size_t i;

 index = tty->index;
 vk = dev_get_drvdata(tty->dev);
 vktty = &vk->tty[index];

 /* Simple write each byte to circular buffer */
 for (i = 0; i < count; i++) {
  vkwrite8(vk, buffer[i], BAR_1,
    VK_BAR_CHAN_DATA(vktty, to, vktty->wr));
  vktty->wr++;
  if (vktty->wr >= vktty->to_size)
   vktty->wr = 0;
 }
 /* Update write offset from shadow register to card */
 vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));
 bcm_vk_tty_doorbell(vk, 0);

 return count;
}

static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)
{
 struct bcm_vk *vk = dev_get_drvdata(tty->dev);

 return vk->tty[tty->index].to_size - 1;
}

static const struct tty_operations serial_ops = {
 .open = bcm_vk_tty_open,
 .close = bcm_vk_tty_close,
 .write = bcm_vk_tty_write,
 .write_room = bcm_vk_tty_write_room,
};

int bcm_vk_tty_init(struct bcm_vk *vk, char *name)
{
 int i;
 int err;
 struct tty_driver *tty_drv;
 struct device *dev = &vk->pdev->dev;

 tty_drv = tty_alloc_driver
    (BCM_VK_NUM_TTY,
     TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
 if (IS_ERR(tty_drv))
  return PTR_ERR(tty_drv);

 /* Save struct tty_driver for uninstalling the device */
 vk->tty_drv = tty_drv;

 /* initialize the tty driver */
 tty_drv->driver_name = KBUILD_MODNAME;
 tty_drv->name = kstrdup(name, GFP_KERNEL);
 if (!tty_drv->name) {
  err = -ENOMEM;
  goto err_tty_driver_kref_put;
 }
 tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
 tty_drv->subtype = SERIAL_TYPE_NORMAL;
 tty_drv->init_termios = tty_std_termios;
 tty_set_operations(tty_drv, &serial_ops);

 /* register the tty driver */
 err = tty_register_driver(tty_drv);
 if (err) {
  dev_err(dev, "tty_register_driver failed\n");
  goto err_kfree_tty_name;
 }

 for (i = 0; i < BCM_VK_NUM_TTY; i++) {
  struct device *tty_dev;

  tty_port_init(&vk->tty[i].port);
  tty_dev = tty_port_register_device_attr(&vk->tty[i].port,
       tty_drv, i, dev, vk,
       NULL);
  if (IS_ERR(tty_dev)) {
   err = PTR_ERR(tty_dev);
   goto unwind;
  }
  vk->tty[i].is_opened = false;
 }

 INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);
 vk->tty_wq_thread = create_singlethread_workqueue("tty");
 if (!vk->tty_wq_thread) {
  dev_err(dev, "Fail to create tty workqueue thread\n");
  err = -ENOMEM;
  goto unwind;
 }
 return 0;

unwind:
 while (--i >= 0)
  tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);
 tty_unregister_driver(tty_drv);

err_kfree_tty_name:
 kfree(tty_drv->name);
 tty_drv->name = NULL;

err_tty_driver_kref_put:
 tty_driver_kref_put(tty_drv);

 return err;
}

void bcm_vk_tty_exit(struct bcm_vk *vk)
{
 int i;

 timer_delete_sync(&vk->serial_timer);
 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
  tty_port_unregister_device(&vk->tty[i].port,
        vk->tty_drv,
        i);
  tty_port_destroy(&vk->tty[i].port);
 }
 tty_unregister_driver(vk->tty_drv);

 kfree(vk->tty_drv->name);
 vk->tty_drv->name = NULL;

 tty_driver_kref_put(vk->tty_drv);
}

void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)
{
 struct bcm_vk_tty *vktty;
 int i;

 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
  vktty = &vk->tty[i];
  if (vktty->pid)
   kill_pid(find_vpid(vktty->pid), SIGKILL, 1);
 }
}

void bcm_vk_tty_wq_exit(struct bcm_vk *vk)
{
 cancel_work_sync(&vk->tty_wq_work);
 destroy_workqueue(vk->tty_wq_thread);
}

Messung V0.5
C=96 H=96 G=95

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.