vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / drivers / network / usb_asix / AX88178Device.cpp
blobee6497419e665486db19604df24008960094b15e
1 /*
2 * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
3 * Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li>
4 * Distributed under the terms of the MIT license.
6 * Heavily based on code of the
7 * Driver for USB Ethernet Control Model devices
8 * Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
9 * Distributed under the terms of the MIT license.
14 #include "AX88178Device.h"
16 #include <net/if_media.h>
18 #include "ASIXVendorRequests.h"
19 #include "Settings.h"
22 // Most of vendor requests for all supported chip types use the same
23 // constants (see ASIXVendorRequests.h) but the layout of request data
24 // may be slightly diferrent for specific chip type. Below is a quick
25 // reference for AX88178 vendor requests data layout.
27 // READ_RXTX_SRAM, //C002_AA0B_0C00_0800 Rx/Tx SRAM Read
28 // WRITE_RXTX_SRAM, //4003_AA0B_0C00_0800 Rx/Tx SRAM Write
29 // SW_MII_OP, //4006_0000_0000_0000 SW Serial Management Control
30 // READ_MII, //c007_aa00_cc00_0200 PHY Read
31 // WRITE_MII, //4008_aa00_cc00_0200 PHY Write
32 // READ_MII_STATUS, //c009_0000_0000_0100 Serial Management Status
33 // HW_MII_OP, //400a_0000_0000_0000 HW Serial Management Control
34 // READ_SROM, //C00B_AA00_0000_0200 SROM Read
35 // WRITE_SROM, //400C_AA00_CCDD_0000 SROM Write
36 // WRITE_SROM_ENABLE, //400D_0000_0000_0000 SROM Write Enable
37 // WRITE_SROM_DISABLE, //400E_0000_0000_0000 SROM Write Disable
38 // READ_RX_CONTROL, //C00F_0000_0000_0200 Read Rx Control
39 // WRITE_RX_CONTROL, //4010_AABB_0000_0000 Write Rx Control
40 // READ_IPGS, //C011_0000_0000_0300 Read IPG/IPG1/IPG2 Register
41 // WRITE_IPGS, //4012_AABB_CC00_0000 Write IPG/IPG1/IPG2 Register
42 // READ_NODEID, //C013_0000_0000_0600 Read Node ID
43 // WRITE_NODEID, //4014_0000_0000_0600 Write Node ID
44 // READ_MF_ARRAY, //C015_0000_0000_0800 Read Multicast Filter Array
45 // WRITE_MF_ARRAY, //4016_0000_0000_0800 Write Multicast Filter Array
46 // READ_TEST, //4017_AA00_0000_0000 Write Test Register
47 // READ_PHYID, //C019_0000_0000_0200 Read Ethernet/HomePNA PHY Address
48 // READ_MEDIUM_STATUS, //C01A_0000_0000_0200 Read Medium Status
49 // WRITE_MEDIUM_MODE, //401B_AABB_0000_0000 Write Medium Mode Register
50 // GET_MONITOR_MODE, //C01C_0000_0000_0100 Read Monitor Mode Status
51 // SET_MONITOR_MODE, //401D_AA00_0000_0000 Write Monitor Mode Register
52 // READ_GPIOS, //C01E_0000_0000_0100 Read GPIOs Status
53 // WRITE_GPIOS, //401F_AA00_0000_0000 Write GPIOs
54 // WRITE_SOFT_RESET, //4020_AA00_0000_0000 Write Software Reset
55 // READ_MIIS_IF_STATE, //C021_AA00_0000_0100 Read MII/GMII/RGMII Iface Status
56 // WRITE_MIIS_IF_STATE, //4022_AA00_0000_0000 Write MII/GMII/RGMII Iface Control
58 // RX Control Register bits
59 // RXCTL_PROMISCUOUS, // forward all frames up to the host
60 // RXCTL_ALL_MULTICAT, // forward all multicast frames up to the host
61 // RXCTL_SEP, // forward frames with CRC error up to the host
62 // RXCTL_BROADCAST, // forward broadcast frames up to the host
63 // RXCTL_MULTICAST, // forward multicast frames that are
64 // matching to multicast filter up to the host
65 // RXCTL_AP, // forward unicast frames that are matching
66 // to multicast filter up to the host
67 // RXCTL_START, // ethernet MAC start operating
68 // RXCTL_USB_MFB, // Max Frame Burst TX on USB
71 // PHY IDs request answer data layout
72 struct AX88178_PhyIDs {
73 uint8 SecPhyID;
74 uint8 PriPhyID2;
75 } _PACKED;
78 // Medium state bits
79 enum AX88178_MediumState {
80 MEDIUM_STATE_GM = 0x0001,
81 MEDIUM_STATE_FD = 0x0002,
82 MEDIUM_STATE_AC = 0x0004, // must be always set
83 MEDIUM_STATE_ENCK = 0x0008,
84 MEDIUM_STATE_RFC = 0x0010,
85 MEDIUM_STATE_TFC = 0x0020,
86 MEDIUM_STATE_JFE = 0x0040,
87 MEDIUM_STATE_PF_ON = 0x0080,
88 MEDIUM_STATE_PF_OFF = 0x0000,
89 MEDIUM_STATE_RE = 0x0100,
90 MEDIUM_STATE_PS_100 = 0x0200,
91 MEDIUM_STATE_PS_10 = 0x0000,
92 MEDIUM_STATE_SBP1 = 0x0800,
93 MEDIUM_STATE_SBP0 = 0x0000,
94 MEDIUM_STATE_SM_ON = 0x1000
98 // Monitor Mode bits
99 enum AX88178_MonitorMode {
100 MONITOR_MODE_MOM = 0x01,
101 MONITOR_MODE_RWLU = 0x02,
102 MONITOR_MODE_RWMP = 0x04,
103 MONITOR_MODE_US = 0x10
107 // General Purpose I/O Register
108 enum AX88178_GPIO {
109 GPIO_OO_0EN = 0x01,
110 GPIO_IO_0 = 0x02,
111 GPIO_OO_1EN = 0x04,
112 GPIO_IO_1 = 0x08,
113 GPIO_OO_2EN = 0x10,
114 GPIO_IO_2 = 0x20,
115 GPIO_RSE = 0x80
119 // Software Reset Register bits
120 enum AX88178_SoftwareReset {
121 SW_RESET_RR = 0x01,
122 SW_RESET_RT = 0x02,
123 SW_RESET_PRTE = 0x04,
124 SW_RESET_PRL = 0x08,
125 SW_RESET_BZ = 0x10,
126 SW_RESET_BIT6 = 0x40 // always set to 1
130 // MII/GMII/RGMII Interface Conttrol
131 enum AX88178_MIISInterfaceStatus {
132 MIIS_IF_STATE_DM = 0x01,
133 MIIS_IF_STATE_RB = 0x02
137 // Notification data layout
138 struct AX88178_Notify {
139 uint8 btA1;
140 uint8 bt01;
141 uint8 btBB; // AX88178_BBState below
142 uint8 bt03;
143 uint16 regCCDD;
144 uint16 regEEFF;
145 } _PACKED;
148 // Link-State bits
149 enum AX88178_BBState {
150 LINK_STATE_PPLS = 0x01,
151 LINK_STATE_SPLS = 0x02,
152 LINK_STATE_FLE = 0x04,
153 LINK_STATE_MDINT = 0x08
157 const uint16 maxFrameSize = 1536;
160 AX88178Device::AX88178Device(usb_device device, DeviceInfo& deviceInfo)
162 ASIXDevice(device, deviceInfo)
164 fStatus = InitDevice();
168 status_t
169 AX88178Device::InitDevice()
171 fFrameSize = maxFrameSize;
172 fUseTRXHeader = true;
174 fReadNodeIDRequest = READ_NODEID;
176 fNotifyBufferLength = sizeof(AX88178_Notify);
177 fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
178 if (fNotifyBuffer == NULL) {
179 TRACE_ALWAYS("Error of allocating memory for notify buffer.\n");
180 return B_NO_MEMORY;
183 TRACE_RET(B_OK);
184 return B_OK;
188 status_t
189 AX88178Device::SetupDevice(bool deviceReplugged)
191 status_t result = ASIXDevice::SetupDevice(deviceReplugged);
192 if (result != B_OK) {
193 return result;
196 result = fMII.Init(fDevice);
198 if (result != B_OK) {
199 return result;
202 size_t actualLength = 0;
203 // get the "magic" word from EEPROM
204 result = gUSBModule->send_request(fDevice,
205 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SROM_ENABLE,
206 0, 0, 0, 0, &actualLength);
208 if (result != B_OK) {
209 TRACE_ALWAYS("Error of enabling SROM access:%#010x\n", result);
210 return result;
213 uint16 eepromData = 0;
214 status_t op_result = gUSBModule->send_request(fDevice,
215 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_SROM,
216 0x17, 0, sizeof(eepromData), &eepromData, &actualLength);
218 if (op_result != B_OK) {
219 TRACE_ALWAYS("Error of reading SROM data:%#010x\n", result);
222 if (actualLength != sizeof(eepromData)) {
223 TRACE_ALWAYS("Mismatch of reading SROM data."
224 "Read %d bytes instead of %d\n", actualLength, sizeof(eepromData));
227 result = gUSBModule->send_request(fDevice,
228 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SROM_DISABLE,
229 0, 0, 0, 0, &actualLength);
231 if (result != B_OK) {
232 TRACE_ALWAYS("Error of disabling SROM access: %#010x\n", result);
233 return result;
236 if (op_result != B_OK) {
237 return op_result;
240 // some shaman's dances with GPIO
241 struct GPIOData {
242 bigtime_t delay;
243 uint16 value;
244 } GPIOCommands[] = {
245 // eeprom bit 8 is off
246 { 40000 , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE },
247 { 30000 , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
248 { 300000 , GPIO_OO_2EN | GPIO_OO_1EN | GPIO_IO_1 },
249 { 30000 , GPIO_OO_2EN | GPIO_IO_2 | GPIO_OO_1EN | GPIO_IO_1 },
250 // eeprom bit 8 is on
251 { 40000 , GPIO_OO_1EN | GPIO_IO_1 | GPIO_RSE },
252 { 30000 , GPIO_OO_1EN },
253 { 30000 , GPIO_OO_1EN | GPIO_IO_1 },
256 bool bCase8 = (eepromData >> 8) != 1;
257 size_t from = bCase8 ? 0 : 4;
258 size_t to = bCase8 ? 3 : 6;
260 for (size_t i = from; i <= to; i++) {
261 result = gUSBModule->send_request(fDevice,
262 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_GPIOS,
263 GPIOCommands[i].value, 0, 0, 0, &actualLength);
265 snooze(GPIOCommands[i].delay);
267 if (result != B_OK) {
268 TRACE_ALWAYS("Error of GPIO setup command %d:[%#04x]: %#010x\n",
269 i, GPIOCommands[i].value, result);
270 return result;
274 uint8 uSWReset = 0;
275 // finally a bit of exercises for SW reset register...
276 result = gUSBModule->send_request(fDevice,
277 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
278 uSWReset, 0, 0, 0, &actualLength);
280 if (result != B_OK) {
281 TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
282 return result;
285 snooze(150000);
287 uSWReset = SW_RESET_PRL | SW_RESET_BIT6;
288 result = gUSBModule->send_request(fDevice,
289 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_SOFT_RESET,
290 uSWReset, 0, 0, 0, &actualLength);
292 if (result != B_OK) {
293 TRACE_ALWAYS("Error of SW reset to %#02x: %#010x\n", uSWReset, result);
294 return result;
297 snooze(150000);
299 result = WriteRXControlRegister(0);
300 if (result != B_OK) {
301 TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n", 0, result);
302 return result;
305 result = fMII.SetupPHY();
307 TRACE_RET(result);
308 return result;
312 status_t
313 AX88178Device::StartDevice()
315 size_t actualLength = 0;
316 status_t result = gUSBModule->send_request(fDevice,
317 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, WRITE_IPGS,
318 0, 0, sizeof(fIPG), fIPG, &actualLength);
320 if (result != B_OK) {
321 TRACE_ALWAYS("Error of writing IPGs:%#010x\n", result);
322 return result;
325 if (actualLength != sizeof(fIPG)) {
326 TRACE_ALWAYS("Mismatch of written IPGs data. "
327 "%d bytes of %d written.\n", actualLength, sizeof(fIPG));
330 uint16 rxcontrol = RXCTL_START | RXCTL_BROADCAST;
331 result = WriteRXControlRegister(rxcontrol);
332 if (result != B_OK) {
333 TRACE_ALWAYS("Error of writing %#04x RX Control:%#010x\n",
334 rxcontrol, result);
337 TRACE_RET(result);
338 return result;
342 status_t
343 AX88178Device::OnNotify(uint32 actualLength)
345 if (actualLength < sizeof(AX88178_Notify)) {
346 TRACE_ALWAYS("Data underrun error. %d of %d bytes received\n",
347 actualLength, sizeof(AX88178_Notify));
348 return B_BAD_DATA;
351 AX88178_Notify *notification = (AX88178_Notify *)fNotifyBuffer;
353 if (notification->btA1 != 0xa1) {
354 TRACE_ALWAYS("Notify magic byte is invalid: %#02x\n",
355 notification->btA1);
358 uint phyIndex = 0;
359 bool linkIsUp = fHasConnection;
360 switch(fMII.ActivePHY()) {
361 case PrimaryPHY:
362 phyIndex = 1;
363 linkIsUp = (notification->btBB & LINK_STATE_PPLS)
364 == LINK_STATE_PPLS;
365 break;
366 case SecondaryPHY:
367 phyIndex = 2;
368 linkIsUp = (notification->btBB & LINK_STATE_SPLS)
369 == LINK_STATE_SPLS;
370 break;
371 default:
372 case CurrentPHY:
373 TRACE_ALWAYS("Error: PHY is not initialized.\n");
374 return B_NO_INIT;
377 bool linkStateChange = linkIsUp != fHasConnection;
378 fHasConnection = linkIsUp;
380 if (linkStateChange) {
381 TRACE("Link state of PHY%d has been changed to '%s'\n",
382 phyIndex, fHasConnection ? "up" : "down");
385 if (linkStateChange && fLinkStateChangeSem >= B_OK)
386 release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
388 return B_OK;
392 status_t
393 AX88178Device::GetLinkState(ether_link_state *linkState)
395 size_t actualLength = 0;
396 uint16 mediumStatus = 0;
397 status_t result = gUSBModule->send_request(fDevice,
398 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_MEDIUM_STATUS,
399 0, 0, sizeof(mediumStatus), &mediumStatus, &actualLength);
401 if (result != B_OK) {
402 TRACE_ALWAYS("Error of reading medium status:%#010x.\n", result);
403 return result;
406 if (actualLength != sizeof(mediumStatus)) {
407 TRACE_ALWAYS("Mismatch of reading medium status."
408 "Read %d bytes instead of %d\n",
409 actualLength, sizeof(mediumStatus));
412 TRACE_FLOW("Medium status is %#04x\n", mediumStatus);
414 linkState->quality = 1000;
416 linkState->media = IFM_ETHER | (fHasConnection ? IFM_ACTIVE : 0);
417 linkState->media |= (mediumStatus & MEDIUM_STATE_FD)
418 ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX;
420 linkState->speed = (mediumStatus & MEDIUM_STATE_PS_100)
421 ? 100000000 : 10000000;
422 linkState->speed = (mediumStatus & MEDIUM_STATE_GM)
423 ? 1000000000 : linkState->speed;
425 TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
426 (linkState->media & IFM_ACTIVE) ? "active" : "inactive",
427 linkState->speed / 1000000,
428 (linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
429 return B_OK;