5.4.2 Драйвер сетевого периферийного устройства

Предыдущая  Содержание  Следующая V*D*V

Чтобы объяснить модель драйвера периферийного устройства, возьмём в качестве примера сетевое устройство. Оно реализовано в файле drivers/usb/gadget/ether.c.

Функция init драйвера любого драйвера периферийного устройства должна вызывать интерфейс usb_gadget_register_driver.

 

static int __init init (void)

{

  return usb_gadget_register_driver (&eth_driver);

}

module_init (init);

 

eth_driver является структурой usb_gadget_driver, заполненной соответствующими обработчиками.

 

static struct usb_gadget_driver eth_driver = {

 

#ifdef CONFIG_USB_GADGET_DUALSPEED

  .speed      = USB_SPEED_HIGH,

#else

  .speed      = USB_SPEED_FULL,

#endif

 

  .function   = (char *) driver_desc,

  .bind       = eth_bind,

  .unbind     = eth_unbind,

  .setup      = eth_setup,

  .disconnect = eth_disconnect,

};

 

При вызове регистрации из драйвера контроллера вызывается usb_gadget_driver.bind. Ожидается, что bind() драйвера периферийного устройства выполнит следующие действия:

 

Проинициализирует зависимые от устройства структуры данных.

Присоединится к необходимой подсистеме драйверов ядра (например, драйверу последовательного порта, сетевому драйверу, драйверу устройств хранения и так далее).

Проинициализирует блок запроса оконечной точки 0.

 

eth_bind представляет собой функцию привязки.

 

static int

eth_bind (struct usb_gadget *gadget)

{

      …

      …

  net = alloc_etherdev (sizeof *dev);

      …

  net->hard_start_xmit = eth_start_xmit;

  net->open = eth_open;

  net->stop = eth_stop;

  net->do_ioctl = eth_ioctl;

 

  /* Выделение памяти для EP0 */

  dev->req = usb_ep_alloc_request (gadget->ep0,

                                   GFP_KERNEL);

      …

  dev->req->complete = eth_setup_complete;

  dev->req->buf = usb_ep_alloc_buffer (gadget->ep0,

                  USB_BUFSIZ, &dev->req->dma, GFP_KERNEL);

      …

  status = register_netdev (dev->net);

      …

}

 

После завершения настройки, устройство полностью сконфигурировано и функционирует как обычное устройство в стеке драйверов ядра. В данном примере сетевой USB драйвер подключает себя к стеку сетевых драйверов с помощью register_netdev(). Это позволяет приложениям использовать этот интерфейс в качестве стандартного сетевого интерфейса.

 

Каждый блок USB запроса (URB) требует, чтобы блоку была выделена память, и чтобы он был связан с процедурой завершения. Все запросы оконечной точки ставятся в очередь, потому что они ждут опроса данных от контроллера хоста/корня шины. Как только запрос был обработан оборудованием, драйвер контроллера вызывает связанную с ним процедуру завершения.

 

Функция передачи данных в любом драйвере по существу делает следующее:

 

Создаёт новый URB или получает его из предварительно созданного пула

Связывает данные и размер данных URB с данными и размером, предоставляемыми драйвером верхнего уровня

Помещает URB в очередь для передачи данных в соответствующей оконечной точке

 

Функцией передачи драйвера сетевого периферийного устройства является eth_start_xmit.

 

static int eth_start_xmit (struct sk_buff *skb,

                           struct net_device *net)

{

  struct eth_dev*dev = (struct eth_dev *) net->priv;

  int length = skb->len;

    …

  req->buf = skb->data;

  req->context = skb;

  req->complete = tx_complete;

  req->length = length;

    …

  retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);

    …

}

 

Приём данных также требует использования URB и делается следующим образом:

 

Создаётся список пустых URB.

Инициализируется каждый из них с надлежащей процедурой завершения. Процедура завершения для приёма указывает верхним уровням стека сетевых протоколов о прибытии данных.

Блоки помещаются в очередь в соответствующей оконечной точке.

 

Во время старта драйвер сетевого периферийного устройства заполняет очередь оконечной точки URB-ами с помощью функции rx_submit.

 

static int

rx_submit (struct eth_dev *dev, struct usb_request *req,

           int gfp_flags)

{

  struct sk_buff *skb;

  size_t size;

 

  size = (sizeof (struct ethhdr) + dev->net->mtu +

                                   RX_EXTRA);

 

  skb = alloc_skb (size, gfp_flags);

    …

  req->buf = skb->data;

  req->length = size;

  req->complete = rx_complete;

  req->context = skb;

  retval = usb_ep_queue (dev->out_ep, req, gfp_flags);

    …

}

 

Индикация приёма данных для сетевого уровня осуществляется в процедуре завершения.

 

static void rx_complete (struct usb_ep *ep,

                         struct usb_request *req)

{

  struct sk_buff *skb = req->context;

  struct eth_dev *dev = ep->driver_da

    …

  skb_put (skb, req->actual);

  skb->dev = dev->net;

  skb->protocol = eth_type_trans (skb, dev->net);

    …

  netif_rx (skb);

}

 

Предыдущая  Содержание  Следующая