5.4.2 Драйвер сетевого периферийного устройства |
Предыдущая Содержание Следующая |
Чтобы объяснить модель драйвера периферийного устройства, возьмём в качестве примера сетевое устройство. Оно реализовано в файле drivers/usb/gadget/ether.c. Функция init драйвера любого драйвера периферийного устройства должна вызывать интерфейс usb_gadget_register_driver.
static int __init init (void) { return usb_gadget_register_driver (ð_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(). Это позволяет приложениям использовать этот интерфейс в качестве стандартного сетевого интерфейса.
Функция передачи данных в любом драйвере по существу делает следующее:
▪Создаёт новый 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); }
| |
Предыдущая Содержание Следующая |